opencompletion.com/research/activity29-battleship.yaml
Russell Ballestrini b257f766f8 Rubric for battleship ending.
modified:   research/activity29-battleship.yaml
2024-09-16 11:59:56 -04:00

559 lines
22 KiB
YAML

default_max_attempts_per_step: 9
tokens_for_ai_rubric: |
based on the game without knowing where each ship was, score the process each player used to target ships.
be sure to look for moves or strategies in the game play that where _not_ smart given the obvious information uncovered.
use chain-of-thought to reason about the progression of the game and the winner.
first summarize the game, we don't need the turn by turn plays.
the game was battleship. the moves were done 1 by 1.
the grid is 0-99.
did any player blunder as the information was learned?
There was a user and an AI playing.
Depending on the game mode the player chooses they are going up against a different algo,
* random
* always plays randomly
* hunter
* keeps track of hits and targets every cell around it no matter what, randomly, else random
* super human hunter
* keeps track of hits and uses a probability grid normalized to 100 and always picks the max or random of any 100.
Did any player miss sinking a ship that was found? was it due to end game or a blunder?
Do not mix up ships, keep careful track of the order they were found and sunk.
sections:
- section_id: "section_1"
title: "Battleship"
steps:
- step_id: "step_0"
title: "Introduction"
content_blocks:
- |
Welcome to Battleship! 🚢
In this game, both you and the AI have a fleet of ships placed randomly on a 10x10 grid.
The grid positions are numbered 0 to 99.
Your goal is to sink all of the AI's ships before it sinks yours.
Let's get started!
- step_id: "step_1"
title: "Choose AI Mode"
question: "Choose the AI mode: Random, Hunter, or Super Human Hunter?"
tokens_for_ai: |
If the user chooses Random, categorize as 'random_mode'.
If the user chooses Hunter, categorize as 'hunter_mode'.
If the user chooses Super Human Hunter, categorize as 'super_hunter_mode'.
feedback_tokens_for_ai: |
If the user chooses Random, acknowledge the choice.
If the user chooses Hunter, acknowledge the choice.
If the user chooses Super Human Hunter, acknowledge the choice.
processing_script: |
import random
def place_ships():
global random
# Define ship sizes and names
ships = {
"Carrier": 5,
"Battleship": 4,
"Cruiser": 3,
"Submarine": 3,
"Destroyer": 2
}
board = [-1] * 100
for ship, size in ships.items():
placed = False
while not placed:
orientation = random.choice(['horizontal', 'vertical'])
if orientation == 'horizontal':
row = random.randint(0, 9)
col = random.randint(0, 9 - size)
start = row * 10 + col
if all(board[start + i] == -1 for i in range(size)):
for i in range(size):
board[start + i] = ship
placed = True
else:
row = random.randint(0, 9 - size)
col = random.randint(0, 9)
start = row * 10 + col
if all(board[start + i * 10] == -1 for i in range(size)):
for i in range(size):
board[start + i * 10] = ship
placed = True
return board
user_board = place_ships()
ai_board = place_ships()
script_result = {
"metadata": {
"user_board": user_board,
"ai_board": ai_board
}
}
buckets:
- random_mode
- hunter_mode
- super_hunter_mode
transitions:
random_mode:
run_processing_script: True
ai_feedback:
tokens_for_ai: "Random Mode enabled for the AI."
metadata_add:
ai_mode: "random"
next_section_and_step: "section_1:step_2"
hunter_mode:
run_processing_script: True
ai_feedback:
tokens_for_ai: "Hunter Mode enabled for the AI."
metadata_add:
ai_mode: "hunter"
next_section_and_step: "section_1:step_2"
super_hunter_mode:
run_processing_script: True
ai_feedback:
tokens_for_ai: "Super Human Hunter Mode enabled for the AI."
metadata_add:
ai_mode: "super_hunter"
next_section_and_step: "section_1:step_2"
- step_id: "step_2"
title: "Take a Shot"
question: "Choose a position to fire at (0-99)."
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: |
Important: Use the metadata to fill in the brackets and provide a conversational tone.
On a new line, announce the user's move and provide feedback.
The user's latest shot was [user_hit_result]:
- If user_hit_result is "hit", consider saying: "Great shot! [user_name] hit an AI ship!"
- If user_hit_result is "miss", consider saying: "Oh no, [user_name] missed the shot. Better luck next time!"
On a new line, announce the AI's move: "The AI fired at position [ai_shot] and it was a [ai_hit_result]."
The AI's latest shot was a [ai_hit_result]:
- If ai_hit_result is "hit", consider saying: "The AI hit one of [user_name]'s ships!"
- If ai_hit_result is "miss", consider saying: "The AI missed [user_name]'s ships this time."
If either [user_sunk_ship_this_round] or [ai_sunk_ship_this_round] is not None,
announce the destruction in a LOT of detail, use many sentences:
- If user_sunk_ship_this_round is not None, consider saying: "The user has sunk the AI's [user_sunk_ship_this_round]!"
- If ai_sunk_ship_this_round is not None, consider saying: "The AI has sunk the [user_name]'s [ai_sunk_ship_this_round]!"
If game_over = True, determine the winner:
- If user_wins = True, consider saying: "Congratulations! The [user_name] has sunk all AI ships and won the game!"
- If ai_wins = True, consider saying: "The AI has sunk all [user_name] ships and won the game!"
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.pyplot as plt
import io
import base64
# Define ship sizes
ship_sizes = {
"Carrier": 5,
"Battleship": 4,
"Cruiser": 3,
"Submarine": 3,
"Destroyer": 2
}
# Define colors for ships
ship_colors = {
"Carrier": "blue",
"Battleship": "green",
"Cruiser": "orange",
"Submarine": "purple",
"Destroyer": "pink"
}
# Retrieve the game state
user_board = metadata.get("user_board")
ai_board = metadata.get("ai_board")
user_shots = metadata.get("user_shots", [])
ai_shots = metadata.get("ai_shots", [])
user_hits = metadata.get("user_hits", [])
ai_hits = metadata.get("ai_hits", [])
game_over = metadata.get("game_over", False)
user_wins = False
ai_wins = False
user_hit_result = "miss"
ai_hit_result = "miss"
user_sunk_ships = metadata.get("user_sunk_ships", [])
ai_sunk_ships = metadata.get("ai_sunk_ships", [])
user_sunk_ship_this_round = None
ai_sunk_ship_this_round = None
# AI state variables
ai_mode = metadata.get("ai_mode", "random")
probability_matrix = metadata.get("probability_matrix", [[1] * 10 for _ in range(10)])
hits = metadata.get("hits", [])
misses = metadata.get("misses", [])
sunk_ships = metadata.get("sunk_ships", [])
# Function to check if a ship is sunk
def check_sunk(board, hits, ship_name):
ship_positions = []
for i, ship in enumerate(board):
if ship == ship_name:
ship_positions.append(i)
for pos in ship_positions:
if pos not in hits:
return False
return True
# Function to draw a line across a sunken ship
def draw_line(ax, board, ship_name):
ship_positions = []
for i, ship in enumerate(board):
if ship == ship_name:
ship_positions.append(i)
if not ship_positions:
return
# Determine if the ship is horizontal or vertical
first_pos = ship_positions[0]
last_pos = ship_positions[-1]
if last_pos - first_pos < 10: # Horizontal
x_start, y_start = first_pos % 10 + 0.5, 9 - first_pos // 10 + 0.5
x_end, y_end = last_pos % 10 + 0.5, 9 - last_pos // 10 + 0.5
else: # Vertical
x_start, y_start = first_pos % 10 + 0.5, 9 - first_pos // 10 + 0.5
x_end, y_end = first_pos % 10 + 0.5, 9 - last_pos // 10 + 0.5
ax.plot([x_start, x_end], [y_start, y_end], color='red', linewidth=2)
# Function to update probability matrix
def update_probability(x, y, hit):
global probability_matrix, hits, misses, sunk_ships, ship_sizes
if hit:
hits.append((x, y))
probability_matrix[y][x] = 0 # Mark hit
# Increase probabilities for adjacent cells
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
nx, ny = x + dx, y + dy
if 0 <= nx < 10 and 0 <= ny < 10 and probability_matrix[ny][nx] > 0:
probability_matrix[ny][nx] += 5 # Increase probability significantly
else:
misses.append((x, y))
probability_matrix[y][x] = -1 # Mark miss
# Set probabilities to 1 for cells that can't fit any remaining ships
max_ship_size = max(size for ship, size in ship_sizes.items() if ship not in sunk_ships)
for y in range(10):
for x in range(10):
if probability_matrix[y][x] > 0 and not can_fit_ship(x, y, max_ship_size):
probability_matrix[y][x] = 1 # Minimum probability
# Function to check if a ship can fit
def can_fit_ship(x, y, ship_size):
# Check horizontal fit
if x + ship_size <= 10:
fit = True
for i in range(ship_size):
if probability_matrix[y][x+i] <= 0:
fit = False
break
if fit:
return True
# Check vertical fit
if y + ship_size <= 10:
fit = True
for i in range(ship_size):
if probability_matrix[y+i][x] <= 0:
fit = False
break
if fit:
return True
return False
# AI chooses a shot
def choose_ai_shot():
global can_fit_ship, update_probability, generate_hunt_targets, random_search, probability_matrix, ai_mode, ai_shots, random, user_board, ai_hits, ai_hit_result
if ai_mode == "super_hunter":
# Use probabilistic grid algorithm
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)
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)
if hunt_targets:
ai_shot = hunt_targets.pop(0)
else:
ai_shot = random_search()
else:
ai_shot = random_search()
else:
# Random mode
ai_shot = random_search()
# Update AI state after the shot
if user_board[ai_shot] != -1:
ai_hits.append(ai_shot)
ai_hit_result = "hit"
if ai_mode == "super_hunter":
update_probability(ai_shot % 10, ai_shot // 10, True)
else:
ai_hit_result = "miss"
if ai_mode == "super_hunter":
update_probability(ai_shot % 10, ai_shot // 10, False)
return ai_shot
# Function for random search
def random_search():
available_positions = []
for i in range(100):
if i not in ai_shots:
available_positions.append(i)
return random.choice(available_positions)
# Function to generate hunt targets around a hit
def generate_hunt_targets(hit_position, ai_hits):
potential_targets = []
row, col = divmod(hit_position, 10)
# Up
if row > 0:
potential_targets.append(hit_position - 10)
# Down
if row < 9:
potential_targets.append(hit_position + 10)
# Left
if col > 0:
potential_targets.append(hit_position - 1)
# Right
if col < 9:
potential_targets.append(hit_position + 1)
# Filter out already hit positions
filtered_targets = []
for pos in potential_targets:
if pos not in ai_hits:
filtered_targets.append(pos)
return filtered_targets
# Get the user's shot
try:
user_shot = int(metadata.get("user_shot"))
except (IndexError, ValueError) as e:
user_shot = -1
if game_over:
script_result = {}
elif 0 <= user_shot < 100 and user_shot not in user_shots:
# The move is valid
user_shots.append(user_shot)
user_hit_result = "miss"
if ai_board[user_shot] != -1:
user_hits.append(user_shot)
user_hit_result = "hit"
# AI makes a move
ai_shot = choose_ai_shot()
ai_shots.append(ai_shot)
# Check if any AI ship is sunk
for ship_name in ship_sizes.keys():
if check_sunk(ai_board, user_hits, ship_name) and ship_name not in user_sunk_ships:
user_sunk_ships.append(ship_name)
user_sunk_ship_this_round = ship_name
# Check if any User ship is sunk
for ship_name in ship_sizes.keys():
if check_sunk(user_board, ai_hits, ship_name) and ship_name not in ai_sunk_ships:
ai_sunk_ships.append(ship_name)
ai_sunk_ship_this_round = ship_name
# Check if all AI ships are hit
all_ai_ships_hit = True
for pos in range(100):
if ai_board[pos] != -1 and pos not in user_hits:
all_ai_ships_hit = False
break
if all_ai_ships_hit:
game_over = True
user_wins = True
ai_wins = False
# Check if all User ships are hit
all_user_ships_hit = True
for pos in range(100):
if user_board[pos] != -1 and pos not in ai_hits:
all_user_ships_hit = False
break
if all_user_ships_hit:
game_over = True
user_wins = False
ai_wins = True
# Plot the boards
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
fig.suptitle("Battleship", fontsize=16)
# User's view of AI's board
axs[0].set_xlim(0, 10)
axs[0].set_ylim(0, 10)
axs[0].set_xticks([])
axs[0].set_yticks([])
axs[0].grid(True)
axs[0].set_title("Your Shots", fontsize=12)
# Plot user shots on AI's board
for i in range(100):
x, y = i % 10, 9 - i // 10
if i in user_shots:
if i in user_hits:
axs[0].text(x + 0.5, y + 0.5, 'X', fontsize=12, ha='center', va='center', color='red')
else:
axs[0].text(x + 0.5, y + 0.5, 'O', fontsize=12, ha='center', va='center', color='black')
axs[0].text(x + 0.5, y + 0.5, str(i), fontsize=8, ha='center', va='center', color='gray')
# AI's view of User's board
axs[1].set_xlim(0, 10)
axs[1].set_ylim(0, 10)
axs[1].set_xticks([])
axs[1].set_yticks([])
axs[1].grid(True)
axs[1].set_title("Your Ships", fontsize=12)
# Plot user ships
for i, ship in enumerate(user_board):
x, y = i % 10, 9 - i // 10
if ship != -1:
axs[1].add_patch(plt.Rectangle((x, y), 1, 1, color=ship_colors[ship], alpha=0.5))
# Plot AI shots on User's board
for i in range(100):
x, y = i % 10, 9 - i // 10
if i in ai_shots:
if i in ai_hits:
axs[1].text(x + 0.5, y + 0.5, 'X', fontsize=12, ha='center', va='center', color='red')
else:
axs[1].text(x + 0.5, y + 0.5, 'O', fontsize=12, ha='center', va='center', color='black')
axs[1].text(x + 0.5, y + 0.5, str(i), fontsize=8, ha='center', va='center', color='gray')
# Draw lines across sunk ships
for ship_name in user_sunk_ships:
draw_line(axs[0], ai_board, ship_name)
for ship_name in ai_sunk_ships:
draw_line(axs[1], user_board, ship_name)
# Add legend
handles = []
for color in ship_colors.values():
handles.append(plt.Rectangle((0, 0), 1, 1, color=color, alpha=0.5))
axs[1].legend(handles, ship_colors.keys(), loc='upper right', fontsize=8)
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')
# gpt-4: If "plot_image" is in the result, set it as the background image
script_result = {
"plot_image": plot_image,
"set_background": True,
"metadata": {
"user_board": user_board,
"ai_board": ai_board,
"user_shot": user_shot,
"ai_shot": ai_shot,
"user_shots": user_shots,
"ai_shots": ai_shots,
"user_hits": user_hits,
"ai_hits": ai_hits,
"game_over": game_over,
"user_wins": user_wins,
"ai_wins": ai_wins,
"user_hit_result": user_hit_result,
"ai_hit_result": ai_hit_result,
"user_sunk_ships": user_sunk_ships,
"ai_sunk_ships": ai_sunk_ships,
"user_sunk_ship_this_round": user_sunk_ship_this_round,
"ai_sunk_ship_this_round": ai_sunk_ship_this_round,
"ai_mode": ai_mode,
"probability_matrix": probability_matrix,
"hits": hits,
"misses": misses,
"sunk_ships": sunk_ships
}
}
else:
script_result = {
"error": f"Invalid shot: {metadata.get('user_shot')}",
"metadata": {}
}
buckets:
- valid_move
- invalid_move
- exit
- restart
transitions:
valid_move:
run_processing_script: True
ai_feedback:
tokens_for_ai: |
The user shot seems valid.
metadata_tmp_add:
user_shot: "the-users-response"
next_section_and_step: "section_1:step_2"
invalid_move:
content_blocks:
- "That move is invalid. Please choose a position between 0 and 99."
metadata_tmp_add:
user_shot: "the-users-response"
next_section_and_step: "section_1:step_2"
exit:
next_section_and_step: "section_1:step_3"
restart:
content_blocks:
- "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 Battleship! 🎉"
- "Feel free to come back anytime for another game."