Subscribed unsubscribe Subscribe Subscribe

最中限・最後の一手に最前を尽くすだけでどれだけ勝てるようになるか

5ラウンド目の3ターン目、つまり最後に残り3枚から選択する場面で最善の一手を選ぶ方法を考えてみた。


・自分が3枚のうちから1枚を選ぶとする
 →自分が得点するか、自分以外(RandomAI1 or RandomAI2)が得点する。すべてのパターンを洗い出す。
 →すべてのパターンから、そのラウンドの結果を算出し、さらにゲームの結果まで算出できる。
 →それぞれの結果が起こりうる確率も計算できる。結果の期待値として、ゲーム結果の評価×確率を算出できる
…というのをすべての選択肢に対して行うことで、自分の選択肢でどれが最善なのかを選ぶことができる。
ゲーム結果の評価として、実際に最終的に決定する勝ち点(2 とか -1 とか)を使ってもいいが、
自分なりに考えた関数を用意してみた。これを調整することである程度評価方法を変えられることになる。

# -*- encoding: utf-8 -*-

from saichugen import Player
from saichugenlib import *

class MyAI(Player):
    name = "MyAI"
    def __init__(self, hand):
        hand.sort()
        self.hand = hand
    def play(self, info):
        handstr = map(to_str, self.hand)
        # 未知カードから自分の手持ちを取り除く
        import copy
        self.unknowns = copy.copy(info['unknowns'])
        for i in self.hand:
            self.unknowns.remove(i)

        # 最終ラウンド&最終ターン以外はランダムに出す
        if (info['iround'] == 4) & (info['iturn'] == 2):
            # 手持ちカードのそれぞれを出したときの評価を決める
            valuations = []
            for i in self.hand:
#                 print "case", to_num(i), ":"
                # 自分の出したカードよりも小さいもの
                lt = []
                # 自分の出したカードよりも大きいもの
                gt = []
                for j in self.unknowns:
                    if j < i:
                        lt.append(to_num(j))
                    else:
                        gt.append(to_num(j))

                patterns = []
                # 自分が得点するパターン
                if (len(lt) > 0)  & (len(gt) > 0):
                    tmp = copy.copy(info['round_score'])
                    tmp[0] += to_num(i)
                    patterns.append((len(lt) * len(gt) * 2, tmp))
                # 自分以外が得点するパターン
                a = 13 * [0]
                for j in range(len(lt)):
                    a[lt[j] - 1] += j
                for j in range(len(gt)):
                    a[gt[j] - 1] += len(gt) - j - 1
                for j in range(len(a)):
                    if a[j] > 0:
                        tmp = copy.copy(info['round_score'])
                        tmp[1] += j + 1
                        patterns.append((a[j], tmp))
                        tmp = copy.copy(info['round_score'])
                        tmp[2] += j + 1
                        patterns.append((a[j], tmp))
                # すべてのパターンの結果を計算
                for j in patterns:
                    # そのラウンドの結果
                    mid = get_mid(j[1])
                    for k in range(3):
                        if j[1][k] != mid:
                            j[1][k] = 0
                    # ラウンドの結果をプラスしたゲームスコア
                    for k in range(3):
                        j[1][k] += info['game_score'][k]
                # 最終結果を評価(get_myscoreによる)
                valuation = 0
                for j in patterns:
#                     print j, get_myscore(j[1])
                    valuation += j[0] * get_myscore(j[1])
                valuations.append(valuation)
#            print valuations
            return self.hand.pop(valuations.index(max(valuations)))

        from random import choice
        return self.hand.pop(choice(range(len(self.hand))))

def get_myscore(xs):
    mid = get_mid(xs)
    if xs[0] == mid:
        return abs(xs[1] - xs[2])
    else:
        return -abs(mid - xs[0])

変数名もメチャクチャで、自分にしか内容を把握できそうにないひどいプログラムだ…orz


とはいえ、これでどの程度結果が良くなるのか試してみる。
前回同様、ランダムAI×2と10000回対戦してトータルスコアを見てみる。

$ time python saichugen.py
[2248, -1193, -1055]

real	0m37.556s
user	0m37.232s
sys	0m0.105s

おぉっ!予想以上の効果!!
何回かやってみたけど、

[2238, -1053, -1185]
[2144, -1135, -1009]
[2199, -1011, -1188]

と、平均0.2得点は出せるようだ。
最後の一手を考えるだけでここまで変わるとは…