from otree.api import * doc = """ This is a simple voting game. """ class Constants(BaseConstants): import math import csv with open('voting/para_table.csv', 'r') as para: para = list(csv.DictReader(para)) num_rounds = len(para) players_per_group = len(para[0])-1 name_in_url = 'voting' instructions_template = 'voting/instructions.html' win_payoff = cu(50) lose_payoff = cu(0) large_team_size = math.ceil(players_per_group / 2) small_team_size = players_per_group - large_team_size max_voting_cost = cu(50) min_voting_cost = cu(0) class Subsession(BaseSubsession): pass class Group(BaseGroup): small_team_vote_count = models.IntegerField(initial=0) large_team_vote_count = models.IntegerField(initial=0) small_team_win = models.BooleanField() def make_field(label): return models.BooleanField( choices=[[True, "買"], [False, "不買"]], label=label, widget=widgets.RadioSelect) # widget=widgets.RadioSelectHorizontal class Player(BasePlayer): wtp_voting_cost = models.CurrencyField() is_large_team = models.BooleanField() is_winner = models.BooleanField() voting_cost = models.CurrencyField() vote = models.BooleanField() S1_Switching = models.CurrencyField() S2_Switching = models.CurrencyField() temp_payoff = models.CurrencyField() p0 = models.BooleanField( initial=True, choices=[[True, "買"], [False, "不買"]], label='裝備的價格是 0 法幣', widget=widgets.RadioSelect) p10 = make_field('裝備的價格是 10 法幣') p20 = make_field('裝備的價格是 20 法幣') p30 = make_field('裝備的價格是 30 法幣') p40 = make_field('裝備的價格是 40 法幣') p50 = models.BooleanField( initial=False, choices=[[True, "買"], [False, "不買"]], label='裝備的價格是 50 法幣', widget=widgets.RadioSelect) p1 = make_field('') p2 = make_field('') p3 = make_field('') p4 = make_field('') p5 = make_field('') p6 = make_field('') p7 = make_field('') p8 = make_field('') p9 = make_field('') # FUNCTIONS def creating_session(subsession: Subsession): import random session = subsession.session subsession.session.vars['para'] = Constants.para.copy() for p in subsession.get_players(): para_round = subsession.session.vars['para'][subsession.round_number - 1] p.is_large_team = para_round['Player' + str(p.id_in_group)] == '1' if subsession.round_number == 1: paying_round = random.randint(1, Constants.num_rounds) session.vars['paying_round'] = paying_round def S1_Switching(player: Player): import numpy as np p1_list = [player.p0, player.p10, player.p20, player.p30, player.p40, player.p50] p1_list = [int(elem) for elem in p1_list] diff_list = np.diff(p1_list) count_neg_ones = sum(diff_list == -1) if count_neg_ones == 1: s1_switching = (sum(p1_list) - 1) * 10 elif count_neg_ones == 0: if sum(p1_list) == 0: s1_switching = -99 elif sum(p1_list) == 6: s1_switching = 50 else: s1_switching = -98 return s1_switching def S2_Switching(player: Player): import numpy as np p2_list = [player.p1, player.p2, player.p3, player.p4, player.p5, player.p6, player.p7, player.p8, player.p9] p2_list = [int(elem) for elem in p2_list] diff_list = np.diff(p2_list) count_neg_ones = sum(diff_list == -1) if count_neg_ones == 1: s2_switching = sum(p2_list) elif count_neg_ones == 0: if sum(p2_list) == 9: s2_switching = 9 elif sum(p2_list) == 0: s2_switching = 0 else: s2_switching = -99 return s2_switching def get_voting_cost(player: Player): wtp_voting_cost = player.S1_Switching + player.S2_Switching return wtp_voting_cost def set_winner(group: Group): import random players = group.get_players() subsession = group.subsession session = group.session for p in players: p.voting_cost = random.randint(Constants.min_voting_cost, Constants.max_voting_cost) p.vote = p.voting_cost <= p.wtp_voting_cost if p.is_large_team == 0 and p.vote == 1: group.small_team_vote_count += 1 elif p.is_large_team == 1 and p.vote == 1: group.large_team_vote_count += 1 if group.small_team_vote_count > group.large_team_vote_count: group.small_team_win = 1 elif group.small_team_vote_count < group.large_team_vote_count: group.small_team_win = 0 else: group.small_team_win = random.randint(0, 1) for p in players: if p.is_large_team == 0 and group.small_team_win == 1: p.payoff = Constants.win_payoff p.is_winner = 1 elif p.is_large_team == 1 and group.small_team_win == 0: p.payoff = Constants.win_payoff p.is_winner = 1 else: p.payoff = Constants.lose_payoff p.is_winner = 0 if p.vote == 1: p.payoff = p.payoff - p.voting_cost p.temp_payoff = p.payoff if subsession.round_number == session.vars['paying_round']: p.payoff = p.payoff else: p.payoff = cu(0) # PAGES class Introduction(Page): pass class Vote_Stage1(Page): form_model = 'player' form_fields = ['p0', 'p10', 'p20', 'p30', 'p40', 'p50'] @staticmethod def before_next_page(player: Player, timeout_happened): player.S1_Switching = S1_Switching(player) if player.S1_Switching == 50: player.S2_Switching = 50 player.wtp_voting_cost = 50 elif player.S1_Switching < 0: player.S2_Switching = -99 player.wtp_voting_cost = -100 def error_message(player, values): import numpy as np p1_list = [values['p0'], values['p10'], values['p20'], values['p30'], values['p40'], values['p50']] p1_list = [int(elem) for elem in p1_list] diff_list = np.diff(p1_list) count_neg_ones = sum(diff_list == -1) count_ones = sum(diff_list == 1) if count_neg_ones != 1: return '您輸入的答案是不合理的。' elif count_ones > 0: return '您輸入的答案是不合理的。' class Vote_Stage2(Page): form_model = 'player' form_fields = ['p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'] @staticmethod def is_displayed(player): return player.S1_Switching != 50 and player.S1_Switching >= 0 def before_next_page(player: Player, timeout_happened): player.S2_Switching = S2_Switching(player) player.wtp_voting_cost = get_voting_cost(player) def vars_for_template(player): rows = [] for i in list(range(1, 10)): rows = rows + [dict(label='裝備的價格是 {} '.format(player.S1_Switching + i), number=i)] return dict(rows=rows) def error_message(player, values): import numpy as np p2_list = [values['p1'], values['p2'], values['p3'], values['p4'], values['p5'], values['p6'], values['p7'], values['p8'], values['p9']] p2_list = [int(elem) for elem in p2_list] diff_list = np.diff(p2_list) count_neg_ones = sum(diff_list == -1) count_ones = sum(diff_list == 1) if (count_neg_ones != 1) and (count_neg_ones != 0): return '您輸入的答案是不合理的。' elif (count_neg_ones == 0) and (sum(p2_list) != 0) and (sum(p2_list) != 9): return '您輸入的答案是不合理的。' elif count_ones > 0: return '您輸入的答案是不合理的。' class ResultsWaitPage(WaitPage): after_all_players_arrive = set_winner class Results(Page): pass page_sequence = [Introduction, Vote_Stage1, Vote_Stage2, ResultsWaitPage, Results]