# -*- coding: utf-8 -*- # from __future__ import division from otree.db import models from otree.constants import BaseConstants from otree.models import BaseSubsession, BaseGroup, BasePlayer from otree import widgets from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random import statistics from pprint import pprint # doc = """ In Cournot competition, firms simultaneously decide the units of products to manufacture. The unit selling price depends on the total units produced. In this implementation, there are 2 firms competing for 1 period. """ class Constants(BaseConstants): name_in_url = 'cournot_class' num_rounds = 10 players_per_group = None training_1_correct = 300 # moved remaining vars to settings.py line: 364 feedback1_question = """Suppose firm Q produced 20 units and firm P produced 30 units. What would be the profit for firm P?""" feedback1_explanation = """Total units produced were 20 + 30 = 50. The unit selling price was {{ total_capacity }} – 50 - {{ marginal_cost }} = 10. The profit for firm P would be the product of the unit selling price and the unit produced by firm P, that is 10 × 30 = 300""" class Subsession(BaseSubsession): # THIS CODE WILL REMAIN...for now: might want to redo Cornot with groups again, so we'll maintain this code albeit commented out...just in case # def creating_session(self): # if self.round_number > 1: #players = self.get_players() # random.shuffle(players) #group_matrix = [] # for i in range(0, len(players), Constants.players_per_group): # group_matrix.append(players[i:i + Constants.players_per_group]) # self.set_groups(group_matrix) def creating_session(self): pass def vars_for_admin_report(self): group = self.get_groups()[0] player_data = [] player_average = [g.average_units for g in group.in_all_rounds()] player_data.append({ 'name': 'Average Units Produced', 'data': player_average }) # for player in group.get_players(): # player_units = [p.units for p in player.in_all_rounds()] # player_data.append({ # 'name':'Units Produced for Player %s' % str(player.id_in_group), # 'data':player_units # }) return { 'units': player_data, 'catagories': list(range(1, Constants.num_rounds, 1)) } class Group(BaseGroup): # Group Models: total_units = models.PositiveIntegerField( doc="""Total units produced by all players""" ) average_units = models.PositiveIntegerField( doc="""Average units produced by all players""" ) # Group Methods: def set_payoffs(self): #players = self.get_players() self.total_units = sum([p.units for p in self.get_players()]) for p in self.get_players(): p.price = self.session.config['total_capacity'] - p.units - (2 / len(self.get_players())) * ( self.total_units - p.units) p.payoff = ( p.price - self.session.config['marginal_cost']) * p.units def average_output(self): average_units = statistics.mean(p.units for p in self.get_players()) # store once for use in 'this' round self.session.vars['grp_average'] = average_units # store again for results page: self.average_units = average_units self.session.vars[self.round_number] = average_units for p in self.get_players(): self.session.vars["Player {}".format(p.id_in_group)] = p.units def best_response(self, previous_round_units): # Grab the groups total units from the previous round: group_in_last_round = self.in_round(self.round_number - 1) print("Group? %s" % group_in_last_round.total_units) # pprint(vars(group_in_last_round)) # pprint is super helpful for printing the whole object, normal print only returned 'Group object (current round number) # I was not able to call the max_capacity() function directly here, so it is rewritten: max_capacity = self.session.config['total_capacity'] / \ len(self.get_players()) # total units without current players previous round: other_player_sum = group_in_last_round.total_units - previous_round_units print(other_player_sum) # Best response: best_response = (((self.session.config['total_capacity'] - self.session.config['marginal_cost']) / 2) - ( (1 / (len(self.get_players()))) * other_player_sum)) # Keep best response within bounds of the game: if best_response < 0: best_response = 0 if best_response > max_capacity: best_response = max_capacity print("Best response is: {}".format(best_response)) return best_response def max_capacity(self): max_capacity = len(self.get_players())*self.session.config['total_capacity'] / \ (3*len(self.get_players())-2) return int(max_capacity) class Player(BasePlayer): # Player Models: training_question_1 = models.FloatField(initial=0) units = models.PositiveIntegerField( initial=None, min=0, max=999, doc="""Quantity of units to produce""" ) price = models.CurrencyField( doc="""Unit price: P = T - Ui - (2/ Tp) * Up, where T is total capacity, Ui is the number of units produced you, Tp is the total number of players and Up is the units produced by other players""" ) def training_question_answer(self): training_answer = ( self.session.config['total_capacity'] - 50 - self.session.config['marginal_cost']) * 30 return training_answer # Player Methods: def is_training_question_1_correct(self): # training_answer = (self.session.config['total_capacity'] - 50 - self.session.config['marginal_cost']) * 30 return self.training_question_1 == self.training_question_answer() def other_player(self): # display the average of what the group has played: return self.session.vars['grp_average'] def role(self): if self.id_in_group == 1: return 'Player 1' if self.id_in_group == 2: return 'Player 2'