model_decision_functions.py
model_decision_functions.py
#
TODO add quick explanation of this library
"""
Functions for converting counts to probabilities, evaluating agent decision and outcomes
"""
# from utils import *
from random import choices
# Basic 3-column probability calculator
# TODO consider revising args to be cols=[], names=[]
def add_prob(df, col_1, col_2, col_3, name1, name2, name3):
"""
Add probability values as new columns
"""
count_1 = df[col_1]
count_2 = df[col_2]
count_3 = df[col_3]
total = count_1 + count_2 + count_3
prob_1 = count_1 / total
prob_2 = count_2 / total
prob_3 = count_3 / total
w_prob = df
w_prob = add_col(w_prob, [name1], prob_1)
w_prob = add_col(w_prob, [name2], prob_2)
w_prob = add_col(w_prob, [name3], prob_3)
return w_prob
# Below supports outcome and dual transition probabilities
def calculate_prob(num_1, num_2, num_3):
"""Calculating 3 probabilities"""
total = num_1 + num_2 + num_3
prob_1 = num_1 / total
prob_2 = num_2 / total
prob_3 = num_3 / total
prob_list = [prob_1, prob_2, prob_3]
return prob_list
################################
### Expected value functions ###
################################
def calculate_move_ev(df, agent_move):
"""Compute expected values for agent_move"""
if agent_move == 'rock':
ev = (df['p_opponent_rock'] * 0) + (df['p_opponent_paper'] * -1) + (df['p_opponent_scissors'] * 3)
elif agent_move == 'paper':
ev = (df['p_opponent_rock'] * 3) + (df['p_opponent_paper'] * 0) + (df['p_opponent_scissors'] * -1)
elif agent_move == 'scissors' :
ev = (df['p_opponent_rock'] * -1) + (df['p_opponent_paper'] * 3) + (df['p_opponent_scissors'] * 0)
return ev
def ev_move_baserate(df_agent):
df_agent['ev_move_baserate_rock'] = calculate_move_ev(df_agent, 'rock')
df_agent['ev_move_baserate_paper'] = calculate_move_ev(df_agent, 'paper')
df_agent['ev_move_baserate_scissors'] = calculate_move_ev(df_agent, 'scissors')
return df_agent
def ev_general(df_agent, agent_move, ev_col_name, prob1, prob2, prob3, move):
#inputs: dataframe, agent move (string), new col name (string), three probability vectors, move (string): either opponent_previous_move or previous_move(cournot)
"""more general ev function: used for EV of outcome transition and dual dependency transitions"""
df_agent['filler_1'] = points_lookup(agent_move, transition_move_lookup("transition_up", df_agent[move])).tolist()
df_agent['filler_2'] = points_lookup(agent_move, transition_move_lookup("transition_down", df_agent[move])).tolist()
df_agent['filler_3'] = points_lookup(agent_move, transition_move_lookup("transition_stay", df_agent[move])).tolist()
df_agent['prod_1'] = df_agent[prob1] * df_agent.filler_1
df_agent['prod_2'] = df_agent[prob2] * df_agent.filler_2
df_agent['prod_3'] = df_agent[prob3] * df_agent.filler_3
df_agent['ev_transitions'] = df_agent['prod_1'] + df_agent['prod_2'] + df_agent['prod_3']
df_agent[ev_col_name] = df_agent['ev_transitions']
df_agent = df_agent.drop(['filler_1', 'filler_2', 'filler_3',
'prod_1', 'prod_2', 'prod_3', 'ev_transitions'], axis=1)
return df_agent
def ev_transitions(df_agent):
df_agent = ev_general(df_agent, 'rock', 'ev_transition_rock', 'p_opponent_transition_up', 'p_opponent_transition_down', 'p_opponent_transition_stay', 'opponent_previous_move')
df_agent = ev_general(df_agent, 'paper', 'ev_transition_paper', 'p_opponent_transition_up', 'p_opponent_transition_down', 'p_opponent_transition_stay', 'opponent_previous_move')
df_agent = ev_general(df_agent, 'scissors', 'ev_transition_scissors', 'p_opponent_transition_up', 'p_opponent_transition_down', 'p_opponent_transition_stay', 'opponent_previous_move')
return df_agent
def ev_cournot(df_agent):
df_agent = ev_general(df_agent, 'rock', 'ev_cournot_transition_rock', 'p_opponent_cournot_transition_up', 'p_opponent_cournot_transition_down', 'p_opponent_cournot_transition_stay', 'previous_move')
df_agent = ev_general(df_agent, 'paper', 'ev_cournot_transition_paper', 'p_opponent_cournot_transition_up', 'p_opponent_cournot_transition_down', 'p_opponent_cournot_transition_stay', 'previous_move')
df_agent = ev_general(df_agent, 'scissors', 'ev_cournot_transition_scissors', 'p_opponent_cournot_transition_up', 'p_opponent_cournot_transition_down', 'p_opponent_cournot_transition_stay', 'previous_move')
return df_agent
def ev_previous_outcome(df_agent):
"""adds 3 ev columns based on previous outcomes"""
df_agent['key'] = list(range(len(df_agent)))
df_wins = df_agent[df_agent['opponent_previous_outcome']=='win']
df_loss = df_agent[df_agent['opponent_previous_outcome']=='loss']
df_tie = df_agent[df_agent['opponent_previous_outcome']=='tie']
#adding columns: w/ ev of rock, paper, scissors
df_wins = ev_general(df_wins, 'rock', 'ev_outcome_transition_rock', 'p_opponent_win_up', 'p_opponent_win_down', 'p_opponent_win_stay', 'opponent_previous_move')
df_wins = ev_general(df_wins, 'paper', 'ev_outcome_transition_paper', 'p_opponent_win_up', 'p_opponent_win_down', 'p_opponent_win_stay', 'opponent_previous_move')
df_wins = ev_general(df_wins, 'scissors', 'ev_outcome_transition_scissors', 'p_opponent_win_up', 'p_opponent_win_down', 'p_opponent_win_stay', 'opponent_previous_move')
#adding columns: w/ ev of rock, paper, scissors
df_loss = ev_general(df_loss, 'rock', 'ev_outcome_transition_rock', 'p_opponent_loss_up', 'p_opponent_loss_down', 'p_opponent_loss_stay', 'opponent_previous_move')
df_loss = ev_general(df_loss, 'paper','ev_outcome_transition_paper', 'p_opponent_loss_up', 'p_opponent_loss_down', 'p_opponent_loss_stay', 'opponent_previous_move')
df_loss = ev_general(df_loss, 'scissors', 'ev_outcome_transition_scissors','p_opponent_loss_up', 'p_opponent_loss_down', 'p_opponent_loss_stay', 'opponent_previous_move')
#adding columns: w/ ev of rock, paper, scissors
df_tie = ev_general(df_tie, 'rock', 'ev_outcome_transition_rock', 'p_opponent_tie_up', 'p_opponent_tie_down', 'p_opponent_tie_stay', 'opponent_previous_move')
df_tie = ev_general(df_tie, 'paper', 'ev_outcome_transition_paper', 'p_opponent_tie_up', 'p_opponent_tie_down', 'p_opponent_tie_stay', 'opponent_previous_move')
df_tie = ev_general(df_tie, 'scissors', 'ev_outcome_transition_scissors', 'p_opponent_tie_up', 'p_opponent_tie_down', 'p_opponent_tie_stay', 'opponent_previous_move')
#merging tables
stacked_unordered = pd.concat([df_wins, df_loss, df_tie])
stacked_ordered = stacked_unordered.sort_values(by=['key'], ascending=True)
df_agent = df_agent.merge(stacked_ordered, 'outer')
df_agent = df_agent.drop(['key'], axis=1)
return df_agent
def ev_previous_outcome_previous_transition(df_agent):
"""adds 3 ev columns based on previous outcome and previous transition"""
df_agent['key'] = list(range(len(df_agent)))
df_wins_up = df_agent[(df_agent['opponent_previous_outcome']=='win') & (df_agent['opponent_previous_transition']=='up')]
df_wins_down = df_agent[(df_agent['opponent_previous_outcome']=='win') & (df_agent['opponent_previous_transition']=='down')]
df_wins_stay = df_agent[(df_agent['opponent_previous_outcome']=='win') & (df_agent['opponent_previous_transition']=='stay')]
df_loss_up = df_agent[(df_agent['opponent_previous_outcome']=='loss') & (df_agent['opponent_previous_transition']=='up')]
df_loss_down = df_agent[(df_agent['opponent_previous_outcome']=='loss') & (df_agent['opponent_previous_transition']=='down')]
df_loss_stay = df_agent[(df_agent['opponent_previous_outcome']=='loss') & (df_agent['opponent_previous_transition']=='stay')]
df_tie_up = df_agent[(df_agent['opponent_previous_outcome']=='tie') & (df_agent['opponent_previous_transition']=='up')]
df_tie_down = df_agent[(df_agent['opponent_previous_outcome']=='tie') & (df_agent['opponent_previous_transition']=='down')]
df_tie_stay = df_agent[(df_agent['opponent_previous_outcome']=='tie') & (df_agent['opponent_previous_transition']=='stay')]
df_wins_up = ev_general(df_wins_up, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_up_win_up', 'p_opponent_up_win_down', 'p_opponent_up_win_stay', 'opponent_previous_move')
df_wins_up = ev_general(df_wins_up, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_up_win_up', 'p_opponent_up_win_down', 'p_opponent_up_win_stay', 'opponent_previous_move')
df_wins_up = ev_general(df_wins_up, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_up_win_up', 'p_opponent_up_win_down', 'p_opponent_up_win_stay', 'opponent_previous_move')
df_wins_down = ev_general(df_wins_down, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_down_win_up', 'p_opponent_down_win_down', 'p_opponent_down_win_stay', 'opponent_previous_move')
df_wins_down = ev_general(df_wins_down, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_down_win_up', 'p_opponent_down_win_down', 'p_opponent_down_win_stay', 'opponent_previous_move')
df_wins_down = ev_general(df_wins_down, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_down_win_up', 'p_opponent_down_win_down', 'p_opponent_down_win_stay', 'opponent_previous_move')
df_wins_stay = ev_general(df_wins_stay, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_stay_win_up', 'p_opponent_stay_win_down', 'p_opponent_stay_win_stay', 'opponent_previous_move')
df_wins_stay = ev_general(df_wins_stay, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_stay_win_up', 'p_opponent_stay_win_down', 'p_opponent_stay_win_stay', 'opponent_previous_move')
df_wins_stay = ev_general(df_wins_stay, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_stay_win_up', 'p_opponent_stay_win_down', 'p_opponent_stay_win_stay', 'opponent_previous_move')
df_loss_up = ev_general(df_loss_up, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_up_loss_up', 'p_opponent_up_loss_down', 'p_opponent_up_loss_stay', 'opponent_previous_move')
df_loss_up = ev_general(df_loss_up, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_up_loss_up', 'p_opponent_up_loss_down', 'p_opponent_up_loss_stay', 'opponent_previous_move')
df_loss_up = ev_general(df_loss_up, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_up_loss_up', 'p_opponent_up_loss_down', 'p_opponent_up_loss_stay', 'opponent_previous_move')
df_loss_down = ev_general(df_loss_down, 'rock','ev_outcome_dual_depend_rock', 'p_opponent_down_loss_up', 'p_opponent_down_loss_down', 'p_opponent_down_loss_stay', 'opponent_previous_move')
df_loss_down = ev_general(df_loss_down, 'paper','ev_outcome_dual_depend_paper', 'p_opponent_down_loss_up', 'p_opponent_down_loss_down', 'p_opponent_down_loss_stay', 'opponent_previous_move')
df_loss_down = ev_general(df_loss_down, 'scissors','ev_outcome_dual_depend_scissors', 'p_opponent_down_loss_up', 'p_opponent_down_loss_down', 'p_opponent_down_loss_stay', 'opponent_previous_move')
df_loss_stay = ev_general(df_loss_stay, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_stay_loss_up', 'p_opponent_stay_loss_down', 'p_opponent_stay_loss_stay', 'opponent_previous_move')
df_loss_stay = ev_general(df_loss_stay, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_stay_loss_up', 'p_opponent_stay_loss_down', 'p_opponent_stay_loss_stay', 'opponent_previous_move')
df_loss_stay = ev_general(df_loss_stay, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_stay_loss_up', 'p_opponent_stay_loss_down', 'p_opponent_stay_loss_stay', 'opponent_previous_move')
df_tie_up = ev_general(df_tie_up, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_up_tie_up', 'p_opponent_up_tie_down', 'p_opponent_up_tie_stay', 'opponent_previous_move')
df_tie_up = ev_general(df_tie_up, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_up_tie_up', 'p_opponent_up_tie_down', 'p_opponent_up_tie_stay', 'opponent_previous_move')
df_tie_up = ev_general(df_tie_up, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_up_tie_up', 'p_opponent_up_tie_down', 'p_opponent_up_tie_stay', 'opponent_previous_move')
df_tie_down = ev_general(df_tie_down, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_down_tie_up', 'p_opponent_down_tie_down', 'p_opponent_down_tie_stay', 'opponent_previous_move')
df_tie_down = ev_general(df_tie_down, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_down_tie_up', 'p_opponent_down_tie_down', 'p_opponent_down_tie_stay', 'opponent_previous_move')
df_tie_down = ev_general(df_tie_down, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_down_tie_up', 'p_opponent_down_tie_down', 'p_opponent_down_tie_stay', 'opponent_previous_move')
df_tie_stay = ev_general(df_tie_stay, 'rock', 'ev_outcome_dual_depend_rock', 'p_opponent_stay_tie_up', 'p_opponent_stay_tie_down', 'p_opponent_stay_tie_stay', 'opponent_previous_move')
df_tie_stay = ev_general(df_tie_stay, 'paper', 'ev_outcome_dual_depend_paper', 'p_opponent_stay_tie_up', 'p_opponent_stay_tie_down', 'p_opponent_stay_tie_stay', 'opponent_previous_move')
df_tie_stay = ev_general(df_tie_stay, 'scissors', 'ev_outcome_dual_depend_scissors', 'p_opponent_stay_tie_up', 'p_opponent_stay_tie_down', 'p_opponent_stay_tie_stay', 'opponent_previous_move')
#merging tables
stacked_unordered = pd.concat([
df_wins_up, df_wins_down, df_wins_stay,
df_loss_up, df_loss_down, df_loss_stay,
df_tie_up, df_tie_down, df_tie_stay
])
stacked_ordered = stacked_unordered.sort_values(by=['key'], ascending=True)
df_agent = df_agent.merge(stacked_ordered, 'outer')
df_agent = df_agent.drop(['key'], axis=1)
return df_agent
#############################
### Move choice functions ###
#############################
def softmax(x, beta = 1):
"""Compute softmax values for each sets of scores in x."""
return np.exp(x * beta) / np.sum(np.exp(x * beta), axis=0)
def get_softmax_probabilities(df, columns):
distribution = []
# ev = df[['ev_agent_rock', 'ev_agent_paper', 'ev_agent_scissors']]
ev = df[columns]
for i in range(df.shape[0]):
soft_max = softmax(ev.iloc[i], beta = 4).tolist() # add aggressive beta term to make max probability move more likely
distribution.append(soft_max)
dist = np.array(distribution)
sofm = pd.DataFrame(dist, columns = ['softmax_prob_rock', 'softmax_prob_paper', 'softmax_prob_scissors'])
return sofm
def pick_move(df, sofm):
moves = np.array([])
for i in range(df.shape[0]):
move_choices = ['rock', 'paper', 'scissors']
distribution = sofm.iloc[i].tolist()
chosen_move = choices(move_choices, distribution)
moves = np.append(moves, chosen_move)
df = df.assign(agent_move = moves)
return df
def assign_agent_outcomes(df):
"""
Assign outcomes for the agent based on agent move choices.
df should include only human rows, since agent outcomes are irrelevant for simulating bots
"""
df.assign(agent_outcome = '')
df['agent_outcome'] = df.apply(lambda x: evaluate_outcome(x['agent_move'], x['opponent_move']), axis=1)
return df