「YSS 7.0」 --- そのデータ構造、及びアルゴリズムについて ---
山下 宏
たまーに加筆してます。追記、で検索してください。
1997年5月29日(1998年5月20日、共立出版の「コンピュータ将棋の進歩2」に掲載)。
私が将棋プログラムに興味を持ったのは1987年の頃であった。当時、古本屋で見つけた1984年発売の「MICRO」という雑誌に掲載されていた将棋プログラム「ESS」 *[9] の記事を読み、以後、将棋プログラム「YSS(Yamashita Shogi System)」を作りはじめた。そして1991年の第2回コンピュータ将棋選手権 *[8] に初参加。以降、連続で参加し、5,4,4,3,4位の後、1997年2月の第7回選手権において「YSS 7.0」が金沢将棋2の5連覇を阻止して初優勝した。
以下では「YSS 7.0」の仕組みと、優勝のきっかけとなった探索アルゴリズムについて説明する。なお、「YSS 7.0」は1997年4月アスキーサムシンググッドより「AI将棋2」として発売されている。
1.データ構造
コンピュータに将棋を指させるためには、最低限、将棋のルールを教える必要がある。
データ構造は、その後のプログラミングにおいて一番影響が大きいため、最初の構想が大事である。
YSS の場合は駒の利きと盤の状態を示すデータ構造を3回変更し、最終的に駒番号を利用した方法を採用した。
なお、プログラムは WindowsNT 上で動き、全てC言語で書かれている。
YSS の基本データ構造は以下の通りである。
すべての駒に 1〜40 までの番号をつける。先手の玉は1、後手の玉は2、飛車は3〜4、角は5〜6、金は7〜10、銀は11〜14、桂は15〜18、香19〜22、歩23〜40である。番号ごとに、その駒の種類、位置を保存している。
盤の座標は、実際の将棋盤では先手から見て右上が(1,1)、左下が(9,9)の (x,y)座標だが、コンピュータ向きではないので、左上が(1,1)、右下が(9,9)の(x,y)座標を用いる。つまり、先手76歩、は先手36歩、となる(X座標が反転する)。
将棋盤は 9×9 なので 9×9 の2次元配列を使うのがわかりやすい。が、高速性を考慮して、ban[256] の1次元配列を使い、座標の位置を、y*16+x で表す。76歩ならば、36歩、となり x=3,y=6 より ban[0x63] がその位置にある駒の駒番号を示す(C言語では先頭に0xを付けると16進法を表す)。盤の周りには1つ大きな枠を作って、そこが盤外であることを明確にしている。つまり実際には11×11の盤にして、利きのデータを書き込む際に、余計な判定をしなくて済むようにしている。
先手の駒の種類は以下の表1の通りである。
−表1−
歩 香 桂 銀 金 角 飛
1 2 3 4 5 6 7
王 と 成香 成桂 成銀 -- 馬 龍
8 9 10 11 12 13 14 15
駒は成ると +8 される。
後手の駒は、これに 0x80(=128)を+したものを使っている。たとえば、後手の龍は 0x8f(= 128+7+8)となる。将棋の駒は16進法で表現するのに、実に都合よく?出来ている。
使われていない駒番号は、駒の種類ごとにスタックに積まれ、持駒を打つときにスタックから空いている駒番号を持ってきて利用している。例えば、図1の局面で先手が持駒の歩を先手47歩、と打つ場合、盤上にない歩は全部で4枚なので
/* スタックの内容 */
kn_stoc[1][0] = 0 (未使用)
kn_stoc[1][1] = 25
kn_stoc[1][2] = 33
kn_stoc[1][3] = 36
kn_stoc[1][4] = 27 使われていない歩は4枚
kn_stn[1] = *kn_stoc[1][4] (を示している)
より、
kn[27][0] = 0x01 駒番号27番は先手の歩になる
kn[27][1] = 0x76 その位置は 0x76 (=47歩)
kn_stn[1]-- スタックを減らす。[1]は歩、[2]は香車になる
となる。
追記:2004年03月07日
失敗した、と今では思っていることに先手、後手の駒の種類に
フラグを立てていなかったことがあります。
YSSでは先手の歩〜王を0x01-0x07、後手を0x81-0x87、盤外を0xff、空白を0x00としているのですが
駒が動けるか、の判定は
if ( board[z] < 0x80 && board[z] != 0 ) 先手は動けない
if ( board[z] > 0x80 ) 後手は動けない
となります。
もし先手を歩〜王を0x11-0x17としておけば、後手を0x21-0x27、盤外を0x40とかにしておけば
if ( board[z] & (0x40 | 0x10) ) 先手の駒は動けない
if ( board[z] & (0x40 | 0x20) ) 後手の駒は動けない
と先手も一度の判定で済みます。
表2 主な YSS のデータ構造用の配列(型は全て int)
ban[256] その位置にある駒の駒番号。
init_ban[256] その位置にある駒の種類
(何もないときは 0、盤外は 0xff、先手の駒は0x01〜0x0f,後手の駒は0x81〜0x8f)
mo_m[8] MAN(先手)の持ち駒の数。
mo_c[8] COM(後手)の持ち駒の数。
kiki_m[256] その位置におけるMANの利きの数。
kiki_c[256] COM
kb_m[256][32] その位置におけるMANの利きの内容(どういった駒が利いているか)。
なお、利きの最大数は18である。
kb_c[256][32]
kn[41][2] 駒番号のテーブル。[][0]...駒の種類、[][1]...駒の位置。
kn_stoc[8][32] 駒の種類別の空いている駒番号。
*kn_stn[8] 上の配列用のポインタ。
sasite[64][64][8] 各深さにおける指し手。64手先まで読み、64手まで生成。手の情報の他に仮評価などが入る。
saizen[64][64][4] 最善応手手順を記憶 最大深さ64。
手は移動元、移動先、取った(打った)駒の種類、成りフラグの4つで表す。
ret_fukasa[64] 評価値の決定した深さを記録
fukasa_add 探索限界深さの小数点部分
depth_max 探索深さ限界
*tejun[64] 今までの手順が入ったポインタ。sasite[][][] の場所を示す。
C言語では、配列の添字に2のべき乗を使うと、内部計算がビットシフトで表現され
高速化されるので、配列はすべて2のべき乗にするとよい。
図1
利きには間接の利きと直接の利き、の2種類がある。直接の利きとは実際にその場所に動ける利きの事で、間接の利き、とは実際にはその場所に動けないが、駒交換が進んだ場合には、その駒も利いてくるよ、という利きのことである。
例えば、図1で86の地点には82の飛車が間接的に利いている、と考える。例外として、飛角香の利きは相手の玉を1つだけ貫く、としている(王の詰み判定で便利)。図1では44の先手の角の利きは敵玉を貫いて22の地点まで利いている。
(ただし、YSSでは71の地点のように、62の銀が71に動けば44の角が利いてくる(影の利き)状態のデータは持っていない)。
kb_c[256][32] には、その地点に利いている駒番号が入っている。例えば、後手から見て86の地点では85の歩(駒番号は26)と83の香車(駒番号じゃ19)と82の飛車(駒番号は3)が利いているので
kiki_c[0x62] = 3; 利きの数は3。
kb_c[0x62][0] = 26; 26番の歩が利いている。
kb_c[0x62][1] = 3 + 0x80; 3番の飛が利いている(間接なので0x80を足している)。
kb_c[0x62][2] = 19 + 0x80; 19番の香が利いている(間接なので0x80を足している)。
これらの基本データを駒が動くたびに変更があった場所だけ部分的に変更して更新している。図1で先手の39銀が38に移動する場合、いったん39銀の利きを全て消す。そして38銀の利きを書く。その際、88の飛車の右側の利きを止めるので、飛車の右側の利きだけを変更している。他に同時に更新しているデータは、ハッシュコード、駒の基本的な損得の2つである。
YSS で用いている主なデータ構造を表2に挙げる。これらのデータ構造から分かるとおり、先手(MAN)と後手(COM)用にデータをはっきり分けてしまったため、全てのサブルーチンにおいて MAN 用と COM 用の2つを持っている。例えば角での両取りを探すルーチンは MAN 用、COM 用の2つある。非常に無駄だが、高速化との兼ね合いも考えると微妙なところである。
2.指し手の生成方法
2.1 指し手の分類
YSSではまず、指し手を「攻める手」と「受ける手」の2つに分ける。
攻める手としては、以下の3通りである。
・相手の駒を取る手。
・駒が成る手。
・相手の駒に当たりをかける手(両取りを含む)。
受ける手としては、
・取ろうとする相手の駒を取る。
・駒打ちが好手の場合は先にその升目に駒を打つ。
・相手の移動先に利きをつける。
・取られる駒が逃げる。
・飛角香が動く手が好手の時は、合駒をする手。
・利きが足されている升目の飛角香に対して合駒する手。
以上の6つの内、駒が逃げる手に対しては、残りの探索深さが3以上の時は、実際に盤上で動かして、駒の損得を計算して上位の数手を選んでいる(飛角は5手、金銀は4手)。それ以外は、実際には盤上では動かさずに(仮評価のみで)評価している。両取りで駒が逃げる場合に、もう一方にひもを付けながら逃げる手、といった手は実際に1手動かすことで発見できる。
2.2 仮評価
指し手の仮評価は、移動する升目における敵と味方の利きの数によりMIN-MAX法で処理している。
例えば、図1において85の歩を取る価値は、駒の価値を表3のように考え、まず駒の交換は価値の低い順に起こると仮定する。この場合は先手は桂、銀、角、飛の順に85に移動し、後手は香、金、飛の順番となる(図1では後手の飛と香が上下逆の場合は、香が先に動けないが、そこまで考慮はしていない)。
ここで、最初は先手には85の歩を取るか取らないか選択する権利がある。歩を桂で取ったとすると後手にはその桂を取るか取らずかの権利が同様にある。これを図にすると第2図のようになる。
この図で先手番を MAX局面、後手番を MIN局面として取った時を左側、取らなかった時を右側の枝とすると、最初取らないと当然のごとく価値は 0、最初取って次に取り返してこないと歩得なので+200、で取り返しにきた後取らないと 歩−桂 になるので
-700。このようにして木を作って下から繰り上げると結局歩を取る手の価値は+160となる。
この値に盤の位置などによる点数を付加して順序付けしている。
再掲、図1
図2
2.3 生成する順番
探索途中の局面で、指し手を生成する順番は以下の通りである。
以下の手は探索深さが残り1手でも読む。
・直前に動いた相手の駒を取る手。
・駒を取る手(成る手)。
・反復生成で持ち上がった、相手の好手を防ぐ手。(後述)
・有効王手。これは駒損しない王手のことである。
・取られる最大の駒が逃げる手(2番目の駒は考慮しない)。
以下の手は残り2手でも読む。
・駒当たり(両取りを含む)。
・空き王手。
・相手玉の上部に金銀を打つ、近づける手。
・空き当たり。
・カット駒(飛角香と王の間で動けない駒)への当たり。
・飛角を敵陣に打ち込む手。
以下の手は残り3手以上。
・王周辺に駒をぶち込む手。
・送りの手筋(ただで王手をして、取られたとき、王で守っていた駒が取れる場合)。
・桂以下の駒取り。
以下の手は深さ2手目までは読む。
・飛車、香車の先の歩を突く。
・3手の読みの手(ただで駒を捨てて、取られた場合に、駒取りや両取りの手が新しく生じる場合)
・歩を叩く手。成り捨てる手。
・歩を垂らす手。
・飛車角の利きを止める手。
・敵陣の金類が寄っていく手。
・直前に指した駒への歩打ち。
・直前に動いた駒に対する当たり、又はその前方に駒が動く手。
・王が動く手。
以下の手は序盤の時のみ、深さ2手目で読む。
・歩への駒当たり。
・位取り(歩打ち)。
・でたらめに盤上の駒が動く。
以下の手は深さ1手目(初手)でのみ読む。
・でたらめに盤上の駒が動く。
・駒の損得を無視した王手。
・歩を打つ手を15手まで生成。
全ての手を生成するサブルーチンにおいて、生成する手の上限を決め、重要と思われる手からピクアップしていく。また、生成する手数も、残りの探索深さに依存している。
また、上に挙げたように、後1手しか読まないのに両取りをかける手は読まない。後2手以上読むときだけである。例えば、王手飛車をかける手は、角打ち、王が逃げる、これで2手を使ってしまう。残り2手だと、飛車を取る手が読めない、と思うかもしれないが、実際は末端の駒の取り合いルーチンで評価できる(確実性には欠ける)。要は、その指し手を指した事によって効果が得られる探索深さが残っている場合にだけ、その手を読ませればいい。
サブルーチン内での指し手の生成の評価付けで特に慎重に作ったのが、駒当たりをかける部分である。YSS では残り探索深さが3以上のとき、上位5手しか駒当たりを生成しない。が、駒当たりは持ち駒を持っている場合など、時に数十手にも及ぶ場合がある。そのため以下の順序で生成する。
・香、桂、銀、金、角、飛を使っての両取り。この内、駒打ちよりは移動手を優先する。
・飛車から香車までの駒当たり。
ここでも盤上の駒が動く当たりを優先し、駒を打つ手、特に、飛角に対して、金銀を打つ手は低く評価している。
なお、歩を打つ当たりは高く評価している。
指し手の並び替えは行っていない。実際の指し手の順番は指し手生成と同時であり、指し手を目的別に生成した後、同じ指し手を消去するだけである(ただし前回の最善手は先頭に持ってくる)。
生成される手数の平均は中終盤において、初手で80手、2手目で40手、3手目で20手、以下4手目以降では6手〜13手程度である。全体としての指し手の数に特に上限は付けていない。反復深化法で探索した場合、6倍の時間をかけるとほぼ1手深く探索できる。
2.4 指し手の反復生成
反復生成とは、受けを狙った指し手の生成法の事である。
受ける手を選ぶ場合、その局面で次に銀がただで取られそうだ、という場合は、その銀に対して利きを付ける、銀が逃げる、といった手を生成する事はたやすい。しかし、飛車を乱暴に切られて、それを同〜と取った時に、王が素抜かれる、といった場合は、その飛車を切る手が、局面を見ただけでは(読みを入れなければ)有力とは判断されない。つまり、飛車切りを受ける手の生成が困難になる。
これを解決する方法が反駁生成である。まず、実際に探索を始める前に、1手パスをして、相手の最善手を求める。そして、この相手の最善手を防ぐ手を生成する。つまり、自分が何もしない時の相手からの一番厳しい指し手を探して、それを防ごうとするのである。
実際には1手パスした時の最善応手手順を求めて、その手順中、相手の手ならばそれを防ぐ手、自分の手ならばその手を、指し手生成リストに加える。
例として第3図を示す。先手は、この局面で何もしないと次に▽54歩から角を追われて77の桂を飛車で取られてしまう。
図3
もしこの局面において55の角が存在しないのであれば77の桂が1手で取られることが分かるので▲78銀、▲68金等、ひもを付ける手の発見は容易である。
ここで、仮に先手が1手パスをしたとする。その場合の相手の最善手は▽54歩から桂を取る手であり、最善応手手順は、▲PASS、▽54歩、▲46角、▽77飛成、▲69玉、となる。
そこでこの応手の内、先手番の手ならばその手を、後手番の手ならばそれを咎める手を最初の局面で生成するわけである。
ここでは▲46角、▲69玉の2手を生成し、後手番の▽54歩を咎める手(ここでは存在しない)、桂を取っての▽77飛成を防ぐ手を生成する。この場合、77にひもを付ける▲78銀、▲68金や、取られる桂が逃げる▲85桂、▲65桂を生成する。
この結果、1手の読みでは分からなかった▲78銀等の受ける手を見つける事が出来る。
なお、これらの手は直前の指し手を防ぐ手以外においては盤面が変化して指すことが不可能なことも当然あり得る。そのため、生成した指し手がルール違反でないか、厳格にチェックする必要がある。
実際の探索においては末端以外の全ての局面で、1手パスをして相手の最善手を探している。
なお、現在の YSSは、最善応手手順中の3手目以降の相手の手を防ぐ手は(初手での生成を除いて)生成していない。これは長手順の最善応手手順が得られた場合、防ぐ手の数が多くなりすぎるためである。
この生成法にも欠点はある。まず、1手パスした時の相手の最善手を求めねばならないため、αβ値を−無限大、+無限大に戻して探索する必要がある。このため、探索の効率が悪くなる(ウィンドウ *[1] のアイデアが使えない)。
また、実際には深さ2手目以降では、それまで確定してるαβ値を用いて、1手パスを行うため、得られた相手の最善手が必ずしも最善ではない場合がある。例えば、本当は次に1手詰があるのだが、飛車を取る手ぐらいでも枝刈りが起きてしまう場合、1手詰を防ぐ手ではなく、飛車取りを防ぐ手を生成してしまう。反復深化法を用いることにより、この現象は改善されたようだが、確実ではない。
また、より深刻な問題として、浅い読みで得られた受ける手、1手だけが、より深い探索で優先されてしまう事がある。例えば深さ2の探索で、相手の1手詰を見つけ、それを防ぐ手を何手か生成する。この内、筋が悪く飛車を打って受ける手が最善そうに見えたとする。そして、深さ3以降の探索の時、ハッシュテーブルから前回の最善手、飛車打を見つけ探索する。この場合、次の1手詰みは防がれるため、相手の最善手は別な手である。そのため、1手詰を防ぐ別の指し手は候補に上がらず考慮されない。
2.5 キラームーブ
1手パスをして得られた相手の最善手は理想的なキラームーブ *[1] となる。ただ、ハッシュテーブルに全ての局面の最善手を登録している現在、あまりその必要性を感じなくなったので使っていない。
3.評価関数
3.1 序盤と終盤の判定
YSSでは評価関数を序盤と、終盤の2つに分けている。現在の局面が序盤か、終盤かの判断は探索を始める前に判定している(探索の途中で変更はしない)。
判定の基準は以下の通りである。
・香車以上の持ち駒を双方合わせて3枚以上持っている。
・敵陣に自分の銀以上の駒、もしくは成駒が1枚でも侵入している(自陣も同様)。
・香車以上の駒が取られる状態にある(双方の駒に対して)。
この条件のどれかが成立した場合は終盤と判定している。ただし、対局開始10手以内は序盤としている。
YSS では駒の価値は常に一定である。ただし、終盤においては位置による増減を厳しく行っている。
3.2 駒の価値
駒の価値は表3の通りである。
4、5段目の値は、駒交換を計算する場合に用いる数値で、駒の価値の2倍、成駒の場合は駒の価値の2倍+成った価値、となっている。持ち駒については付加点を付けている。表は1枚目の付加点で、枚数によっても点数を変えている。例えば金の場合は
1枚目+90、2枚目+40、3枚目+10、4枚目+0、と多く持つほど付加点を少なくしている。
表3 駒の価値
歩 香 桂 銀 金 角 飛
基本価値 100 430 450 640 690 890 1040
駒が成る価値 320 200 190 30 0 260 260
持駒の付加価値 15 50 60 80 90 220 230
駒交換用(基本) 200 860 900 1280 1380 1780 2080
(成駒) 520 1060 1090 1310 0 2040 2340
3.3 序盤の駒組み
序盤では、駒の価値は基本的に一定である。盤の中央ほど価値が高い、や相手玉に近づくとプラス、といった数値は、最大でも10点の差(歩の価値の10%)しか生じない。主な増減基準は「駒組のため増減テーブル」と「位取り」による。
序盤には定跡はまったく使用していない。そのため弱い。ただ、コンピュータがその意味を理解できないような高度な定跡は入れるべきではない、と考えている。
序盤の駒組みには「落とし穴方式」と私は呼んでいる方法を使っている。ある位置に金銀がいる場合は +n 点、といった表を作り、早く動かしたい駒ほど、点数の差が大きくなるように表を作る。こうすることにより、点数の差が大きい順に駒が落ちて(移動して)、最終的に矢倉や美濃囲に組むようになる。これを全ての駒ごとに作成する。
実際にはこの落とし穴の表を「矢倉」「居飛穴」「左美濃」「57銀左急戦」「空中戦」「振飛車」「振飛車穴熊」「駒落ち用(コンピューターが)」「戦型未決定時」と9種類もち、乱数と、相手の飛車の位置などにより選択している。
この落とし穴の表には、実戦的な意味あいを考えた増減も加えている。例えば、香車ならば下段ほど価値が高い、端に跳ねた桂馬はマイナス、盤の隅の金銀は大きくマイナス、7段目の飛車はやや減点、といったものである。これに盤の中央程価値が高くなる数値(中央が+3で隅が-5)を足している。この値はもっとも小さく、影響は少ない。
・位取り
以下、全て先手からみた評価基準を示す。位取りの対象は3,4,5段目にある先手の歩のみに対して行う。
・5段目では3段目に相手の歩があるときは+1、相手の歩がない場合に、先手の歩が「ブラ」なら+2、ひも付きなら+15点。
・4段目では次に成れる場合(相手の利きが前方にないとき)は+100。相手の歩が2段目にある時は+20。ないなら+40。歩がひも付きなら+10。
・3段目は4段目より-10して、4段目と同様である。
つまり、3,4,5段目に歩があり、相手が歩で受けていない場合は価値を高くしている。
また飛車先の歩が切れている場合は+35点。その時、相手が歩で受けていない時はさらに+30点している。
自玉頭の歩が切れている状態は-15点し、矢倉戦で自玉頭の歩を交換しないようにしている。
序盤の駒組は将来は一般化できる、と考えている。基本的には位取りの概念と金銀のバランス問題になるはずである。
3.4 終盤
終盤の評価関数は、駒得と駒の位置による増減、そして王周辺の敵の利きによる安全度の3つに分かれる。
駒得については序盤同様一定で、大きく変化するのは位置による増減、王の安全度である。なお、手番については評価していない。
駒の位置による増減を決めるのは双方の玉の位置による。自玉の近くと相手玉の近くでは加算され、離れたところでは減点される。
表4は先手玉に対する後手の駒の付加点である。駒の価値を100とした場合のその位置における駒の増減表である。50で半分の価値となる。なお、x軸については対称となるので省略してある。例えば、69に先手玉がいる場合、37に金がいる場合は、114 である。金の
価値は690点なので、付加する点数は 690*(114-100)/100 = +96点になる。19に金がいる場合は 690*(75-100)/100 = -172点である。
−−− 表4 −−−
先手の玉に対する後手の駒の相対位置による増減割合。X軸については対称である。
0 1 2 3 4 5 6 7 8 (X距離)
+8 50 50 50 50 50 50 50 50 50
+7 50 50 50 50 50 50 50 50 50
+6 62 60 58 52 50 50 50 50 50
+5 80 78 72 67 55 51 50 50 50
+4 100 99 95 87 78 69 50 50 50
+3 140 130 110 100 95 75 54 50 50
+2 170 160 142 114 98 80 62 55 50 <--- 2段真上を最大とする。
+1 170 165 150 121 94 78 58 52 50
0 170 145 137 115 91 75 57 50 50 <--- 中心。左隅(0 0)に王がいるとする
-1 132 132 129 102 84 71 51 50 50
-2 100 97 95 85 70 62 50 50 50
-3 90 85 80 68 60 53 50 50 50
-4 70 66 62 55 52 50 50 50 50
-5 54 53 51 50 50 50 50 50 50
-6 50 50 50 50 50 50 50 50 50
-7 50 50 50 50 50 50 50 50 50
-8 50 50 50 50 50 50 50 50 50
(Y距離)
表5は先手玉に対する先手駒の付加点である。ただし、桂、香、歩の小駒に対しては、付加点を少なくし、桂、香の駒は玉が実際の位置よりももう1段上にいるとして計算している(桂香では敵玉から3段上が最大となる)。
そして、ある位置にいる駒に対して、先手玉、後手玉からの付加点を計算して大きい値を採用する。
−−− 表5 −−−
人間王に対する人間駒の価値
0 1 2 3 4 5 6 7 8 (X距離)
+8 50 50 50 50 50 50 50 50 50
+7 56 53 50 50 50 50 50 50 50
+6 64 61 55 50 50 50 50 50 50
+5 79 77 70 65 54 51 50 50 50
+4 100 99 95 87 74 58 50 50 50
+3 116 117 101 95 88 67 54 50 50
+2 131 129 124 114 90 71 59 51 50
+1 137 138 132 116 96 76 61 53 50
0 142 142 136 118 98 79 64 52 50 <--- 中心
-1 132 132 129 109 95 75 60 51 50
-2 121 120 105 97 84 66 54 50 50
-3 95 93 89 75 68 58 51 50 50
-4 79 76 69 60 53 50 50 50 50
-5 64 61 55 51 50 50 50 50 50
-6 56 52 50 50 50 50 50 50 50
-7 50 50 50 50 50 50 50 50 50
-8 50 50 50 50 50 50 50 50 50
(Y距離)
こうして作った図が図5である。この図は先手の金の位置による増減を示している。
双方の王の近くで加算され、特に、後手の王の上部では価値が大きくなっている(上から押さえる感覚)。
1手読みの場合、自玉を固めるよりは、相手玉の真上に金を打って寄せに行く手を選ぶわけである。
− 図5 −
後手の持駒:飛 桂 歩二
9 8 7 6 5 4 3 2 1
+---------------------------+
|v香 ・ ・ ・ 銀v飛 ・v桂v香| 一 -324 -303 -262 -207 -103 -34 -20 0 -20
| ・ ・ ・ ・v銀 金 ・ ・ ・| 二 -269 -248 -200 -110 13 200 220 220 220
|v歩 ・ ・ ・ ・ ・ ・v玉v歩| 三 -158 -144 -158 -62 103 255 310 483 310
| ・v歩 銀v歩v歩 ・ 歩v歩 ・| 四 -6 0 -6 -34 144 345 448 483 448
| ・v桂 金 ・ ・v歩 金 ・ ・| 五 117 110 117 6 96 289 414 483 414
| ・ ・ ・ 歩 歩 ・ ・ 銀 ・| 六 200 213 200 165 96 69 207 276 207
| 歩 歩 ・ 金 ・ 歩 ・ ・ 歩| 七 262 255 262 220 110 -27 -6 0 -6
| ・ 玉 ・ ・ ・ ・ ・v馬 角| 八 289 289 289 248 124 -13 -144 -138 -151
| 香 桂 ・ ・ ・ ・ ・ ・ 香| 九 220 220 220 200 62 -34 -172 -262 -276
+---------------------------+
先手の持駒:歩二
龍、馬に関してはその位置がプラスならば加算はするがマイナスならば減点はしない。大駒は移動度が大きいため、戦場から遠ざかっていても働きはあるためである。
また、敵陣に飛車が打ってある場合は、その位置が、どのくらい相手玉の駒に利いているか、で評価点を付けている。例えば、底歩で飛車の利きが止まる位置ではマイナスになる。
なお、高速化のため、歩、香、桂の小駒に対しては、探索途中で王が動いても再計算せずに、探索前の王の位置から増減表を作って加算している。
王の安全度で考慮されるデータは以下の通りである。
・壁形をマイナスに。王が4筋、3筋にいるときに3、2筋に動けない時は減点。
・王の周囲8マスについて敵の利きがある場合に、
・自分の駒があって利きが同数の場合は、その駒の4分の1、利きが勝っている場合は8分の1を減点する。
・駒がない場合で利きが負けてる場合 -250、利きが同数で打ち込める駒がある場合 -200、駒なし -130、それ以外 50。
これは真上のマス目に関してで、真下に行くほど減点は下がる。
・敵の飛車に睨まれて動けない駒はマイナス。
・入玉を考慮して、敵陣に近づくほどプラス。例えば先手玉の場合、1列目においては9段目から1段目まで
0, 0, 0, 150, 450, 900,1300,1550,1600
と、いう感じに数値を付加していく。
追記:2005年01月22日
自玉の近くの点数は高すぎるので、現在は+を3分の1の値まで下げています。
また自玉の近くの金銀の枚数が4枚以上は減点されるようにしています。
3.5 末端での評価
末端の処理方法は、以下の通りである。
まず、王手がかかっている局面では無条件に後1手深く探索する。それ以外では、次に自分が取れる最大の駒得を探しだし、「その駒が取れる」と考えて、その価値を無条件に静的評価関数にプラスしている。プラスする値は駒交換で用いる値である(歩の価値を 100 とした場合、歩が取れるなら +200 する)。それに取れる駒の持ち駒の付加点も加えてる。
読みを打ち切る深さが奇数か偶数か、といった事は一切考慮せず、探索限界が来たら読みを打ち切っている。シンギュラー延長 *[1] は行っていない。
追記:2005年01月22日
Singular Extensionsはちょっと誤解していました。
末端だけで突出した手を1手延長するのではなく、深さが浅いところでも全て突出しているかどうか、を調べる方法です。
・正確な評価値、v がわかっているPVノード(Principal Variation)では それ以外のノードを v - S 、という
マージン(S)をつけた値でNull Window探索。残りのノードが全て悪いことが分かれば延長。
・正確な評価値が分からず、上から伝えられたαβの範囲を上に超えてしまう場合。
(Maxノードではβ以上、Minノードではα以下の値が返って来る場合。Fail-High)
通常はこの1手ですぐに枝刈りされるのですが、この手がSingular、であることを示すために
残りの手に対しても探索を行います。ただ普通の深さで読むとMinMax探索になってしまって効率が悪すぎるので
Maxノードならば β - Sh というマージン(Sh)で読みの深さを r だけ減らした D - r の探索を行い、
残りのノードが全て悪いことを確認します。
CHIPTEST(DEEP BLUEの前のDEEP THOUGTのそのまた前のバージョン)では
マージン(S)はポーン4分の1(25点)でマージン(Sh)はポーン4分の3(75点)を使い、
読みを減らすrは r=2 という値を使って2手浅い探索をしています。
詳しくは原論文を参照のこと。
T.S. Anantharman, M.S. Campbell, F.-h. Hsu,
Singular extensions: Adding selectivty to brute-force searching,
Artificail Intelligence 43 (1) (1990) 99-110
または、
ICCA Journal, Vol. 11, No. 4, (1988) p. 135-143
4.探索アルゴリズム
4.1 0.5手延長アルゴリズム
YSS 6.0 までは深さ固定のαβ法 *[1] を用いていたが、YSS 7.0 からは反復深化法 *[1] を用いて探索している。
今回、反復深化法を採用した理由は、考案した0.5手延長アルゴリズムの有効性を試してみたかったためである。
反復深化法とは、ある固定の深さnまで縦型に読む方法ではなく、深さ1、2、3、・・・nと順次、深さを増して探索していく方法である。
最初に1手の深さまで探索を行い、最善手を見つける。次に2手の深さまで探索を行う。この場合に最初に、1手の深さで見つけた最善手から最初に読みはじめる。以下、同様に3、4、5、・・・n手、と探索の深さを1手づつ深くしていく。前回の探索で得られた最善手を最初に読むことでαβ法での枝刈りの回数を増やして読みの量を減らすことが出来る。
この探索方法は思考時間に制限がある場合に有力に働く。ある深さnの探索途中で時間が切れた場合、それが、1番目の指し手(n-1手の探索での最善手)の探索途中だった場合は、その手を。2番目の途中だった場合は、その手の正確な評価値がまだ判明していないので1番目の手を。3番目以降で、最善手が入れ替わっていた場合は、その手を選ぶようにする。
0.5手延長アルゴリズムとは、反復深化法において、前回の最善手を読む場合にだけ、探索深さ限界を0.5手延長する方法である。これにより基本深さ n の探索を行った場合に、もっともらしい手の探索を最大 2n-1 の深さまで探索する事が出来る。
例として図6を示す。
この図は固定深さ3の探索を0.5手延長アルゴリズムで行った場合である。
各局面(ノード)における可能な指し手(分岐数)は2手づつとし、左側の手が(枝が)最善手だとする。
ノードの横の数字はその局面における探索深さ限界(depth_max)を示している。
各ノードから左の枝をたどって下がっていくと depth_max は 0.5 プラスされる。右に下がる場合は depth_max はそのままである。
探索開始局面( depth_max = 3 )から、左に3回下がっていくと、depth_max は +0.5 +0.5 +0.5 されて depth_max = 4.5 となる。
通常の探索ならば、この深さ3のノードで読みが打ち切られるはずである。しかし、ここでは depth_max = 4.5 なので後1.5手、探索を続ける事になる。さらに左の枝を1回下がると +0.5 で depth_max = 5 となり、もう1手深く探索できる。さらに左の枝を下がった場合、depth_max = 5.5 となり、これが 5 を超えていないので、ここで探索は打ち切られる。
逆に、開始局面から右の枝を3回下がった場合は、depth_max は 3 のままであり、探索は延長されることなく、ここで打ち切られる。
このように、左の枝を下がるたびに(最善手を選ぶたびに) depth_max は 0.5 づつ足されていく。その結果、基本深さ n (=3) の探索をした場合に、もっともらしい手の探索が最大 2n-1 (=5) の深さまで可能となる。
このアルゴリズムの特徴は最大 2n-1 の深さまで探索する可能性を持ちながらそれにかかる時間は(末端局面の数は)、通常の2〜3倍程度で済む、ということである。
例えば 各局面の分岐数を20とし、固定深さ 8 の探索をした場合、最大15手先まで読むにもかかわらず、探索時間は2.35倍にしかならない(表6)。
−表6−
深さ 時間 最大
----------------
2 | 1.04 3 |
3 | 1.14 5 |
4 | 1.28 7 |
5 | 1.46 9 |
6 | 1.70 11 |
7 | 2.00 13 |
8 | 2.35 15 |
9 | 2.79 17 |
10 | 3.32 19 |
----------------
分岐数20で、1手のみ 0.5手延長した場合のかかる時間と最大探索深さ
実際の探索では局面の分岐数は 6〜80 程度であり、そのうち延長される手は最善の手、1手だけである。
どの手が最善手かは、反復深化法での前回の n-1 の深さの探索で得られた最善手をハッシュテーブルに登録しておいて調べている。最善手が登録されていない場合は、第1番目の指し手を前回の最善手と仮定して延長を行っている。
前回の最善手以外の手が、新たに最善手と判明した場合、もう一度その手を 0.5手延長しなおす事はせず、ハッシュテーブルに登録されている最善手を書き換えて終了する。なお、新たな最善手を全て0.5延長しなおした場合にかかる時間は、しない場合の3倍程度であった。
0.5手延長を行った場合に、どの程度の棋力の向上が見られたかは難しい。実際の探索では推定することしかできないが、得られる最善応手手順は 5手読みで6手から7手。7手読みで8手から10手程である(王手の局面では延長を行っているので正確ではない)。
ただ、一般的に読みの深さは深い方が強い。駒交換が生じる局面では同〜と取る手がほとんどの場合最善手になるので交換の変化を深く読むことができる。また、中盤から終盤にかけて駒損を無視して一気に寄せの形まで「もっていく」力がついた。
追記:2003年02月24日
現在のYSSでは最善手が入れ替わるたびにもう一度0.5手延長しなおす方法+多重反復深化(この局面から後5手探索するのに、
ハッシュに4手前の結果がなかったら、そこから深さ1,2,3,4,5と反復深化させる)を組み合わせています。
最善手が切り替わった場合に深く読めるかどうかが0.5手延長では重要なのではないかと思います。
欠点としては全ての手を探索する局面では0.5手延長も意味が大きいのですが、
1手だけ探索してその手で枝刈りが起こる場合、最善手である確率が低い(浅い探索での最善手に過ぎない)
にも関わらず0.5点延長をかけていることです。
追記:2005年08月23日
進歩の2では校正の段階で効果があったことを追加したのですが、こっちには反映させてませんでした。
下は1手1秒での自己対戦の結果です。
--- 連続対戦 1168_half_ext_0 - YSS 11.68, 対戦数=1000 --- 2004-02-17 12:34:32
679: 居左 144手で YSS 11.68 の勝, (YSS 11.68 の454勝225敗) YSS=01:59,02:31,(1329:21,1189:05)
0.5手延長なし。ありが圧勝!勝率=0.67。もっともかなり特化されてるので微妙だが。
--- 連続対戦 YSS 11.69b - 1169b_half_ext_1, 対戦数=1000 --- 2004-02-20 10:19:24
178: 居左 66手で 1169b_half_ext_1 の勝, (1169b_half_ext_1 の89勝89敗) YSS=00:58,00:43,(182:22,203:54)
完全0.5手と通常0.5手では差異は見られない。
上の結果では分かりにくいのでまとめますと、まず、以下の3種類を比較してます。
1. 完全0.5手延長(前回の最善手は最初から0.5手延長して、途中で切り替わった最善手も再度0.5手延長しなおす)
2. 通常0.5手延長(前回の最善手だけ0.5手延長して、途中で切り替わった最善手はそのまま)
3. 0.5手延長なし。文字通り延長しない。
そして結果は、
完全0.5手 − 0.5手延長なし 454勝225敗 勝率 0.67
完全0.5手 − 通常0.5手延長 89勝 89敗 勝率 0.50
ちょっと通常0.5手の検証回数が少ないのですが、0.5手延長をかけた方が明確に勝ち越してます。
もっとも内部では0.5手延長されてる時は詰将棋を少し深く読む、とか特化されている部分があるので
これだけで絶対に効果がある、とは言えないですが。そもそも完全0.5手延長で強くなるように
調整してきたので、その影響も大きいと思います。
表7に評価関数を呼んだ深さの割合を示す。これは YSS 同士を対局させた場合で、初手から投了までの全ての合計である。序盤では浅い深さで読みが打ち切られるため、総探索局面の大部分は中盤以降の探索が占める。6割以上が基本深さ8手を超えている。最大20手の深さまで探索しているのは、王手による延長や駒損変化の2手延長のためである。
− 表7 −
深さ %
0: 0.0:
1: 0.0:
2: 0.0:
3: 0.2:
4: 0.5:
5: 1.2:**
6: 3.3:******
7:10.7:*********************
8:21.0:*****************************************
9:25.7:***************************************************
10:20.3:****************************************
11:10.1:********************
12: 3.9:*******
13: 1.7:***
14: 0.9:*
15: 0.4:
16: 0.1:
17: 0.0:
18: 0.0:
19: 0.0:
20: 0.0:
手数=140,総探索局面=78436893、総思考時間 = 37分30秒(2250)。
基本深さ8、40秒で打ち切り(CPU Alpha500MHz)。
延長の割合が 0.5 が妥当か、という事は不明である。改良案として、割合を 0.1〜0.9 まで変化させる、最善手以外の手でも延長、もしくは逆に短縮する、といった事が考えられる。
図7、図8に、単純なαβ法を用いた場合の max関数と0.5手延長を組み込んだ YSS の min関数を示す。
図7 − 通常のαβ法で用いる max 関数 −
int f_max( int alpha, int beta )
{
if ( depth >= depth_max ) return(評価値);
指し手を生成する。
for ( i=0 ; i < 生成した手数 ; i++ ) {
手を進める
depth++;
k = f_min( alpha, beta );
手を戻す
depth--;
if ( k > alpha ) alpha = k;
if ( alpha >= beta ) return(alpha);
}
return(alpha);
}
図8 − YSS で用いている min 関数 −
int f_min( int alpha, int beta )
{
この局面から後何手探索するかでハッシュテーブルをチェック。
探索済み、又は上限値がα以下ならば評価値が決定した深さとその最善手を返して終了。
if ( depth >= depth_max && 王手ではない ) {
評価値が決定した深さを設定。
評価関数に、取れそうな駒の点を付加して終了。
}
if ( 王手である ) 受ける手を生成。
else {
1手詰を調べる。
残りの探索深さの大きさにより詰将棋を呼ぶ。
直前に移動した駒を取る手を生成。
駒を取る手を生成。
1手パスして相手の最善手を求め、それを防ぐ手を生成。
その他の指し手を生成。
}
同一手をカット。
前回の最善手を先頭に持ってくる。
この局面で2手延長するべきか判定する。
for ( i=0 ; i < 生成した手数 ; i++ ) {
手を進める
depth++;
if ( i==0 ) depth_max += 0.5;
k = f_max( alpha, beta );
if ( k < beta && 2手延長する ) {
depth_max += 2;
k = f_max( alpha, beta );
depth_max -= 2;
}
手を戻す
depth--;
if ( k < beta ) {
beta = k;
評価値が決定した深さをコピー。
最善応手手順をコピー。
}
if ( i==0 ) depth_max -= 0.5;
if ( alpha >= beta ) {
ハッシュテーブルに登録。
return(beta);
}
}
王手が解除されていない時は打ち歩詰めをチェック。
if ( 一度でもbeta値が更新された ) ハッシュに登録。
return(beta);
}
4.2 ハッシュテーブル
YSS ではハッシュテーブル *[1] を詰将棋、実際の探索、共に使用している。どちらも末端局面は登録していない。オープンアドレス法 *[7] を用いて16回まで再ハッシュを行う。再ハッシュの手順は乱数で決めた固定された順番で行う。なお、ハッシュ領域を75%まで使いきった時点でハッシュテーブルは満杯と判定して、以下の登録は行わない。
ハッシュコードは64ビットの値を用いている。当初、32ビットの値を使っていたが、30万局面の探索で2、3回以上の割合で誤認が起こってしまい、信頼性がなく、64ビットに変更した。64ビットの場合、概算では毎秒1万局面探索するプログラムで1日中探索した場合(約 2^32 局面)、同一局面の誤認が起こる確率は1%である。乱数は a=16807, m=2^31-1 の乗数合同法を組み合わせて使っている *[6]。
思考ルーチンでは、最初に詰将棋を読む。そして、詰将棋で詰む、と判明した局面のみ、思考で使う評価値にも登録している。
詰将棋で用いるハッシュテーブルと実際の思考で使う数値は分けている(表8)。
詰将棋では先手の詰と後手の詰でも数値を分けている。これは逆王手がかかる局面での混合を防止するためである。
手番の情報は、ハッシュコードのビットを反転させることで表している。1手パスが2回続くと元に戻る。
−表8−
ハッシュテーブルに登録する値
1. ハッシュコード 64 bit
2. 持ち駒コード(後手の持ち駒の枚数)32 bit
3. 思考用評価値(上限、下限、正確な値)
4. 評価値が上限か、下限か、又は正確な値かを示すフラグ
5. 探索深さ(この局面から何手読んだか)256の倍数で小数点を表す
6. 先手用詰将棋の相対的な探索深さ
7. 先手用詰将棋の評価値(詰、打歩詰、不明、切れ)
8. 後手用詰将棋の相対的な探索深さ
9. 先手用詰将棋の評価値(詰、打歩詰、不明、切れ)
10. 最善手
4.3 2つの地平線効果
コンピュータ将棋はしばしば無意味な歩の叩きなど、将来的にみれば損をする手を指す場合がある。
これは地平線効果と呼ばれる(または水平線効果)。
後手の持駒:金 歩三
9 8 7 6 5 4 3 2 1
+---------------------------+
|v香v桂v玉v金v香 ・ ・ ・v香|一
| ・ ・v銀 ・ ・ ・ ・ ・ ・|二
|v歩v歩v歩v歩v飛 ・ ・v歩v歩|三
| ・ ・ ・ ・ 桂 角 ・ ・ ・|四
| ・ ・ ・ 桂 歩v歩 ・ 歩 ・|五
| ・ ・ ・ ・ ・ ・ ・ ・ ・|六
| 歩 歩 ・ 金v歩 歩 歩 ・ 歩|七
| ・v馬 ・ ・ ・ 銀 ・ ・ ・|八
| 香 ・ ・ 玉 ・ ・ ・v全 ・|九
+---------------------------+
先手の持駒:飛 金 銀 桂
先手:YSS
後手:極
図9は第4回コンピュータ将棋選手権におけるYSS−極戦である。
先手玉は、次に▽58金の1手詰である。最善は▲57銀と歩を払う手なのだが、それだと▽87馬の王手から▽65馬で桂を取られて先手は▲53桂成と飛車を取れなくなってしまう。人間ならば「しかたがない」とあきらめて▲57銀を指すが、コンピュータはそうは考えない。
ここでの YSS の指し手は▲62金であった。なぜか?。この時の YSS は6手読みで6手目の相手からの王手は読めなかった。YSS の読み筋は▲62金▽82玉▲72金▽同金▲53桂成、で次の▽58金の1手詰が6手目なので読めず、この変化を飛車得、と判断したのである。つまり、王手を連発することにより、自分に不利な手を読みの範囲内から押し出してしまったのである。
実戦は▲62金以下、▽82玉▲71銀▽同金▲72金▽同金▲57銀、と進んだ。この変化でもっとも致命的だったのが、▲71銀の王手である。▲62金、▲72金の2手は得な手ではないが、はっきり損ではない(▲62金を▽同金は▲同桂成▽同玉▲53桂成でよし)。しかし、▲71銀の手はただ駒を捨てただけで、これで決定的に形勢を損ねることになった。指した理由は▲62金と同様の理由である。
問題はここである。取りあえず損ではない手で、地平線効果が起こる場合は致命的ではない。しかし、駒損してでも1手詰を防ごう、とする手を指す場合は形勢をいちじるしく損ねてしまう。
このうち、表面上は損にならない手(有効王手や、損のない駒当たり)によって生じる地平線効果は、軽度の地平線効果、として対処はしない(対処の仕方が分からない)。問題とするのは実際に駒損する手を指す重度の地平線効果である。
私は重度の地平線効果を何とかしてなくしたい、と考えた。その結果出た結論は、
1.最初からそんな乱暴な手は読まない。
2.乱暴な手が有効そうに見えた場合、結局、無駄である事をコンピュータに理解させなければならない。
の2つである。
1.の最初から読まない方法は、読みの深い局面で当てはまる。末端近くにおいて重要な事は決して無茶な(乱暴な)手は読まない、ということである。末端の目的は局面を収束させることであるから、表面上有効と思われる手のみを数手読ませればよい。
2.を解決するために YSS で採用した方法は、まずは普通の読みの深さで探索を行い、評価値が更新される場合(地平線効果が起きそうな場合)は損な手を指した駒を取る手の探索を+2手延長する、という方法である。
つまり、第6図の変化において、▲71銀▽同金と指した変化からさらに6手の深さの探索を行うのである。単純には探索空間が倍になるので計算時間も2倍かかる。取れる駒が複数ある場合は、その全てを延長している。
2手延長した時の最善手は、駒を捨てる前(2手前)の局面での最善手になることが予想される。しかし、2手前の最善手をハッシュテーブルから探し、その手のみを読んで時間節約をしようとしたが、結果は非常に不安定であった。なぜならその最善手自体も「歩の叩き」といった地平線効果の影響を受けた手である事が多かったためである。
現在の YSS では3手目と4手目の深さにおいてのみ、この延長探索を取り入れている。そのため、意味不明な駒損する手を指すのは初手と2手目のみである。
この方法により YSS では不利な状況で駒損して暴れ回ることが少なくなった。
追記:2003年03月19日
上の書き方は嘘がありました。▲71銀▽同金と指した後に、▲72金、と指した手が評価値を更新しようとしている場合に
その手のみを2手延長しています。現在ではNullWindowで探索して評価値が更新するかどうかだけを確認し、
またいきなり2手も延長するのではなく、1手延長して評価が下がらないかを確認してから2手延長しています。
また、▲79銀が既に前回の最善手である場合には(もう有効なことが判明した)と考えて2手延長はしません。
にしても2手延長は重すぎます。ほとんどの無意味駒捨てを防げますし、駒を捨てた後に新たに駒当たりが発生する、といった場合にも
多少は有効だったりするのですが、・・・やはり重すぎです。
将皇の佐藤健一さんがWebで公開されている類似局面を利用した方法のほうが
優秀だと思います。
追記2:2004年03月07日
2手の交換をする前の最善手順を利用した地平線効果の判定は読みが浅い段階では
かなり有効なのですが、読みが深くなるとやはり無理がたくさん出てきます。
歩を叩いたために歩切れで最善手順中の自分の歩が打てない場合、
歩を叩いて同〜と取らせたために相手の合駒が出来なくなる場合、
など、双方の手が指せななるケースでも一概に地平線を判定するのは難しいと思います。
鶴岡さんがCSAの例会で発表されていた相手の手を1手、に固定してNullWindowで試す、とかが効果がありそうですがどうでしょう。
追記3:2004年03月07日
ちなみに謎的将棋の高橋さんの指摘によると図9の局面は実は詰があるそうです。
▲62銀、▲53角成、どちらでも長手順の詰があります。
4.4 千日手の打開
ハッシュコードを今までの手数ごとに保存することにより、千日手の判定を行っている。
完全に同一局面で、持ち駒が減っている場合、又は王手がかかっている場合は返ってきた評価値を厳しく減点して指さないようにしている。将来的にはどの局面で打開するか、の判定も必要だろう。純粋に同一の場合は形勢に関わらず、歩損でも打開するようにしている。
5.詰将棋
5.1 詰将棋
YSS では反復深化法とハッシュテーブルを利用した詰将棋ルーチンを使っている。が、それは極めて簡略化したルーチンである。実際に実戦に生じる詰む局面のみが詰めばいいと考え、以下のように読む手を制限している。
・初期状態から飛車2枚分以上駒損する変化は打ち切る。
・中合は読まない(後手の利きが何もない、または後手の王だけ利いているが、先手の利きが2枚以上で敵陣なら合駒しない)
・連続のただ捨て王手は読まない。歩の叩きを除き、直前に指した駒が同〜と取られている場合に、再びただになる王手は読まない。
・距離が4以上の飛角香の打つ王手は読まない。
・飛角歩は成れるなら成る。
王手をかける手の順番については特に考慮していない。王手を避ける手では、王手駒が取れる場合、その手を最初に読ませると効率がよい。
また、末端で最後の1手詰を調べるところは個別に処理している。
まず、王の回り8升を調べ、動ける位置のビットがONになる値を作る。
次に、駒を打つ場合、その駒打ち王手で、王の回り8升が塞がるビットデータを作成しておき、王の動けるビットデータとANDを取る。これによって後手が同玉と取れない駒打ち王手で詰む場合は瞬時に計算できる。
ただし、実際には図10において▲22銀と王手をした場合、飛車の横利きが消えて詰まなくなる例外があった。そこで現在は実際に盤上に打って確かめている。駒が移動する王手では利きが複雑に変化するので、同様に全て実際に動かして、詰、不詰を判定している。
9 8 7 6 5 4 3 2 1
+---------------------------+
| ・ ・ ・ ・ ・ ・ ・v玉v香|一
| ・ ・ ・ ・ ・ 龍 ・ ・ ・|二
| ・ ・ ・ ・ ・ ・ ・ ・ ・|三
| ・ ・ ・ ・ ・ ・ ・ ・ ・|四
| ・ ・ ・ ・ ・ ・ ・ ・ ・|五
これらの簡略化の効果は大きく、20手を超える詰でも1万局面程度を探索するだけで発見する場合がある。無論、中合で詰まない場合もあるので正確な詰ではない。
5.2 必死ルーチン
YSS では以前には必死をかける専用のルーチンがあった。詰を読んだ後に必死を読み、手がなければ思考ルーチンを呼ぶのである。必死ルーチンは、相手玉の回りに利きをでたらめにつけ、相手が平凡に受けてきた場合に(駒を取る、詰めろの手を防ぐ)さらに詰めろをかけて受けが続くかを調べるルーチンである。だが、相手からの王手を読んでいないことが致命的で、必死をかけた瞬間に自玉に王手をかけられて必死が解けて負けになる、ということが起きたために現在は外している。単純な(自玉への王手を読まない)必死ルーチンはつけないほうが安定した終盤を指す。
6.参考棋譜
現在のコンピュータの棋力の参考として、第7回コンピュータ将棋選手権における金沢将棋−YSS戦を紹介する。
大会に参加した YSS 7.0 は VT-Alpha(Alpha500MHz) を使用し、毎秒4万局面を探索する。ハッシュテーブルとして64MBを確保し、150万局面を記憶する。基本深さ8手で探索して、40秒で(20分以上は25秒)打ち切っている。ソースはC言語で38891行。
双方全勝で向かえた6回戦。事実上の決勝戦である。序盤は変則的な出だしから相矢倉に進む。コンピュータは相手の戦型による簡単な区別ぐらいは出来るがより、高級な判断はできない。一見、上手に駒組をしているようだが、それはほとんど相手を見ないで王を固めているにすぎない。居飛車、振飛車、といった簡単な対応はできるが、まだ序盤は初級者レベルだろう。
1手目。▲48銀。いきなりの居飛車指向。振飛車は YSS の居飛穴を敬遠したためらしい。
(当時、YSSは居飛穴には組めなかった。ラッキーである)
4手目。▽32金。後手は矢倉模様の将棋しか指せない。
図11
題名:第29手、△4四歩に▲5五銀です。
後手:YSS
後手の持駒:角
9 8 7 6 5 4 3 2 1
+---------------------------+
|v香v桂 ・ ・ ・ ・ ・v桂v香| 一
| ・v飛 ・v銀 ・v金v金v玉 ・| 二
|v歩 ・v歩 ・v歩 ・v銀v歩v歩| 三
| ・v歩 ・v歩 ・v歩v歩 ・ ・| 四
| ・ ・ ・ ・ 銀 ・ ・ 歩 ・| 五
| ・ ・ 歩 歩 歩 ・ ・ ・ ・| 六
| 歩 歩 銀 ・ ・ 歩 歩 ・ 歩| 七
| ・ ・ 金 ・ 金 ・ ・ 飛 ・| 八
| 香 桂 玉 ・ ・ ・ ・ 桂 香| 九
+---------------------------+
先手:金沢将棋
先手の持駒:角
図11の▲55銀は相手のミスを期待した?手。単なる歩取りに見えるが、▽63銀の後、▽45歩の銀ばさみで殺す筋は、▲44銀!のただ捨てから▲71角でしびれる。
図12
題名:第63手、△5二銀に▲7五歩です。
後手:YSS
後手の持駒:角 銀 桂
9 8 7 6 5 4 3 2 1
+---------------------------+
|v香 ・ ・ ・ ・ ・ ・v桂v香| 一
| ・ ・ ・ ・v銀v飛v金v玉 ・| 二
|v歩 ・ ・ ・ ・ ・ ・v歩v歩| 三
| ・v歩v歩v歩v歩 ・ 歩 ・ ・| 四
| ・v桂 歩 ・ ・v歩 ・ 歩 ・| 五
| ・ 銀 ・ 歩 歩 ・ ・ 銀 ・| 六
| 歩 歩 ・ 金 ・ 歩 ・ ・ 歩| 七
| ・ 玉 金 ・ ・ ・ ・ 飛 角| 八
| 香 桂 ・ ・ ・ ・ ・ ・ 香| 九
+---------------------------+
先手:金沢将棋
先手の持駒:金 歩
図12で▲75歩。金沢将棋の最初の疑問手。とは言ってもどう指すのかは難しい。▲46歩から角を使うのか。
図13
題名:第87手、△4一飛に▲4二金打です。
後手:YSS
後手の持駒:飛 桂 歩二
9 8 7 6 5 4 3 2 1
+---------------------------+
|v香 ・ ・ ・ 銀v飛 ・v桂v香| 一
| ・ ・ ・ ・v銀 金 ・ ・ ・| 二
|v歩 ・ ・ ・ ・ ・ ・v玉v歩| 三
| ・v歩 銀v歩v歩 ・ 歩v歩 ・| 四
| ・v桂 金 ・ ・v歩 金 ・ ・| 五
| ・ ・ ・ 歩 歩 ・ ・ 銀 ・| 六
| 歩 歩 ・ 金 ・ 歩 ・ ・ 歩| 七
| ・ 玉 ・ ・ ・ ・ ・v馬 角| 八
| 香 桂 ・ ・ ・ ・ ・ ・ 香| 九
+---------------------------+
先手:金沢将棋
先手の持駒:歩二
図13の▲42金は悪手。飛車銀は取れるが、取った後の金銀の遊び駒がひどい。逆に以下の順で玉頭の金銀を払われて寄らなくなった。▲25歩を絡めていけば難しかった。
134手目。▽79銀。後手の王も危ない形だがここでは先手の攻めはきわどく切れている。以下は即詰。YSS は7回戦の森田将棋戦も勝ち、予選から14戦全勝で優勝した。
第7回コンピュータ将棋選手権 本戦6回戦 1997/02/09 於:シェラトン・ホテル(千葉)
先手:金沢将棋2 (Alpha500MHz)
後手:YSS7.0 (Alpha500MHz)
▲4八銀 ▽3四歩 ▲2六歩 ▽3二金 ▲7六歩 ▽8八角成
▲同 銀 ▽2二銀 ▲7七銀 ▽3三銀 ▲6六歩 ▽6二銀
▲7八金 ▽5二金 ▲6九玉 ▽4一玉 ▲5八金 ▽3一玉
▲7九玉 ▽4二金右 ▲5六歩 ▽6四歩 ▲5七銀 ▽8四歩
▲2五歩 ▽2二玉 ▲4六銀 ▽4四歩 ▲5五銀 ▽6三銀
▲8八玉 ▽7四歩 ▲6七金右 ▽7三桂 ▲3六歩 ▽4五歩
▲3七桂 ▽3五歩 ▲同 歩 ▽5四歩 ▲4四銀 ▽8五桂
▲3三銀成 ▽同 金右 ▲8六銀 ▽3六銀 ▲4八銀 ▽9二飛
▲1八角 ▽3七銀成 ▲同 銀 ▽4二桂 ▲2六銀 ▽4四金
▲5三銀 ▽4三金引 ▲4二銀不成▽同 飛 ▲3四桂 ▽同 金
▲同 歩 ▽5二銀 ▲7五歩 ▽同 歩 ▲同 銀 ▽7四歩
▲同 銀 ▽7五桂 ▲7六金 ▽6七銀 ▲7五金 ▽3九角
▲6七金 ▽2八角成 ▲5一銀 ▽4三飛 ▲3五桂 ▽4四飛
▲2四歩 ▽同 歩 ▲2三歩 ▽同 金 ▲同 桂成 ▽同 玉
▲3五金 ▽4一飛 ▲4二金 ▽1九馬 ▲5二金 ▽2八飛
▲7八銀 ▽2六飛成 ▲4一金 ▽3五龍 ▲2七角 ▽2八馬
▲1六角 ▽1四玉 ▲3三歩成 ▽2五銀 ▲2六歩 ▽1七馬
▲2五角 ▽同 歩 ▲3四飛 ▽同 龍 ▲同 と ▽1五玉
▲8五銀 ▽同 歩 ▲2九桂 ▽2七馬 ▲2二飛 ▽2六玉
▲2一飛成 ▽3九飛 ▲1九桂 ▽1八馬 ▲3七銀 ▽1五玉
▲2四龍 ▽1六玉 ▲1七歩 ▽同 馬 ▲2五龍 ▽同 玉
▲1七桂 ▽1六玉 ▲2八銀 ▽2九飛成 ▲2五角 ▽1五玉
▲2七銀 ▽7九銀 ▲7七玉 ▽5九角 ▲6八金 ▽同 角成
▲7六玉 ▽8六金 ▲同 歩 ▽同 馬 ▲6七玉 ▽6八銀成
▲5七玉 ▽5九龍 ▲5八金 ▽同 龍 ▲投了
まで148手で YSS の勝ち
金沢将棋の消費時間:24:49
YSS の消費時間:26:02
7.将棋方程式を解く
将棋プログラムで重要なことは、読みと大局観、この2つを正確に作っていく事であろう。これは人間に対しても当てはまる事で、現在の所、YSS は人間の(私の)将棋に対する考えを真似する事で強くなってきている。
以前の私は、読みが大事と考えて、主に指し手を選択する部分に力を入れてきた。事実、駒の損得に関して正しい手が読めるようになると「強い」安定したプログラムにはなる。しかし、どんなに正しい手を読ませても、読んだの後の局面をコンピュータが「いい」と判断しなければ、コンピュータはその手を指してはくれない。また、指し手の選択は、多少手を加えても目に見える棋力の向上は少ない。
それに対して、大局観にあたる評価関数をいじった場合は、その効果は大きい。王の周辺ほど高価値、という終盤用の関数を組み込んだ結果、終盤で、そっぽに金銀を打つことはなくなり全体的にスジがよくなった。無論、欠点もある。王の近くほどプラス、ということは、王の「堅さ」は理解しているが「広さ」は理解していない事である。このため矢倉は得意だが、美濃囲いにした場合に、金銀が王に集結したがり、囲いを崩してしまう。つまり美濃囲いは王の広さを主張する囲いなのである。
評価関数の改良は効果が大きいが、作成自体が漠然としていて難しい。現在は、駒を盤に並べてみて、ここに金を打つならば、自陣の歩を何枚取られても大丈夫か、といったやり方で数値をいじっている。
次に重要なのがバグがないことである。真面目な話、将棋プログラムは巨大になり複雑な構造になっていくので、いかにバグを出さずにプログラムを構築するかは大きなポイントである。新しいサブルーチンを組み込んだ場合は、それが問題なく動作するか、を多くの例題を入力してみて試す必要がある。面倒な作業だが、これを行わないと、部分部分の信用性が下がって、全体的に「あてにならない」プログラムになってしまう。対局が終わった後に馬鹿馬鹿しいバグが見つかるのは日常茶飯事であるから。欠点の早期発見には、他の将棋プログラムとの通信対局が役に立つ。
次に高速性。時間が限られている以上、プログラムの高速性は必要不可欠である。システム全体の速度は、もっとも遅い部分に依存する。将棋プログラムの場合、もっとも時間がかかるのが「ある局面で指し手を生成する」部分と「実際に駒を動かす」部分である。YSS の場合、評価関数にかかる時間は比較的小さい。
また、「例外」が生じた場合の処理も重要である。効率よくプログラムを組むためには、これを未然に予知するのが非常に大きい。例えば、1手詰のパターン判定の失敗がそれである。
チェスでは人間がコンピュータに敗れた。将棋は有段の域に入ってきたといえ、まだ序盤が非常に弱い。高段者同士の対局では駒がぶつかる前に勝負がつくことも多々あり、今後は評価関数の正確さが必要になるだろう。評価関数がアマ有段者程度ならばプロには到底勝てないと考える方もいるであろう。しかし、読みを深める事によりそれはごまかしが利くはずである。
・より直線的に正確に読むこと。
・より序盤を強くすること。
この2つが今後の課題と考えている。
ハードウェアの高速化による棋力の向上は大きい。だが、それに頼っているだけでは進歩はないだろう。全幅探索で強くなっていくならば将棋プログラマの存在自体、意味をなさない。
コンピュータはミスをしない。何手先でも確実に正確に再現できる。将棋方程式に解はあり、今は近似解だがコンピュータはより真の解を見つけだすだろう。単なるシリコンのチップが最も才能のある者の創造力、想像力を超え、彼が到達した最高の地位に居座る日がそこまで来ているのである。その日まで長生きしていただきたい。
8.エピローグ
2010年、名人はコンピュータに対して、序盤で圧倒的に優勢を築いた。しかし、終盤戦。名人に見落としがでる。14手後の角打ちが詰めろ逃れの詰めろになるのだ。相手はそれに気づいているだろうか・・・。いや、まさか。・・・しかし、もしもということがある。彼は疑心暗鬼におちいった。彼の次の1手は用心のため、力をためる手であった。そして勝機は去った。名人は敗れた。彼はこう言うだろう「こんなのは将棋ではない!」・・・と。
参考文献
[1] David Levy, Monty Newborn著, 小谷善行監訳「コンピュータチェス」サイエンス社 1994
[2] 松原仁 編著「コンピュータ将棋の進歩」共立出版 1996
[3] 小谷善行、他「コンピュータ将棋」サイエンス社 1990
[4] Avron Barr 他「人工知能ハンドブック(第1巻)」共立出版 1983
[5] P.H.ウィンストン 長尾真 他訳「人工知能」培風館 1980
[6] Stephen K.Park, Keith W.Miller 西村恕彦訳「乱数生成系で良質なものはほとんどない」bit 1993年4月号
[7] 近藤嘉雪 「Cプログラマのためのアルゴリズムとデータ構造」ソフトバンク 1992
[8] コンピュータ将棋協会資料集 vol.2〜9
[9] 若林宏「将棋プログラム"ESS"」 MICRO 1984年3月号
元のページに戻る