import datetime import json # from django.db import transaction from otree.api import * import random author = "ccrabbe@gmail.com" doc = """ A graphical market for kids """ class C(BaseConstants): NAME_IN_URL = 'mk' PLAYERS_PER_GROUP = None NUM_ROUNDS = 7 class Subsession(BaseSubsession): num_token_colors = models.IntegerField() token_color_1 = models.StringField() token_color_2 = models.StringField() token_color_3 = models.StringField() token_color_4 = models.StringField() subsession_pk = models.IntegerField() message_display_length = models.IntegerField() market_length = models.IntegerField() show_stop_button = models.BooleanField() max_rounds = models.IntegerField() payoff_type = models.StringField() practice_round = models.BooleanField() bundle_value = models.FloatField() def creating_session(subsession: Subsession): if subsession.round_number == 1: subsession.group_randomly() else: group_players_swap_roles(subsession) subsession.subsession_pk = subsession.id if 'mk_show_stop_button' in subsession.session.config: subsession.show_stop_button = subsession.session.config['mk_show_stop_button'] if 'mk_use_practice_round' in subsession.session.config and subsession.session.config['mk_use_practice_round'] and subsession.round_number == 1: subsession.practice_round = True else: subsession.practice_round = False if 'mk_bundle_value' in subsession.session.config: subsession.bundle_value = subsession.session.config['mk_bundle_value'] if 'mk_market_length_seconds' in subsession.session.config: subsession.market_length = subsession.session.config['mk_market_length_seconds'] if subsession.round_number == 1 and subsession.practice_round and 'mk_practice_market_length_seconds' in subsession.session.config: subsession.market_length = subsession.session.config['mk_practice_market_length_seconds'] if 'mk_payoff_type' in subsession.session.config: subsession.payoff_type = subsession.session.config['mk_payoff_type'] if 'mk_message_display_length' in subsession.session.config: subsession.message_display_length = subsession.session.config['mk_message_display_length'] if 'mk_max_rounds' in subsession.session.config: subsession.max_rounds = subsession.session.config['mk_max_rounds'] if 'mk_num_token_colors' in subsession.session.config: subsession.num_token_colors = subsession.session.config['mk_num_token_colors'] if 'mk_token_colors' in subsession.session.config: colors_list = subsession.session.config['mk_token_colors'].split(",") print("colors_list= "+str(colors_list)) subsession.token_color_1 = colors_list[0] subsession.token_color_2 = colors_list[1] subsession.token_color_3 = colors_list[2] if subsession.num_token_colors == 4: subsession.token_color_4 = colors_list[3] else: subsession.token_color_4 = '#000' tuple_string = subsession.session.config['mk_initial_states'] print("tuple_string=",tuple_string) tuple_list = json.loads(tuple_string) print("tuple_list=",tuple_list) if len(tuple_list) != len(subsession.get_players()): raise Exception("length of mk_initial_status is ",len(tuple_list),", but number of players is ",subsession.get_players()) if subsession.round_number == 1: subsession.session.vars['initial_type_order'] = [p[0] for p in tuple_list] # print("intiial_type_order=",subsession.session.vars['initial_type_order']) if subsession.payoff_type == 'random': if subsession.round_number == 1: if subsession.practice_round: randomly_paid_round = random.randint(2, subsession.max_rounds) else: randomly_paid_round = random.randint(1, subsession.max_rounds) for p in subsession.get_players(): p.randomly_paid_round = randomly_paid_round else: for p in subsession.get_players(): p.randomly_paid_round = p.in_round(1).field_maybe_none('randomly_paid_round') for p in subsession.get_players(): pindex = p.id_in_group-1 p.initial_type = tuple_list[pindex][0] p.participant.vars['initial_type'] = p.initial_type p.initial_token_1 = tuple_list[pindex][1] p.initial_token_2 = tuple_list[pindex][2] p.initial_token_3 = tuple_list[pindex][3] p.initial_token_4 = tuple_list[pindex][4] reset_participant_vars(p) p.participant.vars['wallet_0.'+str(subsession.round_number)] = int(p.initial_token_1) p.participant.vars['wallet_1.'+str(subsession.round_number)] = int(p.initial_token_2) p.participant.vars['wallet_2.'+str(subsession.round_number)] = int(p.initial_token_3) p.participant.vars['wallet_3.'+str(subsession.round_number)] = int(p.initial_token_4) p.participant.vars['initial_wallet.'+str(subsession.round_number)] = json.dumps([p.participant.vars['wallet_0.'+str(subsession.round_number)], p.participant.vars['wallet_1.'+str(subsession.round_number)], p.participant.vars['wallet_2.'+str(subsession.round_number)], p.participant.vars['wallet_3.'+str(subsession.round_number)]]) # print("ROUND 1 CREATING SESSION, p.participant.vars=",p.participant.vars) else: for p in subsession.get_players(): p.randomly_paid_round = p.in_round(1).field_maybe_none('randomly_paid_round') # p.initial_token_1 = p.in_round(1).initial_token_1 # p.initial_token_2 = p.in_round(1).initial_token_2 # p.initial_token_3 = p.in_round(1).initial_token_3 # p.initial_token_4 = p.in_round(1).initial_token_4 set_initial_groups(subsession) # if subsession.round_number <= subsession.max_rounds: # pass def group_players_swap_roles(subsession: Subsession): old_groupings = subsession.in_round(1).get_group_matrix() type_order = subsession.session.vars['initial_type_order'] old_p_types = [] old_c_types = [] for g in old_groupings: for index in range(len(g)): if type_order[index] == 'p': old_p_types.append(g[index]) else: old_c_types.append(g[index]) print("g=",g) print("type_order=",type_order) print("old_p_types=",old_p_types) print("old_c_types=",old_c_types) new_groups = [] if subsession.round_number%2==1: for g in old_groupings: group = [] for index in range(len(g)): if type_order[index] == 'p': if len(old_p_types) > 0: choice = random.choice(old_p_types) group.append(choice) old_p_types.remove(choice) else: choice = random.choice(old_c_types) group.append(choice) old_c_types.remove(choice) else: if len(old_c_types) > 0: choice = random.choice(old_c_types) group.append(choice) old_c_types.remove(choice) else: choice = random.choice(old_p_types) group.append(choice) old_p_types.remove(choice) new_groups.append(group) else: for g in old_groupings: group = [] for index in range(len(g)): if type_order[index] == 'p': if len(old_c_types) > 0: choice = random.choice(old_c_types) group.append(choice) old_c_types.remove(choice) else: choice = random.choice(old_p_types) group.append(choice) old_p_types.remove(choice) else: if len(old_p_types) > 0: choice = random.choice(old_p_types) group.append(choice) old_p_types.remove(choice) else: choice = random.choice(old_c_types) group.append(choice) old_c_types.remove(choice) new_groups.append(group) print("round number ",subsession.round_number,", setting group_matrix=",new_groups) subsession.set_group_matrix(new_groups) class Group(BaseGroup): pass class Player(BasePlayer): name = models.StringField(label="Input your experimental number") arrived = models.BooleanField(initial=False) initial_token_1 = models.IntegerField() initial_token_2 = models.IntegerField() initial_token_3 = models.IntegerField() initial_token_4 = models.IntegerField() initial_type = models.StringField() final_token_1 = models.IntegerField() final_token_2 = models.IntegerField() final_token_3 = models.IntegerField() final_token_4 = models.IntegerField() randomly_paid_round = models.IntegerField() potential_payoff = models.FloatField() def set_payoff(player: Player): wallet = [player.final_token_2, player.final_token_3, player.final_token_4] min_value = 9999; for w in range(len(wallet)): if wallet[w] < min_value: min_value = wallet[w] token_value = player.subsession.bundle_value * min_value print("player=",player,", wallet_0=",player.final_token_1,", token_value=",token_value) payoff = player.final_token_1 + token_value if player.subsession.payoff_type == "random": if player.round_number == player.field_maybe_none('randomly_paid_round'): player.potential_payoff = payoff else: player.potential_payoff = payoff # only actually pay on non-practice rounds player.payoff = player.field_maybe_none('potential_payoff') if player.subsession.practice_round and player.round_number == 1: player.payoff = 0 def get_partner(player: Player): if player.participant.vars['current_partner.'+str(player.round_number)] > 0: return player.group.get_player_by_id(player.participant.vars['current_partner.'+str(player.round_number)]) else: return None # PAGES class Decision(Page): form_fields=[] form_model='player' @staticmethod def get_timeout_seconds(player: Player): return player.subsession.market_length @staticmethod def is_displayed(player: Player): return player.subsession.round_number <= player.subsession.max_rounds @staticmethod def before_next_page(player: Player, timeout_happened): # return offer amounts to the wallet if timeout_happened: player.participant.vars['wallet_0.'+str(player.round_number)] += player.participant.vars['offering_0.'+str(player.round_number)] player.participant.vars['wallet_1.'+str(player.round_number)] += player.participant.vars['offering_1.'+str(player.round_number)] player.participant.vars['wallet_2.'+str(player.round_number)] += player.participant.vars['offering_2.'+str(player.round_number)] player.participant.vars['wallet_3.'+str(player.round_number)] += player.participant.vars['offering_3.'+str(player.round_number)] player.final_token_1 = player.participant.vars['wallet_0.'+str(player.round_number)] player.final_token_2 = player.participant.vars['wallet_1.'+str(player.round_number)] player.final_token_3 = player.participant.vars['wallet_2.'+str(player.round_number)] player.final_token_4 = player.participant.vars['wallet_3.'+str(player.round_number)] set_payoff(player) def vars_for_template(player: Player): if 'time_started_round_'+str(player.round_number) not in player.session.vars: player.session.vars['time_started_round_'+str(player.round_number)] = datetime.datetime.now() rval = {} rval['state'] = get_market_state(player) if get_partner(player): rval['other_id'] = get_partner(player).id_in_group rval['partner'] = get_partner(player) else: rval['other_id'] = None rval['partner'] = None rval['timeLeft'] = player.subsession.market_length - (datetime.datetime.now()-player.session.vars['time_started_round_'+str(player.round_number)]).total_seconds() return rval def live_method(player: Player, data): group_object = player.group content = json.loads(data) print("content=",content) # print("content[0]=",content[0]) message_type = content["message_type"] # check the message_type - if we're doing more complicated things later different messages can mean different things # ... this helps me organize if message_type == 'offer_update': # make sure to do all these operations at once so that there's less chance of threading issues # with transaction.atomic(): group_object = player.group sender = player #group_object.get_player_by_id(player_id) receiver = get_partner(sender) color = content['color'] value = content['value'] # print("sender.participant.vars[\'wallet_\']=" + str( # sender.participant.vars['wallet_' + str(color)]) + ", value=" + str(value)) # print("sender.participant.vars[\'offering_\']=" + str( # sender.participant.vars['offering_' + str(color)]) + ", value=" + str(value)) if color == 'cash': if (sender.participant.vars['wallet_' + str(color)+'.'+str(sender.round_number)] - value >= 0) and ( sender.participant.vars['offering_' + str(color)+'.'+str(sender.round_number)] + value >= 0): # update the sender's offering state sender.participant.vars['offering_' + str(color)+'.'+str(sender.round_number)] += value # update the sender's wallet state sender.participant.vars['wallet_' + str(color)+'.'+str(sender.round_number)] -= value # update the receiver's offered state receiver.participant.vars['offered_' + str(color)+'.'+str(sender.round_number)] += value # sender.save() # receiver.save() print("saved sender and receiver") # prepare the message to send reply = { 'type': 'marketkids_message', 'message_type': 'offer_update', 'from': player.id_in_group, 'color': color, 'value': value, } reply['state_' + str(sender.id_in_group)] = get_market_state(sender) reply['state_' + str(receiver.id_in_group)] = get_market_state(receiver) Event.create( sender=sender, receiver=receiver, type='offer_update', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), offer_color=color, offer_amount=value, ) return ({player.id_in_group: reply, receiver.id_in_group: reply}) else: print("sender.participant.vars=",sender.participant.vars) if (sender.participant.vars['wallet_' + str(color)+'.'+str(sender.round_number)] - value >= 0) and ( sender.participant.vars['offering_' + str(color)+'.'+str(sender.round_number)] + value >= 0): # update the sender's offering state sender.participant.vars['offering_' + str(color)+'.'+str(sender.round_number)] += value # update the sender's wallet state sender.participant.vars['wallet_' + str(color)+'.'+str(sender.round_number)] -= value # update the receiver's offered state receiver.participant.vars['offered_' + str(color)+'.'+str(sender.round_number)] += value # sender.participant.save() # receiver.participant.save() print("saved sender and receiver") # prepare the message to send reply = { 'type': 'marketkids_message', 'message_type': 'offer_update', 'from': player.id_in_group, 'color': color, 'value': value, } reply['state_' + str(sender.id_in_group)] = get_market_state(sender) reply['state_' + str(receiver.id_in_group)] = get_market_state(receiver) Event.create( sender=sender, receiver=receiver, type='offer_update', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), offer_color=color, offer_amount=value, ) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) return({player.id_in_group: reply, receiver.id_in_group: reply}) elif message_type == 'exit_button_click': # make sure to do all these operations at once so that there's less chance of threading issues sender = player # group_object.get_player_by_id(player_id) receiver = get_partner(sender) sender.participant.vars['exit_clicked.'+str(sender.round_number)] = True print("player " + str(player.id_in_group) + "clicked their EXIT button") reply = { 'type': 'marketkids_message', 'message_type': 'exit_button_click', 'state_' + str(sender.id_in_group): get_market_state(sender), 'state_' + str(receiver.id_in_group): get_market_state(receiver), } Event.create( sender=sender, receiver=receiver, type='exit_button_click', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), ) if sender.participant.vars['exit_clicked.'+str(sender.round_number)] and receiver.participant.vars['exit_clicked.'+str(sender.round_number)]: # prepare the message to send reply['state_' + str(sender.id_in_group)] = get_market_state(sender) reply['state_' + str(receiver.id_in_group)] = get_market_state(receiver) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) return ({player.id_in_group: reply, receiver.id_in_group: reply}) elif message_type == 'check_button_click': # make sure to do all these operations at once so that there's less chance of threading issues sender = player #group_object.get_player_by_id(player_id) receiver = get_partner(sender) sender.participant.vars['check_clicked.'+str(sender.round_number)] = True # sender.save() # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) print("player " + str(player.id_in_group) + "clicked their CHECK button") reply = { 'type': 'marketkids_message', 'message_type': 'check_button_click', 'state_' + str(sender.id_in_group): get_market_state(sender), 'state_' + str(receiver.id_in_group): get_market_state(receiver), } Event.create( sender=sender, receiver=receiver, type='check_button_click', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), ) if sender.participant.vars['check_clicked.'+str(sender.round_number)] and receiver.participant.vars['check_clicked.'+str(sender.round_number)]: # then we send the message to activate both players' OK buttons sender.participant.vars['ok_enabled.'+str(sender.round_number)] = True receiver.participant.vars['ok_enabled.'+str(sender.round_number)] = True # sender.save() # receiver.save() # prepare the message to send reply['state_' + str(sender.id_in_group)]= get_market_state(sender) reply['state_' + str(receiver.id_in_group)]= get_market_state(receiver) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) return ({player.id_in_group: reply, receiver.id_in_group: reply}) elif message_type == 'x_button_click': # group_object = models_module.Group.objects.get(id=group_id) sender = player #group_object.get_player_by_id(player_id) receiver = get_partner(sender) sender_id = sender.id_in_group receiver_id = receiver.id_in_group sender.participant.vars['ok_clicked.'+str(sender.round_number)] = False sender.participant.vars['check_clicked.'+str(sender.round_number)] = False sender.participant.vars['ok_enabled.'+str(sender.round_number)] = False # sender.save() receiver.participant.vars['ok_clicked.'+str(sender.round_number)] = False receiver.participant.vars['check_clicked.'+str(sender.round_number)] = False receiver.participant.vars['ok_enabled.'+str(sender.round_number)] = False # receiver.save() print("player " + str(player.id_in_group) + "clicked their X button") reply = { 'type': 'marketkids_message', 'message_type': 'x_button_click', 'state_' + str(sender_id): get_market_state(sender), 'state_' + str(receiver_id): get_market_state(receiver), } Event.create( sender=sender, receiver=receiver, type='x_button_clicked', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), ) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) return ({player.id_in_group: reply, receiver.id_in_group: reply}) elif message_type == 'ok_button_click': sender = player #group_object.get_player_by_id(player_id) receiver = get_partner(sender) sender_id = sender.id_in_group receiver_id = receiver.id_in_group sender.participant.vars['ok_clicked.'+str(sender.round_number)] = True print("player " + str(player.id_in_group) + "clicked their OK button") reply = { 'type': 'marketkids_message', 'message_type': 'ok_button_click', 'state_' + str(sender_id): get_market_state(sender), 'state_' + str(receiver_id): get_market_state(receiver), } Event.create( sender=sender, receiver=receiver, type='ok_button_click', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), ) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) new_groups = [] if sender.participant.vars['ok_clicked.'+str(sender.round_number)] and receiver.participant.vars['ok_clicked.'+str(sender.round_number)]: # then the trade occurs, swap the contents of the wallet and tell the subjects to submit the form and move on player.session.vars['available_players.' + str(player.round_number)].append(sender.id_in_group) player.session.vars['available_players.' + str(player.round_number)].append(receiver.id_in_group) sender.participant.vars['wallet_0.'+str(sender.round_number)] += receiver.participant.vars['offering_0.'+str(sender.round_number)] sender.participant.vars['wallet_1.'+str(sender.round_number)] += receiver.participant.vars['offering_1.'+str(sender.round_number)] sender.participant.vars['wallet_2.'+str(sender.round_number)] += receiver.participant.vars['offering_2.'+str(sender.round_number)] sender.participant.vars['wallet_3.'+str(sender.round_number)] += receiver.participant.vars['offering_3.'+str(sender.round_number)] receiver.participant.vars['offering_0.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_1.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_2.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_3.'+str(sender.round_number)] = 0 receiver.participant.vars['wallet_0.'+str(sender.round_number)] += sender.participant.vars['offering_0.'+str(sender.round_number)] receiver.participant.vars['wallet_1.'+str(sender.round_number)] += sender.participant.vars['offering_1.'+str(sender.round_number)] receiver.participant.vars['wallet_2.'+str(sender.round_number)] += sender.participant.vars['offering_2.'+str(sender.round_number)] receiver.participant.vars['wallet_3.'+str(sender.round_number)] += sender.participant.vars['offering_3.'+str(sender.round_number)] sender.participant.vars['initial_wallet.'+str(sender.round_number)] = json.dumps([sender.participant.vars['wallet_0.'+str(sender.round_number)], sender.participant.vars['wallet_1.'+str(sender.round_number)], sender.participant.vars['wallet_2.'+str(sender.round_number)], sender.participant.vars['wallet_3.'+str(sender.round_number)]]) receiver.participant.vars['initial_wallet.'+str(sender.round_number)] = json.dumps([receiver.participant.vars['wallet_0.'+str(sender.round_number)], receiver.participant.vars['wallet_1.'+str(sender.round_number)], receiver.participant.vars['wallet_2.'+str(sender.round_number)], receiver.participant.vars['wallet_3.'+str(sender.round_number)]]) sender.participant.vars['offering_0.'+str(sender.round_number)] = 0 sender.participant.vars['offering_1.'+str(sender.round_number)] = 0 sender.participant.vars['offering_2.'+str(sender.round_number)] = 0 sender.participant.vars['offering_3.'+str(sender.round_number)] = 0 sender.participant.vars['offered_0.'+str(sender.round_number)] = 0 sender.participant.vars['offered_1.'+str(sender.round_number)] = 0 sender.participant.vars['offered_2.'+str(sender.round_number)] = 0 sender.participant.vars['offered_3.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_0.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_1.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_2.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_3.'+str(sender.round_number)] = 0 sender.participant.vars['ok_enabled.'+str(sender.round_number)] = False receiver.participant.vars['ok_enabled.'+str(sender.round_number)] = False sender.participant.vars['ok_clicked.'+str(sender.round_number)] = False receiver.participant.vars['ok_clicked.'+str(sender.round_number)] = False sender.participant.vars['check_clicked.'+str(sender.round_number)] = False receiver.participant.vars['check_clicked.'+str(sender.round_number)] = False sender.participant.vars['previous_partner.' + str(sender.round_number)] = sender.participant.vars['current_partner.' + str(sender.round_number)] receiver.participant.vars['previous_partner.' + str(sender.round_number)] = receiver.participant.vars['current_partner.' + str(sender.round_number)] sender.participant.vars['current_partner.'+str(sender.round_number)] = -1 receiver.participant.vars['current_partner.' + str(sender.round_number)] = -1 # sender.save() # receiver.save() # prepare the message to send print("sender_id=" + str(sender_id) + ", receiver_id=" + str(receiver_id)) reply['message_type'] = 'trade_occurs' reply['state_' + str(sender.id_in_group)] = get_market_state(sender) reply['state_' + str(receiver.id_in_group)] = get_market_state(receiver) timestamp=str(datetime.datetime.now()) Event.create( sender=sender, receiver=receiver, type='trade_occurs_sender', timestamp=timestamp, sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(reply['state_' + str(sender.id_in_group)]), ) Event.create( sender=sender, receiver=receiver, type='trade_occurs_receiver', timestamp=timestamp, sender_initial_type=receiver.initial_type, sender_initial_wallet=receiver.participant.vars['initial_wallet.' + str(sender.round_number)], grouping_start_time=str(receiver.participant.vars['matching_time.' + str(sender.round_number)]), state=json.dumps(reply['state_' + str(receiver.id_in_group)]), ) # send the message to the whole group. If you wanted to send an individual message you'd use # self.send_json(reply) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # reply # ) return ({player.id_in_group: reply, receiver.id_in_group: reply}) elif message_type == 'exit_acknowledge': new_groups = get_new_groups(player) player.participant.vars['exiting_flag'+str(player.round_number)] = False replies = {} for g in new_groups: for p in g: reply = {} reply['type'] = 'marketkids_message' reply['message_type'] = 'new_grouping' state = get_market_state(p) state['clear_messages'] = True reply['state_' + str(p.id_in_group)] = state replies[p.id_in_group] = reply # print("exit acknowleged, new groups, returning replies=", replies) return replies elif message_type == 'trade_acknowledge': new_groups = get_new_groups(player) replies = {} for g in new_groups: for p in g: reply = {} reply['type'] = 'marketkids_message' reply['message_type'] = 'new_grouping' state = get_market_state(p) state['clear_messages'] = True reply['state_' + str(p.id_in_group)] = state replies[p.id_in_group] = reply print("trade occurred, returning replies=", replies) return replies elif message_type == 'communication': print("communication received (message=" + str(content['message'] + " from " + str(content['from']))) # group_object = models_module.Group.objects.get(id=group_id) sender = player#group_object.get_player_by_id(player_id) receiver = get_partner(sender) sender_id = sender.id_in_group receiver_id = receiver.id_in_group # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, # content # ) color = str(content['color']) if color == '-1': color = '' elif color == 'color': color = '' elif color == '': pass else: color = ' color '+color print("message = "+str(content['message'])) print("color = "+color) text = str(content['message'])+color print("recording for output, text=",text.replace("'","")) Event.create( sender=sender, receiver=receiver, type='communication', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(get_market_state(sender)), message_text = text.replace("'","") ) return ({player.id_in_group: content, receiver.id_in_group: content}) elif message_type == 'exit_sure' and not player.participant.vars['exiting_flag'+str(player.round_number)]: partner = player.group.get_player_by_id(player.participant.vars['current_partner.'+str(player.round_number)]) sender = player receiver = partner player.session.vars['available_players.' + str(player.round_number)].append(player.id_in_group) player.session.vars['available_players.' + str(player.round_number)].append(partner.id_in_group) sender.participant.vars['exiting_flag'+str(sender.round_number)] = True receiver.participant.vars['exiting_flag'+str(sender.round_number)] = True receiver.participant.vars['wallet_0.'+str(sender.round_number)] += receiver.participant.vars['offering_0.'+str(sender.round_number)] receiver.participant.vars['wallet_1.'+str(sender.round_number)] += receiver.participant.vars['offering_1.'+str(sender.round_number)] receiver.participant.vars['wallet_2.'+str(sender.round_number)] += receiver.participant.vars['offering_2.'+str(sender.round_number)] receiver.participant.vars['wallet_3.'+str(sender.round_number)] += receiver.participant.vars['offering_3.'+str(sender.round_number)] receiver.participant.vars['offering_0.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_1.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_2.'+str(sender.round_number)] = 0 receiver.participant.vars['offering_3.'+str(sender.round_number)] = 0 sender.participant.vars['wallet_0.'+str(sender.round_number)] += sender.participant.vars['offering_0.'+str(sender.round_number)] sender.participant.vars['wallet_1.'+str(sender.round_number)] += sender.participant.vars['offering_1.'+str(sender.round_number)] sender.participant.vars['wallet_2.'+str(sender.round_number)] += sender.participant.vars['offering_2.'+str(sender.round_number)] sender.participant.vars['wallet_3.'+str(sender.round_number)] += sender.participant.vars['offering_3.'+str(sender.round_number)] sender.participant.vars['initial_wallet.'+str(sender.round_number)] = json.dumps([sender.participant.vars['wallet_0.'+str(sender.round_number)], sender.participant.vars['wallet_1.'+str(sender.round_number)], sender.participant.vars['wallet_2.'+str(sender.round_number)], sender.participant.vars['wallet_3.'+str(sender.round_number)]]) receiver.participant.vars['initial_wallet.'+str(sender.round_number)] = json.dumps([receiver.participant.vars['wallet_0.'+str(sender.round_number)], receiver.participant.vars['wallet_1.'+str(sender.round_number)], receiver.participant.vars['wallet_2.'+str(sender.round_number)], receiver.participant.vars['wallet_3.'+str(sender.round_number)]]) sender.participant.vars['offering_0.'+str(sender.round_number)] = 0 sender.participant.vars['offering_1.'+str(sender.round_number)] = 0 sender.participant.vars['offering_2.'+str(sender.round_number)] = 0 sender.participant.vars['offering_3.'+str(sender.round_number)] = 0 sender.participant.vars['offered_0.'+str(sender.round_number)] = 0 sender.participant.vars['offered_1.'+str(sender.round_number)] = 0 sender.participant.vars['offered_2.'+str(sender.round_number)] = 0 sender.participant.vars['offered_3.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_0.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_1.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_2.'+str(sender.round_number)] = 0 receiver.participant.vars['offered_3.'+str(sender.round_number)] = 0 sender.participant.vars['ok_enabled.'+str(sender.round_number)] = False receiver.participant.vars['ok_enabled.'+str(sender.round_number)] = False sender.participant.vars['ok_clicked.'+str(sender.round_number)] = False receiver.participant.vars['ok_clicked.'+str(sender.round_number)] = False sender.participant.vars['check_clicked.'+str(sender.round_number)] = False receiver.participant.vars['check_clicked.'+str(sender.round_number)] = False sender.participant.vars['previous_partner.' + str(sender.round_number)] = sender.participant.vars['current_partner.' + str(sender.round_number)] receiver.participant.vars['previous_partner.' + str(sender.round_number)] = receiver.participant.vars['current_partner.' + str(sender.round_number)] sender.participant.vars['current_partner.'+str(sender.round_number)] = -1 receiver.participant.vars['current_partner.' + str(sender.round_number)] = -1 replies = {} g = player.group for p in g.get_players(): if p == partner or p==player: reply = {} reply['type'] = 'marketkids_message' if p==player: reply['send_acknowledge'] = True else: reply['send_acknowledge'] = True reply['message_type'] = 'exit' state = get_market_state(p) state['clear_messages'] = True reply['state_' + str(p.id_in_group)] = state replies[p.id_in_group] = reply print("exit button clicked, returning replies=", replies) Event.create( sender=sender, receiver=receiver, type='exit_button_click', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(sender.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(sender.round_number)]), state=json.dumps(replies[player.id_in_group]['state_' + str(sender.id_in_group)]), ) return replies elif message_type == 'stop_sure': partner = player.group.get_player_by_id(player.participant.vars['current_partner.'+str(player.round_number)]) replies = {} g = player.group for p in g.get_players(): if p == partner: reply = {} reply['type'] = 'marketkids_message' reply['message_type'] = 'other_stop' state = get_market_state(p) state['clear_messages'] = True reply['state_' + str(p.id_in_group)] = state replies[p.id_in_group] = reply print("stop_sure, returning replies=", replies) return replies elif message_type == 'other_stop_acknowledge': player.participant.vars['previous_partner.'+str(player.round_number)] = player.participant.vars['current_partner.'+str(player.round_number)] player.participant.vars['current_partner.' + str(player.round_number)] = -1 new_groups = get_new_groups(player) replies = {} for g in new_groups: for p in g: reply = {} reply['type'] = 'marketkids_message' reply['message_type'] = 'new_grouping' state = get_market_state(p) state['clear_messages'] = True reply['state_' + str(p.id_in_group)] = state replies[p.id_in_group] = reply print("other_stop occurred, returning replies=", replies) return replies def get_market_state(player: Player): state = {} state['wallet_0'] = player.participant.vars['wallet_0.'+str(player.round_number)] state['wallet_1'] = player.participant.vars['wallet_1.'+str(player.round_number)] state['wallet_2'] = player.participant.vars['wallet_2.'+str(player.round_number)] state['wallet_3'] = player.participant.vars['wallet_3.'+str(player.round_number)] state['offered_0'] = player.participant.vars['offered_0.'+str(player.round_number)] state['offered_1'] = player.participant.vars['offered_1.'+str(player.round_number)] state['offered_2'] = player.participant.vars['offered_2.'+str(player.round_number)] state['offered_3'] = player.participant.vars['offered_3.'+str(player.round_number)] state['offering_0'] = player.participant.vars['offering_0.'+str(player.round_number)] state['offering_1'] = player.participant.vars['offering_1.'+str(player.round_number)] state['offering_2'] = player.participant.vars['offering_2.'+str(player.round_number)] state['offering_3'] = player.participant.vars['offering_3.'+str(player.round_number)] state['ok_enabled'] = player.participant.vars['ok_enabled.'+str(player.round_number)] state['ok_clicked'] = player.participant.vars['ok_clicked.'+str(player.round_number)] state['check_clicked'] = player.participant.vars['check_clicked.'+str(player.round_number)] state['exit_clicked'] = player.participant.vars['exit_clicked.'+str(player.round_number)] if get_partner(player): state['other_ok_clicked'] = get_partner(player).participant.vars['ok_clicked.'+str(player.round_number)] state['other_check_clicked'] = get_partner(player).participant.vars['check_clicked.'+str(player.round_number)] state['other_exit_clicked'] = get_partner(player).participant.vars['exit_clicked.'+str(player.round_number)] state['partnered'] = True else: state['other_ok_clicked'] = False state['other_check_clicked'] = False state['other_exit_clicked'] = False state['partnerered'] = False state['clear_messages'] = False # print("get_state returning: " + str(state)) return state def set_initial_groups(subsession: Subsession): print("********* SETTING INITIAL GROUPS FOR ROUND_NUMBER=",subsession.round_number) newpairs = [] attempted = [] splayers = subsession.get_players() group = splayers[0].group players = group.get_players() print("players=",players) print("players.id_in_group=",[p.id_in_group for p in players]) available = [p.id_in_group for p in players] original_available = available.copy() subsession.session.vars['available_players.'+str(subsession.round_number)] = available for p in players: p.participant.vars['previous_partner.'+str(subsession.round_number)] = -1 p.participant.vars['current_partner.'+str(subsession.round_number)] = -1 print("initial_grouping while loop, available=", available,", attempted=",attempted) while len(available) - len(attempted) >= 1: newplayer1 = None unattempted = [p for p in available if p not in attempted] newplayer1 = random.choice(unattempted) newplayer1_type = group.get_player_by_id(newplayer1).initial_type # available.remove(newplayer1) attempted.append(newplayer1) newplayer2 = None print("potential new player=", newplayer1) previous_partner = group.get_player_by_id(newplayer1).participant.vars['previous_partner.'+str(subsession.round_number)] print("previous partner=", previous_partner, ", available=", available) possible = [p for p in available if (p != previous_partner and p!=newplayer1)] #and group.get_player_by_id(p).initial_type != group.get_player_by_id(newplayer1).initial_type)] print("len(possible_matches)=", len(possible)) if len(possible) > 0: newplayer2 = random.choice(possible) if newplayer1 and newplayer2: newpairs.append([group.get_player_by_id(newplayer1), group.get_player_by_id(newplayer2)]) subsession.session.vars['available_players.'+str(subsession.round_number)].remove(newplayer1) subsession.session.vars['available_players.'+str(subsession.round_number)].remove(newplayer2) timestamp = datetime.datetime.now() group.get_player_by_id(newplayer1).participant.vars['matching_time.'+str(subsession.round_number)] = timestamp group.get_player_by_id(newplayer2).participant.vars['matching_time.'+str(subsession.round_number)] = timestamp group.get_player_by_id(newplayer1).participant.vars['current_partner.'+str(subsession.round_number)] = newplayer2 group.get_player_by_id(newplayer2).participant.vars['current_partner.'+str(subsession.round_number)] = newplayer1 attempted.remove(newplayer1) print("MATCH! newplayer1=",newplayer1,", newplayer2=",newplayer2) print("available=",available,", attempted=",attempted,", newpairs=",[[a.id_in_group,b.id_in_group] for [a,b] in newpairs]) print("available=", available, ", attempted=", attempted, ", newpairs=",newpairs) sender = group.get_player_by_id(newplayer1) receiver = group.get_player_by_id(newplayer2) Event.create( sender=sender, receiver=receiver, type='new_group', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(subsession.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(subsession.round_number)]), state=json.dumps(get_market_state(sender)), ) Event.create( sender=receiver, receiver=sender, type='new_group', timestamp=str(datetime.datetime.now()), sender_initial_type=receiver.initial_type, sender_initial_wallet=receiver.participant.vars['initial_wallet.'+str(subsession.round_number)], grouping_start_time=str(receiver.participant.vars['matching_time.'+str(subsession.round_number)]), state=json.dumps(get_market_state(receiver)), ) else: print("newplayer1=",newplayer1,", newplayer2=",newplayer2,", skipping matching with ",newplayer1," still in attempted.") print("looping initial_grouping while loop, available=", available, ", attempted=", attempted) def reset_participant_vars(p: Player): p.participant.vars['previous_partner.'+str(p.round_number)] = None p.participant.vars['matching_time.'+str(p.round_number)] = None p.participant.vars['wallet_0.'+str(p.round_number)] = None p.participant.vars['wallet_1.'+str(p.round_number)] = None p.participant.vars['wallet_2.'+str(p.round_number)] = None p.participant.vars['wallet_3.'+str(p.round_number)] = None p.participant.vars['initial_wallet.'+str(p.round_number)] = None p.participant.vars['offered_0.'+str(p.round_number)] = 0 p.participant.vars['offered_1.'+str(p.round_number)] = 0 p.participant.vars['offered_2.'+str(p.round_number)] = 0 p.participant.vars['offered_3.'+str(p.round_number)] = 0 p.participant.vars['offering_0.'+str(p.round_number)] = 0 p.participant.vars['offering_1.'+str(p.round_number)] = 0 p.participant.vars['offering_2.'+str(p.round_number)] = 0 p.participant.vars['offering_3.'+str(p.round_number)] = 0 p.participant.vars['check_clicked.'+str(p.round_number)] = False p.participant.vars['ok_clicked.'+str(p.round_number)] = False p.participant.vars['ok_enabled.'+str(p.round_number)] = False p.participant.vars['exit_clicked.'+str(p.round_number)] = False pass def get_new_groups(oldplayer: Player): # oldplayer.session.vars['available_players.'+str(oldplayer.round_number)].append(oldplayer.id_in_group) # oldreceiver.session.vars['available_players'].append(oldreceiver.id_in_group) # oldplayer.participant.vars['previous_partner.'+str(oldplayer.round_number)] = oldplayer.participant.vars['current_partner.'+str(oldplayer.round_number)] # oldreceiver.participant.vars['previous_partner'] = oldreceiver.participant.vars['current_partner'] group = oldplayer.group players = group.get_players() newpairs = [] newpairids = [] attempted = [] available = oldplayer.session.vars['available_players.'+str(oldplayer.round_number)] print("available=", available, ", len(attempted)=", len(attempted)) while len(available)-len(attempted) >= 1: unattempted = [p for p in available if p not in attempted] if len(unattempted)>0: newplayer1 = random.choice(unattempted) attempted.append(newplayer1) newplayer2 = None print("potential new player=",newplayer1) previous_partner = group.get_player_by_id(newplayer1).participant.vars['previous_partner.'+str(oldplayer.round_number)] print("previous partner=",previous_partner,", available=",available) possible = [p for p in available if (p != previous_partner and p!=newplayer1)] #and group.get_player_by_id(p).initial_type != group.get_player_by_id(newplayer1).initial_type)] print("len(possible_matches)=",len(possible)) if len(possible) > 0: newplayer2 = random.choice(possible) if newplayer1 and newplayer2: print("returning new group: (",newplayer1,",",newplayer2,")") newpairs.append([group.get_player_by_id(newplayer1),group.get_player_by_id(newplayer2)]) newpairids.append([newplayer1, newplayer2]) available.remove(newplayer1) available.remove(newplayer2) oldplayer.session.vars['available_players.'+str(oldplayer.round_number)] = available timestamp = datetime.datetime.now() group.get_player_by_id(newplayer1).participant.vars['matching_time.'+str(oldplayer.round_number)] = timestamp group.get_player_by_id(newplayer2).participant.vars['matching_time.'+str(oldplayer.round_number)] = timestamp group.get_player_by_id(newplayer1).participant.vars['current_partner.'+str(oldplayer.round_number)] = newplayer2 group.get_player_by_id(newplayer2).participant.vars['current_partner.'+str(oldplayer.round_number)] = newplayer1 attempted.remove(newplayer1) sender = group.get_player_by_id(newplayer1) receiver = group.get_player_by_id(newplayer2) Event.create( sender=sender, receiver=receiver, type='new_group', timestamp=str(datetime.datetime.now()), sender_initial_type=sender.initial_type, sender_initial_wallet=sender.participant.vars['initial_wallet.'+str(oldplayer.round_number)], grouping_start_time=str(sender.participant.vars['matching_time.'+str(oldplayer.round_number)]), state=json.dumps(get_market_state(sender)), ) Event.create( sender=receiver, receiver=sender, type='new_group', timestamp=str(datetime.datetime.now()), sender_initial_type=receiver.initial_type, sender_initial_wallet=receiver.participant.vars['initial_wallet.'+str(oldplayer.round_number)], grouping_start_time=str(receiver.participant.vars['matching_time.'+str(oldplayer.round_number)]), state=json.dumps(get_market_state(receiver)), ) print("len(available)=",len(available),", len(attempted)=",len(attempted)) print("while loop looping, available=",available) print("get_new_groups returning, newgroups=",newpairids) return newpairs class InitialWait_ADV(Page): @staticmethod def is_displayed(player: Player): return player.round_number <= player.subsession.max_rounds @staticmethod def before_next_page(player: Player, timeout_happened): p = player p.participant.vars['wallet_0.'+str(player.round_number)] = int(p.initial_token_1) p.participant.vars['wallet_1.'+str(player.round_number)] = int(p.initial_token_2) p.participant.vars['wallet_2.'+str(player.round_number)] = int(p.initial_token_3) p.participant.vars['wallet_3.'+str(player.round_number)] = int(p.initial_token_4) p.participant.vars['initial_wallet.'+str(player.round_number)] = json.dumps([p.participant.vars['wallet_0.'+str(player.round_number)], p.participant.vars['wallet_1.'+str(player.round_number)], p.participant.vars['wallet_2.'+str(player.round_number)], p.participant.vars['wallet_3.'+str(player.round_number)]]) p.participant.vars['offered_0.'+str(player.round_number)] = 0 p.participant.vars['offered_1.'+str(player.round_number)] = 0 p.participant.vars['offered_2.'+str(player.round_number)] = 0 p.participant.vars['offered_3.'+str(player.round_number)] = 0 p.participant.vars['offering_0.'+str(player.round_number)] = 0 p.participant.vars['offering_1.'+str(player.round_number)] = 0 p.participant.vars['offering_2.'+str(player.round_number)] = 0 p.participant.vars['offering_3.'+str(player.round_number)] = 0 p.participant.vars['check_clicked.'+str(player.round_number)] = False p.participant.vars['ok_clicked.'+str(player.round_number)] = False p.participant.vars['ok_enabled.'+str(player.round_number)] = False p.participant.vars['exit_clicked.'+str(player.round_number)] = False p.participant.vars['exiting_flag'+str(player.round_number)] = False class FinalResults_ADV(Page): @staticmethod def is_displayed(player: Player): return player.round_number == player.subsession.max_rounds class Results(Page): @staticmethod def is_displayed(player: Player): return player.round_number <= player.subsession.max_rounds @staticmethod def vars_for_template(player: Player): rval={} state = get_market_state(player) rval['wallet_0'] = state['wallet_0'] rval['wallet_1'] = state['wallet_1'] rval['wallet_2'] = state['wallet_2'] rval['wallet_3'] = state['wallet_3'] wallet = [state['wallet_1'],state['wallet_2'],state['wallet_3']] min_value = 9999; for w in range(len(wallet)): if wallet[w] < min_value: min_value = wallet[w] rval['token_value'] = "" + str(player.subsession.bundle_value * min_value) return rval class EnterName(Page): form_model = 'player' form_fields=['name'] @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.label = player.name class mkInstr_ADV(Page): form_model = 'player' form_fields = [] @staticmethod def is_displayed(player: Player): return player.round_number == 1 page_sequence = [mkInstr_ADV, EnterName,InitialWait_ADV,Decision,Results,FinalResults_ADV] class Event(ExtraModel): sender = models.Link(Player) receiver = models.Link(Player) type = models.StringField() timestamp = models.StringField() sender_initial_type = models.StringField() sender_initial_wallet = models.StringField() sender_round_initial_wallet = models.StringField() grouping_start_time = models.StringField() state = models.LongStringField() offer_color = models.StringField() offer_amount = models.StringField() message_text = models.StringField() def custom_export(players): yield['#### MARKET FOR KIDS EVENTS TABLE'] yield['session_code','round_number','sender.id','receiver.id','sender_name','receiver_name','timestamp','event_type','sender_initial_type','sender_initial_wallet_1', 'sender_initial_wallet_2','sender_initial_wallet_3','sender_initial_wallet_4','grouping_start_time','offer_color', 'offer_amount','message_text'] events = Event.filter() for e in events: sender_initial_wallet = json.loads(e.sender_initial_wallet) # print("e=",e) yield([e.sender.session.code,e.sender.round_number,e.sender.participant.id,e.receiver.participant.id,e.sender.participant.label, e.receiver.participant.label,e.timestamp,e.type,e.sender_initial_type, sender_initial_wallet[0],sender_initial_wallet[1],sender_initial_wallet[2],sender_initial_wallet[3], e.grouping_start_time,e.offer_color,e.offer_amount,e.message_text]) yield[''] yield [''] yield['#### MARKET FOR KIDS FINAL HOLDINGS TABLE'] yield['session_code','round_number','player.id','wallet_0','wallet_1','wallet_2','wallet_3'] for p in players: if 'current_partner.'+str(p.round_number) in p.participant.vars: state = get_market_state(p) yield([p.session.code,p.round_number,p.participant.id,state['wallet_0'],state['wallet_1'],state['wallet_2'],state['wallet_3']])