opencompletion.com/research/activity28-killer-squares.yaml
Russell Ballestrini 51b74be7d9 Fix YAML validator and activity file validation errors
- Updated validator terminal step detection to only flag truly terminal steps
- Fixed validator to accept integers and booleans in buckets (as supported by app.py)
- Fixed metadata_remove format in activity17 from dictionary to list of strings
- Added proper terminal section to activity3.yaml without questions/buckets
- Fixed missing restart transition and bucket in activity28
- Removed unused game_end transitions from battleship files
- Updated exit transitions to go directly to step_4 (goodbye step)
- Applied black formatting to validator code

All 30 activity YAML files now validate successfully with 0 errors and 0 warnings.
2025-08-10 19:38:50 -04:00

279 lines
11 KiB
YAML

default_max_attempts_per_step: 9
sections:
- section_id: "section_1"
title: "Killer Squares"
steps:
- step_id: "step_0"
title: "Introduction"
content_blocks:
- |
Welcome to Killer Squares! 🎮
In this game, both you and the AI will secretly choose a square.
Then, you will attempt to "kill" a square. If you hit the AI's secret spot, you win!
If the AI hits your secret spot, you lose. If nobody hits, the game continues.
The board positions are numbered 0 to 8 as follows:
```
0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8
```
- step_id: "step_1"
title: "Choose Your Secret Spot"
question: "Choose a secret spot (0-8) for this round."
tokens_for_ai: |
If the user wants to exit, categorize as 'exit'.
If the move is valid, categorize as 'valid_move'.
If the move is invalid, categorize as 'invalid_move'.
feedback_tokens_for_ai: |
DO NOT TELL THE AI SECRET.
If there is an error in the metadata the move was likely invalid.
Always speak in first person. DO NOT START WITH "ai_move:".
On a new line, provide feedback on the user's move.
If the move is valid, proceed to the next step.
If the move is invalid, prompt the user to try again.
processing_script: |
import random
# Initialize or retrieve the game state
user_secret = metadata.get("user_secret", None)
ai_secret = random.randint(0, 8)
# Get the user's secret spot
try:
user_secret = int(metadata.get("user_secret"))
except (IndexError, ValueError) as e:
user_secret = -1
# Check if the move is valid
if 0 <= user_secret < 9:
script_result = {
"metadata": {
"user_secret": user_secret,
"ai_secret": ai_secret,
}
}
else:
script_result = {
"error": f"Invalid secret spot: {metadata.get('user_secret')}",
"metadata": {}
}
buckets:
- valid_move
- invalid_move
- restart
- exit
transitions:
valid_move:
run_processing_script: True
ai_feedback:
tokens_for_ai: "You've chosen your secret spot. Now, let's move to the killing round."
metadata_add:
user_secret: "the-users-response"
next_section_and_step: "section_1:step_2"
invalid_move:
ai_feedback:
tokens_for_ai: "That move is invalid. Please choose a position between 0 and 8."
metadata_add:
user_secret: "the-users-response"
next_section_and_step: "section_1:step_1"
exit:
next_section_and_step: "section_1:step_3"
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: "Kill a Square"
question: "Choose a square to kill (0-8)."
tokens_for_ai: |
If the user wants to restart or play again, categorize as 'restart'.
If the user wants to exit, categorize as 'exit'.
If the move is valid, categorize as 'valid_move'.
If the move is invalid, categorize as 'invalid_move'.
feedback_tokens_for_ai: |
DO NOT reveal the AI's secret spot until game_over = True.
ALWAYS speak in first person. DO NOT START WITH "ai_move:".
If there is an error in the metadata, the move was likely invalid.
On a new line, provide feedback on the user's move:
- If the move is valid, check if the user's shot hit my (AI's) secret spot (ai_secret).
- If the user's shot hits my secret spot, say: "You hit my secret spot!"
- If the user's shot misses, say: "You missed my secret spot."
If the move is invalid, prompt the user to try again.
My move is the last item in the ai_shots list. For example, if ai_shots = [5, 3], my move is 3.
Announce my move: "I shoot at position [my move]."
If game_over = True, determine the winner:
- If user_wins = True, say: "Congratulations! You hit my secret spot and won the round!"
- If ai_wins = True, say: "I hit your secret spot and won the round!"
If game_over = True, describe the carnage of the final strike.
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
# Retrieve the game state
user_secret = metadata.get("user_secret")
ai_secret = metadata.get("ai_secret")
user_shots = metadata.get("user_shots", [])
ai_shots = metadata.get("ai_shots", [])
game_over = metadata.get("game_over", False)
# Get the user's kill move
try:
user_kill = int(metadata.get("user_kill"))
except (IndexError, ValueError) as e:
user_kill = -1
if game_over:
script_result = {}
elif 0 <= user_kill < 9:
# the move is valid.
user_shots.append(user_kill)
if user_kill == ai_secret:
game_over = True
user_wins = True
ai_wins = False
draw = False
user_title = "You Win!"
ai_title = "AI's Moves"
else:
# AI makes a move, avoiding its own secret spot
available_positions = []
for i in range(9):
if i not in ai_shots and i != ai_secret:
available_positions.append(i)
ai_kill = random.choice(available_positions) if available_positions else None
if ai_kill is not None:
ai_shots.append(ai_kill)
if ai_kill == user_secret:
game_over = True
user_wins = False
ai_wins = True
draw = False
user_title = "Your Moves"
ai_title = "AI Wins!"
else:
game_over = False
user_wins = False
ai_wins = False
draw = False
user_title = "Your Moves"
ai_title = "AI's Moves"
else:
game_over = True
user_wins = False
ai_wins = False
draw = True
user_title = "Your Moves"
ai_title = "It's a Draw!"
# Plot the boards
fig, axs = plt.subplots(1, 2, figsize=(6, 3))
fig.suptitle("Killer Squares", fontsize=16)
fig.tight_layout(h_pad=4)
# User's board
axs[0].set_xlim(0, 3)
axs[0].set_ylim(0, 3)
axs[0].set_xticks([])
axs[0].set_yticks([])
axs[0].grid(True)
axs[0].set_title(user_title, fontsize=12)
for i in range(9):
x = i % 3
y = 2 - i // 3
axs[0].text(x + 0.5, y + 0.5, str(i), fontsize=12, ha='center', va='center', color='gray')
for user_kill in user_shots:
ux, uy = user_kill % 3, 2 - user_kill // 3
axs[0].text(ux + 0.5, uy + 0.5, 'X', fontsize=24, ha='center', va='center', color='red')
# AI's board
axs[1].set_xlim(0, 3)
axs[1].set_ylim(0, 3)
axs[1].set_xticks([])
axs[1].set_yticks([])
axs[1].grid(True)
axs[1].set_title(ai_title, fontsize=12)
for i in range(9):
x = i % 3
y = 2 - i // 3
axs[1].text(x + 0.5, y + 0.5, str(i), fontsize=12, ha='center', va='center', color='gray')
for ai_kill in ai_shots:
axx, axy = ai_kill % 3, 2 - ai_kill // 3
axs[1].text(axx + 0.5, axy + 0.5, 'X', fontsize=24, ha='center', va='center', color='blue')
buf = io.BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0.1)
plt.close(fig)
buf.seek(0)
plot_image = base64.b64encode(buf.getvalue()).decode('utf-8')
script_result = {
"plot_image": plot_image,
"metadata": {
"user_secret": user_secret,
"ai_secret": ai_secret,
"user_shots": user_shots,
"ai_shots": ai_shots,
"game_over": game_over,
"user_wins": user_wins,
"ai_wins": ai_wins,
"draw": draw,
}
}
else:
script_result = {
"error": f"Invalid kill move: {metadata.get('user_kill')}",
"metadata": {}
}
buckets:
- valid_move
- invalid_move
- exit
- restart
transitions:
valid_move:
run_processing_script: True
ai_feedback:
tokens_for_ai: |
If somebody wins explain the move that triggered the kill shot.
Only if game_over is True reveal the ai secret spot number otherwise never tell the player the secret!
metadata_tmp_add:
user_kill: "the-users-response"
next_section_and_step: "section_1:step_2"
invalid_move:
ai_feedback:
tokens_for_ai: "That move is invalid. Please choose a position between 0 and 8."
metadata_tmp_add:
user_kill: "the-users-response"
next_section_and_step: "section_1:step_2"
exit:
next_section_and_step: "section_1:step_3"
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_3"
title: "Goodbye"
content_blocks:
- "Thank you for playing Killer Squares! 🎉"
- "Feel free to come back anytime for another game."