from __future__ import division import random from otree.api import * from .config import C author = "Felix Holzmeister & Armin Pfurtscheller" doc = """ Bomb Risk Elicitation Task (BRET) à la Crosetto/Filippin (2013), Journal of Risk and Uncertainty (47): 31-65. """ # ******************************************************************************************************************** # # *** CLASS SUBSESSION *** # # ******************************************************************************************************************** # class Subsession(BaseSubsession): pass # ******************************************************************************************************************** # # *** CLASS GROUP *** # # ******************************************************************************************************************** # class Group(BaseGroup): pass # ******************************************************************************************************************** # # *** CLASS PLAYER *** # # ******************************************************************************************************************** # class Player(BasePlayer): # whether bomb is collected or not # store as integer because it's easier for interop with JS bomb = models.IntegerField() # location of bomb bomb_row = models.PositiveIntegerField() bomb_col = models.PositiveIntegerField() # number of collected boxes boxes_collected = models.IntegerField() # --- set round results and player's payoff # ------------------------------------------------------------------------------------------------------------------ pay_this_round = models.BooleanField() round_result = models.CurrencyField() # FUNCTIONS def set_payoff(player: Player): # determine round_result as (potential) payoff per round if player.bomb: player.round_result = cu(0) else: player.round_result = player.boxes_collected * C.BOX_VALUE # set payoffs if to round_result of randomly chosen round # randomly determine round to pay on player level if player.subsession.round_number == 1: player.participant.vars["round_to_pay"] = random.randint(1, C.NUM_ROUNDS) if C.RANDOM_PAYOFF: if player.subsession.round_number == player.participant.vars["round_to_pay"]: player.pay_this_round = True player.payoff = player.round_result else: player.pay_this_round = False player.payoff = cu(0) # set payoffs to round_result if else: player.payoff = player.round_result player.participant.vars["bret_result"] = player.payoff # PAGES # ******************************************************************************************************************** # # *** CLASS INSTRUCTIONS *** # # ******************************************************************************************************************** # class Instructions(Page): # only display instruction in round 1 @staticmethod def is_displayed(player: Player): return player.subsession.round_number == 1 # variables for use in template @staticmethod def vars_for_template(player: Player): return { "num_rows": C.NUM_ROWS, "num_cols": C.NUM_COLS, "num_boxes": C.NUM_ROWS * C.NUM_COLS, "num_nobomb": C.NUM_ROWS * C.NUM_COLS - 1, "box_value": C.BOX_VALUE, "time_interval": C.TIME_INTERVAL, } # ******************************************************************************************************************** # # *** CLASS BOMB RISK ELICITATION TASK *** # # ******************************************************************************************************************** # class Decision(Page): # form fields on player level form_model = "player" form_fields = [ "bomb", "boxes_collected", "bomb_row", "bomb_col", ] # jsonify BRET settings for Javascript application @staticmethod def vars_for_template(player: Player): reset = player.participant.vars.get("reset", False) if reset == True: del player.participant.vars["reset"] input = not C.DEVILS_GAME if not C.DYNAMIC else False otree_vars = { "reset": reset, "input": input, "random": C.RANDOM, "dynamic": C.DYNAMIC, "num_rows": C.NUM_ROWS, "num_cols": C.NUM_COLS, "feedback": C.FEEDBACK, "undoable": C.UNDOABLE, "box_width": C.BOX_WIDTH, "box_height": C.BOX_HEIGHT, "time_interval": C.TIME_INTERVAL, } return {"otree_vars": safe_json(otree_vars)} @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.vars["reset"] = True set_payoff(player) # ******************************************************************************************************************** # # *** CLASS RESULTS *** # # ******************************************************************************************************************** # class Results(Page): # only display results after all rounds have been played @staticmethod def is_displayed(player: Player): return player.subsession.round_number == C.NUM_ROUNDS # variables for use in template @staticmethod def vars_for_template(player: Player): total_payoff = sum([p.payoff for p in player.in_all_rounds()]) player.participant.vars["bret_payoff"] = total_payoff return { "player_in_all_rounds": player.in_all_rounds(), "box_value": C.BOX_VALUE, "boxes_total": C.NUM_ROWS * C.NUM_COLS, "boxes_collected": player.boxes_collected, "bomb": player.bomb, "bomb_row": player.bomb_row, "bomb_col": player.bomb_col, "round_result": player.round_result, "round_to_pay": player.participant.vars["round_to_pay"], "payoff": player.payoff, "total_payoff": total_payoff, } # ******************************************************************************************************************** # # *** PAGE SEQUENCE *** # # ******************************************************************************************************************** # page_sequence = [Decision] if C.INSTRUCTIONS == True: page_sequence.insert(0, Instructions) if C.RESULTS == True: page_sequence.append(Results)