将棋盤の上で会話をしたい

将棋盤の上で会話をしたい

2026.04.12techdoc.048

将棋漫画には独特の演出がある。

対局者ふたりが盤を挟んで黙って座っているだけなのに,地の文や心の声によって,まるで盤上で殴り合いをしているかのような濃密なやりとりが描かれる。固有結界のような心象風景の中でお喋りしたり。あれが昔から好きだ。もちろんあれらは漫画的な誇張で,本物のプロ棋士の対局室は,もっと張りつめた静けさがあるんだろうと思う(行ったことはないが)。それでもああいう,盤の上だけで会話が成立しているように見える状況には憧れがある。

ちなみに僕は将棋がほとんど分からない。中学生の頃にちょっとだけやったが,金と銀の動きの違いを,たぶん毎回間違えていた。当時,近所に穴熊の組み方を知っている同級生がいて,そいつがいつも無双していた。組まれきってしまうと,こちらの駒はろくに動けない。何回かやって,いじけて自陣で飛車を反復横跳びさせながら時間を潰すなんてことがたびたびあった。

まあ,そんなんでも漫画の演出は楽しめる。実際,ルールが完璧に分からなくても『ハチワンダイバー』も『3月のライオン』も『龍と苺』も,どれもめちゃめちゃおもしろい。

話を戻すと,僕はああいう「盤上で会話している」感じに憧れる。とはいえ,棋力はゼロに等しいので,別のアプローチを思いついた。

棋譜のなかにテキストの会話そのものを物理的に埋め込んでしまえばいいのではないか。

外見上は普通の対局譜にしか見えない。でも,鍵を持っている人間が読み解くと,対局の裏でAさんとBさんが普通に雑談しているというような。

調べたら,こういうのはステガノグラフィーという分野らしい。古典的には画像のピクセルの最下位ビットにメッセージを忍ばせたり,文章中の特定の文字を変えて情報を埋めたりする技術が知られている。今回は媒体が画像でも英文でもなく棋譜になる,という話だ。


さいしょ

将棋の盤は9×9の81マスある。日本語のひらがなは清音だけなら46字,濁音や半濁音,拗音や促音を入れても80字には収まる。じゃあ盤面の各マスにひらがなを割り当てて,駒の行き先で文字を表現すればいけるのでは?

たとえば5五は「あ」,5六は「い」,みたいに対応表を決めておく。「Aさんが飛車を5五に動かす」=「あ」を送信,という感じ。これなら理屈はシンプルで,暗号鍵もいらない。秘伝の対応表だけ共有しておけば誰でも復号できる。

数秒後に無理だなと気づいた。

まず,指したいマスに駒を動かせるとは限らない。「あ」を送りたい局面で,5五に動かせる駒がそもそも存在しなかったらどうするのか。将棋は囲碁と違って,各駒が動ける範囲がきっちり決まっている。

次に,仮に動かせたとしても,その手が将棋として自然である保証がない。たとえば自陣の金を意味もなく後ろに引いたら,観戦者から見て明らかに不審な手になる。棋譜が将棋として自然に見えることは,この遊びの大前提だ。

しかし対応表でやるのが無理なら,どうすればいいのか。

しばらく考えて,別の角度に気づいた。文字とマスを直接対応付けようとするから無理がある。そうではなく,何か順番に並んだものから1つ選ぶという構造と対応付ければいいのだ。「N個の選択肢から何番目を選んだか」という事実だけが,純粋な数字として伝わる。各局面で『指せる手』はちゃんとリストとして列挙できるのだから,そのリストから何番目を選んだかで情報を表現すればよかろう。


合法手と文字表記

調べていくと,いくつかのことが分かってきた。

ある局面で指せる手のことを「合法手」と呼ぶらしい。中盤の局面で合法手は80〜100手くらいだそうで,案外たくさんある。一方,自分の玉の王手を避けないとか,二歩などが「指せない手」にあたる。

で,指し手は文字列で表現される。よく見かける「3四歩」とかの他にも,USI(Universal Shogi Interface)形式という表記方法がある。たとえば 7g7f は「7七から7六に駒を動かす」,P*5e は「持ち駒の歩を5五に打つ」を意味する。これはこれで格好いい。

最初の実装は簡単だった。合法手をぜんぶ列挙してUSI文字列の辞書順に並べ,その中から何番目を指したかで情報を表す。AさんとBさんで同じ並べ方をすれば,何番目を指したかは双方で一致するので,エンコードもデコードも機械的に成立する。

動かしてみたら,たしかに送受信できた。ただし,指される手があからさまに変だった。USIの辞書順は局面の意味とは何の関係もないので,序盤からいきなり香車を1マス上げたり,自陣の金を斜めに動かしたりするなど,僕のような素人が見ても明らかにおかしい棋譜ができあがった。まだまだ自然な棋譜とはいえない。

ここで将棋エンジンの登場となる。

チェスの世界で長年最強クラスを争ってきたオープンソースの探索エンジンに Stockfish というのがある。チェス界では常識みたいな存在らしい。これに将棋やその他の変種ルール対応を加えた fairy-stockfish という派生があって,局面を渡すと,各合法手にスコア(評価値)を付けて返してくれる。

7g7f → +120  (悪くない)
2g2f → +110
6i7h →  +95
...
9g9f →  -10  (微妙)
1g1f →  -50

評価値の高い順に並べ替えて,上位 K 手だけを選択肢にすることにした。並べ方の基準を『辞書順』から『強い順』に取り替えたわけだ。これなら選ばれた手は必ず強めの手なので,棋譜全体としては自然に見えるはず。

たとえば K=8 にすれば,ある局面で「上位8手のなかのどれか」を選べる。つまり,この場合だと,1手で log₂(8) = 3 ビット分の情報を載せられる,ということになる。

1手で3ビット送れる。これでひらがなを送りたい。

Document image

文字をどう表すか

ひらがなは78字ある。これに,あとで導入する事情で1枠だけ余分に持っておきたいので,合計79のシンボルを使うとする。すると,1文字あたり log₂(79) ≈ 6.3 ビットが必要になる。

1手で3ビットだから,1文字を送るのに2手ちょっと必要になる。シンプルに割り切れない。

ここで,文字と手を直接対応付けるのは諦めることにした。

メッセージ → 巨大な整数 → 手のrank列 → 棋譜

メッセージはまず79進数として整数化する。普段使う10進数で「123」と書いたら 1×100 + 2×10 + 3 を意味するように,79進数で「あい」と書いたら,それぞれの文字IDに 79⁰,79¹ を掛けて足す。日常では10進数しか使わないが,コンピュータの世界では2進数や16進数を見るので,まあ79進でもなんでもいいわけだ。

できあがった巨大な整数を,今度は K(=8)進数として下から1桁ずつ取り出していく。10進数で「1234」を分解するときに,1234 mod 10 = 4(一の位),1234 ÷ 10 = 123123 mod 10 = 3(十の位),と繰り返すのと同じ。modとは「割った余り」のことで,要するにいちばん下の桁を取り出す操作である。

取り出した値は 0 から K-1 の範囲に収まるので,これをそのまま「Top-K手のうち何番目を指すか」として使えば,整数を指し手の列に変換できる。

復号する側は逆操作をおこなう。同じ鍵(エンジンの探索の深さと K)で同じ局面の Top-K を再計算し,棋譜に登場する手が何番目だったかを取り出して,K進数として上から積み上げ直す。鍵を共有していない人間は,局面ごとの Top-K を再現できないので,何が書いてあるのか分からない。つまり,「局面の評価のやり方(=将棋観のようなもの)」が秘密鍵っぽくなるわけだ。

Document image

終わりをどう告げるか

ここまでで,ひとまず動くものができた。

動かしてみたところ,Aさんが永遠に喋り続ける問題が発生した。

考えてみれば当たり前である。「ここで送信を終えますよ」という約束事を一切作っていなかった。Aさんが指した手の rank を順に積み上げていくと整数はどんどん大きくなっていくが,デコーダ側からすると「いまの累積値こそが完成したメッセージ」なのか「まだ続きがあって,あと数手したらメッセージになる」のかを区別する手段がない。区別できないので,なんとなく一番もっともらしい長さで切る,みたいな判定もできない。

なので,メッセージの末尾に「終わりのしるし(EOT)」を付けることにした。普通の文字としては使われない記号を1つ用意して,メッセージ末尾にこれを付加してから整数化する。デコーダはその記号を見つけたら「ここで送信完了」と判定する。アルファベットを78字(ひらがな)+ 1字(EOT)= 79字にしたのはこのためだった。

これで送信完了が伝わるようになった。が,まだ問題があった。

メッセージを整数化する都合で,79進展開した最高位の桁が「0」だとそこの情報が消える(1230123 と書いても同じ整数なのと同様)。EOTの直後に「最高位の桁を1にする」という追加のしるしも入れておく必要がある。これで終端パターンは [EOT, 1] の2要素になった。

ところがこのパターン,デコード時にそこそこの確率でメッセージ本体に偶然出現してしまうことが分かった。終わるつもりがなかった位置で『終わったと誤認』され,1つの発話が途中でぶった切られて,続きが次の発話に押し出される,みたいな現象が起きる。

しかたがないのでもう1個増やす。終端パターンを [EOT, EOT, 1] の3要素に拡張すると,本体に偶然出現する確率はぐっと下がって,実用上ぶつからない。こういう「配列の終わりを示す目印」のことを番兵(sentinel)と呼ぶらしいことをここで知った。これも格好いいですね。

Document image

できたもの

というわけで,ようやく安定して動くプロトコルができあがった。

Aさんが先手,Bさんが後手で,おたがいに2発話ずつ会話してから,適当に詰みまで指し続ける,という設定。なお受信側は Top-K のうち,ほどほどの手をランダムで選ぶようにしている。以下が生成された棋譜である。

先手:Aさん
後手:Bさん
手数----指手---------消費時間--
   1   1八飛(28)
   2   7二飛(82)
   3   7八金(69)
   4   8二銀(71)
   5   5八飛(18)
   6   2四歩(23)
   7   6九玉(59)
   8   5四歩(53)
   9   2八銀(39)
  10   7一飛(72)
  11   3八飛(58)
  12   9四歩(93)
  ... (中略) ...
 140   5七角成(24)
 141   5四銀(45)
 142   5二玉(63)
 143   5一金打
 144   詰み

序盤の1八飛とか5八飛のあたりからすでに不自然な気がする。各手はエンジンが評価した Top-8 の中から選ばれてはいるが,エンジンはその局面だけしか考慮していない。将棋の「筋」や「構想」といった長期的な一連の流れをガン無視しているのだ。このあたりは改修の余地があるが,まぁ最初の方よりはまだマシになっていると思いたい。ルールも覚束ないような者同士の対局ならこういう感じになるんじゃないですかね。

さて,この棋譜の裏で,AさんとBさんはどういった会話をしているのだろうか。最近の調子を訊いているかもしれないし,勝負ごとゆえ憎まれ口を叩いているのかもしれない。  
 
 
答え:

[1] 先手: 「こんきのあにめどう」
[2] 後手: 「かみいなぼたんいい」
[3] 先手: 「なるほどね」
[4] 後手: 「いいよ」

 
 
 
 
...え,『上伊那ぼたん、酔へる姿は百合の花』の話!!?!?  
 
 
 
 

郡上かなで先輩!???!!!

 
 
 
 

ウー


復号できる相手のこと

ところで,これを実世界で人間ふたりがやるには,けっこう厳しい前提が要求される。

ひとつ目は,各局面で全合法手を瞬時に列挙できること。これはたぶん,将棋がばか強い人ならいけそうだ。たぶん。問題はふたつ目で,列挙したリストを完全に同じ基準で順位付けできること

今回のプロトコルではエンジンが評価値を付けてくれるから,双方が同じエンジンを同じ設定で使う限り,Top-K の並びは一致する。

しかし,生身の人間ふたりが盤を挟んで本気でこれをやろうとすると,ふたりの将棋観が完全に同じであるか,もしくはお互いの将棋観を完全に把握していることが必要になる。つまり「あなたならこの局面でこの順に手を評価するはず」という予測が双方でばっっちり正確に成立しないと,復号できない。このプロトコルで,盤上の自然な対局を装ったまま誰にも気づかれずに雑談を成立させるには,相手の頭の中をかなり深いところまで知っている必要がある。

...それはそれで良いのでは?

盤上は将棋,盤下は雑談。その雑談の存在は,将棋観の共有によって担保される。漫画の演出で見ていた「盤の上だけで会話が成立しているように見える」ものの正体は,案外こういう種類の親密さだったのかもしれない,と思った。


おまけ

最終的なコード(長いので折りたたみ)