from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random author = 'Jake ZHANG Shiyu' doc = """ An attemp to do experiment on Cheap Talk with Otree. """ class Constants(BaseConstants): name_in_url = 'antidote' players_per_group = None num_rounds = 15 Roles = ['S', 'TR', 'FR'] class Subsession(BaseSubsession): typeO_prob = models.FloatField(initial=0.2) conflict = models.IntegerField(initial=6) num_FR = models.IntegerField(initial=1) local_seed = models.IntegerField(initial=-1) sender_Mistake = models.FloatField(initial=0) def creating_session(self): self.typeO_prob = self.session.config['typeO_prob'] self.conflict = self.session.config['conflict'] self.num_FR = self.session.config['num_FR'] self.sender_Mistake = self.session.config['SM_numerator'] / self.session.config['SM_denominator'] # to check whether (num_participants - num_FR) is even or not players = self.get_players() if (len(players) - self.num_FR) % 2 != 0: print("The remaining subjects can not be partitioned into pairs," "numeber of false R is decreased by one") self.num_FR = self.num_FR - 1 # use local random to assign roles to participants self.local_seed = random.randint(0, 999) if self.round_number < 6: local_random = random.Random(self.local_seed) local_random.shuffle(players) for index in range(0, len(players)): if index < self.num_FR: # for the first num_FR players, they are assigned FR players[index].roles = Constants.Roles[2] players[index].pseudo_group = index + 1 local_random = random.Random(self.local_seed + players[index].pseudo_group) draw = local_random.random() if draw <= self.typeO_prob: players[index].type = 0 else: players[index].type = 1 else: # The rest (n-num_FR) players are assigned S or TR players[index].roles = Constants.Roles[(index - self.num_FR) % 2] players[index].pseudo_group = (index - self.num_FR) // 2 + self.num_FR + 1 if (index - self.num_FR) % 2 == 0: # a lucky draw determine the type for S local_random = random.Random(self.local_seed + players[index].pseudo_group) draw = local_random.random() if draw <= self.typeO_prob: players[index].type = 0 else: players[index].type = 1 else: # Then TR's type is set to be the same as the paired S's players[index].type = players[index - 1].type else: local_random = random.Random(99) local_random.shuffle(players) num_pairs = int((len(players) - self.num_FR) / 2) to_shuffle = players[self.num_FR + num_pairs:] random.Random(self.local_seed + self.round_number).shuffle(to_shuffle) players = players[ :self.num_FR + num_pairs] + to_shuffle # The first (num_FR + num_pairs) player are not shuffled for index in range(0, len(players)): if index < self.num_FR: # for the first num_FR players, they are assigned FR players[index].roles = Constants.Roles[2] players[index].pseudo_group = index + 1 local_random = random.Random(self.local_seed + players[index].pseudo_group) draw = local_random.random() if draw <= self.typeO_prob: players[index].type = 0 else: players[index].type = 1 else: if index < self.num_FR + num_pairs: # for those whose index is btw num_FR and (num_FR + # num_pairs), they are assigned S players[index].roles = Constants.Roles[0] players[index].pseudo_group = index + 1 local_random = random.Random(self.local_seed + players[index].pseudo_group) draw = local_random.random() if draw <= self.typeO_prob: players[index].type = 0 else: players[index].type = 1 else: # for the remaining, they are assigned to be TR, and is paired with one whose index is theirs - num_pairs players[index].roles = Constants.Roles[1] players[index].pseudo_group = index - num_pairs + 1 players[index].type = players[index - num_pairs].type class Group(BaseGroup): pass # dice = models.IntegerField(initial=-1, min=1, max=6) class Player(BasePlayer): roles = models.StringField() type = models.IntegerField(initial=-1, min=0, max=1) message_sent = models.IntegerField(initial=-1) action = models.IntegerField(initial=-1, min=0, max=10) message_received = models.IntegerField(initial=-1, min=0, max=1) pseudo_group = models.IntegerField() def live_instruct(self, data): if data['read'] == 1: if self.read == -1: self.read = 1 return {self.id_in_group: {'read': self.read}} read = models.IntegerField(initial=-1) q1 = models.IntegerField(choices=[ [1, "My role will be randomly determined for each of the official rounds. " + "So, I could play the role of True R or False R as well in the later rounds."], [2, 'My role is fixed as S for the rest of the experiment. In each official round, ' 'I will be randomly paired with a True R participant who knows that he will receive a message from me.'], [3, 'My role is fixed as S for the rest of the experiment. In each official round, I will be paired with a True R ' 'participant who does not know whether s/he is True R or False R.'], [4, 'The True R participant in my pair will decide how many grams of herbal extract to put to make an antidote. But his/her decision does not affect my point.'] ], widget=widgets.RadioSelect, label='Suppose at the beginning of the 1st official round, your role is assigned to be S. ' 'Which of the following statements is true?') q2 = models.IntegerField(choices=[ [1, 'My role will be randomly determined for each of the official rounds. So, I could play the role of S in the later rounds.'], [2, 'My role will be True R for the rest of the experiment. In each round, I will receive a message from S who knows exactly the poison type for me.'], [3, 'My role will be False R for the rest of the experiment. In each round, I will receive a message from S who does not know what type of poison I am affected.'], [4, 'My role will be fixed but I don’t know if my role is True R or False R. In any case, I will receive a message from S who knows exactly the poison type for me in each round.'], [5, 'My role will be fixed but I don’t know if my role is True R or False R. The chances are half-and-half that the message I receive is from S who knows exactly the poison type for me or from S who does not know what type of poison I am affected.'] ], widget=widgets.RadioSelect, label='Suppose at the beginning of the 1st official round, you are informed that your role is R. ' 'Which of the following statements is true?') q3 = models.IntegerField(choices=[ [1, 'No S in the experiment knows the poison type for Albert.'], [2, 'Albert is matched with some S who knows the poison type for Albert.'], [3, 'The message Albert receives is from some S who knows poison type for Albert.'], [4, 'The message Albert receives will always be wrong.'] ], widget=widgets.RadioSelect, label='Imagine a participant Albert whose role is False R. Which of the followings is true?') q4a = models.IntegerField(label='you put 0 gram of the herbal extract and the poison is type 0? Your Answer:') q4b = models.IntegerField(label='you put 4 gram of the herbal extract and the poison is type 1? Your Answer:') q4c = models.IntegerField(label='you put 8 gram of the herbal extract and the poison is type 1? Your Answer:') q4d = models.IntegerField(label='you put 10 gram of the herbal extract and the poison is type 0? Your Answer:') q5a = models.IntegerField(label='0 gram of the herbal extract and the poison is type 1? Your Answer:') q5b = models.IntegerField(label='3 gram of the herbal extract and the poison is type 0? Your Answer:') q5c = models.IntegerField(label='6 gram of the herbal extract and the poison is type 0? Your Answer:') q5d = models.IntegerField(label='10 gram of the herbal extract and the poison is type 1? Your Answer:') q6a = models.IntegerField(min=0, max=10, label="[There is no right or wrong answer to this question] What action do you want to choose?") q6b = models.IntegerField( label="What is your earning from the action you chose above if the poison is TYPE 0 which occurs with 20% chance?") q6c = models.IntegerField( label=" What is your earning from the action you chose above if the poison is TYPE 1 which occurs with 80% chance?") q7 = models.IntegerField(choices=[ [1, 'R always receives the message “The poison is TYPE 0”.'], [2, 'R may receive the message “The poison is TYPE 1” instead.'], [3, 'When receiving the message “The poison is TYPE 0”, R surely knows that S chooses to send the message “The poison is TYPE 0”.'], ], widget=widgets.RadioSelect, label='Suppose S sends the message “The poison is TYPE 0”. Which of the following statements is true?') q8 = models.IntegerField(choices=[ [1, 'R always receives the message “The poison is TYPE 1”.'], [2, 'R may receive the message “The poison is TYPE 0” instead.'], [3, 'When receiving the message “The poison is TYPE 1”, R surely knows that S chooses to send the message “The poison is TYPE 1”.'], ], widget=widgets.RadioSelect, label='Suppose S sends the message “The poison is TYPE 1”. Which of the following statements is false?') def set_error_message(self, value): if self.subsession.num_FR > 0: correct_answers = {"q1": 3, "q2": 5, "q3": 1, "q4a": 300, "q4b": 300 - 3 * ((10 - 4) ** 2), "q4c": 300 - 3 * ((10 - 8) ** 2), "q4d": 0, "q5a": 300 - (10 + self.subsession.conflict) ** 2, "q5b": 300 - (self.subsession.conflict - 3) ** 2, "q5c": 300 - (self.subsession.conflict - 6) ** 2, "q5d": 300 - (self.subsession.conflict + 10 - 10) ** 2, "q6a": value['q6a'], "q6b": 300 - 3 * ((value['q6a']) ** 2), "q6c": 300 - 3 * ((10 - value['q6a']) ** 2), 'q7': 1, 'q8': 1 } else: if self.subsession.sender_Mistake == 0: correct_answers = {"q1": 2, "q2": 2, "q3": 1, "q4a": 300, "q4b": 300 - 3 * ((10 - 4) ** 2), "q4c": 300 - 3 * ((10 - 8) ** 2), "q4d": 0, "q5a": 300 - (10 + self.subsession.conflict) ** 2, "q5b": 300 - (self.subsession.conflict - 3) ** 2, "q5c": 300 - (self.subsession.conflict - 6) ** 2, "q5d": 300 - (self.subsession.conflict + 10 - 10) ** 2, "q6a": value['q6a'], "q6b": 300 - 3 * ((value['q6a']) ** 2), "q6c": 300 - 3 * ((10 - value['q6a']) ** 2), 'q7': 1, 'q8': 1 } else: correct_answers = {"q1": 2, "q2": 2, "q3": 1, "q4a": 300, "q4b": 300 - 3 * ((10 - 4) ** 2), "q4c": 300 - 3 * ((10 - 8) ** 2), "q4d": 0, "q5a": 300 - (10 + self.subsession.conflict) ** 2, "q5b": 300 - (self.subsession.conflict - 3) ** 2, "q5c": 300 - (self.subsession.conflict - 6) ** 2, "q5d": 300 - (self.subsession.conflict + 10 - 10) ** 2, "q6a": value['q6a'], "q6b": 300 - 3 * ((value['q6a']) ** 2), "q6c": 300 - 3 * ((10 - value['q6a']) ** 2), 'q7': 1, 'q8': 1 } list_answers = list(value.items())[0:] list_correct_answers = list(correct_answers.items()) if list_answers != list_correct_answers: Text = 'Some of your answers are incorrect. It indicates that your understanding may not be fully accurate. ' \ 'Please try it again. You can always go back to the experimental instructions if you find anything unclear.' return Text def set_error_message2(self, value): correct_answers = {"q6a": value['q6a'], "q6b": 300 - 3 * ((value['q6a']) ** 2), "q6c": 300 - 3 * ((10 - value['q6a']) ** 2)} list_answers = list(value.items())[0:] list_correct_answers = list(correct_answers.items()) if list_answers != list_correct_answers: Text = 'Some of your answers are incorrect. It indicates that your understanding may not be fully accurate. ' \ 'Please try it again. You can always go back to the experimental instructions if you find anything unclear.' return Text