opencompletion.com/research/activity29-battleship.yaml
Russell Ballestrini 9246565407 battleship, super human hunter mode
modified:   research/activity29-battleship.yaml
2024-09-09 07:12:58 -04:00

525 lines
21 KiB
YAML

default_max_attempts_per_step: 9
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."