情報論理工学 研究室 第8回: ミニマックス法.

Slides:



Advertisements
Similar presentations
平成 27 年 10 月 21 日. 【応用課題 2-1 】 次のビット列は、ある 10 進数を 8 ビット固定小数点表示で表した時の ものです。ただし、小数点の位置は 3 ビット目と 4 ビット目の間としてお り、負数は2の補数で表しています。このとき、元の 10 進数を求めてく ださい。
Advertisements

 C 川船 美帆.  強い人工知能の作成 o 「遺伝的アルゴリズム」  「どうぶつしょうぎ」のアプリケーショ ン作成 o スマートフォン向けアプリケーション.
2行+αチョンプに関する考察 京都大学 ○後藤順一 伊藤大雄.
3次元nクイーン問題の 解に関する研究 論理工学研究室 伊藤精一
人工知能概論 第4回 探索(3) ゲームの理論.
プログラムのパタン演習 解説.
データ構造と アルゴリズム 第十二回 知能情報学部 知能情報学科 新田直也.
第11回 整列 ~ シェルソート,クイックソート ~
アルゴリズムとデータ構造 2013年6月18日
アルゴリズムとデータ構造1 2005年7月8日
データ構造とアルゴリズム論 第6章 探索のアルゴリズム
四路の碁アプリ開発 情報論理工学研究所 高倉秀斗.
On the Enumeration of Colored Trees
データ構造と アルゴリズム 理工学部 情報システム工学科 新田直也.
リバーシの並列化 並列化するときに起こる問題を定義しろ おぷてぃまいざー SSAIとMSAIは比較しろ  前田昂寛.
Lispとは ゲーム理論 minimaxアルゴリズム αβアルゴリズム ソースコードの一部
人 工 知 能 第3回 探索法 (教科書21ページ~30ページ)
アルゴリズムとデータ構造 2012年7月23日
インタラクティブ・ゲーム制作 <プログラミングコース>
遺伝アルゴリズムによる NQueen解法 ~遺伝補修飾を用いた解探索の性能評価~
アルゴリズムとデータ構造 2012年6月14日
ゲームプレイング (Game Playing)
多数の遊休PC上での 分散ゲーム木探索 導入 ゲーム木探索 ⇒遊休PCを利用して高速化 例)コンピュータ将棋における次手の計算
ゲームプレイング (Game Playing)
モンテカルロ法によるミニ囲碁 増井拓視 情報理論工学研究所.
コンピュータ将棋におけるカーネル法を用いた静的評価関数の学習
ゲームプレイング (Game Playing)
データ構造とアルゴリズム論 第6章 探索のアルゴリズム
アルゴリズムとデータ構造 2011年7月14日
単位 おねだり ☆オセロ おねだり隊☆D班.
データ構造と アルゴリズム 第八回 知能情報学部 新田直也.
モンテカルロ碁 電気通信大学 村松研究室 下川和也.
アルゴリズムとデータ構造 2011年6月14日
京都大学 ○太田圭亮 川原純 伊藤大雄 堀山貴史
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
JAVAでつくるオセロ 伊東飛鳥、宮島雄一 長畑弘樹、ソギ原直人.
第11回 整列 ~ シェルソート,クイックソート ~
情報論理工学 研究室 第6回: リバーシの合法手生成.
二分探索木によるサーチ.
情報論理工学 研究室 第5回: 局面・駒石・手の表現.
4人版リバーシYoninの解析 情報論理研究室 藤本 侑花
アルゴリズムとデータ構造 第3章 ヒープ 6月10日分
京都大学大学院情報学研究科 宮川博光 伊藤大雄
近畿大学理工学部情報学科 情報論理研究室 松浦 美里
Cプログラミング演習 第10回 二分探索木.
近畿大学理工学部情報学科 情報論理工学研究室 赤井 隆純
プログラミング 4 整列アルゴリズム.
逆算法に基づく 詰将棋の列挙 堀山貴史 中塚裕之 岩間一雄 (京都大学) 持駒なし 9手詰め.
アルゴリズムとデータ構造 2010年7月26日
情報処理Ⅱ 第2回:2003年10月14日(火).
プログラミング 4 探索と計算量.
モンテカルロ法を用いた 立体四目並べの対戦プログラム
情報論理工学 研究室 第7回: 強い手の選択.
第4回 メモリ管理 主記憶(メインメモリ)の管理 固定区画方式と可変区画方式 空き領域の管理 スワッピング.
近畿大学 理工学部 情報学科 情報論理工学部研究室 潘小月
データ構造とアルゴリズム論 第6章 探索のアルゴリズム
アルゴリズムとプログラミング (Algorithms and Programming)
アルゴリズムとデータ構造1 2008年7月24日
飛び駒を考慮した逆算法に基づく詰将棋問題の列挙
情報工学概論 (アルゴリズムとデータ構造)
飛び駒を考慮した逆算法に基づく詰将棋問題の列挙
F班 メンバー 班長 雨堤 智宏 アルゴリズム解析 角田 泰彬 竹林 秀高 ppt作成 清水 貴史
囲碁プログラム 彩の仕組み 山下 宏 2008年9月4日 FIT2008.
数値解析Ⅱ ~五目並べのプログラミング~ C班.
ヒープソート.
暗号技術・セキュリティ 情報工学科  04A1004 石川 真悟.
人工知能概論 第4回 探索(3) ゲームの理論.
情報処理Ⅱ 第2回 2004年10月12日(火).
Ibaraki Univ. Dept of Electrical & Electronic Eng.
プログラミング論 バイナリーサーチ 1.
Presentation transcript:

情報論理工学 研究室 第8回: ミニマックス法

「強い手」の選択 「強い手」の選択 強い手とは? ゲームによって異なる 大きな得点が得られる手 相手の得点を下げる手 価値の高い駒を取る手 価値の高い駒を守る手 有利な地点を取る手 相手に不利な地点を取らせる手 有利な選択ができるようになる手 相手に不利な選択を強要する手 ゲームによって異なる

「強い手」を得る手法 「強い手」を得る手法 局面の評価値計算 定跡・定石データベースの利用 先読み 完全読み切り・必勝読み切り モンテカルロ法

局面の評価値計算 局面の評価値計算 現在の局面からどのくらい優勢かを計算する 得点を多く取っている 盤上に強い駒がある 強いカードを持っている 有利な地点を抑えている 相手を攻撃できる 相手の攻撃を防げる 可能な手の数が多い :

評価値計算による手の選択 各合法手に対する1手先の局面を生成 各局面の評価値を計算 最も評価値の高い手を選択 +5 -2 +7 -5 +1

ArrayList<Move> moveList = generateMoveList (); // 合法手リスト生成 /* 最も評価値の高い手を選ぶ */ Move SelectMove () { ArrayList<Move> moveList = generateMoveList (); // 合法手リスト生成 if (moveList.isEmpty()) return null; // 合法手無し int selectedValue = -∞; // 局面の評価値 Move selectedMove = null; // 選択した手 for (Move move : moveList) { Phase phase = nextPhase (move); // 1手先の局面を生成 int value = phase.getValue(); // 局面の評価値を求める if (value > selectedValue) { // 評価値最大の手を記憶 selectedMove = move; selectedValue = value; } return move;

駒割による評価値:将棋 9 8 7 6 5 4 3 2 1 八 七 六 五 四 三 二 一 九 先手:101点 後手:85点 ▲8六銀まで 歩 ▲8六銀 歩 香 桂 金 玉 桂 香 角 飛 金 先手:101点 後手:85点 (先手の銀得) 歩 歩 歩 歩 銀 歩 歩 歩 飛車で 只取り できる? 歩 8六の銀が 8二の飛車で 取れそう 銀 歩 銀 歩 歩 歩 歩 歩 歩 角 金 飛 銀 香 桂 玉 金 桂 香 歩 ▲8六銀まで

駒割による評価値:将棋 9 8 7 6 5 4 3 2 1 八 七 六 五 四 三 二 一 九 先手:93点 後手:93点 △8六同飛まで 歩 ▲8六銀 △同飛 歩 香 桂 金 玉 桂 香 銀 金 先手:93点 後手:93点 (互いに駒損無し) 角 歩 歩 歩 歩 銀 歩 歩 歩 歩 飛 歩 銀 歩 歩 しかし… 歩 歩 歩 歩 角 金 飛 銀 香 桂 玉 金 桂 香 歩 △8六同飛まで

駒割による評価値:将棋 ▲9五角が 王手飛車! 9 8 7 6 5 4 3 2 1 八 七 六 五 四 三 二 一 九 先手:93点 歩 ▲8六銀 △同飛 ▲9五角 歩 香 桂 金 玉 桂 香 銀 金 先手:93点 後手:93点 (互いに駒損無し) 角 歩 歩 歩 歩 銀 歩 歩 歩 角 歩 ▲9五角が 王手飛車! 飛 歩 銀 歩 歩 歩 歩 歩 歩 金 飛 銀 香 桂 玉 金 桂 香 歩 ▲9五角まで

先読み 現在の局面は優勢でも 数手先に逆転される場合がある 局面の先読みが必要 ○ 優勢のようだ しかし… 数手先 × 劣勢!

先読み 先読み 数手先の局面を生成し、評価値を計算 ○番 ×番 ○番 +2 +2 -6 +1 -4 +5 +2 +3 -4 +5 -2 -6 この局面の 評価値を求める -4 +5 -2 -6 +2 -2 -1 +3

ミニマックス(mini-max)法 ミニマックス法 自分にとっての最善手=相手にとっての最悪手 ⇒ 相手が常に最善手を指してくると仮定 ⇒ 相手が常に最善手を指してくると仮定 自分の手番:最も評価値の高い手を採用 相手の手番:最も評価値の低い手を採用

各頂点の得点計算 ○番:最大値を選択 ×番:最少値を選択 ○番 ×番 +3 -5 +1 +3 -4 -2 -2 -5 +5 +1

int maxScore (int depth) { if (depth == 0) // 深さ制限に達した場合 /* 局面の評価値を計算する(自分の手番) */ /* depth 先まで読んで最も高い評価値を返す */ int maxScore (int depth) { if (depth == 0) // 深さ制限に達した場合 return value; // 先読み無しの評価値を返す int scoreMax = -∞; ArrayList<Move> moveList = generateMoves(); // 合法手リスト生成 for (Move move : moveList ) { // 全ての合法手に対して判定 Phase phase = nextPhase (move); // 次の局面を生成 int score = phase.minScore (depth -1); // 次局面(相手番)の評価値計算 if (score > scoreMax) // 自分にとって良い手が見つかった場合 scoreMax = score; // 評価値更新 } return scoreMax; // 最も高い評価値を返す

int minScore (int depth) { if (depth == 0) // 深さ制限に達した場合 /* 局面の評価値を計算する(相手の手番) */ /* depth 先まで読んで最も低い評価値を返す */ int minScore (int depth) { if (depth == 0) // 深さ制限に達した場合 return value; // 先読み無しの評価値を返す int scoreMin = +∞; ArrayList<Move> moveList = generateMoves(); // 合法手リスト生成 for (Move move : moveList ) { // 全ての合法手に対して判定 Phase phase = nextPhase (move); // 次の局面を生成 int score = phase.maxScore (depth -1); // 次局面(自番)の評価値計算 if (score < scoreMin) // 相手にとって良い手が見つかった場合 scoreMin = score; // 評価値更新 } return scoreMin; // 最も低い評価値を返す

先読み手数が増えると可能な局面数は指数的に増える 各局面での合法手数 2 4 8 先 読 み 手 数 10 1,000 1,000,000 109 20 1012 1018 30 1027 40 1024 1036 50 1015 1030 1045 先読み手数が増えると可能な局面数は指数的に増える ⇒適当な枝刈りが必要

ミニマックス法 無駄な探索はどれ? 最も評価値の 高い手を採用 -3 -3 -8 -5 -6 +9 -3 +6 +2 -6 +4 -8 -2 +3 +7 +5 -2 -6 最も評価値の 低い手を採用 無駄な探索はどれ?

-3未満の評価値が出た枝は探索しなくていい ミニマックス法 -3以上確定 ⇒-3未満の手は 絶対に採用されない >-3 -6以下確定 -3 <-6 <-5 <-6 +9 -3 +6 +2 -6 +4 -5 +5 -2 -6 この部分の探索は不要 -3未満の評価値が出た枝は探索しなくていい

アルファベータ(alpha-beta)法 アルファベータ法 ミニマックス法の改良アルゴリズム 必要の無い探索は行わない 絶対に採用されない手は読まない α:それまでに発見した自番で最も大きな評価値 β:それまでに発見した相手番で最も小さい評価値 相手の手番:αよりも小さい評価値になれば探索打ち切り 自分の手番:βよりも大きい評価値になれば探索打ち切り

/* αβ法により局面の評価値を計算する(自分の手番) */ /* depth 先まで読んで最も高い評価値を返す */ int maxScore (int depth, int alpha, int beta) { if (depth == 0) // 深さ制限に達した場合 return value; // 先読み無しの評価値を返す int scoreMax = -∞; ArrayList<Move> moveList = generateMoves(); // 合法手リスト生成 for (Move move : moveList ) { // 全ての合法手に対して判定 Phase phase = nextPhase (move); // 次の局面を生成 int score = phase.minScore (depth -1, alpha, beta); // 次局面の評価値計算 if (score≧beta) // β値を上回ったら探索中止 return score; // この値は採用されることは無い if (score > scoreMax) // 自分にとって良い手が見つかった場合 scoreMax = score; // 評価値更新 if (scoreMax > alpha) alpha = scoreMax; // α値更新 } return scoreMax; // 最も高い評価値を返す

/* αβ法により局面の評価値を計算する(相手の手番) */ /* depth 先まで読んで最も低い評価値を返す */ int minScore (int depth, int alpha, int beta) { if (depth == 0) // 深さ制限に達した場合 return value; // 先読み無しの評価値を返す int scoreMin = +∞; ArrayList<Move> moveList = generateMoves(); // 合法手リスト生成 for (Move move : moveList ) { // 全ての合法手に対して判定 Phase phase = nextPhase (move); // 次の局面を生成 int score = phase.maxScore (depth -1, alpha, beta); // 次局面の評価値計算 if (score≦alpha) // α値を下回ったら探索中止 return score; // この値は採用されることは無い if (score > scoreMin) // 相手にとって良い手が見つかった場合 scoreMin = score; // 評価値更新 if (scoreMin < beta) beta = scoreMin; // β値更新 } return scoreMin; // 最も低い評価値を返す

アルファベータ法の効率 枝刈りできるのはどこ? +5 +5 +3 -1 -4 +5 +6 +8 +9 +3 +4 +6 +7 -1 +3 +3 +7 -4 -2 -1 +5 枝刈りできるのはどこ?

アルファベータ法 枝刈り可能 +5 α値 +5 +5 +6 +8 +9 +3 -1 -4 左から探索 <+3 <-1 <-4 +5 +6 +8 +9 +3 -1 -4 左から探索 枝刈り可能

アルファベータ法 全く枝刈りできない! +5 α値 α値 α値 +5 +3 -1 -4 +5 +6 +8 +9 +3 +4 +6 +7 -1 +3 +7 -4 -2 -1 +5 右から探索 全く枝刈りできない!

アルファベータ法の効率 アルファベータ法の効率 良い手から先に探索すると効率がいい +5 +6 +8 +9 +3 +4 +7 -1 -4 -4 -2 良い手 悪い手 良い手 悪い手 効率高 効率低

そもそも「良い手」がわかるなら探索の必要無し アルファベータ法の効率 アルファベータ法の効率 良い手から先に探索すると効率がいい でもどうやって「良い手」を探す? そもそも「良い手」がわかるなら探索の必要無し 「良さそうな手」から先に探索

効率のいい探索 まず予備探索を行う ミニマックス法(先読み数:小)で探索 評価値順に手をソートする アルファベータ法(先読み数:大)で探索 探索が2回必要だが、 枝刈りできるメリットの方が大きい

ネガマックス(negamax)法 ネガマックス法 関数maxScore()とminScore()を一つにまとめる この2つはほぼ同じ関数(最大値か最小値かの違い) ⇒評価値の符号を反転させれば同一 自分の手番:評価値そのまま使用 相手の手番:評価値*-1を使用 ⇒手番に関係なく評価値が最大の手を探せばいい

/* ネガマックス法により局面の評価値を計算する */ /* depth 先まで読んで最も高い評価値を返す */ int negaMaxScore (int depth, int alpha, int beta) { if (depth == 0) // 深さ制限に達した場合 return value; // 先読み無しの評価値を返す int scoreMax = -∞; ArrayList<Move> moveList = generateMoves(); // 合法手リスト生成 for (Move move : moveList ) { // 全ての合法手に対して判定 Phase phase = nextPhase (move); // 次の局面を生成 int score = -phase.negaMaxScore (depth -1, -beta, -alpha); // 次局面 if (score≧beta) // β値を上回ったら探索中止 return score; // この値は採用されることは無い if (score > scoreMax) // 良い手が見つかった場合 scoreMax = score; // 評価値更新 if (scoreMax > alpha) alpha = scoreMax; // α値更新 } return scoreMax; // 最も高い評価値を返す

ネガマックス法 int negaMaxScore (int depth, int alpha, int beta) int score = -phase.negaMaxScore (depth-1, -beta, -alpha) 相手番の評価値計算  符号を入れ替え、α:=-β、β:=-αとして計算する 評価値 v が α < v < β ⇔評価値 –v が –β < -v < -α

同一局面の処理 探索中同一局面が現れるケース 手順前後の同一局面 千日手 手順が違っても同一局面となる ⇒局面の評価値を再利用できる 手順中で同一局面が現れる(千日手) ⇒探索の無限ループを回避する必要がある

手順前後の同一局面 手順1 1:f5, 2:d6, 3:c3, 4:d3, 5:c4 手順2 8 7 6 5 4 3 2 1 a b c d e f g h ● 手順1 1:f5, 2:d6, 3:c3, 4:d3, 5:c4 手順2 1:f5, 2:d6, 3:c4, 4:d3, 5:c3 ● ● どちらも同じ局面になる ● ●

手順前後の同一局面 3:c3 3:c4 4:d3 4:d3 5:c4 5:c3 評価値をコピー +5 +5 同一局面

千日手による同一局面 a b c d 5 6 7 8 a b c d 5 6 7 8 a b c d 5 6 7 8 a b c d 5 6 Qd8+ ... Kb8 ... Ka7 a b c d 5 6 7 8 a b c d 5 6 7 8 Qa4+

千日手による同一局面 同一局面 Qd8+ ... Ka7 Qa4+ ... Kb8 千日手なので判定打ち切り

局面の同一判定 equals()メソッド 同一の局面か判定する boolean equals (Phase phase) { for (int i=0; i<SIZE; ++i) for (int j=0; j<SIZE; ++j) if (this.board[i][j] ≠ phase.board[i][j]) return false; // 1箇所でも異なればfalse if (this.turn ≠phase.turn) return false; : return true; // 全て同じならtrue } だがこの判定は時間がかかる

局面の同一判定 探索中には多くの局面が現れる 局面の同一判定は時間がかかる 同一の可能性のある局面を絞り込む ハッシュ関数による同一判定  探索中には多くの局面が現れる  局面の同一判定は時間がかかる 同一の可能性のある局面を絞り込む ハッシュ関数による同一判定  ハッシュ関数で局面を数値化、  同一のハッシュ値を持つ局面のみ同一判定

※実際はもっと複雑なハッシュ関数を用いる ハッシュ関数の例:チェス a b c d e f g h 1 2 3 4 5 6 7 8 駒がある:1 駒が無い:0 として64ビットの 数値で表現 11011111 11100101 00010010 00001000 00101000 0010000 11111111 10111001 DFE51208 2820F7B9 ※実際はもっと複雑なハッシュ関数を用いる

同一局面の判定 No Yes No Yes No Yes 同一のハッシュ値の 局面があるか? 同一局面か? 同一局面無し 探索木上で祖先か? 優れたハッシュ関数なら この判定は不要 Yes 探索木上で祖先か? No 千日手 Yes 評価値をコピー

宿題:3目並べの着手選択 3目並べ着手選択 先手後手それぞれを人間かCPUのどちらが受け持つかを選べるようにせよ 探索の深さは適当な値に設定する 先読み無しの評価値も適当と思われる方法を採る