import random from otree.api import * import time from otree import settings doc = """ Poker Bargain Spiel für Museumsnacht mit Claus Jochen """ class C(BaseConstants): NAME_IN_URL = 'chip_bargain' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 2 CHIPS_TOTAL = 20 RED_ROLE = 'Red' GREEN_ROLE = 'Green' # GREEN_FACTOR = [1,4] # RED_FACTOR = [2,1] FACTOR_FIRST = [4, 1] ## Erweitern wenn mehr Runden FACTOR_SECOND = [1, 2] ## Erweitern wenn mehr Runden TIME_FOR_BARGAIN = 60 VOTES_TILL_FORCED = 3 class Subsession(BaseSubsession): TREATMENT = models.StringField(initial='online') # on_premise als Alternative ADULT = models.BooleanField(initial=True) FIRST = models.StringField(initial='red') UNIVERSITY = models.BooleanField(initial=True) ## Studenten oder breite Bevölkerung PAYMENT_MACHINE = models.BooleanField(initial=False) # ja = Münzwurf , nein = experimentator CASH = models.BooleanField(initial=False) # True: Bar False: Überweisung NUMCARDS = models.IntegerField(initial=-1) FORCEDWAIT = models.BooleanField(Initial=False) #True: Gruppe muss warten bis Experimentator weiterklickt; False: Gruppe kann sofort durchklicken class Group(BaseGroup): shared = models.BooleanField() bargain_timeout = models.FloatField() accepted = models.BooleanField() ## only if both accept aborted = models.BooleanField(initial=False) chooser = models.IntegerField(initial=1) round_chosen = models.IntegerField(initial=-1) red_first = models.BooleanField(initial=False) class Player(BasePlayer): factor = models.IntegerField() offer = models.IntegerField(initial=-1) offer_accept = models.BooleanField() selected_payoff = models.CurrencyField(doc='nur zum speichern des gewählten Payoffs') ### Questionaire: number_participant = models.IntegerField( label='Bitte geben Sie die Nummer ein, die der Experimentator Ihnen zugewiesen hat', ) age = models.IntegerField( label="Zu welcher der nachfolgenden Alterskategorien gehören Sie?", choices=[ [1, "<=17"], [2, "18-20"], [3, "21-29"], [4, "30-39"], [5, "40-49"], [6, "50-59"], [7, "60-69"], [8, "70-79"], [9, ">=80"] ] ) age_exact = models.IntegerField( label="Bitte geben Sie Ihr Alter ein" ) gender = models.StringField( label="Bitte geben Sie Ihr Geschlecht an", choices=[ "männlich", "weiblich", "divers", ], widget=widgets.RadioSelect ) siblings = models.StringField( label="Wie viele Geschwister haben Sie?", choices=["0", "1", "mehrere Geschwister"], widget=widgets.RadioSelect ) educational_qualification = models.StringField( label="Ihr höchster Bildungsabschluss?", choices=["kein Schulabschluss", "Hauptschulabschluss", "Realschulabschluss(mittlere Reife)", "Gymnasium(Abitur)", "Abgeschlossene Ausbildung", "Fachhochschulabschluss", "Hochschule(Bachelor)", "Hochschule(Diplom / Magister / Master / Staatsexamen)", \ "Hochschule(Promotion)"], widget=widgets.RadioSelect ) study_status = models.StringField( label="Studieren Sie oder haben Sie studiert?", choices=["Aktuell im Studium", "Ich habe studiert", "weder noch"], widget=widgets.RadioSelect ) work_negotiation = models.StringField( label="Wie häufig kommt es in Ihrem Beruf vor, dass Sie mit jemandem verhandeln (müssen)?", choices=[ "sehr häufig", "eher häufig", "manchmal", "selten", "gar nicht" ], widget=widgets.RadioSelect ) private_negotiation = models.StringField( label="Wie häufig kommt es privat vor, dass Sie mit jemandem verhandeln (müssen)?", choices=[ "sehr häufig", "eher häufig", "manchmal", "selten", "gar nicht" ], widget=widgets.RadioSelect ) division_part1 = models.StringField( label="Wie zufrieden waren Sie mit der Aufteilung der Pokerchips in Spiel 1?", choices=[ "sehr zufrieden", "eher zufrieden", "neutral", "eher unzufrieden", "unzufrieden" ], widget=widgets.RadioSelect ) division_part2 = models.StringField( label="Wie zufrieden waren Sie mit der Aufteilung der Pokerchips in Spiel 2?", choices=[ "sehr zufrieden", "eher zufrieden", "neutral", "eher unzufrieden", "unzufrieden" ], widget=widgets.RadioSelect ) knowledge_opposite = models.StringField( label="Wie gut kennen Sie Ihre(n) VerhandlungspartnerIn?", choices=[ "verwandt", "verheiratet", "befreundet", "bekannt", "flüchtig", "gar nicht" ], widget=widgets.RadioSelect ) children_example_question = models.StringField( label="Dies ist eine Beispiel Kind Frage", ) def creating_session(subsession: Subsession): subsession.TREATMENT = 'on_premise' subsession.ADULT = True subsession.FIRST = 'red' if 'treatment' in subsession.session.config: subsession.TREATMENT = subsession.session.config['treatment'] if 'adult' in subsession.session.config: subsession.ADULT = subsession.session.config['adult'] if 'first' in subsession.session.config: var_first = subsession.session.config['first'] for g in subsession.get_groups(): if var_first == 'random': if subsession.round_number == 1: g.red_first = random.choice([True,False]) else: g.red_first = g.in_round(1).red_first else: g.red_first = var_first=='red' subsession.FIRST = var_first if 'university' in subsession.session.config: subsession.UNIVERSITY = subsession.session.config['university'] if 'payment' in subsession.session.config: subsession.PAYMENT_MACHINE = subsession.session.config['payment'] == 'machine' if 'cash' in subsession.session.config: subsession.CASH = subsession.session.config['cash'] if 'numcards' in subsession.session.config: subsession.NUMCARDS = subsession.session.config['numcards'] if 'forcedwait' in subsession.session.config: subsession.FORCEDWAIT = subsession.session.config['forcedwait'] def get_offer_by_color(player: Player): if player.role == 'Green': return player.get_others_in_group()[0].offer, player.offer return player.offer, player.get_others_in_group()[0].offer # PAGES class NumberStage(Page): form_model = 'player' form_fields = ["number_participant"] @staticmethod def is_displayed(player: Player): return player.subsession.TREATMENT == 'on_premise' and player.round_number == 1 def get_red_green_factor(player: Player): #todo: das in die Berech red_first = player.group.red_first green_factor = C.FACTOR_SECOND[::-1] red_factor = C.FACTOR_FIRST[::-1] if red_first: green_factor = C.FACTOR_SECOND red_factor = C.FACTOR_FIRST return red_factor[player.round_number - 1], green_factor[player.round_number - 1],red_first class ForcedWaitStage(WaitPage): wait_for_all_groups = True @staticmethod def vars_for_template(player: Player): body_text = 'Bitte warten Sie, bis der Experimentator das Experiment für alle startet.' if player.round_number == C.NUM_ROUNDS: body_text='Bitte warten Sie, bis der Experimentator das Experiment für alle fortsetzt.' return dict(body_text= body_text) def is_displayed(player: Player): return player.subsession.FORCEDWAIT class Instructions(Page): @staticmethod def vars_for_template(player: Player): red, green, red_first = get_red_green_factor(player) if player.role==C.RED_ROLE: player.factor = red else: player.factor = green return dict( round1=player.round_number == 1, redFirst=red_first, red=red, green=green, erneut='' if player.round_number == 1 else 'erneut', num_chips=C.CHIPS_TOTAL ) class ShowColor(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def vars_for_template(player: Player): return dict( ##auf Deutsch colorDE='Rot' if player.role == C.RED_ROLE else 'Grün' ) class WaitToStart(WaitPage): @staticmethod def after_all_players_arrive(group: Group): group.bargain_timeout = time.time() + C.TIME_FOR_BARGAIN #print(ausgabe) class Bargain(Page): form_model = 'player' form_fields = ['offer_accept'] @staticmethod def live_method(player: Player,data): offer = data['offer'] forced = data['forced'] declined = data['declined'] group = player.group opp = player.get_others_in_group()[0] player.offer = offer my_id = player.id_in_group response = dict() player_type = 'green' if player.role == C.GREEN_ROLE else 'red' offer_red, offer_green = get_offer_by_color(player) if player.offer + opp.offer == C.CHIPS_TOTAL: group.shared = True response = dict(type='finished', shared=True, offer_red=offer_red, offer_green=offer_green, id_in_group=my_id, player_type=player_type, offer_me=player.offer, offer_opp=opp.offer) # hier zeit einteilen elif forced: ### Geforced ## anderen noch die daten geben opp.offer = C.CHIPS_TOTAL - offer; response = dict(type='forced', offer_red=offer_red, offer_green=offer_green, id_in_group=my_id, opp_id=opp.id_in_group, player_type=player_type, offer_me=player.offer, offer_opp=opp.offer ) elif declined: ## händisch abgebrochen player.group.aborted = True response = dict(type='declined') ### Schmeiss den empfänger auch raus else: response = dict( id_in_group=my_id, type='bargain', offer_red=offer_red, offer_green=offer_green, player_type=player_type, offer_me=player.offer, offer_opp=opp.offer ) return {0: response} @staticmethod def vars_for_template(player: Player): opp = player.get_others_in_group()[0] my_winnings = [num * player.factor for num in range(0, C.CHIPS_TOTAL + 1)] # +1 auf das Cvalues opp_winnings = [num * opp.factor for num in range(0, C.CHIPS_TOTAL + 1)] # +1 auf das Cvalues player_type = 'Grün' if player.role == C.GREEN_ROLE else 'Rot' return dict( my_winnings=my_winnings, opp_winnings=opp_winnings, color=player_type, ##auf Deutsch colorDE='Rot' if player.role == C.RED_ROLE else 'Grün', # title für Mein letztes angebot title_force="Wenn Sie diesen Button drücken stoppt die Verhandlung mit Ihrer zur Zeit eingestellten Verteilung. Denken Sie daran, das dieses Angebot ein forciertes ist & deshalb wahrscheinlicher von Ihrem Gegenüber abgelehnt wird.", title_abort='Mit diesem Button brechen Sie die Verhandlung ab. Sie müssen mit einem weiteren Button bestätigen', title_abort_confirm = 'Mit diesem Button brechen Sie die Verhandlung ab und sowohl Sie als auch Ihr(e) SpielpartnerIn erhalten jeweils 0 Geldeinheiten.', title_abort_reject = 'Mit diesem Button kehren Sie zur Verhandlung zurück.' ) @staticmethod def js_vars(player: Player): return dict( my_winnings=[num * player.factor for num in range(0, C.CHIPS_TOTAL + 1)], # +1 auf das Cvalues opp_winnings=[num * player.get_others_in_group()[0].factor for num in range(0, C.CHIPS_TOTAL + 1)], # +1 auf das Cvalues items=[num for num in range(0, C.CHIPS_TOTAL + 1)], num_chips=C.CHIPS_TOTAL, my_id=player.id_in_group, role=player.role, amount_till_forced = C.VOTES_TILL_FORCED ) @staticmethod def before_next_page(player: Player, timeout_happened): ## todo: Berechnung machen pass class ResultsWaitPage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): p1 = group.get_players()[0].in_round(group.round_number) p2 = group.get_players()[1].in_round(group.round_number) group.accepted = p1.offer_accept and p2.offer_accept if group.accepted: p1.payoff = p1.offer * p1.factor #C.FACTOR_FIRST[group.round_number - 1] p2.payoff = p2.offer * p2.factor #C.FACTOR_SECOND[group.round_number - 1] class Results(Page): @staticmethod def vars_for_template(player: Player): return dict( accepted=player.group.accepted, my_chips=player.offer, opp_chips=player.get_others_in_group()[0].offer, abort=player.group.aborted ) class ChooserChoosing(WaitPage): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS @staticmethod def after_all_players_arrive(group): if C.NUM_ROUNDS == group.round_number: group.chooser = random.randint(1,2) class CoinFlip(Page): @staticmethod def is_displayed(player: Player): return player.subsession.PAYMENT_MACHINE and player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player: Player): return dict( chooser= player.group.chooser == player.id_in_group ) @staticmethod def live_method(player: Player, data): result = data['result'] player.group.round_chosen = int(result) class WaitCoinFlip(WaitPage): @staticmethod def after_all_players_arrive(group: Group): pass @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS class ResultsTotal(Page): @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player: Player): online = player.subsession.TREATMENT == 'online' ## TODO: Behnud besprechen was mit lab angefangen wird: Antwort Behnud: erst einmal drinlassen. Im Moment keine Verwendung. ## TODO @Philip: bitte noch eine Steuerungsvariable numcards aufnehmen, die sagt wieviele Teilnehmernummern gezogen werden sollen lab = player.subsession.TREATMENT == 'lab' machine = player.subsession.PAYMENT_MACHINE payoff_round = player.group.round_chosen if online and payoff_round != -1: ## kein cointflip player.selected_payoff = player.in_round(payoff_round).payoff return dict( chips_1=player.in_round(1).offer if player.group.in_round(1).accepted else 0, money_1=int(player.in_round(1).payoff), chips_2=player.offer if player.group.accepted else 0, money_2=int(player.payoff), online = online, chosen_round = player.group.round_chosen, machine = machine, cash = player.subsession.CASH, num_cards = player.subsession.NUMCARDS ) class QuestionaireAdult(Page): form_model = 'player' form_fields = [ 'age', 'age_exact', 'gender', 'siblings', 'educational_qualification', 'study_status', 'work_negotiation', 'private_negotiation', 'division_part1', 'division_part2', 'knowledge_opposite'] @staticmethod def is_displayed(player: Player): return player.subsession.ADULT and player.round_number == C.NUM_ROUNDS and not player.subsession.UNIVERSITY class QuestionaireStudent(Page): template_name = 'chip_bargain/QuestionaireAdult.html' form_model = 'player' form_fields = ['age', 'age_exact', 'gender', 'siblings', 'educational_qualification', 'work_negotiation', 'private_negotiation', 'division_part1', 'division_part2'] @staticmethod def is_displayed(player: Player): return player.subsession.ADULT and player.subsession.UNIVERSITY and player.round_number == C.NUM_ROUNDS class QuestionaireChildren(Page): ## todo hier die richtigen Kinderfragen hinzufügen form_model = 'player' form_fields = [ 'age', 'gender', 'children_example_question' ] @staticmethod def is_displayed(player: Player): return not player.subsession.ADULT and player.round_number == C.NUM_ROUNDS class End(Page): @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS page_sequence = [NumberStage , ForcedWaitStage , Instructions , ShowColor , WaitToStart , Bargain , ResultsWaitPage , Results , ChooserChoosing , CoinFlip , WaitCoinFlip , ResultsTotal , QuestionaireAdult , QuestionaireStudent , QuestionaireChildren , End ]