from otree.api import * author = 'Niklas Weller, Adam Kadmani' doc = """ 2D Packing Problem - Experiment """ class Constants(BaseConstants): name_in_url = 'packing_experiment' players_per_group = None num_rounds = 1 goal_correct_value = 'Packing all boxes while minimizing the used height in the given container' numberOfRounds_correct_value = '2 Rounds' rotationAllowed_correct_value = 'Yes, rotation is possible' move_correct_value = 'Depends, only the most recently packed box can be moved' class Subsession(BaseSubsession): pass class Group(BaseGroup): def runIt(self): p = self.get_players() p.payoff = 0 QUESTIONS = { 'age': { 'label': 'Which category below includes your age?', 'choices': [ 'younger than 21', '21-40', '41-60', '61 or older', ], 'widget': widgets.RadioSelect, }, 'gender': { 'label': 'What is your gender?', 'choices': ['Female', 'Male', 'Other'], 'widget': widgets.RadioSelect, }, 'education': { 'label': 'What is the highest level of school you have completed or the highest degree you have received?', 'choices': [ 'Less than high school degree', 'High school degree or equivalent', 'College degree or equivalent', ], 'widget': widgets.RadioSelect, }, 'student': { 'label': 'Are you currently enrolled in a college / university?', 'choices': [ 'No', 'Yes', ], 'widget': widgets.RadioSelect, }, 'employment': { 'label': 'Which of the following categories best describes your employment status?', 'choices': [ 'Part-time employed', 'Full-time employed', 'Unemployed', ], 'widget': widgets.RadioSelect, }, 'income': { 'label': 'How much total combined money did all members of your household earn in 2018?', 'choices': [ '$0 – $19,999', '$20,000 – $29,999', '$30,000 – $39,999', '$40,000 – $49,999', '$50,000 or more', ], 'widget': widgets.RadioSelect, }, 'device1': { 'label': 'What kind of device did you use?', 'choices': [ 'Desktop Computer or Mac', 'Laptop or MacBook', 'Smartphone or Tablet', 'Other', ], 'widget': widgets.RadioSelect, }, 'device2': { 'label': 'How did you pack the boxes?', 'choices': [ 'Using the mouse', 'Using the Touchpad', 'Using the Touchscreen', 'Other', ], 'widget': widgets.RadioSelect, }, 'region': { 'label': 'Which region do you currently live in?', 'choices': [ 'US', 'Canada', 'Rest of the World', ], 'widget': widgets.RadioSelect, }, 'batandball': { 'label': '''A bat and a ball cost $1.10 in total. The bat costs a dollar more than the ball. How many dollars does the ball cost?''', 'min': 0, }, 'machineswidgets': { 'label': '''If it takes 5 machines 5 minutes to make 5 widgets, how many minutes would it take 100 machines to make 100 widgets?''', 'min': 0, }, 'lilypads': { 'label': '''In a lake, there is a patch of lily pads. Every day, the patch doubles in size. If it takes 48 days for the patch to cover the entire lake, how many days would it take for the patch to cover half of the lake?''', 'min': 0, }, 'method': { 'label': '''Did you use any strategy to solve these problems, or was your approach more intuitive?''', 'choices': [ 'Intuitive', 'Strategic', ], 'widget': widgets.RadioSelect, }, 'explain': { 'label': '''Please explain you approach briefly''', }, 'timep': { 'label': 'Please specify the time pressure you experienced?', 'choices': [ 'Very low', 'Low', 'Medium', 'High', 'Verry high', ], 'widget': widgets.RadioSelect, }, } class Player(BasePlayer): numberOfRounds = models.StringField( choices=['2 Rounds', '3 Rounds', '1 Round'], label='How many rounds of packing tasks will you need to complete?', ) rotationAllowed = models.StringField( choices=[ 'Yes, rotation is possible', 'No, rotation is not possible', 'Depends, only one box can be rotated', ], label='Will you be able to rotate boxes?', ) move = models.StringField( choices=[ 'Yes, every box can be moved at any time', 'No, once a box is placed it is impossible to change its position', 'Depends, only the most recently packed box can be moved', ], label='Will you be able to move boxes after you have placed them inside the container?', ) goal = models.StringField( choices=[ 'Packing all boxes in the least possible number of containers', 'Packing all boxes while minimizing the used height in the given container', 'Filling the container as efficiently as possible with a few selected boxes', ], label='What should you aim for in the experiment', ) num_page_loads = models.IntegerField( initial=0, doc="""increases whenever a page loads, refresh adds 1 again""" ) age = models.StringField(**QUESTIONS['age']) gender = models.StringField(**QUESTIONS['gender']) method = models.StringField(**QUESTIONS['method']) explain = models.StringField(**QUESTIONS['explain']) education = models.StringField(**QUESTIONS['education']) device1 = models.StringField(**QUESTIONS['device1']) device2 = models.StringField(**QUESTIONS['device2']) timep = models.StringField(**QUESTIONS['timep']) payoffB = models.CurrencyField(initial=0) payoffC = models.CurrencyField(initial=0) # x1 = models.IntegerField() # y1 = models.IntegerField() # x2 = models.IntegerField() # y2 = models.IntegerField() # x3 = models.IntegerField() # y3 = models.IntegerField() # x4 = models.IntegerField() # y4 = models.IntegerField() # x5 = models.IntegerField() # y5 = models.IntegerField() # x6 = models.IntegerField() # y6 = models.IntegerField() # x7 = models.IntegerField() # y7 = models.IntegerField() # x8 = models.IntegerField() # y8 = models.IntegerField() # x9 = models.IntegerField() # y9 = models.IntegerField() # x10 = models.IntegerField() # y10 = models.IntegerField() # x11 = models.IntegerField() # y11 = models.IntegerField() # x12 = models.IntegerField() # y12 = models.IntegerField() # x13 = models.IntegerField() # y13 = models.IntegerField() # x14 = models.IntegerField() # y14 = models.IntegerField() # x15 = models.IntegerField() # y15 = models.IntegerField() # seq1 = models.StringField() # seq2 = models.StringField() # seq3 = models.StringField() # seq4 = models.StringField() # seq5 = models.StringField() # seq6 = models.StringField() # seq7 = models.StringField() # seq8 = models.StringField() # seq9 = models.StringField() # seq10 = models.StringField() # seq11 = models.StringField() # seq12 = models.StringField() # seq13 = models.StringField() # seq14 = models.StringField() # seq15 = models.StringField() # highestPoint = models.IntegerField() # aRotationCounter = models.IntegerField() bx1 = models.IntegerField() by1 = models.IntegerField() bx2 = models.IntegerField() by2 = models.IntegerField() bx3 = models.IntegerField() by3 = models.IntegerField() bx4 = models.IntegerField() by4 = models.IntegerField() bx5 = models.IntegerField() by5 = models.IntegerField() bx6 = models.IntegerField() by6 = models.IntegerField() bx7 = models.IntegerField() by7 = models.IntegerField() bx8 = models.IntegerField() by8 = models.IntegerField() bx9 = models.IntegerField() by9 = models.IntegerField() bx10 = models.IntegerField() by10 = models.IntegerField() bx11 = models.IntegerField() by11 = models.IntegerField() bx12 = models.IntegerField() by12 = models.IntegerField() bx13 = models.IntegerField() by13 = models.IntegerField() bx14 = models.IntegerField() by14 = models.IntegerField() bx15 = models.IntegerField() by15 = models.IntegerField() bx16 = models.IntegerField() by16 = models.IntegerField() bx17 = models.IntegerField() by17 = models.IntegerField() bx18 = models.IntegerField() by18 = models.IntegerField() bx19 = models.IntegerField() by19 = models.IntegerField() bx20 = models.IntegerField() by20 = models.IntegerField() bseq1 = models.StringField() bseq2 = models.StringField() bseq3 = models.StringField() bseq4 = models.StringField() bseq5 = models.StringField() bseq6 = models.StringField() bseq7 = models.StringField() bseq8 = models.StringField() bseq9 = models.StringField() bseq10 = models.StringField() bseq11 = models.StringField() bseq12 = models.StringField() bseq13 = models.StringField() bseq14 = models.StringField() bseq15 = models.StringField() bseq16 = models.StringField() bseq17 = models.StringField() bseq18 = models.StringField() bseq19 = models.StringField() bseq20 = models.StringField() bHighestPoint = models.IntegerField() bRotationCounter = models.IntegerField() epochB = models.FloatField() epochC = models.FloatField() r1 = models.IntegerField() r2 = models.IntegerField() r3 = models.IntegerField() r4 = models.IntegerField() r5 = models.IntegerField() r6 = models.IntegerField() r7 = models.IntegerField() r8 = models.IntegerField() r9 = models.IntegerField() r10 = models.IntegerField() r11 = models.IntegerField() r12 = models.IntegerField() r13 = models.IntegerField() r14 = models.IntegerField() r15 = models.IntegerField() r16 = models.IntegerField() r17 = models.IntegerField() r18 = models.IntegerField() r19 = models.IntegerField() r20 = models.IntegerField() cx1 = models.IntegerField() cy1 = models.IntegerField() cx2 = models.IntegerField() cy2 = models.IntegerField() cx3 = models.IntegerField() cy3 = models.IntegerField() cx4 = models.IntegerField() cy4 = models.IntegerField() cx5 = models.IntegerField() cy5 = models.IntegerField() cx6 = models.IntegerField() cy6 = models.IntegerField() cx7 = models.IntegerField() cy7 = models.IntegerField() cx8 = models.IntegerField() cy8 = models.IntegerField() cx9 = models.IntegerField() cy9 = models.IntegerField() cx10 = models.IntegerField() cy10 = models.IntegerField() cx11 = models.IntegerField() cy11 = models.IntegerField() cx12 = models.IntegerField() cy12 = models.IntegerField() cx13 = models.IntegerField() cy13 = models.IntegerField() cx14 = models.IntegerField() cy14 = models.IntegerField() cx15 = models.IntegerField() cy15 = models.IntegerField() cx16 = models.IntegerField() cy16 = models.IntegerField() cx17 = models.IntegerField() cy17 = models.IntegerField() cx18 = models.IntegerField() cy18 = models.IntegerField() cx19 = models.IntegerField() cy19 = models.IntegerField() cx20 = models.IntegerField() cy20 = models.IntegerField() cseq1 = models.StringField() cseq2 = models.StringField() cseq3 = models.StringField() cseq4 = models.StringField() cseq5 = models.StringField() cseq6 = models.StringField() cseq7 = models.StringField() cseq8 = models.StringField() cseq9 = models.StringField() cseq10 = models.StringField() cseq11 = models.StringField() cseq12 = models.StringField() cseq13 = models.StringField() cseq14 = models.StringField() cseq15 = models.StringField() cseq16 = models.StringField() cseq17 = models.StringField() cseq18 = models.StringField() cseq19 = models.StringField() cseq20 = models.StringField() cr1 = models.IntegerField() cr2 = models.IntegerField() cr3 = models.IntegerField() cr4 = models.IntegerField() cr5 = models.IntegerField() cr6 = models.IntegerField() cr7 = models.IntegerField() cr8 = models.IntegerField() cr9 = models.IntegerField() cr10 = models.IntegerField() cr11 = models.IntegerField() cr12 = models.IntegerField() cr13 = models.IntegerField() cr14 = models.IntegerField() cr15 = models.IntegerField() cr16 = models.IntegerField() cr17 = models.IntegerField() cr18 = models.IntegerField() cr19 = models.IntegerField() cr20 = models.IntegerField() cHighestPoint = models.IntegerField() cRotationCounter = models.IntegerField() # dx1 = models.IntegerField() # dy1 = models.IntegerField() # dx2 = models.IntegerField() # dy2 = models.IntegerField() # dx3 = models.IntegerField() # dy3 = models.IntegerField() # dx4 = models.IntegerField() # dy4 = models.IntegerField() # dx5 = models.IntegerField() # dy5 = models.IntegerField() # dx6 = models.IntegerField() # dy6 = models.IntegerField() # dx7 = models.IntegerField() # dy7 = models.IntegerField() # dx8 = models.IntegerField() # dy8 = models.IntegerField() # dx9 = models.IntegerField() # dy9 = models.IntegerField() # dx10 = models.IntegerField() # dy10 = models.IntegerField() # dx11 = models.IntegerField() # dy11 = models.IntegerField() # dx12 = models.IntegerField() # dy12 = models.IntegerField() # dx13 = models.IntegerField() # dy13 = models.IntegerField() # dx14 = models.IntegerField() # dy14 = models.IntegerField() # dx15 = models.IntegerField() # dy15 = models.IntegerField() # dseq1 = models.StringField() # dseq2 = models.StringField() # dseq3 = models.StringField() # dseq4 = models.StringField() # dseq5 = models.StringField() # dseq6 = models.StringField() # dseq7 = models.StringField() # dseq8 = models.StringField() # dseq9 = models.StringField() # dseq10 = models.StringField() # dseq11 = models.StringField() # dseq12 = models.StringField() # dseq13 = models.StringField() # dseq14 = models.StringField() # dseq15 = models.StringField() # dHighestPoint = models.IntegerField() # dRotationCounter = models.IntegerField() rounds1 = models.BooleanField() rot1 = models.BooleanField() move1 = models.BooleanField() goal1 = models.BooleanField() b = models.IntegerField( initial=0, ) # FUNCTIONS def numberOfRounds_error_message(player: Player, numberOfRounds): if numberOfRounds != Constants.numberOfRounds_correct_value: player.b += 1 def rotationAllowed_error_message(player: Player, rotationAllowed): if rotationAllowed != Constants.rotationAllowed_correct_value: player.b += 1 def move_error_message(player: Player, move): if move != Constants.move_correct_value: player.b += 1 def goal_error_message(player: Player, goal): if goal != Constants.goal_correct_value: player.b += 1 # PAGES class Intro(Page): pass class Quiz(Page): form_model = 'player' form_fields = ['goal', 'numberOfRounds', 'rotationAllowed', 'move'] # class PageA(Page): # form_model = 'player' # form_fields = [ # 'x1', # 'y1', # 'x2', # 'y2', # 'x3', # 'y3', # 'x4', # 'y4', # 'x5', # 'y5', # 'x6', # 'y6', # 'x7', # 'y7', # 'x8', # 'y8', # 'x9', # 'y9', # 'x10', # 'y10', # 'x11', # 'y11', # 'x12', # 'y12', # 'x13', # 'y13', # 'x14', # 'y14', # 'x15', # 'y15', # 'seq1', # 'seq2', # 'seq3', # 'seq4', # 'seq5', # 'seq6', # 'seq7', # 'seq8', # 'seq9', # 'seq10', # 'seq11', # 'seq12', # 'seq13', # 'seq14', # 'seq15', # 'highestPoint', # 'aRotationCounter', # ] class PageB(Page): form_model = 'player' form_fields = [ 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'r16', 'r17', 'r18', 'r19', 'r20', 'bx1', 'by1', 'bx2', 'by2', 'bx3', 'by3', 'bx4', 'by4', 'bx5', 'by5', 'bx6', 'by6', 'bx7', 'by7', 'bx8', 'by8', 'bx9', 'by9', 'bx10', 'by10', 'bx11', 'by11', 'bx12', 'by12', 'bx13', 'by13', 'bx14', 'by14', 'bx15', 'by15', 'bx16', 'by16', 'bx17', 'by17', 'bx18', 'by18', 'bx19', 'by19', 'bx20', 'by20', 'bseq1', 'bseq2', 'bseq3', 'bseq4', 'bseq5', 'bseq6', 'bseq7', 'bseq8', 'bseq9', 'bseq10', 'bseq11', 'bseq12', 'bseq13', 'bseq14', 'bseq15', 'bseq16', 'bseq17', 'bseq18', 'bseq19', 'bseq20', 'bHighestPoint', 'bRotationCounter', 'epochB', ] @staticmethod def vars_for_template(player: Player): player.num_page_loads += 1 @staticmethod def before_next_page(player: Player, timeout_happened): player.payoff = max((450 - player.bHighestPoint) / 40, 0) value = player.payoff player.payoffB = value class PageC(Page): form_model = 'player' form_fields = [ 'cr1', 'cr2', 'cr3', 'cr4', 'cr5', 'cr6', 'cr7', 'cr8', 'cr9', 'cr10', 'cr11', 'cr12', 'cr13', 'cr14', 'cr15', 'cr16', 'cr17', 'cr18', 'cr19', 'cr20', 'cx1', 'cy1', 'cx2', 'cy2', 'cx3', 'cy3', 'cx4', 'cy4', 'cx5', 'cy5', 'cx6', 'cy6', 'cx7', 'cy7', 'cx8', 'cy8', 'cx9', 'cy9', 'cx10', 'cy10', 'cx11', 'cy11', 'cx12', 'cy12', 'cx13', 'cy13', 'cx14', 'cy14', 'cx15', 'cy15', 'cx16', 'cy16', 'cx17', 'cy17', 'cx18', 'cy18', 'cx19', 'cy19', 'cx20', 'cy20', 'cseq1', 'cseq2', 'cseq3', 'cseq4', 'cseq5', 'cseq6', 'cseq7', 'cseq8', 'cseq9', 'cseq10', 'cseq11', 'cseq12', 'cseq13', 'cseq14', 'cseq15', 'cseq16', 'cseq17', 'cseq18', 'cseq19', 'cseq20', 'cHighestPoint', 'cRotationCounter', 'epochC', ] @staticmethod def vars_for_template(player: Player): player.num_page_loads += 1 @staticmethod def before_next_page(player: Player, timeout_happened): player.payoff += max((450 - player.cHighestPoint) / 40, 0) value = player.payoff player.payoffC = value - player.payoffB # class PageD(Page): # form_model = 'player' # form_fields = [ # 'dx1', # 'dy1', # 'dx2', # 'dy2', # 'dx3', # 'dy3', # 'dx4', # 'dy4', # 'dx5', # 'dy5', # 'dx6', # 'dy6', # 'dx7', # 'dy7', # 'dx8', # 'dy8', # 'dx9', # 'dy9', # 'dx10', # 'dy10', # 'dx11', # 'dy11', # 'dx12', # 'dy12', # 'dx13', # 'dy13', # 'dx14', # 'dy14', # 'dx15', # 'dy15', # 'dseq1', # 'dseq2', # 'dseq3', # 'dseq4', # 'dseq5', # 'dseq6', # 'dseq7', # 'dseq8', # 'dseq9', # 'dseq10', # 'dseq11', # 'dseq12', # 'dseq13', # 'dseq14', # 'dseq15', # 'dHighestPoint', # 'dRotationCounter', # ] class Questionnaire(Page): form_model = 'player' # form_fields = ['age', 'gender', 'education', 'device1', 'device2', 'method', 'explain',] form_fields = ['gender', 'device1', 'device2', 'timep', 'method', 'explain'] class Results(Page): form_model = 'player' form_fields = ['bHighestPoint', 'cHighestPoint'] @staticmethod def vars_for_template(player: Player): # this stores the paricipant's unique code that otree (automatically) assigns them. return { "completion_code": player.participant.code, } class Error(Page): @staticmethod def is_displayed(player: Player): return player.b != 0 page_sequence = [ Intro, Quiz, Error, # PageA, PageB, PageC, # PageD, Questionnaire, Results, ] # no $0.00 payoff in the reward guide - should get under the marker # store whether every box is rotated or not