opencompletion.com/research/activity27-tic-tac-toe.yaml
Russell Ballestrini 1ca6f67c3d 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
2025-08-10 16:01:58 -04:00

204 lines
7.8 KiB
YAML

default_max_attempts_per_step: 9
sections:
- section_id: "section_1"
title: "Tic Tac Toe"
steps:
- step_id: "step_0"
title: "Introduction"
content_blocks:
- |
Welcome to Tic Tac Toe! 🎮
You will be playing against the AI. You are 'X' and the AI is 'O'.
The board positions are numbered 0 to 8 as follows:
<img src="/static/images/tic-tac-toe.png">
- step_id: "step_1"
title: "Your Move"
question: "Enter a position number (0-8) to place your 'X'. Say restart or exit to quit."
tokens_for_ai: |
Using the metadata, determine if the game is over and 'restart'.
If the user wants to restart or play again, categorize as 'restart'
If ai_wins or user_wins or is_draw is true, categorize as 'restart'.
If the user wants to exit, categorize as 'exit'.
If the game_over is True categorize as 'restart'.
Finally check:
If the move is valid, categorize as 'valid_move'.
If the move is invalid, categorize as 'invalid_move'.
feedback_tokens_for_ai: |
Always speak in first person. DO NOT START WITH "ai_move:".
Player is always X, You the AI are always O.
If there is an error in the metadata the move was likely invalid.
On a new line, provide feedback on the user's move.
Only announce a winner or tie if game_over is True.
The player makes the first and last move.
If the move is invalid, prompt the user to try again.
If the move is invalid, give a list of valid moves.
If the move is valid & no errors say your move on the last line (ai_move) for example: I move to 8 and draw a O".
processing_script: |
import random
win_conditions = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], # rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], # columns
[0, 4, 8], [2, 4, 6] # diagonals
]
def check_win(board, player, win_conditions):
# Check for win and return the winning condition if there is one
for condition in win_conditions:
win = True
for i in condition:
if board[i] != player:
win = False
break
if win:
return condition
return None
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))
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
ax.set_xticks([])
ax.set_yticks([])
ax.grid(True)
for i, mark in enumerate(board):
x = i % 3
y = 2 - i // 3
if mark != " ":
ax.text(x + 0.5, y + 0.5, mark, fontsize=24, ha='center', va='center')
else:
# Plot the cell number if the cell is empty
ax.text(x + 0.5, y + 0.5, str(i), fontsize=12, ha='center', va='center', color='gray')
# Draw the winning line if there is one
if win_line:
for i in range(len(win_line) - 1):
start = win_line[i]
end = win_line[i + 1]
x_start, y_start = start % 3 + 0.5, 2 - start // 3 + 0.5
x_end, y_end = end % 3 + 0.5, 2 - end // 3 + 0.5
ax.plot([x_start, x_end], [y_start, y_end], 'r-', linewidth=2)
buf = io.BytesIO()
plt.savefig(buf, format='png')
plt.close(fig)
buf.seek(0)
return base64.b64encode(buf.getvalue()).decode('utf-8')
# Reconstruct the board from moves
user_moves = metadata.get("user_moves", [])
ai_moves = metadata.get("ai_moves", [])
ai_move = None
board = [" "] * 9
for move in user_moves:
board[int(move)] = "X"
for move in ai_moves:
board[int(move)] = "O"
# Get the user's latest move
try:
user_move = int(metadata.get("user_move"))
except (IndexError, ValueError) as e:
# Remove the invalid move from user_moves
user_move = -1
# Check if the move is valid
if 0 <= user_move < 9 and board[user_move] == " ":
board[user_move] = "X"
user_moves.append(user_move)
user_win_line = check_win(board, "X", win_conditions)
if not user_win_line:
# ai makes a move.
available_positions = []
for i in range(len(board)):
if board[i] == " ":
available_positions.append(i)
if available_positions:
ai_move = random.choice(available_positions)
board[ai_move] = "O"
ai_moves.append(ai_move)
ai_win_line = check_win(board, "O", win_conditions)
is_draw = True
for x in board:
if x == " ":
is_draw = False
break
game_over = any([user_win_line, ai_win_line, is_draw])
win_line = user_win_line if user_win_line else ai_win_line
script_result = {
"plot_image": plot_board(board, win_line),
"set_background": not game_over,
"ai_move": ai_move,
"user_move": user_move,
"metadata": {
"user_moves": user_moves,
"ai_moves": ai_moves,
"board": board,
"game_over": game_over,
"ai_wins": ai_win_line is not None,
"user_wins": user_win_line is not None,
"is_draw": is_draw
}
}
else:
script_result = {
"error": f"Invalid move: {metadata.get('user_move')}",
"metadata": {
"user_moves": user_moves,
},
}
# Debugging: Print the current board state
print("Current board state:", board)
buckets:
- valid_move
- invalid_move
- restart
- exit
transitions:
valid_move:
run_processing_script: True
ai_feedback:
tokens_for_ai: |
at first glance it seems like a valid user_move.
DO NOT:
* DRAW THE GAME BOARD
* DESCRIBE THE GAME BOARD
metadata_tmp_add:
user_move: "the-users-response"
next_section_and_step: "section_1:step_1"
invalid_move:
ai_feedback:
tokens_for_ai: "That move is invalid. Please choose an empty position between 0 and 8."
metadata_tmp_add:
user_move: "the-users-response"
next_section_and_step: "section_1:step_1"
exit:
next_section_and_step: "section_1:step_2"
restart:
ai_feedback:
tokens_for_ai: "Restarting the game. Let's start fresh!"
metadata_clear: True
next_section_and_step: "section_1:step_0"
- step_id: "step_2"
title: "Goodbye"
content_blocks:
- "Thank you for playing Tic Tac Toe! 🎉"
- "Feel free to come back anytime for another game."