Fix code quality issues from PR review

- Add matplotlib.use("Agg") backend configuration to prevent runtime errors in headless environments
- Add error handling guards for script results that might return None
- Fix AI targeting logic to exclude already-fired cells in super hunter and hunter modes
- Update CLAUDE.md with matplotlib best practices
This commit is contained in:
Russell Ballestrini 2025-08-10 16:01:58 -04:00
parent 4d909aaecb
commit 1ca6f67c3d
7 changed files with 38 additions and 23 deletions

View file

@ -19,4 +19,7 @@
## Documentation
- Update relevant documentation when making significant changes
- Keep README files current with new features or setup changes
- Document any new environment variables or configuration options
- Document any new environment variables or configuration options
## Python/Matplotlib Best Practices
- Always add `matplotlib.use("Agg")` before importing matplotlib.pyplot to prevent runtime errors in headless environments

4
app.py
View file

@ -1744,7 +1744,7 @@ def handle_activity_response(room_name, user_response, username):
temp_metadata["user_response"] = user_response
pre_result = execute_processing_script(
temp_metadata, step["pre_script"]
)
) or {}
# Update metadata with pre-script results
for key, value in pre_result.get("metadata", {}).items():
activity_state.add_metadata(key, value)
@ -1990,7 +1990,7 @@ def handle_activity_response(room_name, user_response, username):
print(f"DEBUG: Executing post-script")
result = execute_processing_script(
activity_state.dict_metadata, post_script
)
) or {}
plot_image_base64 = result.pop("plot_image", None)

View file

@ -2,6 +2,8 @@ default_max_attempts_per_step: 3
# Common processing script for all plotting steps
common_processing_script: &plotting_script |
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot
import numpy
import io

View file

@ -59,6 +59,8 @@ sections:
def plot_board(board, win_line=None):
import io
import base64
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 3))

View file

@ -111,6 +111,8 @@ sections:
If game_over = True, suggest: "Would you like to restart and play again, or would you prefer to exit?"
processing_script: |
import random
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import io
import base64

View file

@ -205,6 +205,8 @@ sections:
processing_script: |
import random
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import io
import base64
@ -564,19 +566,20 @@ sections:
max_prob = 0
candidates = []
for i in range(100):
x, y = i % 10, i // 10
if probability_matrix[y][x] > max_prob:
max_prob = probability_matrix[y][x]
candidates = [i]
elif probability_matrix[y][x] == max_prob:
candidates.append(i)
if i not in ai_shots: # Exclude already-fired cells
x, y = i % 10, i // 10
if probability_matrix[y][x] > max_prob:
max_prob = probability_matrix[y][x]
candidates = [i]
elif probability_matrix[y][x] == max_prob:
candidates.append(i)
ai_shot = random.choice(candidates)
elif ai_mode == "hunter":
# Simple hunter mode logic
if hits:
# Target adjacent cells of the last hit
last_hit = hits[-1]
hunt_targets = generate_hunt_targets(last_hit, ai_hits)
hunt_targets = generate_hunt_targets(last_hit, ai_shots)
if hunt_targets:
ai_shot = hunt_targets.pop(0)
else:
@ -609,7 +612,7 @@ sections:
return random.choice(available_positions)
# Function to generate hunt targets around a hit
def generate_hunt_targets(hit_position, ai_hits):
def generate_hunt_targets(hit_position, ai_shots):
potential_targets = []
row, col = divmod(hit_position, 10)
@ -626,10 +629,10 @@ sections:
if col < 9:
potential_targets.append(hit_position + 1)
# Filter out already hit positions
# Filter out already fired positions
filtered_targets = []
for pos in potential_targets:
if pos not in ai_hits:
if pos not in ai_shots:
filtered_targets.append(pos)
return filtered_targets

View file

@ -183,6 +183,8 @@ sections:
processing_script: |
import random
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import io
import base64
@ -534,19 +536,20 @@ sections:
max_prob = 0
candidates = []
for i in range(100):
x, y = i % 10, i // 10
if probability_matrix[y][x] > max_prob:
max_prob = probability_matrix[y][x]
candidates = [i]
elif probability_matrix[y][x] == max_prob:
candidates.append(i)
if i not in ai_shots: # Exclude already-fired cells
x, y = i % 10, i // 10
if probability_matrix[y][x] > max_prob:
max_prob = probability_matrix[y][x]
candidates = [i]
elif probability_matrix[y][x] == max_prob:
candidates.append(i)
ai_shot = random.choice(candidates)
elif ai_mode == "hunter":
# Simple hunter mode logic
if hits:
# Target adjacent cells of the last hit
last_hit = hits[-1]
hunt_targets = generate_hunt_targets(last_hit, ai_hits)
hunt_targets = generate_hunt_targets(last_hit, ai_shots)
if hunt_targets:
ai_shot = hunt_targets.pop(0)
else:
@ -579,7 +582,7 @@ sections:
return random.choice(available_positions)
# Function to generate hunt targets around a hit
def generate_hunt_targets(hit_position, ai_hits):
def generate_hunt_targets(hit_position, ai_shots):
potential_targets = []
row, col = divmod(hit_position, 10)
@ -596,10 +599,10 @@ sections:
if col < 9:
potential_targets.append(hit_position + 1)
# Filter out already hit positions
# Filter out already fired positions
filtered_targets = []
for pos in potential_targets:
if pos not in ai_hits:
if pos not in ai_shots:
filtered_targets.append(pos)
return filtered_targets