from otree.api import * c = cu doc = '' class C(BaseConstants): NAME_IN_URL = 'International_Trade_Game' PLAYERS_PER_GROUP = 4 NUM_ROUNDS = 4 SADIARABICA_ROLE = 'Sadi Arabica' CHINLAND_ROLE = 'Chinland' AMERIKAS_ROLE = 'Amerikas' BRAZILIA_ROLE = 'Brazilia' RESOURCE_ORDER_REFERENCE = ('energy', 'products', 'medical', 'food') SADIARABICA_STOCKPILE = (10, 20, 20, 20) CHINLAND_STOCKPILE = (20, 0, 20, 20) AMERIKAS_STOCKPILE = (20, 10, 20, 30) BRAZILIA_STOCKPILE = (0, 0, 10, 30) SADIARABICA_OUTPUT = (80, 0, 0, 0) CHINLAND_OUTPUT = (0, 80, 0, 0) AMERIKAS_OUTPUT = (0, 0, 80, 0) BRAZILIA_OUTPUT = (0, 0, 0, 80) SADIARABICA_RESOURCE_COST = (1, 4, 3, 5) CHINLAND_RESOURCE_COST = (5, 1, 4, 3) AMERIKAS_RESOURCE_COST = (3, 5, 1, 4) BRAZILIA_RESOURCE_COST = (4, 3, 5, 1) SHOCK_EVENT_SEQ = ('Global pandemic', 'Brazilia oil find', 'Chinland earthquake', 'Brazilia drought') SADIARABICA_SAVINGS = 70 CHINLAND_SAVINGS = 50 AMERIKAS_SAVINGS = 100 BRAZILIA_SAVINGS = 20 ENERGY_SHORTAGE_HIKE = 0.075 PRODUCTS_SHORTAGE_HIKE = 0.05 MEDICAL_SHORTAGE_HIKE = 0.1 FOOD_SHORTAGE_HIKE = 0.075 ENERGY_SHORTAGE_REDUCTION = 0.75 PRODUCTS_SHORTAGE_REDUCTION = 0.5 MEDICAL_SHORTAGE_REDUCTION = 1 FOOD_SHORTAGE_REDUCTION = 0.75 class Subsession(BaseSubsession): pass def vars_for_admin_report(subsession: Subsession): session = subsession.session payoffs = sorted([p.savings for p in subsession.get_players()]) return dict(payoffs=payoffs) class Group(BaseGroup): oil_shock_round = models.IntegerField() drought_shock_round = models.IntegerField() earthquake_shock_round = models.IntegerField() pandemic_shock_round = models.IntegerField() amerikas_check = models.BooleanField(initial=True) brazilia_check = models.BooleanField(initial=True) chinland_check = models.BooleanField(initial=True) sadiarabica_check = models.BooleanField(initial=True) def set_country_stats(group: Group): # import math players = group.get_players() # Get order of shock events for index,event in enumerate(C.SHOCK_EVENT_SEQ): if event == "Brazilia oil find": group.oil_shock_round = index + 2 elif event == "Brazilia drought": group.drought_shock_round = index + 2 elif event == "Chinland earthquake": group.earthquake_shock_round = index + 2 elif event == "Global pandemic": group.pandemic_shock_round = index + 2 round = group.round_number if group.round_number == 1: for p in players: p.needs_energy = 20 p.needs_products = 20 p.needs_medical = 20 p.needs_food = 20 if p.role == C.SADIARABICA_ROLE: p.savings = C.SADIARABICA_SAVINGS p.stockpile_energy = C.SADIARABICA_STOCKPILE[0] p.stockpile_products = C.SADIARABICA_STOCKPILE[1] p.stockpile_medical = C.SADIARABICA_STOCKPILE[2] p.stockpile_food = C.SADIARABICA_STOCKPILE[3] p.outputs_energy = C.SADIARABICA_OUTPUT[0] p.outputs_products = C.SADIARABICA_OUTPUT[1] p.outputs_medical = C.SADIARABICA_OUTPUT[2] p.outputs_food = C.SADIARABICA_OUTPUT[3] p.costs_energy = C.SADIARABICA_RESOURCE_COST[0] p.costs_products = C.SADIARABICA_RESOURCE_COST[1] p.costs_medical = C.SADIARABICA_RESOURCE_COST[2] p.costs_food = C.SADIARABICA_RESOURCE_COST[3] elif p.role == C.CHINLAND_ROLE: p.savings = C.CHINLAND_SAVINGS p.stockpile_energy = C.CHINLAND_STOCKPILE[0] p.stockpile_products = C.CHINLAND_STOCKPILE[1] p.stockpile_medical = C.CHINLAND_STOCKPILE[2] p.stockpile_food = C.CHINLAND_STOCKPILE[3] p.outputs_energy = C.CHINLAND_OUTPUT[0] p.outputs_products = C.CHINLAND_OUTPUT[1] p.outputs_medical = C.CHINLAND_OUTPUT[2] p.outputs_food = C.CHINLAND_OUTPUT[3] p.costs_energy = C.CHINLAND_RESOURCE_COST[0] p.costs_products = C.CHINLAND_RESOURCE_COST[1] p.costs_medical = C.CHINLAND_RESOURCE_COST[2] p.costs_food = C.CHINLAND_RESOURCE_COST[3] elif p.role == C.AMERIKAS_ROLE: p.savings = C.AMERIKAS_SAVINGS p.stockpile_energy = C.AMERIKAS_STOCKPILE[0] p.stockpile_products = C.AMERIKAS_STOCKPILE[1] p.stockpile_medical = C.AMERIKAS_STOCKPILE[2] p.stockpile_food = C.AMERIKAS_STOCKPILE[3] p.outputs_energy = C.AMERIKAS_OUTPUT[0] p.outputs_products = C.AMERIKAS_OUTPUT[1] p.outputs_medical = C.AMERIKAS_OUTPUT[2] p.outputs_food = C.AMERIKAS_OUTPUT[3] p.costs_energy = C.AMERIKAS_RESOURCE_COST[0] p.costs_products = C.AMERIKAS_RESOURCE_COST[1] p.costs_medical = C.AMERIKAS_RESOURCE_COST[2] p.costs_food = C.AMERIKAS_RESOURCE_COST[3] elif p.role == C.BRAZILIA_ROLE: p.savings = C.BRAZILIA_SAVINGS p.stockpile_energy = C.BRAZILIA_STOCKPILE[0] p.stockpile_products = C.BRAZILIA_STOCKPILE[1] p.stockpile_medical = C.BRAZILIA_STOCKPILE[2] p.stockpile_food = C.BRAZILIA_STOCKPILE[3] p.outputs_energy = C.BRAZILIA_OUTPUT[0] p.outputs_products = C.BRAZILIA_OUTPUT[1] p.outputs_medical = C.BRAZILIA_OUTPUT[2] p.outputs_food = C.BRAZILIA_OUTPUT[3] p.costs_energy = C.BRAZILIA_RESOURCE_COST[0] p.costs_products = C.BRAZILIA_RESOURCE_COST[1] p.costs_medical = C.BRAZILIA_RESOURCE_COST[2] p.costs_food = C.BRAZILIA_RESOURCE_COST[3] # Apply shock event effects, if any # Brazilia finds oil reserves if round == group.oil_shock_round and p.role == C.BRAZILIA_ROLE: p.outputs_energy += 20 p.costs_energy = 1 p.shock_oil_state = True # Chinland suffers an earthquake if round == group.earthquake_shock_round and p.role == C.CHINLAND_ROLE: p.outputs_products -= 10 p.needs_medical += 10 # Brazilia suffers a drought if round == group.drought_shock_round and p.role == C.BRAZILIA_ROLE: p.outputs_food -= 40 # Global pandemic occurs if round == group.pandemic_shock_round: p.needs_medical += 20 elif group.round_number >= 2: for p in players: p.savings = p.in_round(group.round_number - 1).savings p.penalty_flag = False p.stockpile_energy = p.in_round(group.round_number - 1).stockpile_energy p.stockpile_products = p.in_round(group.round_number - 1).stockpile_products p.stockpile_medical = p.in_round(group.round_number - 1).stockpile_medical p.stockpile_food = p.in_round(group.round_number - 1).stockpile_food p.loan_record = p.in_round(group.round_number - 1).loan_record p.shock_oil_state = p.in_round(group.round_number - 1).shock_oil_state p.shortfall_energy = p.in_round(group.round_number - 1).shortfall_energy p.shortfall_products = p.in_round(group.round_number - 1).shortfall_products p.shortfall_medical = p.in_round(group.round_number - 1).shortfall_medical p.shortfall_food = p.in_round(group.round_number - 1).shortfall_food # Set initial production, needs, cost figures before applying penalty, shock event p.needs_energy = 20 p.needs_products = 20 p.needs_medical = 20 p.needs_food = 20 p.outputs_energy = p.in_round(1).outputs_energy p.outputs_products = p.in_round(1).outputs_products p.outputs_medical = p.in_round(1).outputs_medical p.outputs_food = p.in_round(1).outputs_food p.costs_energy = p.in_round(1).costs_energy p.costs_products = p.in_round(1).costs_products p.costs_medical = p.in_round(1).costs_medical p.costs_food = p.in_round(1).costs_food # If Brazilia finding oil shock event has taken place if p.shock_oil_state == True and p.role == C.BRAZILIA_ROLE: p.outputs_energy += 20 p.costs_energy = 1 # If a penalty is in effect, set penalty flag to display warning to player if p.shortfall_energy > 0 or p.shortfall_products > 0 or p.shortfall_medical > 0 or p.shortfall_food > 0: p.penalty_flag = True # Apply shock event effects # Brazilia finds oil reserves if round == group.oil_shock_round and p.role == C.BRAZILIA_ROLE: p.outputs_energy += 20 p.costs_energy = 1 p.shock_oil_state = True # Chinland suffers an earthquake if round == group.earthquake_shock_round and p.role == C.CHINLAND_ROLE: p.outputs_products -= 10 p.needs_medical += 10 # Brazilia suffers a drought if round == group.drought_shock_round and p.role == C.BRAZILIA_ROLE: p.outputs_food -= 40 # Global pandemic occurs if round == group.pandemic_shock_round: p.needs_medical += 20 # Apply penalty effect if there is a deficit in resources, note resources with shortfall shortfall_list = [] if p.shortfall_energy > 0: shortfall_list.append("energy") energy_penalty_hike = p.shortfall_energy * C.ENERGY_SHORTAGE_HIKE energy_penalty_reduction = p.shortfall_energy * C.ENERGY_SHORTAGE_REDUCTION p.costs_energy += energy_penalty_hike p.costs_food += energy_penalty_hike p.costs_medical += energy_penalty_hike p.costs_products += energy_penalty_hike p.outputs_energy -= energy_penalty_reduction p.outputs_food -= energy_penalty_reduction p.outputs_medical -= energy_penalty_reduction p.outputs_products -= energy_penalty_reduction if p.shortfall_products > 0: shortfall_list.append("consumer products") products_penalty_hike = p.shortfall_products * C.PRODUCTS_SHORTAGE_HIKE production_penalty_reduction = p.shortfall_products * C.PRODUCTS_SHORTAGE_REDUCTION p.costs_energy += products_penalty_hike p.costs_food += products_penalty_hike p.costs_medical += products_penalty_hike p.costs_products += products_penalty_hike p.outputs_energy -= production_penalty_reduction p.outputs_food -= production_penalty_reduction p.outputs_medical -= production_penalty_reduction p.outputs_products -= production_penalty_reduction if p.shortfall_medical > 0: shortfall_list.append("medical supply") medical_penalty_hike = p.shortfall_medical * C.MEDICAL_SHORTAGE_HIKE medical_penalty_reduction = p.shortfall_medical * C.MEDICAL_SHORTAGE_REDUCTION p.costs_energy += medical_penalty_hike p.costs_food += medical_penalty_hike p.costs_medical += medical_penalty_hike p.costs_products += medical_penalty_hike p.outputs_energy -= medical_penalty_reduction p.outputs_food -= medical_penalty_reduction p.outputs_medical -= medical_penalty_reduction p.outputs_products -= medical_penalty_reduction if p.shortfall_food > 0: shortfall_list.append("food") food_penalty_hike = p.shortfall_food * C.FOOD_SHORTAGE_HIKE food_penalty_reduction = p.shortfall_food * C.FOOD_SHORTAGE_REDUCTION p.costs_energy += food_penalty_hike p.costs_food += food_penalty_hike p.costs_medical += food_penalty_hike p.costs_products += food_penalty_hike p.outputs_energy -= food_penalty_reduction p.outputs_food -= food_penalty_reduction p.outputs_medical -= food_penalty_reduction p.outputs_products -= food_penalty_reduction # Set penalty intel, resources with shortfall if len(shortfall_list) > 0: for num, resource in enumerate(shortfall_list, start=1): if num == 1: p.penalty_intel += resource else: p.penalty_intel += (", " + resource) # Production can never be less than 0, correct if needed if p.outputs_energy < 0: p.outputs_energy = 0 if p.outputs_products < 0: p.outputs_products = 0 if p.outputs_medical < 0: p.outputs_medical = 0 if p.outputs_food < 0: p.outputs_food = 0 else: pass Group.set_country_stats = set_country_stats class Player(BasePlayer): loan_record = models.LongStringField(blank=True, initial='') savings = models.FloatField() stockpile_energy = models.FloatField() stockpile_products = models.FloatField() stockpile_medical = models.FloatField() stockpile_food = models.FloatField() needs_energy = models.FloatField() needs_products = models.FloatField() needs_medical = models.FloatField() needs_food = models.FloatField() outputs_energy = models.FloatField() outputs_products = models.FloatField() outputs_medical = models.FloatField() outputs_food = models.FloatField() costs_energy = models.FloatField() costs_products = models.FloatField() costs_medical = models.FloatField() costs_food = models.FloatField() shortfall_energy = models.FloatField(initial=0) shortfall_products = models.FloatField(initial=0) shortfall_medical = models.FloatField(initial=0) shortfall_food = models.FloatField() shock_oil_state = models.BooleanField(initial=False) penalty_flag = models.BooleanField(initial=False) penalty_intel = models.StringField(initial='') def live_update(player: Player, data): group = player.group if data['type'] == 'check': # Updates validity of player inputs and whether round can proceed country = data['country'] check = data['check'] if country == "amerikas": group.amerikas_check = check elif country == "brazilia": group.brazilia_check = check elif country == "chinland": group.chinland_check = check elif country == "sadiarabica": group.sadiarabica_check = check # Update all players if group.amerikas_check and group.brazilia_check and group.chinland_check and group.sadiarabica_check: response = { 'type': 'players_check', 'check': True } return {0: response} else: response = { 'type': 'players_check', 'check': False } return {0: response} elif data['type'] == 'repayment': # Repayment between countries country = data['receiving'] if country == "amerikas": receiving_country = "Amerikas" elif country == "brazilia": receiving_country = "Brazilia" elif country == "chinland": receiving_country = "Chinland" elif country == "sadiarabica": receiving_country = "Sadi Arabica" receiving_player = group.get_player_by_role(receiving_country) receiving_id = receiving_player.id_in_group response = { 'type': 'repayment', 'sending': data['sending'], 'amount': data['amount'], 'interest': data['interest'], 'rounds': data['rounds'], 'round_payment': data['round_payment'], 'repayment': data['repayment'] } return {receiving_id: response} elif data['type'] == 'submit': # Another player has submitted their decisions and ended their round # Send a response only to the other players other_players = player.get_others_in_group() response_data = { 'type': 'submit', 'submitted': data['country'] } response = {} for p in other_players: id = p.id_in_group response[id] = response_data return response else: # Convert shortened country name to proper name country = data['receiving'] if country == "amerikas": receiving_country = "Amerikas" elif country == "brazilia": receiving_country = "Brazilia" elif country == "chinland": receiving_country = "Chinland" elif country == "sadiarabica": receiving_country = "Sadi Arabica" receiving_player = group.get_player_by_role(receiving_country) receiving_id = receiving_player.id_in_group if data['type'] == 'trade': response = { 'type': 'trade', 'sending': data['sending'], 'resource': data['resource'], 'quantity': data['quantity'], 'price': data['price'] } elif data['type'] == 'loan': response = { 'type': 'loan', 'sending': data['sending'], 'amount': data['amount'], 'rounds': data['rounds'], 'interest': data['interest'] } return {receiving_id: response} def otherPlayers(player: Player): group = player.group players = player.get_others_in_group() players_data = [] for p in players: players_data.append({ "country": p.role, "savings": p.savings, "stockpile_energy": p.stockpile_energy, "stockpile_products": p.stockpile_products, "stockpile_medical": p.stockpile_medical, "stockpile_food": p.stockpile_food, "costs_energy": p.costs_energy, "costs_products": p.costs_products, "costs_medical": p.costs_medical, "costs_food": p.costs_food }) return players_data Player.live_update = live_update class Welcome(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def js_vars(player: Player): return dict( country = player.role ) class Introduction_wait_page(WaitPage): after_all_players_arrive = set_country_stats class Country_Introduction(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def js_vars(player: Player): return dict( country = player.role, penalty_check = player.penalty_flag ) class How_To_Play(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def js_vars(player: Player): return dict( your_country = player.role, loan_record = player.loan_record, round = player.round_number, events_arr = C.SHOCK_EVENT_SEQ, penalty_mode = player.penalty_flag ) class Waiting_For_All(WaitPage): pass class Action_Page(Page): form_model = 'player' form_fields = ['loan_record', 'savings', 'stockpile_energy', 'stockpile_products', 'stockpile_medical', 'stockpile_food', 'shortfall_energy', 'shortfall_products', 'shortfall_medical', 'shortfall_food'] live_method = 'live_update' @staticmethod def js_vars(player: Player): return dict( your_country = player.role, loan_record = player.loan_record, round = player.round_number, events_arr = C.SHOCK_EVENT_SEQ, penalty_mode = player.penalty_flag ) class Wait_For_Next_Round(WaitPage): pass class Conclusion(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def js_vars(player: Player): return dict( country = player.role, your_country = player.role, round = player.round_number, player_energy = player.stockpile_energy, player_products = player.stockpile_products, player_medical = player.stockpile_medical, player_food = player.stockpile_food, other_players = otherPlayers(player) ) page_sequence = [Welcome, Introduction_wait_page, Country_Introduction, How_To_Play, Waiting_For_All, Action_Page, Wait_For_Next_Round, Conclusion]