The Student
My late entry to the competition, spawned originally out of a simple desire to beat naiive, which has been remarkably successful. Eventually it morphed into this monstrosity that attempts to predict opponent moves and tracks likelihood of a some strategies I or people I talked to thought of.
import random
def strategize(grid, store):
possible_round_results = [
((0, 0), (grid[0][0], grid[0][0]), 0),
((0, 1), (grid[0][1], grid[1][0]), grid[0][1] - grid[1][0]),
((0, 2), (grid[0][2], grid[2][0]), grid[0][2] - grid[2][0]),
((1, 0), (grid[1][0], grid[0][1]), grid[1][0] - grid[0][1]),
((1, 1), (grid[1][1], grid[1][1]), 0),
((1, 2), (grid[1][2], grid[2][1]), grid[1][2] - grid[2][1]),
((2, 0), (grid[2][0], grid[0][2]), grid[2][0] - grid[0][2]),
((2, 1), (grid[2][1], grid[1][2]), grid[2][1] - grid[1][2]),
((2, 2), (grid[2][2], grid[2][2]), 0)
]
# Interpret data from Store to try to determine opponent and optimize
if('facing_self' in store and store['facing_self']):
# Determine move combination that gives the highest total points
max_score_result_index = max([(x[1][0] + x[1][1], i) for (i, x) in enumerate(possible_round_results)])[1]
if(store['should_pick_low'] is None):
random.seed()
return possible_round_results[max_score_result_index][0][random.randrange(0,2)]
elif(store['should_pick_low']):
return max(possible_round_results[max_score_result_index][0])
else:
return min(possible_round_results[max_score_result_index][0])
opponent_move = -1
if('opponent_strategy_probabilities' in store):
opponent_probabilities = [(store['opponent_strategy_probabilities'][x], x) for x in store['opponent_strategy_probabilities']]
opponent_probabilities.remove((store['opponent_strategy_probabilities']['self'], 'self'))
most_likely_opponent = max(opponent_probabilities)[1]
if(most_likely_opponent == 'safe_scorer'):
# safe_scorer
opponent_move = 0
safe_moves = []
for i in range(0,9,3):
if(min([possible_round_results[i][2], possible_round_results[i+1][2], possible_round_results[i+2][2]]) >= 0):
safe_moves.append((possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]))
if(len(safe_moves) > 0):
opponent_move = max(safe_moves)[1]
else:
#resort to naiive_score_differential after loosing out on safety
opponent_move = max([(possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]) for i in range(0, 9, 3)])[1]
elif(most_likely_opponent == 'hurtful'):
# hurtful
opponent_move = min([(sum(x), i) for (i, x) in enumerate(grid)])[1]
elif(most_likely_opponent == 'cooperative'):
# cooperative
opponent_move = max([(x[i], i) for (i, x) in enumerate(grid)])[1]
elif(most_likely_opponent == 'minimize_losses'):
# minimize_losses
opponent_move = max([(min(x), i) for (i, x) in enumerate(grid)])[1]
elif(most_likely_opponent == 'highest_expected_val'):
# highest_expected_val
opponent_move = max([(x[2], x[0][1]) for x in possible_round_results])[1]
elif(most_likely_opponent == 'naiive_score_differential'):
# naiive_scored_differential
opponent_move = max([(possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]) for i in range(0, 9, 3)])[1]
else:
# naiive
opponent_move = max([(sum(x), i) for (i, x) in enumerate(grid)])[1]
else:
#assumed naiive
opponent_move = max([(sum(x), i) for (i, x) in enumerate(grid)])[1]
score_margin = 10
if('my_score' in store and store['my_score'] >= store['opponent_score'] + score_margin):
return max([(x[opponent_move], i) for (i, x) in enumerate(grid)])[1]
else:
possible_moves = []
for result in possible_round_results:
if(result[0][1] == opponent_move):
possible_moves.append(result)
return max([(x[2], i) for (i, x) in enumerate(possible_moves)])[1]
def interpret(grid, moves, store):
possible_round_results = [
((0, 0), (grid[0][0], grid[0][0]), 0),
((0, 1), (grid[0][1], grid[1][0]), grid[0][1] - grid[1][0]),
((0, 2), (grid[0][2], grid[2][0]), grid[0][2] - grid[2][0]),
((1, 0), (grid[1][0], grid[0][1]), grid[1][0] - grid[0][1]),
((1, 1), (grid[1][1], grid[1][1]), 0),
((1, 2), (grid[1][2], grid[2][1]), grid[1][2] - grid[2][1]),
((2, 0), (grid[2][0], grid[0][2]), grid[2][0] - grid[0][2]),
((2, 1), (grid[2][1], grid[1][2]), grid[2][1] - grid[1][2]),
((2, 2), (grid[2][2], grid[2][2]), 0)
]
# Estimate probability that opponent is using a specific strategy
naiive_move = max([(sum(x), i) for (i, x) in enumerate(grid)])[1]
naiive_score_differnetial_move = max([(possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]) for i in range(0, 9, 3)])[1]
cooperative_move = max([(x[i], i) for (i, x) in enumerate(grid)])[1]
minimize_losses_move = max([(min(x), i) for (i, x) in enumerate(grid)])[1]
highest_expected_val_move = max([(x[2], x[0][1]) for x in possible_round_results])[1]
hurtful_move = min([(sum(x), i) for (i, x) in enumerate(grid)])[1]
safe_scorer_move = 0
safe_moves = []
for i in range(0,9,3):
if(min([possible_round_results[i][2], possible_round_results[i+1][2], possible_round_results[i+2][2]]) >= 0):
safe_moves.append((possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]))
if(len(safe_moves) > 0):
safe_scorer_move = max(safe_moves)[1]
else:
#resort to naiive_score_differential after loosing out on safety
safe_scorer_move = max([(possible_round_results[i][2] + possible_round_results[i+1][2] + possible_round_results[i+2][2], possible_round_results[i][0][0]) for i in range(0, 9, 3)])[1]
self_move = None
if(('facing_self' in store) and (store['facing_self'])):
#find the two possibilities that might have been used, compare to move, and set to move if it matches to maintain self-facing status
max_score_result_index = max([(x[1][0] + x[1][1], i) for (i, x) in enumerate(possible_round_results)])[1]
possible_moves = possible_round_results[max_score_result_index][0]
if(moves[1] == possible_moves[0] or moves[1] == possible_moves[1]):
self_move = moves[1]
if(moves[0] < moves[1] and store['should_pick_low'] is None):
store['should_pick_low'] = True
elif(moves[0] > moves[1] and store['should_pick_low'] is None):
store['should_pick_low'] = False
else:
self_move = -1
else:
self_move = strategize(grid, store)
# Populate default values for store
if(not 'num_turns' in store):
store['num_turns'] = 0
store['my_score'] = 0
store['opponent_score'] = 0
store['opponent_strategy_probabilities'] = {
'self': 0,
'naiive': 0, # pick the row with the highest sum
'naiive_score_differential': 0, # pick the row with the highest average point gain over opponent
'hurtful': 0, # try to give opponent the column with lowest sum
'cooperative': 0, # try to behave cooperatively
'minimize_losses': 0, # pick row with highest minimum
'highest_expected_val': 0, # pick row with highest sum of score differences
'safe_scorer': 0 # determine rows with safety, then pick row with highest score differential
}
store['facing_self'] = False
store['should_pick_low'] = None
store['num_turns'] += 1
store['my_score'] += grid[moves[0]][moves[1]]
store['opponent_score'] += grid[moves[1]][moves[0]]
# Used to tune adaptation of bot
most_recent_weighting = 5
if(moves[1] == naiive_move):
store['opponent_strategy_probabilities']['naiive'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['naiive'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['naiive'] = ((store['opponent_strategy_probabilities']['naiive'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == naiive_score_differnetial_move):
store['opponent_strategy_probabilities']['naiive_score_differential'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['naiive_score_differential'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['naiive_score_differential'] = ((store['opponent_strategy_probabilities']['naiive_score_differential'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == cooperative_move):
store['opponent_strategy_probabilities']['cooperative'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['cooperative'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['cooperative'] = ((store['opponent_strategy_probabilities']['cooperative'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == minimize_losses_move):
store['opponent_strategy_probabilities']['minimize_losses'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['minimize_losses'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['minimize_losses'] = ((store['opponent_strategy_probabilities']['minimize_losses'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == highest_expected_val_move):
store['opponent_strategy_probabilities']['highest_expected_val'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['highest_expected_val'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['highest_expected_val'] = ((store['opponent_strategy_probabilities']['highest_expected_val'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == hurtful_move):
store['opponent_strategy_probabilities']['hurtful'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['hurtful'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['hurtful'] = ((store['opponent_strategy_probabilities']['hurtful'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == safe_scorer_move):
store['opponent_strategy_probabilities']['safe_scorer'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['safe_scorer'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['safe_scorer'] = ((store['opponent_strategy_probabilities']['safe_scorer'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(moves[1] == self_move):
store['opponent_strategy_probabilities']['self'] = (most_recent_weighting + (store['opponent_strategy_probabilities']['self'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
else:
store['opponent_strategy_probabilities']['self'] = ((store['opponent_strategy_probabilities']['self'] * (store['num_turns'] - 1))) / (most_recent_weighting + store['num_turns'] - 1)
if(store['opponent_strategy_probabilities']['self'] == 1 and store['num_turns'] > 4):
store['facing_self'] = True
else:
store['facing_self'] = False
store
is a totally unnecessary \$\endgroup\$