詰将棋コンピュータってなんなのか

WFP72号WFP74号WFP121号WFP122号などで話題になっている詰将棋コンピュータについて、説明をしてみたい。ついてきてくれるとありがたいが、さてどうなのだろう。読み飛ばしていた方に雰囲気だけでも感じてもらえると本望。

コンピュータって何?

最初からざっくり端折るが、プログラムを動かせるのがコンピュータ。で、プログラムになにか入力を入れると結果が返ってくる。入力(ゲームの操作とか)や出力(ゲームの動きとか)が何回もあるタイプもあるが、ここでは最初に一回なにか入れて何か返ってくるものに限定する。

具体的なものにしよう。

計算式「1+2」を入れると「3」が返ってくる。これはシンプルな計算機だ。これは計算機というプログラム。

何を入れても常に”Hello World!”と帰ってくる。これも立派なプログラム。プログラムを変えることで入力に対応する出力が変わるから全体の仕組みがコンピュータ。

じゃあ、コンピュータに「将棋盤」を想定して、「何でも」に持駒、プログラムに「受方13歩、1手」と入れてみよう。詰将棋コンピュータへの第一歩だ。

この場合、結果はこうなる。長手数での詰みはごめん無視して。最善詰とでも思って。

・持駒金0枚>不詰
・持駒金1,2,3,4枚>1手詰、金の駒余り0,1,2,3枚

これはこう捉えることがでできる。「将棋盤」というコンピュータで、「盤面13歩11玉、1手」という盤面(プログラム)を与えると、「持ち駒金の数に応じて、「不詰(0枚)、金の数-1」を結果として返す。

つまり、金の数だけに注目してみれば、「金の数を1引いてくれる」プログラムなのだ!

結果の何に注目するは詰め上がりの盤面配置でもいいし、手数でもいいし、余った駒でもいいが、今誌上で試みられているのは余った駒、不詰かどうかに注目するものだ。

こういうプログラムでも良い。今度はプログラムに手数指定はない。

非限定は無視すると、2n手という手数を返す。つまりこれは、金の数を2倍にして手数で教えてくれるプログラム。
非限定が嫌いな向きには盤面:「51玉 アンチキルケ自玉ステイルメイト」入力:「歩n枚」で限定できる。「52歩 同玉」*nの2n手。やほう。

つまり、入力と結果の関係にどういう意味があるか、まったくないかをさておいておけば、将棋盤はコンピュータに、盤面プログラムに、今は持ち駒であることが多い可変要素入力に置き換えられる。役に立つかはともかく、最初から将棋盤はコンピュータで、盤面はプログラムになっているのだ!

Q:詰将棋をコンピュータにあてはめないの?
A:ルールも伝統ルールじゃないどころか固定じゃないし、まだ将棋盤のほうがコンピュータの示すところに近いかな、と。

Q:じゃあなんで詰将棋コンピュータっていうの?
A:そのほうがかっこいいじゃん!

でも、

それをコンピュータっていわれてもしっくりこないというのはごもっとも。ガクモン的にはそう考えるよ、ってお話です。やっぱりコンピュータと言うからには、もうちょっと頭いい動作をさせたい

そこで、パーツを組み立て、複雑な操作を達成しました、というのが詰将棋コンピータの内容だ。最初は「持駒を2倍にして余った駒の数で返す」だ。手数で返すよりはるかに難しい。

パーツ紹介

・無条件で特定の駒1枚を渡して玉を運ぶ
 きほんの「き」的に大事なパーツ。コンピュータ的には「1を足す」だ。

・特定の持駒を1枚使わせて玉を運ぶ、使えない=特定の持駒がないときは別のところに玉を運ぶ
 前者も大事なパーツだが、後者の「条件分岐」がもっともっと大事。if文だ!

これで2倍にできる。WFPの問題ではこんなしくみ。

①持駒燕の数を1枚減らす
 >減らせた! 歩を2枚渡す >玉を①に戻す
 >減らせない! >詰むほうに玉を運んで終了(詰み)

歩を2枚渡すのはなんのことはない、1枚渡すパーツを2つつなげるだけだ。3つつなげれば3倍だ。

実際の図面では駒余りにしないための「歩を全部没収して桂を1枚渡す」「桂を使わせる」パーツがあるが、この試みにおいてはいらないと思う。

ここまでついてこられれば雰囲気は掴んでもらえたのではなかろうか。

ちょいと脇道にそれると、伝統ルールの持駒変換はこれを部分的に実現している。伝統ルールでは持駒変換した駒を使い切って収束させているが、持駒変換した駒の品切れを利用するなどして使わずに収束させれば良い。受方持駒も可変=入力内容にする必要はあるが、香>桂桂の持駒変換を繰り返し、桂合を切らして桂をつかわずに詰ませる、といったことは不可能ではないだろう。気持ち的な難点は「香の数を2倍にして桂の数で返すプログラム」なのに入力それ自体に「受方持ち駒は香の数の2倍」という2倍の計算が含まれること。

持駒の2乗をする図面(プログラム)簡単説明

ここからはもうコンピュータサイエンスのお話だ。どういうパーツがあれば2乗という計算ができるか、という話がメインになる。いきなり持駒の2乗だけ駒がもらえるパーツがつくれれば詰将棋的には偉いが、ここではコンピュータ的に簡単なパーツを組み上げて2乗を実現する。

実は新しいパーツはいらない。苦手な方は目が拒否する記述ではこう。JZDECが歩を減らしつつ0になってないかチェックするパーツ、INCが歩を増やすパーツだ。

その代わりに、記録しておきたい数が4つあるので、歩A、歩B、歩C、歩Dに登場してもらう。

・歩Aの数を歩B、歩Cにコピーする。歩Aはゼロになる。
 実は前に作ったパーツほとんどそのまま。これが1~3の部分で、下準備。

①持駒歩Aの数を1枚減らす
 >減らせた! 歩Bを1枚渡す、歩Cを1枚渡す >玉を①に戻す
 >減らせない! >続きに玉を運ぶ

という構造だ。

今歩A3枚で始めたとき、歩A,B,C,Dの数は(0,3,3,0)となった。引き算と足し算だけで魔法のように9が現れる。以下の部分はこう翻訳される。

①歩Cを減らす>減らせなければ詰み 
 >減らせた! ②歩Bを減らす
          >減らせた! 歩A,歩Dを増やして②へ
 
          >減らせない! 歩Dの数だけ歩Bを増やして①へ

太字の部分は歩Bの数だけ歩A,歩Dを増やしているわけだ。なのでさらに翻訳。

①歩Cを減らす>減らせなければ詰み 
 >減らせた! 歩Bの数だけ歩A,歩Dを増やして、増えた後の歩Dの数を歩Bにコピーして歩Dを0にして①へ

かーなり想像しにくいので具体的にやってみよう。

(0,3,3,0)
(0,3,2,0)歩Cを減らす
(3,0,2,3)歩Bの数だけ歩A,歩Dを増やして、
(3,3,2,0)増えた後の歩Dの数を歩Bにコピーして歩Dを0にして
(3,3,1,0)歩Cを減らす
(6,0,1,3)歩Bの数だけ歩A,歩Dを増やして、
(6,3,1,0)増えた後の歩Dの数を歩Bにコピーして歩Dを0にして
(6,3,0,0)歩Cを減らす
(9,0,0,3)歩Bの数だけ歩A,歩Dを増やして、
(9,3,0,0)増えた後の歩Dの数を歩Bにコピーして歩Dを0にして
(9,3,0,0)歩Cを減らせなかったので終わり

どういうことをやりたいのかというと、3を3回足したい(nをn回足したい)のだ。
3から1をひいて合計3を足し、
2から1をひいて合計に3を足し、
1から1をひいて合計に3を足し、
0から1はひけないので合計9=3×3で終了

一見減っていく”3,2,1″と足すべき”3″と”合計”の歩A,B,Cで足りそうなのだが、
3を足すとその足した3にあたる歩の数がゼロになってしまう。それを覚えておくための歩Dなのだ。

これでめでたく2乗ができた。

それから

こんな感じで、必要があればパーツをつくり、パーツの組み合わせでいけるならそれを組み合わせて複雑な動作をさせるのだ。そしてそして、ほとんど新規パーツなしで詰か不詰で素数判定までできる(WFP121号)。最初の例で歩をなくすためにつかった「ある駒を1枚減らすパーツ」をメインで使うだけだ。減らなかったときの分岐が玉移動ではなく不詰で止まるだけで、新規パーツとは言えないだろう。

が、なぜ素数判定ができるのか、はもうサイエンスだ。やろうとしているのは7は2でも3でも4でも5でも6でも割り切れないから素数、9は2で割り切れないけど3で割り切れてしまったので素数ではない、ということだ。単純といえば単純だが、操作(使うパーツ)が最小限に分解されているためややこしくなっている。実例では覚えておくべき数字のメモ帳であるところの歩ABCD…の種類をきりつめているため、さらに複雑さを増している。

雰囲気だけ説明しよう。たとえば12を6で割ろうとする。12から6を引いて6。0じゃないのでもう一回6を引いてちょうど0。これは割り切れる。成功ルートへ。11を6で割ろうとする。6を引いて5。0じゃないのでもう一回6を引こうとする。ところで、6を引くには「1を引く」を6回繰り返すのだが、途中の5回めで0になってしまい失敗ルートへ分岐。これは割り切れない。

この操作を割る数を2,3,4,5…と失敗ルートへ行くたびに増やしていき、nが素数の場合は割る数がnになったらようやく成功ルートへ行く。さて、成功ルートへ行ったとき、素数のときは歩Bの数<歩Eの数となっているが、合成数(素数でない)のときは途中で成功ルートへ行くわけだが、歩Bの数>歩Eの数となるように作られている。これで歩Bがなくなるまで「歩Bを1枚徴収」「歩Eを1枚徴収」のルートをぐるぐるさせると、合成数のときは先に歩Eが尽きて不詰。素数のときは無事に通過できて詰。「1をたす」「1をひく、無理ならストップ(不詰)」「1をひく、無理なら分岐」だけで素数かどうかまで判定できるのだ。後者2つは同一とみなしたって良い。

また、もう100%コンピュータの話になるが、紹介した3つのパーツでどこまで出来て、なにができないか、ということを研究する学問がある。もうちょっと複雑なパーツがある言語でも同様だ。たとえば「あらゆるプログラム言語は、その言語で書かれたプログラムがエラーで終わる(無限ループするとか)か異常を出さずに最後まで終わるかを判別させるプログラムを書くことができない」なんて命題が示せる。自身の言語で書かれた完璧なデバッガはありませんよ、ということだ。ああ、分かる人向けの表現を使ってしまった。

Q:ここまでわかっていてなんで回答しなかったの? 
A:ごめんなさい。

与太

WFP114号のRSA-2048が素因数分解できれば解けるあの問題は、素数なら不詰、2つの素因数からなる合成数なら単一解または部分的に対称性のある2解を示しつつ素因数分解してくれるプログラム。3つ以上の素数の合成数だと余詰となるが、出力された解のひとつ(全部でもいいけど)は複数ある元の数を2つの整数の積に分解したものだ。これは詰め上がりの駒位置を出力結果とみなすタイプでもある。

素因数分解に頼らずこの詰将棋が解ける、なんてマジックができればそれは全く新しい素因数分解方法になるが、それは流石に夢物語かもしれない。だが、夢を見ても良いじゃないか。フェルマーの最終定理だって一見関係ない楕円関数をあれこれいじって証明されたのだから。

3行で。

将棋盤はルールと図面をプログラムとしたコンピュータとみなせるよ!
基本パーツ2つの組み合わせだけで2乗から素数判定までできるよ!
すごいね!