from otree.api import ( models, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, ) #the diffent packages and superclasses used in the game, all super class provide various standard attributes to elements import random author = 'Soum' doc = """ Description about the game only for designer's reference """ class Constants(BaseConstants): #the different parameters used in the game that DONOT change once the session is created #since we have only one round in this game I am generating true_demand and the forecast chart in Constants rather than #subsession name_in_url = 'demand_planner_multi_player' num_of_players = 2 num_of_forecaster = 9 num_of_products = 7 players_per_group = None min_demand = 150 max_demand = 250 mean_demand = 200 sd_demand = 10 forecaster_error_mean = 0 forecaster_error_sd = 45 bonus_multiplier = 0.01 num_rounds = 1 def get_truncated_normal(mean, sd, low, upp):#this function helps to truncate the true_demand to remain within bounds of max and min demand while True: truncnorm = random.normalvariate(mean, sd) if (truncnorm >= low and truncnorm <= upp): break return truncnorm #true_demand for both the sections of the game is generated here true_market_demand = [0] * num_of_products for c1 in range(0, num_of_products): demand_distribution = get_truncated_normal(mean=mean_demand, sd=sd_demand, low=min_demand, upp=max_demand) true_market_demand[c1] = int(demand_distribution)#true_demand generation for section one of the game #this function helps generate forecast for a forecaster def forecast(num_of_products=num_of_products, forecaster_error_mean=forecaster_error_mean, forecaster_error_sd=forecaster_error_sd, max_forecast=max_demand, true_market_demand=true_market_demand): def get_truncated_normal(mean, sd, low, upp): # this function helps to truncate the true_demand to remain within bounds of max and min demand while True: truncnorm = random.normalvariate(mean, sd) if (truncnorm >= low and truncnorm <= upp): break return truncnorm forecasted = [0] * num_of_products for c2 in range(0, num_of_products): forecast_distribution = get_truncated_normal(mean=forecaster_error_mean, sd=forecaster_error_sd,low=-max_forecast, upp=max_forecast) forecasted[c2] = int(forecast_distribution) + true_market_demand[c2]# this the formula where we get the offset of the forecast from the true_demand return forecasted # forecasts of 9 forecasters for player 1 A = forecast(); B = forecast(); C = forecast(); D = forecast(); E = forecast(); F = forecast(); G = forecast() H = forecast(); I = forecast() forecast_matrix_P1 = [A, B, C, D, E, F, G, H, I] # forecasts of 9 forecasters for player 2 A2 = forecast(); B2 = forecast(); C2 = forecast(); D2 = forecast(); E2 = forecast(); F2 = forecast(); G2 = forecast() H2 = forecast(); I2 = forecast() forecast_matrix_P2 = [A2, B2, C2, D2, E2, F2, G2, H2, I2] class Subsession(BaseSubsession): ''' #generating true_demand for every round def vars_for_template(self): def get_truncated_normal(mean, sd, low, upp): return truncnorm( (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd) #creating true demand demand_distribution = get_truncated_normal(mean=Constants.mean_demand, sd=Constants.sd_demand, low=Constants.min_demand, upp=Constants.max_demand) true_market_demand = [0] * Constants.num_of_products for c1 in range(0, Constants.num_of_products): true_market_demand[c1] = int( round(demand_distribution.rvs())) # true_demand generation for section one of the game #creating forecasts from true demand def forecast(num_of_products=Constants.num_of_products, forecaster_error_mean=Constants.forecaster_error_mean, forecaster_error_sd=Constants.forecaster_error_sd, max_forecast=Constants.max_demand, true_market_demand=true_market_demand): def get_truncated_normal(mean, sd, low, upp): # this function helps to truncate the forecast to remain within bounds of max and min demand return truncnorm( (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd) forecast_distribution = get_truncated_normal(mean=forecaster_error_mean, sd=forecaster_error_sd, low=-max_forecast, upp=max_forecast) forecasted = [0] * num_of_products for c2 in range(0, num_of_products): forecasted[c2] = int(round(forecast_distribution.rvs())) + true_market_demand[ c2] # this the formula where we get the offset of the forecast from the true_demand return forecasted #forecasts of 9 forecasters A = forecast(); B = forecast(); C = forecast(); D = forecast(); E = forecast(); F = forecast(); G = forecast() H = forecast(); I = forecast() forecast_matrix_P1 = [A,B,C,D,E,F,G,H,I] A2 = forecast(); B2 = forecast(); C2 = forecast(); D2 = forecast(); E2 = forecast(); F2 = forecast(); G2 = forecast() H2 = forecast(); I2 = forecast() forecast_matrix_P2 = [A2, B2, C2, D2, E2, F2, G2, H2, I2] return {'Forecast_matrix_P1': forecast_matrix_P1, 'Forecast_matrix_P2': forecast_matrix_P2} ''' pass class Group(BaseGroup): pass class Player(BasePlayer):#the attributes of the player we want to be displayed in the final data chosen_product = models.IntegerField(choices=[1, 2, 3,4,5,6,7]) chosen_product2 = models.IntegerField(min=1,max=Constants.num_of_products) chosen_quantity = models.IntegerField(min=Constants.min_demand, max=Constants.max_demand) chosen_quantity2 = models.IntegerField(min=Constants.min_demand, max=Constants.max_demand) bonus = models.FloatField() bonus2 = models.FloatField() true_demand_stage1= models.IntegerField() true_demand_stage2= models.IntegerField() forecast_stage1 =models.StringField() forecast_stage2 = models.StringField() #forecast_player = models.LongStringField() #true_demand = models.IntegerField()