#from asyncio import constants #gov_co2, from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, # gov_co2, ) import random # なにかコードについて質問がある場合は、長瀬までご連絡ください。 # メールアドレスは、odenshacho@icloud.comです class Constants(BaseConstants): # 実験appと同じ名前にする name_in_url = 'carbon_b' # 何人1組にするか設定 players_per_group = 4 # ラウンド数の設定 # なおターンという概念がotreeにないので、ラウンドごとに処理を分けて実質ターンを作れば良い。 num_rounds = 6 # 実験説明用にincludeするテンプレートファイルの場所を指定 # 通常は、実験appの名前/instructions.htmlとし、instructions.htmlというテンプレートファイルをつくる instructions_template = 'carbon_b/instructions.html' # 政府から配布される排出枠の上限 2022ver gov_co2 = [400,400,300,300,200,200] # 政府から配布される排出枠の上限 2022年度バージョン。最終的にはこっちを使いたい Jyogen_list = [400,400,300,300,200,200] #売り上げ生産能力 2022年度追加機能 2022ver Unit_price_list = [150,150,120,120] # 各入札者の資本金 shihonkin = 10000 # 技術導入に対する投資額 invest_price = 2000 # 生産における必要枠数それぞれ expand_need = 120 normal_need = 100 shrink_need = 80 non_need = 0 # 生産における必要枠数それぞれ(チケット使用時) expand_need_ticket = 84 normal_need_ticket = 70 shrink_need_ticket = 56 non_need_ticket = 0 # 辞書の指定した値のキーをリストとして取り出す関数 def get_keys_by_value(dict, value): return [key for key, val in dict.items() if val == value] class Subsession(BaseSubsession): game_app = models.StringField(initial="均一") Jyogen = models.IntegerField() def creating_session(self): self.Jyogen = Constants.Jyogen_list[self.round_number-1] import random Unit_list = random.sample(Constants.Unit_price_list, 4) for p in self.get_players(): if self.round_number == 1: p.unit_price_ability = Unit_list[p.id_in_group-1] else: p.unit_price_ability = p.in_round(1).unit_price_ability p.expand_profit = Constants.expand_need* p.unit_price_ability p.normal_profit = Constants.normal_need* p.unit_price_ability p.shrink_profit = Constants.shrink_need* p.unit_price_ability p.non_profit = 0 for g in self.get_groups(): g.current_gov_co2 = Constants.gov_co2[self.round_number-1] class Group(BaseGroup): # 1200枠超えてたらここをTrueに変える can_haibun = models.BooleanField(initial=False) # 全員で何枠入札してるか数えたやつ total_purchase_num = models.IntegerField(initial=0) # 政府から配布される排出枠の上限 current_gov_co2 = models.IntegerField() # 均衡価格 kinko_price = models.IntegerField() # 枠トレードが起こったかどうかフラグ waku_trade_flag = models.BooleanField(initial=False) # 企業ごとの初期配分量を決定するための処理 def do_shoki_haibun(self): # データベースからプレイヤーのデータを持ってくる players = self.get_players() # playersの中身[P1, P2, P3, P4] # 変数定義 purchase_price_lst = [] purchase_player_id = [] purchase_num_lst = [] # priceでまとめたpurchase_num辞書 purchase_priceSum_num_dict = {} for player in players: purchase_price_lst.append(player.purchase_price) purchase_player_id.append(player.id_in_group) purchase_num_lst.append(player.purchase_num) if player.purchase_price in purchase_priceSum_num_dict: purchase_priceSum_num_dict[player.purchase_price] += player.purchase_num else: purchase_priceSum_num_dict[player.purchase_price] = player.purchase_num # この時点でpurchase_num_lstの中身は、[P1の希望枠数,P2の希望枠数,P3の希望枠数,P4の希望枠数] # sum関数を使ってリストの要素をすべて足し合わせ、total_purchase_numに代入 self.total_purchase_num = sum(purchase_num_lst) # 1200枠以上あれば初期配分可能、そうじゃなかったらやり直し。 # pages.pyでcan_haibunがTrueのとき初期配分ページをすべてスキップするようにしている。 if self.total_purchase_num >= self.current_gov_co2: self.can_haibun = True else: # このFalseに特に意味はないのですが、こうするとこれ以下の関数の処理を強制的に行わないようにできる。 return False # 1200枠以上になってて配分が可能だったら下の処理が走る。 # 入札価格用のリストを作成 purchase_price_lst = [] # この時点では、まだpurchase_price_lstは空のリスト # ループでそれぞれのプレイヤーの入札価格をリストに追加 for player in players: purchase_price_lst.append(player.purchase_price) # この時点でpurchase_price_lstの中身は、[P1の入札価格,P2の入札価格,P3の入札価格,P4の入札価格] # 入札価格が低い順に並び替え purchase_price_hikuijun = sorted(purchase_price_lst) # これでpurchase_price_hikuijunの中身が[一番低い入札価格, ....., 一番高い入札価格] # まず何が重複してるか調べる必要がある # print(purchase_price_hikuijun) # リストの中の要素がなくなるまでループ処理 while len(purchase_price_hikuijun) > 0: # 現状一番高い入札価格をリストから取り出す。 # popは取り出す関数。 highest_price = purchase_price_hikuijun[-1] # 同じ値段をつけたプレイヤーが何人いるか数える # count関数は要素を数えられる same_highprice_count = purchase_price_hikuijun.count(highest_price) # 同じhighest_priceをつけてる他のプレイヤーがいない場合 if same_highprice_count==0: highest_price = purchase_price_hikuijun.pop() # 一番高い価格をつけているプレイヤーのインデックスをしらべる。 # index関数は要素がリストの何番目か調べる関数 player_idx = purchase_price_lst.index(highest_price) # そのインデックス使ってどのプレイヤーがbuyerが決定する。 buyer = players[player_idx] # 購入量を決定する buy_amount = buyer.purchase_num # 政府の残り排出枠が購入量よりも多ければ if self.current_gov_co2 >= buy_amount: # 合計代金の計算 total_cost = buy_amount * highest_price # 購入者の所持金の計算 buyer.shojikin -= total_cost # 購入者の初期配分を決定 buyer.shoki_haibun = buy_amount # 政府の残り排出枠を計算 self.current_gov_co2 -= buy_amount # 政府の残り排出枠が購入量よりも少ないとき、政府の残り枠分しか購入できない else: # 購入料を政府の残り枠数と同じにする buy_amount = self.current_gov_co2 # 合計代金の計算 total_cost = buy_amount * highest_price # 購入者の所持金の計算 buyer.shojikin -= total_cost # 購入者の初期配分を決定 buyer.shoki_haibun = buy_amount # 政府の残り排出枠を計算 self.current_gov_co2 -= buy_amount # 同じ価格をつけているプレイヤーが他にいる場合 # ちなみにこのカウントが1のとき2人いるという意味 elif same_highprice_count>0: # 空リスト定義 buyers = [] buy_amounts = [] while same_highprice_count > 0: highest_price = purchase_price_hikuijun.pop() # 一番高い価格をつけている1人目のプレイヤーのインデックスをしらべる。 # index関数は要素がリストの何番目か調べる関数 player_idx = purchase_price_lst.index(highest_price) # これしないと2回目のindex関数でまた同じプレイヤーを引っ張って来てしまう。 purchase_price_lst[player_idx] = None # 購入者を設定 buyer = players[player_idx] # リストに追加 buyers.append(buyer) # 購入量を設定 buy_amount = buyer.purchase_num # リストに追加 buy_amounts.append(buy_amount) # カウントを一つを減らす same_highprice_count -= 1 # デバッグ用 print("buyers") print(buyers) # 政府の残り排出枠が購入量よりも多ければ if self.current_gov_co2 >= sum(buy_amounts): # enumerateはfor文でインデックスつけながらループさせるときに便利なやつ for idx, buy_amount in enumerate(buy_amounts): # 購入者の合計代金の計算 total_cost = buy_amount * highest_price # 購入者の所持金の計算 buyers[idx].shojikin -= total_cost # 購入者の初期配分を決定 buyers[idx].shoki_haibun = buy_amount # 政府の残り排出枠を計算 self.current_gov_co2 -= buy_amount # 政府の残り排出枠が購入量よりも少ないとき、政府の残り枠分しか購入できない else: # 分配比率計算用の分母を計算 bunbo = sum(buy_amounts) num_can_buy = self.current_gov_co2 for idx, buy_amount in enumerate(buy_amounts): print("num_can_buy") print(num_can_buy) print("current_gov_co2") print(self.current_gov_co2) # それぞれの配分量を設定 buy_amount_haibun = num_can_buy * buy_amount // bunbo # あまり計算用 self.current_gov_co2 -= buy_amount_haibun # 購入者の合計代金の計算 total_cost = highest_price * buy_amount_haibun # 購入者の所持金の計算 buyers[idx].shojikin -= total_cost # 購入者の初期配分を決定 buyers[idx].shoki_haibun = buy_amount_haibun # 同じ価格をつけているプレイヤーにランダムで余りを売らせる if self.current_gov_co2 > 0: p = random.choice(buyers) p.shoki_haibun += self.current_gov_co2 p.shojikin -= p.purchase_price * self.current_gov_co2 self.current_gov_co2 -= self.current_gov_co2 # 枠が余っていたらプレイヤーにランダムで余りを売らせる if self.current_gov_co2 > 0: p = random.choice(players) p.shoki_haibun += self.current_gov_co2 p.shojikin -= self.kinko_price * self.current_gov_co2 # 誰にランダムでいくつ余り割り振ったか保存 p.took_amari = self.current_gov_co2 self.current_gov_co2 = 0 # 初期配分量を現在の枠数のところに代入 for player in players: player.waku_num = player.shoki_haibun class Player(BasePlayer): # 資産(いわゆる最終の利得になる部分) shisan = models.IntegerField(initial=0) # 個人ごとの売り上げ生産能力 unit_price_ability = models.IntegerField() # 排出枠に対する入札量 purchase_num = models.IntegerField(label='') # 排出枠に対する入札価格 purchase_price = models.IntegerField(label='') # 初期配分量 shoki_haibun = models.IntegerField(initial=0) # 余りを受け取った人は余り枠数表示 took_amari = models.IntegerField(initial=0) # 各入札者の所持金 shojikin = models.IntegerField(initial=Constants.shihonkin) # 所持金枠トレード前 shojikin_before_trade=models.IntegerField() # 現在の枠数 waku_num = models.IntegerField() #売上金 expand_profit =models.IntegerField() normal_profit = models.IntegerField() shrink_profit = models.IntegerField() non_profit = models.IntegerField() #総資産 sou_shisan = models.IntegerField() def calc_B(self): self.sou_shisan = sum([p.shisan for p in self.in_all_rounds()]) '''枠トレード前の状態を保存するフィールド''' # 枠を買う量(保存用) desired_buy_num=models.IntegerField() # 枠を買う価格(保存用) desired_buy_price=models.IntegerField() # 枠を売る量(保存用) desired_sell_num=models.IntegerField() # 枠を売る価格(保存用) desired_sell_price=models.IntegerField() # 実際買った量 bought_num = models.IntegerField(initial=0) # 実際売った量 sold_num = models.IntegerField(initial=0) # 枠を買う量 buy_num=models.IntegerField(min=0,label='',) # 枠を買う価格 buy_price=models.IntegerField(min=0,label='',) # 枠を売る量 sell_num=models.IntegerField(min=0,label='',) # 枠を売る価格 sell_price=models.IntegerField(min=0,label='',) invest = models.StringField( choices=[['はい', 'はい'], ['いいえ', 'いいえ']], label='', widget=widgets.RadioSelectHorizontal, ) seisan = models.StringField( choices=[['拡大', '拡大生産'], ['通常', '通常生産'], ['縮小', '縮小生産'], ['しない', '生産しない']], label='', widget=widgets.RadioSelectHorizontal, ) emission = models.StringField( choices=[['買う', '排出枠を買う'], ['しない', '売買しない'], ['売る', '排出枠を売る'],], label='', widget=widgets.RadioSelectHorizontal, )