コンピュータプレイヤー
Q学習は膨大な学習(試合)を行いながら、最適な解を求めていく。そのため人が三目並べをやりながら学習させることは不可能に近い。そこで学習用の相手をコンピュータにやってもらう。そのプログラムがクラスPlayerRandomだ。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import random import Const """ランダムの打つプレイヤー """ class PlayerRandom: def play(self,kiban,qtable,greedy): ret = kiban.serchBlank() #打てる箇所を取得 return random.choice(ret) #ランダムに打てる箇所を選択 def whois(self): return Const.RANDOM |
9行目で全ての打てる座標を取得し、10行目でランダムに選択している。
AIプレイヤー
Q値に基づき手を打つクラスがPlayerAIだ。16行~29行目の処理で、打てる座標の各Q値を取得しながら、最大のQ値、座標を決定している。もし最大値が複数ある場合はランダムに選択している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import random import Const """AI用クラス """ class PlayerAI: def play(self,kiban,qtable,greedy): key = kiban.kibanToString() #棋盤情報から状態情報へ変換 data = qtable.get(key) #状態に対応するQ値を取得 ret = kiban.serchBlank() #打てる箇所を取得 if random.random() < greedy:#一定確率でランダムに打つ(学習するため) return random.choice(ret) #ランダムに打てる箇所を選択 else: xymax=[] xymax.append([ret[0][0],ret[0][1]]) #打てる箇所の1件目を第一候補とする max = data[ret[0][0]][ret[0][1]] ret.pop(0) for xy in ret: #打てる箇所の2件目から処理。Q値の高い箇所を探す if data[xy[0]][xy[1]] > max: #最高Q値があった場合に格納する max = data[xy[0]][xy[1]] xymax.clear() xymax.append([xy[0],xy[1]]) elif data[xy[0]][xy[1]] == max: #最高Q値が複数ある場合に格納する xymax.append(([xy[0],xy[1]])) return random.choice(xymax) #最高Q値が複数あると想定し、その中からランダム選択 def whois(self): return Const.AI |
13行目のif文は一定の確率でランダムに手を選択するためのものだ。これはQ値に基づいた手のみを打っていると学習範囲が狭くなってしまう、これを防いでいるのだ。
人プレイヤー
最終的にはAndroidアプリに学習内容を反映させる予定だが、学習が進んだAIと今すぐにでも対戦したくなる。そこで人が打てるクラスPlayerHumanを作成した。X(横):0~2、Y(縦):0~2の入力で手を打つことができる。と、いっても実は次のデバック機能が活躍した。
コマンド | 内容 |
---|---|
d | 棋盤表示 |
a | 全てのQ値表示 |
pキー値 | キーに対応するQ値を表示 |
b | 強制終了 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import random import Const """人用クラス """ class PlayerHuman: def play(self,kiban,qtable,greedy): flg = False while True: y = input('X:') if not y: print("何か打ってよー") continue if y[0] == 'p': #状態を入力すると対応するQ値表示 入力例)p100000000 if y[1:] not in qtable.table.keys(): print("キーがありません") else: print(qtable.get(y[1:])) continue elif y[0] == 'd': #棋盤表示 kiban.disp() continue elif y[0] == 'b': #強制終了 flg = True break elif y[0] == 'a': #全ての状態:Q値を表示 qtable.disp() continue x = input('Y:') if not x.isnumeric() or not y.isnumeric(): print("数字ではありません") continue x2 = int(x) y2 = int(y) if( x2 >= Const.BANSU or y2 >= Const.BANSU): print("盤がありません。") elif kiban.checkKiban(x2,y2) == False: print("既に打たれています") continue else: break if flg == True: return [] else: return [x2,y2] def whois(self): return Const.HUMAN |