from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random import numpy as np author = 'Kengo Suzuki' doc = """ This game is originally designed by Sato K, Toda M, and Yamagishi T (1985). https://doi.org/10.4992/jjpsy.56.277 KS replicated this game as a web application for research and educational use. """ class Constants(BaseConstants): name_in_url = 'STY1985' # The name of game displayed on URL players_per_group = 3 # The number of players per game num_rounds = 50 # The number of rounds per game num_trees = 3 # The number of trees per game max_tree_size = 9 # Maximum value of tree size initial_tree_size = 3 # Initial value of tree size class Subsession(BaseSubsession): pass class Group(BaseGroup): size_tree1 = models.IntegerField( max = Constants.max_tree_size, ) size_tree2 = models.IntegerField( max = Constants.max_tree_size, ) size_tree3 = models.IntegerField( max = Constants.max_tree_size, ) size_tree4 = models.IntegerField( max = Constants.max_tree_size, ) num_cut_trees_inputs = models.IntegerField( initial=0, ) num_cut_trees_modify = models.IntegerField( initial=0, ) ave_size_of_cut_trees = models.FloatField( initial = 0, ) size_tree_average = models.FloatField( initial = 0, ) total_point_get = models.IntegerField( initial = 0, ) total_point_accumulation = models.IntegerField( initial = 0, ) def update(self): if self.round_number == 1: self.size_tree1 = Constants.initial_tree_size self.size_tree2 = Constants.initial_tree_size self.size_tree3 = Constants.initial_tree_size self.size_tree4 = Constants.initial_tree_size else: self.size_tree1 = self.in_round(self.round_number-1).size_tree1 self.size_tree2 = self.in_round(self.round_number-1).size_tree2 self.size_tree3 = self.in_round(self.round_number-1).size_tree3 self.size_tree4 = self.in_round(self.round_number-1).size_tree4 def solve(self): players = self.get_players() # Get list of players cut_inputs = [p.cut_input for p in players] # list of player's decisions cut_modify = [] for i in range(len(cut_inputs)): cut_modify.append(cut_inputs[i]) # list of tree size size_trees = [] for i in range(Constants.num_trees): tree_number = i + 1 tree_name = 'self.size_tree' + str(tree_number) size_trees.append(eval(tree_name)) # count the number of players who decided to cut each tree count_cut = [] for i in range(Constants.num_trees): count_cut.append(cut_modify.count(i+1)) # list of players whose decisions are duplicated players_duplicate = [0]*Constants.players_per_group for i in range(Constants.players_per_group): for j in range(Constants.players_per_group): if (cut_modify[i] != 0 and cut_modify[i] == cut_modify[j] and i != j): players_duplicate[i] = 1 # modify the decisions of players list_players = [] list_trees = [] while sum(players_duplicate)>0: for i in range(Constants.num_trees): # for each tree if count_cut[i] >1: # if more than one player try to cut the tree for j in range(Constants.players_per_group): # list of players who want to cut the tree if cut_inputs[j] == i+1: list_players.append(j+1) for k in range(Constants.num_trees): # list of trees with the same size as the tree if size_trees[k] == size_trees[i] and count_cut[k] != 1: list_trees.append(k+1) while len(list_trees) < len(list_players): list_trees.append(0) random.shuffle(list_trees) # randomize who can get the tree for l in range(len(list_players)): # modify cut cut_modify[list_players[l]-1] = list_trees[l] for m in range(Constants.players_per_group): # update players_duplicate for n in range(Constants.players_per_group): if (cut_modify[m] != 0 and cut_modify[m] == cut_modify[n] and m != n): players_duplicate[m] = 1 else: players_duplicate[m] = 0 for o in range(Constants.num_trees): # update count_cut count_cut[o] = (cut_modify.count(o + 1)) for i in range(Constants.players_per_group): # update Field cut_modify players[i].cut_modify = cut_modify[i] cut_modify_check = [p.cut_modify for p in players] # デバッグ用 # increase points of players for i in range(Constants.players_per_group): if cut_modify[i]>0: players[i].point_get = 2**(size_trees[cut_modify[i]-1]-1) for i in range(Constants.players_per_group): if self.round_number == 1: players[i].point_accumulation = players[i].point_get else: players[i].point_accumulation = players[i].in_round(self.round_number-1).point_accumulation + players[i].point_get point_get_check = [p.point_get for p in players] # デバッグ用 point_accumulation_check = [p.point_accumulation for p in players] # デバッグ用 # record total point self.total_point_get = sum(point_get_check) self.total_point_accumulation = sum(point_accumulation_check) # record the number of cut trees for i in range(Constants.players_per_group): if cut_inputs[i] > 0: self.num_cut_trees_inputs = self.num_cut_trees_inputs +1 for i in range(Constants.players_per_group): if cut_modify[i] > 0: self.num_cut_trees_modify = self.num_cut_trees_modify +1 # record average size of cut trees ave_size_of_cut_trees = [] for i in range(Constants.num_trees): if count_cut[i] != 0: ave_size_of_cut_trees.append(size_trees[i]) if len(ave_size_of_cut_trees)>0: self.ave_size_of_cut_trees = sum(ave_size_of_cut_trees) / len(ave_size_of_cut_trees) else: # self.ave_size_of_cut_trees = np.nan self.ave_size_of_cut_trees = 0 # reset the size of cut trees size_trees_after = [] for i in range(Constants.num_trees): if count_cut[i]>0: size_trees_after.append(0) else: size_trees_after.append(size_trees[i]) # trees grow for i in range(Constants.num_trees): if size_trees_after[i] < Constants.max_tree_size: size_trees_after[i] = size_trees_after[i]+1 for i in range(Constants.num_trees): # update Field size_treeX tree_number = i + 1 trees_grow = 'self.size_tree' + str(tree_number) + '= size_trees_after[i]' exec(trees_grow) # record average tree size just after cut self.size_tree_average = sum(size_trees_after) / Constants.num_trees print('players is', players) # デバッグ用 print('size_trees is', size_trees) # デバッグ用 print('cut_inputs is', cut_inputs) # デバッグ用 print('count_cut is', count_cut) # デバッグ用 print('players_duplicate is', players_duplicate) # デバッグ用 print('list_players is', list_players) # デバッグ用 print('list_trees is', list_trees) # デバッグ用 print('cut_modify is', cut_modify) # デバッグ用 print('cut_modify_check is', cut_modify_check) print('point_get_check is', point_get_check) print('point_accumulation_check is', point_accumulation_check) print('total_point_get is', self.total_point_get) print('total_point_accumulation is', self.total_point_accumulation) print('num_cut_trees_inputs is', self.num_cut_trees_inputs) print('num_cut_trees_modify is', self.num_cut_trees_modify) print('ave_size_of_cut_trees is', ave_size_of_cut_trees) print('ave_size_of_cut_trees is', self.ave_size_of_cut_trees) print('size_trees_after is', size_trees_after) print('size_tree_average is', self.size_tree_average) print('size_tree1 is', self.size_tree1) # デバッグ用 print('size_tree2 is', self.size_tree2) # デバッグ用 print('size_tree3 is', self.size_tree3) # デバッグ用 print('size_tree4 is', self.size_tree4) # デバッグ用 class Player(BasePlayer): cut_input = models.IntegerField( initial=0, min = 0, max =Constants.num_trees ) cut_modify = models.IntegerField( initial = 0, min = 0, max =Constants.num_trees ) point_get = models.IntegerField( initial = 0 ) point_accumulation = models.IntegerField( initial = 0 )