from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random author = 'Franziska Heinicke' doc = """ Bargaining with a nuclear option """ class Constants(BaseConstants): name_in_url = 'bargain' players_per_group = 2 num_rounds = 11 endowmentsA = [[30, 50, 60, 50, 30, 40, 60, 60, 40, 60, 30], [30, 60, 40, 60, 60, 40, 30, 50, 60, 50, 30], [40, 60, 60, 40, 60, 30, 30, 50, 60, 50, 30], [30, 60, 30, 60, 40, 30, 50, 50, 40, 60, 60], [30, 60, 40, 60, 30, 40, 60, 50, 30, 60, 50], [30, 50, 60, 50, 40, 60, 30, 60, 40, 60, 30], [60, 50, 40, 30, 60, 50, 60, 30, 60, 40, 30], [60, 60, 60, 60, 50, 50, 40, 40, 30, 30, 30], [30, 40, 60, 30, 60, 50, 60, 30, 40, 50, 60], [30, 30, 30, 40, 40, 50, 50, 60, 60, 60, 60], [60, 60, 50, 30, 40, 50, 60, 40, 60, 30, 30], [60, 40, 50, 30, 30, 30, 50, 60, 60, 40, 60], [50, 60, 60, 60, 40, 50, 30, 40, 30, 60, 30], [50, 40, 30, 50, 60, 60, 30, 60, 30, 40, 60], [60, 60, 60, 30, 50, 50, 30, 40, 30, 40, 60], [60, 60, 30, 30, 60, 50, 30, 60, 40, 50, 40], [30, 60, 60, 30, 30, 40, 60, 60, 50, 40, 30], [30, 50, 60, 50, 30, 40, 60, 60, 40, 60, 30], [30, 60, 40, 60, 60, 40, 30, 50, 60, 50, 30], [40, 60, 60, 40, 60, 30, 30, 50, 60, 50, 30], [30, 60, 30, 60, 40, 30, 50, 50, 40, 60, 60], [30, 60, 40, 60, 30, 40, 60, 50, 30, 60, 50], [30, 50, 60, 50, 40, 60, 30, 60, 40, 60, 30], [60, 50, 40, 30, 60, 50, 60, 30, 60, 40, 30], [60, 60, 60, 60, 50, 50, 40, 40, 30, 30, 30], [30, 40, 60, 30, 60, 50, 60, 30, 40, 50, 60], [30, 30, 30, 40, 40, 50, 50, 60, 60, 60, 60], [60, 60, 50, 30, 40, 50, 60, 40, 60, 30, 30], [60, 40, 50, 30, 30, 30, 50, 60, 60, 40, 60], [50, 60, 60, 60, 40, 50, 30, 40, 30, 60, 30], [50, 40, 30, 50, 60, 60, 30, 60, 30, 40, 60], [60, 60, 60, 30, 50, 50, 30, 40, 30, 40, 60], [60, 60, 30, 30, 60, 50, 30, 60, 40, 50, 40], [30, 60, 60, 30, 30, 40, 60, 60, 50, 40, 30] ] endowmentsB = [[30, 60, 30, 50, 60, 30, 40, 60, 60, 50, 40], [40, 50, 60, 60, 40, 30, 60, 50, 30, 60, 30], [30, 40, 60, 60, 50, 40, 30, 60, 30, 50, 60], [30, 30, 60, 40, 60, 40, 60, 50, 30, 60, 50], [60, 30, 60, 40, 40, 30, 50, 60, 30, 60, 50], [30, 50, 60, 60, 30, 50, 40, 40, 60, 30, 60], [60, 60, 60, 60, 50, 50, 40, 40, 30, 30, 30], [60, 50, 40, 30, 60, 50, 60, 30, 60, 40, 30], [30, 30, 30, 40, 40, 50, 50, 60, 60, 60, 60], [30, 40, 60, 30, 60, 50, 60, 30, 40, 50, 60], [60, 40, 50, 30, 60, 60, 30, 30, 50, 60, 40], [50, 30, 60, 40, 60, 30, 50, 60, 30, 60, 40], [60, 40, 30, 50, 60, 50, 30, 30, 40, 60, 60], [50, 60, 40, 60, 60, 40, 60, 50, 30, 30, 30], [50, 40, 30, 30, 60, 50, 60, 30, 40, 60, 60], [30, 60, 60, 30, 40, 50, 40, 50, 30, 60, 60], [30, 30, 40, 60, 40, 60, 60, 50, 50, 30, 60], [30, 60, 30, 50, 60, 30, 40, 60, 60, 50, 40], [40, 50, 60, 60, 40, 30, 60, 50, 30, 60, 30], [30, 40, 60, 60, 50, 40, 30, 60, 30, 50, 60], [30, 30, 60, 40, 60, 40, 60, 50, 30, 60, 50], [60, 30, 60, 40, 40, 30, 50, 60, 30, 60, 50], [30, 50, 60, 60, 30, 50, 40, 40, 60, 30, 60], [60, 60, 60, 60, 50, 50, 40, 40, 30, 30, 30], [60, 50, 40, 30, 60, 50, 60, 30, 60, 40, 30], [30, 30, 30, 40, 40, 50, 50, 60, 60, 60, 60], [30, 40, 60, 30, 60, 50, 60, 30, 40, 50, 60], [60, 40, 50, 30, 60, 60, 30, 30, 50, 60, 40], [50, 30, 60, 40, 60, 30, 50, 60, 30, 60, 40], [60, 40, 30, 50, 60, 50, 30, 30, 40, 60, 60], [50, 60, 40, 60, 60, 40, 60, 50, 30, 30, 30], [50, 40, 30, 30, 60, 50, 60, 30, 40, 60, 60], [30, 60, 60, 30, 40, 50, 40, 50, 30, 60, 60], [30, 30, 40, 60, 40, 60, 60, 50, 50, 30, 60] ] matching_size = 2 max_waiting = 90 final_waiting = 120 waiting_round1 = 600 instructions_template = 'channelsmin/instructions.html' class Subsession(BaseSubsession): #def creating_session(self): #if self.round_number == 1: #for p in self.get_players(): #p.participant.vars['paying_round'] = random.randint(1, 11) #if (p.id_in_group % 2) == 0: #p.participant.vars['type'] = 'B' #else: #p.participant.vars['type'] = 'A' def group_by_arrival_time_method(self, waiting_players): from collections import defaultdict d = defaultdict(list) import time if self.round_number == 1: a_players = [p for p in waiting_players if p.participant.vars['type'] == 'A'] b_players = [p for p in waiting_players if p.participant.vars['type'] == 'B'] for p in a_players: if p.matching_group == 0: a_players_matching = [p.matching_group for p in self.get_players() if p.participant.vars['type'] == 'A'] max_a = max(a_players_matching) if max_a == 0: p.participant.vars['matching'] = 1 p.matching_group = p.participant.vars['matching'] else: a_current_match = [p for p in self.get_players() if p.matching_group == max_a and p.participant.vars['type'] == 'A'] if len(a_current_match) == Constants.matching_size: p.participant.vars['matching'] = max_a + 1 p.matching_group = p.participant.vars['matching'] else: p.participant.vars['matching'] = max_a p.matching_group = p.participant.vars['matching'] for p in b_players: b_players_matching = [p.matching_group for p in self.get_players() if p.participant.vars['type'] == 'B'] if p.matching_group == 0: max_b = max(b_players_matching) if max_b == 0: p.participant.vars['matching'] = 1 p.matching_group = p.participant.vars['matching'] else: b_current_match = [p for p in self.get_players() if p.matching_group == max_b and p.participant.vars['type'] == 'B'] if len(b_current_match) == Constants.matching_size: p.participant.vars['matching'] = max_b + 1 p.matching_group = p.participant.vars['matching'] else: p.participant.vars['matching'] = max_b p.matching_group = p.participant.vars['matching'] for p in waiting_players: if time.time() - p.participant.vars['wait_page_arrival'] > Constants.waiting_round1: p.participant.vars['active'] = False p.active = False p.participant.vars['first_out'] = True p.participant.vars['matching'] = 0 p.matching_group = 0 return [p] else: if p.matching_group != 0: matching_number = p.participant.vars['matching'] p.participant.vars['active'] = True p.participant.vars['drop_out'] = False p.participant.vars['first_out'] = False p.participant.vars['cumulative_payoff'] = 0 p.paying_round = p.participant.vars['paying_round'] players_in_this_matching = d[matching_number] players_in_this_matching.append(p) a_players_this_matching = [p for p in players_in_this_matching if p.participant.vars['type'] == 'A'] b_players_this_matching = [p for p in players_in_this_matching if p.participant.vars['type'] == 'B'] a_players_total = [p for p in self.get_players() if p.matching_group == matching_number and p.participant.vars['type'] == 'A'] b_players_total = [p for p in self.get_players() if p.matching_group == matching_number and p.participant.vars['type'] == 'B'] if len(a_players_this_matching) >= 1 and len(b_players_this_matching) >= 1 and len(a_players_total) == Constants.matching_size and len(b_players_total) == Constants.matching_size: return [a_players_this_matching[0], b_players_this_matching[0]] else: for p in waiting_players: p.paying_round = p.participant.vars['paying_round'] p.matching_group = p.participant.vars['matching'] p.active = p.participant.vars['active'] p.drop_out = p.participant.vars['drop_out'] matching_number = p.participant.vars['matching'] total_now = [p for p in self.get_players() if p.matching_group == matching_number and p.active] total_last = [p for p in self.in_round(self.round_number - 1).get_players() if p.matching_group == matching_number and p.active] a_players = [p for p in waiting_players if p.matching_group == matching_number and p.participant.vars['type'] == 'A' and p.active] b_players = [p for p in waiting_players if p.matching_group == matching_number and p.participant.vars['type'] == 'B' and p.active] if not p.active: return [p] else: if len(total_last) == len(total_now) or time.time() - p.participant.vars['wait_page_arrival'] > Constants.max_waiting: if len(a_players) >= 1 and len(b_players) >= 1: b_players = random.sample(b_players, len(b_players)) return [a_players[0], b_players[0]] if time.time() - p.participant.vars['wait_page_arrival'] > Constants.final_waiting: p.participant.vars['active'] = False p.active = False if p.participant.vars['cumulative_payoff'] == 0: p.participant.vars['paying_round'] = self.round_number - 1 p.paying_round = self.round_number - 1 p.round_payoff = p.in_round(self.round_number - 1).round_payoff p.payoff = p.in_round(self.round_number - 1).round_payoff p.participant.vars['cumulative_payoff'] = p.in_round(self.round_number - 1).round_payoff if p.in_round(self.round_number - 1).group.failed: p.participant.vars['disagreement'] = True else: p.participant.vars['disagreement'] = False if p.in_round(self.round_number - 1).group.nuclear: p.participant.vars['nuclear'] = True else: p.participant.vars['nuclear'] = False return [p] class Group(BaseGroup): endowA = models.IntegerField() endowB = models.IntegerField() currentA = models.IntegerField(initial=100) historyA = models.CharField(initial="") currentB = models.IntegerField(initial=100) historyB = models.CharField(initial="") nuclear = models.BooleanField() firstpage_done = models.BooleanField(initial=False) final_offer = models.IntegerField(initial=0) time_final = models.CharField() failed = models.BooleanField(initial=False) arrival = models.CharField() def set_time(self): import time self.arrival = str(int(time.time() * 1000)) def set_payoffs(self): playerA = self.get_player_by_role('A') playerB = self.get_player_by_role('B') if self.failed: if self.nuclear: playerA.round_payoff = 0 playerB.round_payoff = 9 else: playerA.round_payoff = self.endowA playerB.round_payoff = self.endowB else: playerA.round_payoff = self.final_offer playerB.round_payoff = 100 - self.final_offer players = self.get_players() for p in players: if p.paying_round == self.round_number: p.payoff = p.round_payoff p.participant.vars['cumulative_payoff'] = p.round_payoff if self.failed: p.participant.vars['disagreement'] = True else: p.participant.vars['disagreement'] = False if self.nuclear: p.participant.vars['nuclear'] = True else: p.participant.vars['nuclear'] = False else: p.payoff = 0 class Player(BasePlayer): matching_group = models.IntegerField(initial=0) active = models.BooleanField(initial=True) drop_out = models.BooleanField(initial=True) paying_round = models.IntegerField() round_payoff = models.IntegerField() def role(self): if self.participant.vars['type'] == 'A': return 'A' else: return 'B'