CEDEC AI CHALLENGE 2014 まとめ(Part2 戦略)
戦略
大まかな戦略
・公開されている情報から相手の動きを予測する(期待値計算に使用)。
・対戦サーバーに公開される対戦ログを参考にする。
・熱狂度の配分により、目標値を設定する。
・乱択アルゴリズム(モンテカルロ)に頼る。
ただし、予測や配分などによる重み付けを行い、
期待度が高い女の子が選ばれやすくなるようにしている。
モンテカルロ戦略根拠
不完全情報要素が強い
他プレイヤーの熱狂度配分ポイントが半分近く公開されないため、予測がたてづらい。
全ポイント45ptの内、休日の20ptが公開されない。
20ptの配分によりゲームの優劣は大きく変わるため、現時点で自分が勝っているか、負けているがわからない。
また公開情報として与えられる休日のデートの有無もプレイヤーの特定が難しく、
予測にばらつきがでてしまう。
4人同時ゲーム
本ゲームは4人が同時にデート相手を決定する。
4人の各プレイヤーの動きにより、自分の優劣が左右されるため、確実に勝てる方法を決められない。
他プレイヤーの戦略の影響
各プレイヤーの戦略が自分の配分に大きく影響する。
例えば、「相手プレイヤーの動きにあわせて女の子へ好感度を配分」、「決められた定数値を割り当てる」、
「好感度の高い相手にすべての好感度を費やす」...などの戦略が予選に見られた。
ある一人のプレイヤーが「好感度の高い相手にすべての好感度を費やす」戦略をとった場合、
全体のゲームバランスが大きく変化する。
故に、どのような相手であっても勝てるように戦略をたてるのが難しい。
(当日、全パターンを探索し、期待値の高い動作を選択する選手がいた。この対策を行えば、本項目は対象外になると思われる。)
▶︎よって乱数を用いてゆらぎを与えることが必須と考える。
熱狂度分布による割当根拠
ログの解析をした結果
例えば「6・5・5・5・5・4・3」という熱狂度の組み合わせがあった場合、
6点の女の子を狙うより、5点の女の子を2人狙う方が確実に期待値が高い。
なぜなら6点の女の子は狙われやすく、また5点の女の子は4人居るため、競争相手が分散される可能性が高いためである。
よってただ単に、熱狂度の高い女の子を狙うよりかは、熱狂度の分布に応じて戦略を変えるべきだと考えた。
▶︎実際のログでも5点の女の子を狙っている選手が一位の結果が多いように思う。
対戦サーバーのログ収集
予選以前より、対戦サーバー( http://arena.ai-comp.net )において対戦が行われていた。
自身ははじめに、対戦サーバーのログを見て、1位と2位の割当を平均化し、それを割り付けることを考えていた。
以下、その時に使用したスクリプトを記載しておく。
ログ収集(Ruby)
require 'open-uri' url = "http://arena.ai-comp.net/contests/1/battle_results/" file = "Match_" #START_CNT = 1651 START_CNT = 5225 #END_CNT = 5224 END_CNT = 5546 for cnt in START_CNT..END_CNT do filename = file + cnt.to_s + ".txt" urlname = url + cnt.to_s open(filename,"w"){|f| open(urlname){|g| f.write g.read } } sleep(1) end
ログ解析(Ruby)(公開するレベルでない)
#変数宣言 f_enth="" #熱狂度 f_real = [] #好感度 f_score = [] #最終スコア f_romeo="" filecnt = 0 CNT_START = 11852 #CNT_END = 5224 CNT_END = 12603 total_cnt = 0 OUT_FILE = "sum.csv" myEgentFlg = 0 HEADER = "ID,Match,\ Es_Girl0,P0_Point0,P1_Point0,P2_Point0,P3_Point0,\ Es_Girl1,P0_Point1,P1_Point1,P2_Point1,P3_Point1,\ Es_Girl2,P0_Point2,P1_Point2,P2_Point2,P3_Point2,\ Es_Girl3,P0_Point3,P1_Point3,P2_Point3,P3_Point3,\ Es_Girl4,P0_Point4,P1_Point4,P2_Point4,P3_Point4,\ Es_Girl5,P0_Point5,P1_Point5,P2_Point5,P3_Point5,\ Es_Girl6,P0_Point6,P1_Point6,P2_Point6,P3_Point6,\ Es_Girl7,P0_Point7,P1_Point7,P2_Point7,P3_Point7,\ P0_Score,P1_Score,P2_Score,P3_Score,\ EthNum6,EthNum54,EthNum3,ROMEO_AI_NO" #ヘッダー書き込み File.open(OUT_FILE,"a") do |file| file.write(HEADER + "\n") end for filecnt in CNT_START .. CNT_END do #一行ずつ読み出し filename = "Match_" + filecnt.to_s + ".txt" open(filename){|file| myEgentFlg = 0 while line = file.gets if line =~ /Enthusiasm/ #再度一行読み出し(熱狂度) f_enth = file.gets end if line =~ /Real Love/ #8行分読み出し(女の子の好感度) for cnt in 0..7 do f_real[cnt] = file.gets end end if line =~ /Ranking:/ #スコア読み出し(4人プレイヤー分) for cnt in 0..3 do f_score[cnt] = file.gets end end end } #不要データ削除 全配分が0のプレイヤーがいる場合 girlsPoint = [] for cnt in 0..7 do girlsPoint[cnt] = Array.new(4) s_tmp = f_real[cnt].split(" ") s_tmp[3] = s_tmp[3].gsub("\n","") for cnt2 in 0..3 do girlsPoint[cnt][cnt2] = s_tmp[cnt2].to_i end end flg = 0 for cnt in 0..3 do sum = 0 for cnt2 in 0..7 do sum += girlsPoint[cnt2][cnt] if girlsPoint[cnt2][cnt] > 40 #一人に40以上与えているのは不要サンプル flg = 1 break end end if sum < 40 || flg == 1#トータル配分が40以下の場合不要サンプル flg = 1 break end end if flg == 1 next end #フォーマット整形 #Format #ID,X #Match_xxx #Player,0,1,2,3 #girl0,a,b,c,d,A #girl1,.... #... #girl7,e,f,g,h,G #score,y,y,y,y #enthNum,x,y,z #end enthArray = f_enth.split(" ") enthArray[7] = enthArray[7].gsub("\n", "") girls = [] for cnt in 0..7 do girls[cnt] = "" s_tmp = f_real[cnt] s_tmp = s_tmp.gsub(" ", ",") s_tmp = s_tmp.gsub("\n", ",") girls[cnt] += s_tmp end score = [] for cnt in 0..3 do #Example "Player 2: 25 popularity\n" scoreArray = f_score[cnt].split(" ") scoreArray[1] = scoreArray[1].gsub(":",""); score[scoreArray[1].to_i] = scoreArray[2]; end scores = score[0] + "," + score[1] + "," + score[2] + "," + score[3] #EnthAnalyze enthNumArray = [0,0,0] for cnt in 0..7 do if enthArray[cnt].to_i == 6 enthNumArray[0]+=1 elsif enthArray[cnt].to_i == 5 || enthArray[cnt].to_i == 4 enthNumArray[1]+=1 else enthNumArray[2]+=1 end end enthNum = enthNumArray[0].to_s + "," + enthNumArray[1].to_s + "," + enthNumArray[2].to_s #FileWrite body = total_cnt.to_s + "," #ID body += filecnt.to_s + "," #Match for cnt in 0..7 do body += enthArray[cnt] + "," body += girls[cnt] end body += scores + "," body += enthNum File.open(OUT_FILE,"a") do |file| file.write(body + "\n") end total_cnt += 1 end
実際に提出したコードと結果はpart3で