500万詰将棋の答えを将棋エンジンで解くことを試みる

さて

自分のサイトにやねうらおうさん提供の500万局の詰将棋局面を任意に表示できるページを前回作成したわけだが、詰め手順まではついていない。 これはもともと提供される局面にも回答は添付されていないのであって、基本的にはじぶんで解くか(それほど難しい問題はそれほど無い)さもなくば将棋エンジン(やねうらおうなど)を自分のパソコンに導入して局面をsfenではりつけて検索させれば回答は導け出せますという使い方が前提になっているわけだ。

自分のサイトでもページからsfenを取得できるので詰将棋が解ける環境を自分のパソコン上に持っていればいいことになる。

ただ前にも書いたが、サイトのページ上で回答もそのまま見ることができるようになれば便利なことは確かである。

これを実現しようとすると最初に考えるのは詰将棋を解くAPIを用意してWeb PageからオンデマンドそのAPIを叩けに行けば良いという方法。 これにはAPIを供給するサーバーが必要になる。  すこし探してみたらClaude.ai関連のMCPサーバーの実装で将棋エンジンとブリッジするサーバー構築をexpressを使った形ですでにGitHubにあげている方がいた。azumausu/shogi-mcp

実際にこれでサーバーを作成し、やねうらおうを導入してみたら、うまく作動することは確認できた。  でもやはり、サーバーを常にスタンバイさせておかなければならない、という課題は残るし、リアルタイムに詰みを返せるような高速のエンジンを動かすためのバーチャルマシンはそれなりのスペックのものが必要になる。  こちらは最低限のリソースとマシンスペックでサイトを運営するのを旨としているので、方針がそぐわない。

よって考え方を切り替え、時間がかかってもよいので退職以来3年間眠っている業務用のノートパソコンでひたすら局面を解析させて500万問の詰め手順を導き出し、これをデータベースのテーブル上に格納してここから答えを引き出す方針で進めることにした。

これを行うためには、ノートパソコンで将棋エンジンを起動し、これに局面を送って、詰みの回答を読み取ることを延々と繰り返すというスクリプトが必要になる。

なので、以下Pythonのお勉強の続きをすることになったわけです。

将棋のEngineと通信する部分のモジュールを以下のような構成で考える。

将棋エンジン(やねうらおう)をサブプロセスで起動する。
このプロセスにstdioを通じて詰将棋を解いてね、というコマンドを送り 答えをいただく。
これを問題の数だけ繰り返す。
すべて終わったらサブプロセスを終了

ちなみにこのエンジンを起動させたあと、コンソールで詰将棋を解くためには、
まず “usi” コマンドを送って、”usiok”が戻ってくるのをまってUSIプロトコルが働くことを確認し、そののち setoption name xxx value yyyというフォーマットでオプション類を設定する。

これが終わったら、以下のプロセスを問題の数だけ繰り返す。
”isready”を送ってエンジンの局面を初期化”readyok”と返ってくるのを待つ。
“position sfen sfen文字列” と送って局面を通達。
“go mate” で詰み検索を開始。
stdoutに返されてくるinfo文字列を読み解きこの中に mate xx (xx は例えば11手詰めならば11)  という文字が含まれていれば、所定手数の詰みを見つけたということなので ”stop”コマンドを送信して検討をストップさせ、文字列の中から詰め手順だけ抜き出して親プロセスに返す。 (親プロセスで画面表示するなりファイルにセーブするなりデータベースに書き込むなりの処理をする)

すべて終了したらサブプロセスを終了しスクリプトも終了

ということだと思うので、この考えをもとに実際に書いてみました

ヘルパーFunction その1。 詰めたよ、という文字列が所定手数になっているかの確認。11手詰めのはずの詰めだと13手とか17手の詰めの解答が最初戻ってくるのでそれを無視するためのもの。 ただし、11手詰めのはずなに9手とか7手で詰みます、といってくることがあることがわかったので所定手数以下の文字列についても正解と判断するようになっている。

start_engineという関数は将棋エンジンをsubProcessで起動し、option設定を終えたのちにこのプロセスオブジェクトをメインプログラムに返す。`

ちなみにEngineとOptionsについては以下をメインにて定義する。非力なノートパソコンで走らせるのでThreadをデフォルトの4から2にしないとエンジンが固まることがあった。FV_SCALEは水匠の推奨値。定跡ファイルは必要ないのでno_bookを指定

このプロセスオブジェクトででエンジンが動いているので、stdin/stdoutのパイプを使って交信することができるようになる。

Send_command関数でSFENの局面をエンジンに渡し回答を得るのだが、そのためのヘルパー関数を用意する

initialize_engine関数ではstopを送って探索を終了させたのち、isreadyを送って局面値を初期化する。  try_mate_againではisreadyを送らず、go mateを送るので、そのときメモリー上にある局面を改めて探索して詰みを探す。
query_mate関数はsfen文字列をpositionとしてセットし、go mateを送って詰みの探索開始するためのもの

send_commandsはこれらのヘルパーを使いながら詰みを検索。

swipe_printはコンソールで、画面がスクロールしないように同じ行に出力を重ねていくヘルパー関数

で、send_commandを500万回繰り返せばすべての解答が得られるはず

stop_engine関数でエンジンを終了しサブプロセスを終了。proc.communicateはプロセスの終了処理も行ってくれる。

で、下のスクリプトでは上の関数を適宜に呼び出して、mate11.sfenの最初の500行を解いてみている。 その結果をみながら、上のsend_commandの中身は何度か修正している。 

いつまで待っても所定手数の詰みが見つけられない、という可能性もあることがわかったのでタイムアウトを設けることにした。 最初はThreadingでタイムアウトを生じさせ、このときKeyboardInterruptを生成してこれを処理する手法を試してみたがCTL-Cが乗っ取られるのがどうも気に食わない。 async.ioライブラリーも読んでみたがブロックIOとどのように整合させてよいのかがよくわからない。 ので最初はどろくさくWhileループの中で時間をチェックして一定時間たっていたらbreakするという簡単な方法で書いてみた。 ただし、これポーリングで時間を見ているだけなので、ループ中のstdout.readline()がブロックしている限り時間切れにはならない。タイムアウト15秒くらいまでなら、だらだらと文字列が出力されているようなのでとりあえずハングすることはないとたかをくくって書いてみたら問題なく動いている。 これで良しにしようと思っていたが、さらに日を置いて調べてみたら、os.set_blockingという関数を見つけた。Linuxのみに対応かと思ったら、WindowsのpipeにもPython3.12から対応しました、となっていて、これはラッキー。 これをつかうとstdioがブロックされなくなり、readline()で何もないときには空の文字列が返ってくるようになるので、Timeoutも割り込みなどの処理を使うことなく検知できるようになった。 また、エンジン、検討途中でドツボにはまって正解がなかなか出せなくなる、ということも起こるということを経験的に知った。この場合は出力を待つよりも一度ご破算にして解答を聞き直すとすぐに正解にたどり着くような現象が多くみられた。(実に人間のようである)のでタイムアウトをやたら長くするより、リトライと組み合わせたほうが効率が良いと考えリトライも追加。たいてい2回か3回のリトライで正解にたどり着くようだが、中には30回目40回目のリトライで正解、というケースも500問中2問や3問発生し、それでも正解にたどり着かなかった場合はあきらめて、最後に読んだ文字列を取得するようにしてみた。何回か試してみたが、試すごとにエンジンがドツボにはまる問題には、ある程度の一貫性はあるものの、前回30回以上リトライした問題が次のRunでは最初のトライで解答がでてきたり、このあたりのランダム性は不思議だなあと思うのである。この設定による500問11手詰めの探索で、全問に11手の(またはそれ以下)詰め手順が返されてくるのはタタイムアウトを15秒以上、リトライ回数を50回マックスくらいにすればよさそうだ。タイムアウトをもっと長くすれば確実性は増しそうだが、全部の問題を解く時間がどんどん長くなる。プログラムの出力を見ていると、9割以上の問題は2,3秒で指定手数の正解が出てしまうのであるが、つまずいてリトライが30回発生する問題は解くのに10分かかってしまうということである。またリトライ無しにすれば全問にかかる時間は少なくなるが不完全な(13手詰め以上の)解答が増えてくる。とりあえず時間節約のため、全部解かせたあとで、11手詰めになっていない問題をあとから再度検索させる方法も考えたが、2度手間になってしまうので時間がかかっても全問指定手数の解答までたどり着く方向で考えることにしたのだが、 この実験結果から試算すると11手詰めの問題100万個すべてに11手以下の詰みをみつけるのには最低2か月はかかりそうだ。 ちなみに3手詰めのほうはリトライなどはさすがに発生してしないので4日ほどで終わりそうである。

なにせノートブック用2コア4スレッドのスカイレークという非力なCPUの上にRAMも8ギガバイトである。遅いのは致し方がない。改めて500万問という数字の理不尽さをしみじみ感じてしまうと同時にCPUの性能のちがいが顕著に見えてくる将棋エンジンのコンピューターパワーの使いたおし方は凄いなあと思う。

とにかく、mysqlのテーブルに出力された文字列を書き込むパイソンスクリプトも別途作成し、上記のスクリプトを組み合わせて、500万問を3手詰めから順番になめていくメインプログラムを起動させたのが三日前。画面をブランクにし、ひっそりたたずむノートパソコン。 ときおり、画面を開いてはコンソール上に出てくる問題番号を見て進行状況を確認しております。 うんうん、まだ3手詰のあたりは順調だが道は長い。(長すぎる!)

500万局の詰将棋

やねうらおさんの233万局面の定跡ファイルを最近使わせてもらったのだが、そのとき、彼のブログで5年も前に3手詰めから11手詰めまでの実戦詰将棋局面を500万例ほど生成して、これをテキストファイルを圧縮した形で無償公開されていることに気が付いた。で、 将棋盤に表示できるようなユーティリティーが欲しい旨のコメントをしている読者もいるみたいだし, これも早速自分のサイトのページに応用させてもらおうかと思った次第である。

で、まずは提供されている圧縮ファイルだが、解凍するとmate3.sfen, mate5.sfen, mate7.sfen, mate9.sfen, mate11.sfen の5つのファイルがでてくる。 それぞれテキストファイルであり、中身はsfen 書式で書かれた局面情報が1行1局面で記述されている。

これをweb siteのページにランダム、あるいは手数と行番号を指定して将棋盤のグラフィックスとして表示させたいわけである。

すべてのデータをブラウザに読み込ませるわけにはいかないので、データ自体はサーバー側に持たせ、ブラウザからは手数と行番号を指定してサーバーに問い合わせ、該当するSFEN一行を取得させて将棋盤上の駒として描画させる、という手法で実現させる。 サーバーでのデータ検索を最適化させるためにmysqlデータベースのテーブルにデータを格納して使う。

データテーブルは一つとして、コラムは手数と問題、各手数の問題カウント、および 通し番号として記録するとしてテーブル構成は以下となる。

FieldTypeNullKeydefaultextra
idINTNOPRIMARYnullauto increment
sfenvarchar(120)YESnull
moveintYESMULnull
rowintYESMULnull

このテーブルをまず作っておき、ここにテキストファイルからデータを流し込むわけだ。 で、今度こそPythonを使って見ようとPyCharmを立ち上げ(個人で使う分には無料なんだよね)ブラウザの検索機能で情報を拾いまくりながら、以下のプログラムを作った。

move と counterで検索することになるので、これらのフィールドはインデックスを作成しておく。

20行以上のパイソンを書いたのはおそらく初めてだが、 なるほどユーザーが多いわけだ。

このパイソンスクリプトを走らせてみる。 上のコードでは各行にスラッシュが8個ある、のと、スペースで分解したアレイ素子の因子が4つになる、というのを valid SFENの簡易判定基準とし、それ以外の行はスキップする構造にしてあったのだが、なんとここで10件近くのスキップが発生した。 テキスト行の情報も表示するようになっていたので ファイルをのぞいてみると、sfenが2行くっついた、つまり改行が抜けている個所が何件かあるのが分かった。 プログラムで生成したはずのファイルに何故にこのようなイレギュラリティが発生するのか不思議ではある。 とにかく手作業でこれらの行を分割し、テーブルをいったん削除して再度の挑戦を行った。Pythonのmysql connectorが優秀なのか、パソコンのハードを更新したからなのかはよくわからないが、500万件10分もかからず終わってしまった。

実は500万件なかったのである。mysqlのデータベースが言うには実際には以下の件数の局面が入っていた。ただこれで目くじら立てるのは野暮というものだ。

3手 998,405件
5 手998,827件
7 手999,071件
9 手999,673件
11 手999,998件

サーバーにPHPでAPIポイントをつくり、例えば /api/mate/3/1245というようなHTTPリクエストをサーバーに送れば、3手詰め、1245行目のsfenがjson formatでかえってくるような仕組みを作る。

ブラウザ側のほうはReactとほぼ同じように使えるPREACTをViteとTypeScriptのTool chainを使用しバタバタと書き上げて、出来上がったのがこのページ。 上の数字をもう一度よく見てもらいたいが、 200問くらいの問題集でも一冊全部解くのはなかなか根気がいるのに、いったい誰がこの圧倒的な数の暴力に立ち向かうというのだ?(ちなみに私は高校時代に買った内藤詰将棋200選をまだ全部解いてない)

やねうらおさんのブログにもかいてあるのだが、これは妙手が入った詰将棋とは違って実戦詰将棋的な問題集になる。 ほとんど並べ詰めのようなものもあれば、少し考える必要のあるものもある。 しかしなのだ。 例えば、この局面 ランダムにピックアップしたものだが、

9手詰め 第88,592問目

最初に2二金打ち、同金としてから4二桂成 同玉以下は 尻にと金をすべらせていってからの飛車成りまでの送り詰めで9手と言っても難しい詰めではない。 詰将棋だと言って出されれば級位者でも解ける問題だ。 ただ実戦となるとこの局面で4五の桂馬が5三と3三を封鎖しており、さらに金を捨てて、3一の金をどかせれば尻金で詰む、そして4一と金のとき5二玉と逃げれば6一飛成で早く詰む、と局面の駒配置がよく見えなければ詰めようとも思わないわけで、実際これくらいの局面で3切れのShogi warsでは初段、下手すると2段レベルとの対戦でも手駒の金は温存したいと、初手から4二桂成と角を取ってくる可能性は自分の経験から言ってかなり高い。

だとすると、この類の局面を大量にながめて勘を鍛えておくことは棋力向上の役に立つのかもしれない。

それはさておき、ここまでできたなら将棋エンジンもつけて回答も導きだせるようにしたらどうだと言われそうだが、残念ながらそちらのほうの知見は自分はまったく持っていないのであった。思うにサーバーにエンジンを実装してというかバーチャルマシンを一台、それもパーフォーマンスの高いやつを立ち上げて将棋エンジンを常駐させ、これのAPIをたたいて正解を求めるというようなイメージなんだが、いかにもリソース費用がかかりそうだ。lishogiのサイトなどは実現しているみたいだが、どうしているのだろうか。 そのうちだれかが無料のAPIサービスを提供してくれないかと無想するわけである。

AIにコードを書いてもらって失敗した話

将棋の局面を記述する書式にSFENという記述方式がある。
これは盤上の駒の配置、手番、先手と後手の持ち駒、手数などがコンパクトに文字列にまとめられたもので、Kyokumen.jpの局面検索に使われていたり、将棋エンジンの定跡ファイルにも使われていたりする。

SFENの書式についてはこの記事にうまく説明されている。

今回、YaneuraouさんがMIT Licenseで新たに公開された233万手の定跡ファイルをみたところ、後手番の局面がなく、先手後手盤面を反転して検索する方法がとられている。つまり、 例えば初期画面から後手が3四歩と指した局面が先手番として登録されている。 屋根裏王のエンジンにはflippedBoardというパラメーターがあり、これをOnするだけで対応するよう設計されているのだが、定跡ファイルをブラウザで将棋盤上に表示させる自分のWebページに追加するためには、後手の手番の時に検索をかけるSFENの局面を反転させてサーバーに問い合わせる必要が生じた。サーバーから帰ってくる候補手も同様に、例えば2六歩を8四歩などと反転させる必要がある

最初の局面を反転させる部分、TypeScript関数として用意すると以下のようになる。

 

このコードで何をやっているかというと

1.まずは SFEN文字列をスペースをデリミターとして文字列アレイに分解する。
2.それぞれのアレイ因子を board, turn, hands, moveCountという変数にわりあてる。 ちなみにMoveCountは今回の定跡ファイルに使用するにはゼロに置き換える必要がある。(上のコードでは未反映)
3.board(駒の盤面位置情報)をさらにdelimeter ‘/’ で各列に分解したアレイを生成
4. array.reverse()を使って列順をひっくり返す。上段の列が下段に、下段の列が上段になる。
5.さらに、さらに各列の文字列を一文字づつに分解してアレイを作る。
6.大文字の駒(後手の駒)を小文字(先手の駒)、小文字の駒(先手の駒)を大文字に置換する
7.array.reduce()を使って、+シンボル(成り駒)因子と次の駒の因子を繋げて一つのアレー因子にまとめる

8. これを.reverse()で左右反転
9. 各列のアレイを文字列に変換
10。各列の文字列アレーをを’デリミター/’で接合し、一つの文字列を生成。→flippedBoard
11. 手番もひっくり返す (flippedTurn)
12. 持ち駒を先手と後手入れ替える (pivotHands) この部分、使ったロジックは以下のとおり

B3Ps14pを S14Pb3pとしたい。
まずは大文字小文字を入れ替える
b3p2S14P (flipped)
最初の大文字のインデックスを取ればよいと思えるが、数字がついた場合(2S)となっていた場合はNG
なので文字列を逆転させてみる
P41S2p3b reversed
これで最初の小文字のインデックスを取る。上の例だと5。文字列長さ8なので 8-5=3
secondPart =flipped.slice(0,3) = b3p
firstPart = flipped.slice(3) =2S14P
これを組み立てる
final = firstPart+secondPart = 2S14Pb3p
うまくいっているのでは?

先手の持ち駒だけの場合 例えば 2P
小文字のインデックスはマイナス1 この場合は処理をスキップ
後手の持ち駒だけの場合 例えば14p 逆転文字列での小文字のインデックスは0 この場合も処理をスキップ

13 .flifppedboard flippedTurn flippedHands moveCountを一つの文字列につなぎ合わせ、この値を返す
とまあ、書いてしまえば簡単な作業の連続だが、実はこのコード、最初AIに聞いて提示されたものが動かなくて、修正を入れ始めたら、結局七割がたは書き直してしまった、というものになる。

最初、Brave Brouserの検索窓に ”Given sfen string, write a javascript function that returns flipped sfen” と打ったらあっさり回答をよこしたのでこれをTypeScriptに書き直し、そのまま定跡ファイルに使ってみたのだた、局面検索がまったくヒットしなかった。  コードを解析して分かったことはAIがくれた回答は上記のステップから 7,8そして12の処理が抜けていた。その結果盤面の上下反転はしたが、そのあとが左右反転をしていないため、ミラーイメージになってしまい、さらに持ち駒があった場合は先手と後手が逆のまま。 Webページの仕組みはこのsfenをサーバーに送ってサーバーのデータベースからこのsfenに対応するmoves情報を返してもらい、次の指し手の候補をリストアップするというものなので、ミラーイメージの局面照会にはヒットする候補手はありませんという答えしか返ってこない。

AIはSFENが何たるかをあまり理解できないまま、WEBに落ちている情報をかき集めて使ったようで、プロンプトで、SFENの定義から始め、期待されるアウトプットのイメージをしっかり伝えておかなければだめだったか。

逆に良いねと思ったのはアレイやオブジェクトの関数をチェイニングして記述する、いわゆるFunctionalな書き方になっていてスマートである。 これは真似したい。

とにかく、AIに仕事をさせたら、今の時点では検証が大事なわけだが、それなら最初から提供されたコードは参考までに留めておいて、実際に使うコードは自分で書いたほうが楽しいかもね(仕事でプログラミングしている人はこうは言えまい)。

追記:Yaneuraouさんからflipsfenをpythonで作成した時の顛末のリンクを教えていただいた。 https://yaneuraou.yaneu.com/2023/12/15/chatgpt-wrote-a-program-to-flip-a-shogi-board/  同じようなところでハマっているんだ、と可笑しくなった半面。 ちょっと待て、これは2年前の記事。 これを読んでいたらこんなに時間を使わなくてもよかったじゃん とも思った次第。

やねうらおうの定跡ファイルをサイトに使ってみた

ずーっと海外に住んでいて、将棋を指す相手がいなく、ヒカルの碁でネット碁の存在を知り、 こんなものがあるんだ、と検索して、Kurnickというゲームサイトを見つけ、そこでネット将棋を始めてみたら、みんな結構無茶苦茶な指し方をするのにびっくりして、英語の将棋入門サイトを作成したのが、今から17年くらい前、最初は静止画像をべたべたはりつけ、ブログに毛が生えたようなサイトだったが、フラッシュアプリを使った将棋盤というのが世の中にあるのがわかり、これを使って詰将棋やら最初の数手の説明、定跡の説明なんかをしていた。
割と早い段階で、フラッシュが駆逐されそうだということに気が付き、JavaScriptを勉強してフラッシュ無しでも動く将棋盤を自前で作成して自分のサイトで使い始めた。これがもうかれこれ12年以上前。これが幸いしてFlashが完全終息 した際のインパクトは避けることができた。

当時はそれなりに海外の将棋入門者の役にたったのではと思っているのだがそのうちYouTubeにHidetchさんが英語の将棋入門チャンネルを開き、さらに81道場を開設するにいたって、これは時代が変わったと悟った。 今ではほかのリソース、例えばYoutubeに上がる将棋の動画などもさらに充実し、DiscordではカロリーナのShogi Harborなど。 加えて自分もそろそろ終活を考慮しはじめるべき歳なのでこのサイト、もう閉じようかと思ったりするのだが、Statsを見るとまだ世界中からぼちぼちロボットではなく人間のアクセスがある。のでなんとなく続けている。 それにページを更新するごとにPHPの新しい使い方とかTypeScriptの勉強にもなるので、頭の老化防止にもつながっているような気がする。→どちらかというとこれが現在一番のモチベーションになっている。

最近何気にWikipediaの将棋の項目(英語版)を観たら外部リンクのリストの最初に自分のサイトが載っていて驚いた。ますます閉じにくくなった。

このサイトの中で何度か書き直しているページの中に、初めの数手の内容をまとめようとしたページがある。 最初はYamajunさんのサイトにある棋譜にリンクしていた。  しかしながら他のサイトへのリンクというのは実にあてにならないもので、 このリンクを含むリンク先の大部分が2019年時点で消失していた。 2009年当時Webの90%を占めていたFlash Player base のサイトが ほぼ全滅したのが大きいと思われる。
ならば自前でつくろうと考え、やり直しを重ねたこのページ。 一応形にはなったが、これは掘り下げていけばいくほど、もっと広げていきたい衝動に駆られる。

最近 やねうらお さんのGithubでやねうら王に使われている定跡の4年前のレベルのものが公開されているのに気が付いた。 自由にお使いくださいと書いてある。前にNHKの将棋講座の特集でだれかが将棋ソフトの作者さんたちは気前が良すぎるんですと言っていたが、まったくだ。  定跡フォーマットを見るとSFEN + move(s)の繰り返しのテキストファイルだ。 幸いSFENの読み込みと書き出し機能は自作のWeb用将棋盤表示アプリにすでに実装ずみなので、このファイルをそのまま使わせてもらってWebで定跡表示ができるのではないかと思いついた。

遠い昔 やねうらお さんというハンドルネームの方の、将棋界の内情を色々批評していたブログを見ていたようなきがするのだが、同一人物かどうかはさだかではない。 ただ、今や将棋プログラム界のドンとでもいえるような大活躍をされているようである。 5歳のときにTK‐80を使って8080のニーモニックを覚えたと書いているのを見ると、多分 自分より20歳くらい若い働き盛りなのでありましょう。自分がTK-80に触ったのはまだ日本にいたころ、取引先の業者がNECの代理店をやっていて、「遊んでみて」と持ってきたのが最初だ。モニターのルーチンを使ってスクロール表示をくるくる回しただけで満足して 終わったような気がする。

ともあれ、定跡ファイルをばらしてMySqlデータベースに落とし込んでみた。Pythonを覚える良い機会と思ったがコマンドラインでもPHPが使えることを思い出し、コードもあっさりかけてしまったので、Pythonの学習はまたもやお預けだ。(ファイルサイズが87メガバイトの大定跡ファイルは、MySQLに落とし込むのにPHPをコマンドラインで実行させて4~5時間ほどかかった。 専用サーバーではなく、他の業務と兼用なので、こんなものなのかもしれない)

局面数指し手数
やねうら王定跡(2016)130,182155,443
やねうら王大定跡(2016)428,2442,433,672
やねうら王テラショック【WCSC29】285,463512,860
各定跡ファイルの持つ局面数と指し手数

Web ServerにSFEN文字列を送るとと候補手のアレィをリスポンスとして返してくれるようなAPIを作成し、ページを追加してみた

我ながら GUI デザインのセンスが無いなあと思うのは毎度のことだが、 面白いなとおもったのは最初の定跡(説明によるとFloodGateで収集した棋譜を24手まで統計的にまとめたもの)の初手の頻度が人間の指す初手の頻度とほぼ一致していた点。2016年当時ということもあるのだろうが、ソフトというのは、もっと自由な手を指すのかと思っていた。この統計頻度の高い指し手を選び続けて行くと先手左美濃対後手四間飛車になる。 こういう風に指すんですねえ。
一方局面評価をしまくって精選したという100Tera Shock定跡なるデータファイルのほうでは評価値が高い指し手を選び続けると横歩取りに誘導される。

大定跡は一番充実しているはずなのだが、評価値の高い指し手を選んでいってもはやばやと定跡から外れるケースが多い。 原因は Mysqlへの落とし込みに失敗しているか、最初から定跡ファイルがそういう作りになっているかのどちらかだがよくわからない。ただ、下の表に示したように、初期局面1にたいし、二番目の(一手目)局面が14種類、三番目の局面(2手目)が56種類 と多くなっていくものの局面数が、指数関数的には広がっていないので結構枝をはらっているのはわかる。でも第一候補の次の指し手が無いというのはなんかおかしい。 暇ができたら見直してみます。

11
214
356
4135
5323
6678
71289
81922
92714
103623
114812
125995
137181
148407
159741
1611136
1712492
1814160
1915731
2017425
2118979
2220500
2321883
2423224
2524374
2625479
2726567
2827704
2928792
3029952
3131001
3231954

これだけの局面数と指し手を評価して枝をはらっていくというのは相当にリソースと時間が必要になると思うのだけど、それをgithub上にポンと公開してくれているのは 大丈夫ですか、と思ってしまう。恩恵にあずかっているわけですが。

JavaScriptで動く将棋盤のページを作る。コンセプトの整理

あらためて、考え方を整理してみる。

Webページは HTMLファイル、CSSファイル、JavaScriptファイルという三つの構成要素から成り立っている。そこで今回試作したページのすみわけを考えると以下のようになる。

html ファイル:将棋盤の入れ物を定義し、CSS,JavaScriptファイルにリンクさせる。
CSSファイル:将棋盤と駒の表示の仕組みを定義する。これをHTMLのエレメントの属性に対するスタイル定義で行う。
JavaScriptファイル: 将棋盤の初期配置と駒の動きのデータ、および、駒を初期配置の通りに並べて表示し、駒を動かす関数およびボタンの表示。 データ部分と機能部分を分けて二つのファイルで構成するようにする。
JavaScriptでも”描画”作業は行っていない。WebのDOM(DataObjectModel)を操作してHTMLのエレメント属性を変更させているだけで、あとはブラウザにお任せである。
一度画像データをサーバーから読み込めばデータがローカルにキャッシングされる。ブラウザとしては初期画面で盤と駒のデータを読み込んだあとは時々成り駒のデータをダウンロードしにいくだけなので、作画速度で目に見えるような遅れはない。
Web page を表示するためには将棋の盤、マス目、各コマの画像データの他に
filename.html
shogiboard.css
boarddata.js
boardfunction.js
の4つのファイルが必要、boarddata.jsに棋譜データを書き込んで動きを指定する、ということになる。実際にはboarddata01.js, boarddata02.js などと作っておいて、html からのリンク先指定を変えてやるような操作になる。
HTML内部、JavaScriptへのリンク部分
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script> 
<script src="boardfunction.js"></script> 
<script src="boarddata01.js"></script>
HTML内部、CSSファイルへのリンク部分
<link href="shogiboard.css" rel="stylesheet">
ところで、
この将棋盤、自分のサイトに表示するには少し大きすぎる。
もっと小さい画像データがないかなあ、と思っていたのだが、CSSでイメージをスケーリングすれば解決できることに気が付いた。 Cascaded Style Sheet は変更部分だけ記述すれば、最後に置かれた記述が依然の記述を書き換えるので
約65%のスケールダウンには以下のような内容のCSSファイルをリンク先に追加すればよい。
.board {width:286px; height: 312px;} 
.komadai {width:100px; height:150px;} 
.komadai img,.koma, .marker {width:30px; height:33px;} 
.comment {padding-left:7px; width:450px;min-height:100px;}
#boardbase {width:323px; height:337px;} 
.c1 {left:258px;} .c2 {left: 228px;} .c3 {left:198px;} 
.c4 {left:168px;} .c5 {left: 138px;} .c6 {left:108px;} 
.c7 {left:78px;} .c8 {left: 48px;}  .c9 {left: 18px;} 
.r1 {top:17px;}   .r2 {top:50px;}    .r3 {top:83px;} 
.r4{top:116px;}   .r5{top:149px;}    .r6 {top:182px;} 
.r7{top:215px;}   .r8{top:248px;}    .r9{top:281px;}
このファイル名、shogiboard-small.css とすれば HTML内部、CSSファイルへのリンク部分は
<link href="shogiboard.css" rel="stylesheet">
<link href="shobiboard-small.css rel="stylesheet">
となる。
動くサンプルはこちら
下にファイルと機能のまとめをマインドマップで整理したものをリンクしておく

将棋盤に戻しボタンをつけてみる(JavaScript)

将棋盤の上の駒がボタンを押すごとに一手づつうごくようになって喜んだのもつかの間、手を戻すボタンがないのがひどく問題に見えてきた。 これは機能を追加せねば、と考えたのだが、最初の設計時点で履歴をとるような構想は一切なかったのではたと困った。動きを逆になぞってみても、駒を取る、というアクションがデータ構造上明示されていないので、もとに戻すのが無理なんである。 何か方法があるはずだと思いつつ2~3日、ほおっておいた。

いっそのこと、一手ごとに将棋盤全部のスナップショットを取ってそれを変数配列に格納してはどうだろうと考えついた。 スナップショットとは言っても、画像データをコピーするのではなく、JavaScriptが精製したhtmlのツリー構造をそのまま変数にコピーしてしまおうというわけだ。JavaScript ではinnerHTMLといタグ内の文字列をそっくりコピーできる関数がある。JQueryでは.html()メソッドにファクタリングされているので、これを使えばなんとかなるのではないかと実験してみたらあっさり動いてしまった。
ちなみに40枚駒を並べた将棋盤のスナップショットは文字列の大きさで5キロバイト程度だった。 これなら200手動かしてもでも1メガバイトで収まる。 Z80でプログラミングしている時代なら、5キロバイトとわかった時点で、間抜け扱いだったが、時代は変わったのだ。これでいいのだと自分を納得させる。 ブラウザ上の JavaScript で動いているわけだから、データがネットワークを移動するわけでもなく、単にブラウザの使用メモリーがそれだけ増えるだけ、ということになる。
まずはデータを格納する配列変数を用意する。
var board {
.
.
history:[],
}
という具合にGloval variable に Array としてhistory[]を定義しておき、次に<div id=”snapshot”>…</div> 内のhtml構文をboard.history[]にセーブする関数 takesnapshot()を定義する。
function takeSnapshot(i){
board.history[i]=$('.forSnapshot').html();
}
次に、このboard.history[]配列から現状の<div id=”forsnapshot”>…</div>内に内容をロードする関数を定義する。 同時に”一手進めるボタン”が無効化されていれば、それを有効にする作業も行う。
function setBoardToHistory(i){
$('#aButton').removeAttr("disabled");
$('.forSnapshot').empty().html(board.history[i]);
}

>

次に上記の関数を呼び出して、一手だけ戻る関数 stepback()を定義する。ここでは一応board.index が0以下にならないようにチェックを入れている。
function stepback(){
if(board.index>0)
setBoardToHistory(--board.index);
}
そして、animateBoard() function に太字部分を追加。
function animateBoard(){
var zAction=board.moves[board.index];
takeSnapshot(board.index);
parseAction(zAction);
board.index+=1;
if (board.moves[board.index].charAt(0)=='x') $('#aButton').attr("disabled","disabled")
;

}
ボタン機能を追加
function setupButton() {
$("#aButton").click(function () {animateBoard()}).attr("value","Forward for solution");
$("<input type='button'>").click(function() {stepback()}).attr('value','Step back').appendTo('#buttonBar');
}

 
上のルーチンで、

<div id='buttonBar'>
<input  type="button"  id="aButton" value="Javascript did not load" />
</div>

というエレメントにStep back というボタンが動的に追加される。(aButton のほうも同じように動的に追加したほうがコードがコンパクトになりそうだ)

これでもとに戻るボタンを追加することができた。 デモはこちら

JavaScriptで将棋の駒を動かす2

前述のグローバルデータを読み込んで、駒の処理をする関数を定義していく。

function animateBoard(){
var zAction=board.moves[board.index];
parseAction(zAction);
board.index+=1;
if (board.moves[board.index].charAt(0)=='x') $('#aButton').attr("disabled","disabled")   ;
}

上がメインの関数になる。これは呼ばれるごとにboard.indexで指定されるboard.moves[]文字列を読み出し、parseAction関数に渡す。 ’x’ を読み込んだ時点で、駒を動かすボタンを無効化するようにしている。

function parseAction(aAction) {

if (aAction.charAt(0) == '*') postComment(aAction.slice(1));
else {
if (aAction.charAt(1) == 'd') makeAdrop(aAction.charAt(0), aAction.charAt(4), aAction.substr(2, 2));
else {
makeAmove(aAction.charAt(0).toUpperCase(), aAction.charAt(1), aAction.substr(4, 2), aAction.substr(2, 2));

}
if(aAction.indexOf('*')>0){postComment(aAction.slice(aAction.indexOf('*')+1))}
}
}

parseAction 関数では文字列を解析し、コマンドを実行する。コメントであれば、postComment,  駒を打つのであれば、makeAdrop, それ以外はmakeAmove関数にパラメーターを渡し、最後にもう一度コメントの有無を確認している。

function postComment(comment) {$('#scomment').empty().append(comment);}

postComment はid=”scomment”のhtml エレメントをまず空にしてから、コメントを張り付ける、というだけのもの。

function makeAdrop(side,koma,position) {
var png=side.toUpperCase()+komatopng(koma);
if (side.toUpperCase()=='S') side='#senteMochigoma';
else side='#goteMochigoma';
setMarker(position);
position=cordToClass(position);
emptyComment();
var selector=side+' [src$="'+png+'"]';
$(selector).first().addClass(position).appendTo('#boardbase');
}

駒をドロップする、ということは先手、または後手の駒台から当該する駒のイメージエレメントを探し出し、このイメージエレメントのクラスを変更して将棋板のブロックにぶら下げる(appendTo)という作業になる。appendTo自体はエレメントを移動するコマンドなので、駒台のイメージエレメントをdetachする必要はない。

function makeAmove(side,promote, from, to) {
emptyComment();
//if to position is already occupied, then capture that image element to 'side's mochigoma
//for this we check the lenth of selector. ie, if $(".c6 .r7").length>0 then there is an element.
if ($(cordToSelector(to)).length>0) captureKoma(side,to);
// then set a marker to "to" position
setMarker(to);
// then move the piece, it just involves the changing of class
$(cordToSelector(from)).attr('class', cordToClass(to));
// then check if the piece is promoted by checking the variable promote
if (promote=='+') {promoteKoma(side,to);}
}

駒を動かすmakeAmoveという関数は、駒の移動先にすでに他の駒がいるかどうかを判定し、もし存在すればそのコマを自分の駒台におくという作業(captureKoma)を行う。 次に駒を移動させ、もし、成る、という行為が指定されていれば駒を昇進させる(promoteKoma) 駒の移動自体前述したごとくClass属性を変更するだけという非常に簡単な作業。

function captureKoma(side,cord){
var komaban,koma;
komaban=(side=='S')?'#senteMochigoma':'#goteMochigoma';
koma=$(cordToSelector(cord)).data("koma");
$(cordToSelector(cord)).first().attr("class","").attr("src",board.pathname+side+koma).appendTo(komaban);
}

captureKoma では先手か後手かを判断して、どちらの駒台に駒を移動するかを決める。駒の移動は当該イメージエレメントのClass属性を削除して駒台エレメントにappendするだけ。

function promoteKoma(side,cord) {
var koma;

koma = $(cordToSelector(cord)).data("koma");
koma = (koma == "hi.png") ? "ryu.png" : koma = (koma == "kaku.png") ? "uma.png" : 'n' + koma;
$(cordToSelector(cord)).first().attr("src", board.pathname + side + koma);
}

promoteKoma では、画像ファイルの名前を変更する。 なお、駒の画像ファイル名をイメージエレメント自体に記録しておくために”data-koma”という属性をエレメント作成時に追加しているのだが、これはjquery では.data(“koma”) というメソッドで読むことができる。駒がなっても、この画像ファイル名自体は変更していない。(駒を取った時に、このデータを読み込んで駒台に置かれる駒の画像としている。竜を取っても、駒台に置かれるのは飛車、というわけ)

上記の関数に使われるサブ関数の定義は以下の通り。

function emptyComment() {$('#scomment').empty();} コメント欄を空に
function cordToClass(cord){ return 'koma c'+cord.charAt(0)+' r'+cord.charAt(1);}

位置情報をクラス属性に変換(例:34→”koma c3 r4″)

function cordToSelector(cord){return ('.koma.c'+cord.charAt(0)+'.r'+cord.charAt(1));}

位置情報をjQueryのセレクター情報に変換(例:34→’.koma.c3.r4 ‘)

function setMarker(cord){
var markerClass;
markerClass="marker c"+cord.charAt(0)+' r'+cord.charAt(1);
$('#marker').attr("class",markerClass);
}

これは、動いた駒を示すために、駒の背景色を変えてるためのmarkerセットするルーチン。

 

動かすためのボタンを設定

function setupButton() {
$("#aButton").click(function () {animateBoard()}).attr("value","Forward for solution");
}

 

初期設定にボタン設定を追加

$(function () {
initializeBoard();
setupButton();

});

 

これで一応ボタンをクリックすることにより、駒が一手づつ動く将棋盤ができた。(デモサンプル) とたんに気が付いたのが、手を戻す機能がほしい、ということ。 というわけで次回は戻しボタンの実装をする。

 

将棋駒をJavaScriptで動かす

前回作った将棋盤で、初期画面を表示することができたので、ここから駒を動かすしかけをJavaScriptで作成していく。

駒の動きを表すデータ形式をまず決める。やはり、boardというグローバルオブジェクトの中にmoves というString配列 として用意することにする。

一文字目:先手・後手の指示、s (先手)またはg(後手)の一文字で表記。先手が2手続けて指す、ような場合にも対応、  ‘x’で終了。
二文字目:駒を打つ場合にはd(drop) ,駒をなる場合には’+’, それ以外は読み込まない。
三、四文字目:駒の移動先の座標 3四の場合は34 etc.,
五、六文字目:二文字目、drop指定以外は駒の移動元の座標 3三の場合は33 etc.,
二文字目がDrop指定の場合、五文字目は駒の指定一文字jとなる。(p,l,n,s,g,r or b)

たとえば、後手3三の駒が3四に行く場合の表記は “g-3433″となる。 コマの種類に無頓着に3三にあった駒を3四に移動する、という考え方である。

また、後手側が歩を2三に打つ場合は”gd23p”となる。

駒をとる、という表記がないが、これは移動した先に駒があれば、それを取るということにする。よって明示的には記述しない。

また、’*’以下の文字列をコメントとして認識し、コメント欄に表示できるようにする。 一文字目にあれば、駒を動かさず、コメントのみ更新する。

 

配列のポインターとしてboard.index という変数を指定しておく。 初期値は当然0.

これらのルールによって、一連の駒の動きをあらわすとたとえば以下のようなデータ配列となる。


object={
.
.
.

moves:[
"*this is a comment that should go to comment line",
"s-2627 *here, the sente moves a piece from 27 to 26",//2六歩
"g-8483", //8四歩
"s-2526", // 2五歩
"g-7261", // 7二金
"s-2425", // 2四歩
"g-2423 *capturing the piece is implied", //同歩
"s-2428", // 同飛車
"gd23p", // 2三歩打
"s+2324",// 同飛車成り
"g-3241", //3二金
"s-2223", // 2二竜
"g-2231", //同銀
"x" //終了
],
index:0,
.
.
}

将棋盤と駒をWEB上に表示する2

というわけで、将棋の駒の個々の画像データ表示位置をCSSに記述することによって将棋盤に将棋の駒を配置することができるようになったが、いちいち

<img href=”……png” alt=”” > という属性をつけたイメージタグを駒の数だけ書いてWebページを作るのも気が利かない、というか、手間がかかってしょうがない。 そこで JavaScript を使って作業を簡単にすることを考える。

まずは駒配置のデータ形式を グローバルバリアブル(board)に格納することとし、その構文を以下のようObject literal で記述する

var board;

board= { onBoard:{ G:
['11l','21n','31s','41g','51k','61g','71s','81n','91l', '22b','82r', '13p','23p','33p','43p','53p','63p','73p','83p','93p'],
S:
['19l','29n','39s','49g','59k','69g','79s','89n','99l', '28r','88b', '17p','27p','37p','47p','57p','67p','77p','87p','97p'] },

onHand:{ S:['g', 'g', 'l', 'p'], G:['b', 'l', 'l', 'p'] },

盤面の配置はonBoard とし、その中にS配列(先手) G配列(後手)をつくる。同じく持ち駒の配列をonHand.S, Onhand.G という二つの配列に記述する。

駒の種類については英語文字一文字であらわす。 (香車=Lanceー&gt;l、桂馬=Knight→n、銀=Silver→s、 金=Gold→g、角=Bishop→b,飛車=Rook→r, 王=King→k)  なお、成駒はすべてそれぞれの英文字の大文字とする(成銀→S、成桂→N etc.,)

ちなみに上の例では平手の初期画面となる。(よって、onHand 部分は本当は空行列になるべきだが、これはテスト用のデータなのでこうなってます)こうやって先手、後手の駒の配置をまとめて記述することによって前回よりはデータを作る手間が楽になる。

次にこのデータを読み込んでイメージタグを生成するfunction を定義する。

JQuery の拡張機能を多用するのでまずは以下の記述が必要

 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
function cordToClass(cord){ return 'koma c'+cord.charAt(0)+' r'+cord.charAt(1);}

そして function initializeBoard() で将棋板と駒台を表示

function initializeBoard() { var i,cord,png;

for (i=0; i<board.onBoard.S.length; i++){

png=komatopng(board.onBoard.S[i].charAt(2)); cord=board.onBoard.S[i].substr(0,2);

$('<img alt=""/>') .addClass(cordToClass(cord)).attr('src',board.pathname+'S'+png).attr('data-koma',png).appendTo('#boardbase'); } for (i=0; i<board.onBoard.G.length; i++){

png=komatopng(board.onBoard.G[i].charAt(2)); cord=board.onBoard.G[i].substr(0,2);

$('<img alt=""/>') .addClass(cordToClass(cord)).attr('src',board.pathname+'G'+png).attr('data-koma',png).appendTo('#boardbase'); }

for (i = 0; i < board.onHand.S.length; i++) { png=komatopng(board.onHand.S[i]);
$('<img class="" alt=""/>') .attr('src', board.pathname+'S' + png ).attr('data-koma',png).appendTo('#senteMochigoma'); }

for (i = 0; i< board.onHand.G.length; i++) { png=komatopng(board.onHand.G[i]);
$('<img class="" alt=""/>') .attr('src', board.pathname + 'G' + png).attr('data-koma',png).appendTo('#goteMochigoma'); }

}

上記のfunction をブラウザーがデータを読み込んだ直後に実行させるために、

$(function () {initializeBoard();}

Jquery 大活躍の図である。

きもは

$('<mg alt=""/>') .addClass(cordToClass(cord)).attr('src',board.pathname+'S'+png).attr('data-koma',png).appendTo('#boardbase');

という一行なのだが、 これほとんどJquery のlibrary 機能ををつかっているわけで、

$(‘<img alt=”” />’) で新規にイメージエレメントをつくり、

.addClass(“…”) で クラス情報を追加し、(.addClass(“koma  c7  r9″)など
.att(…) で src属性を追加(src=”images/shogiboard/Sgin.png”など) (.attr(‘data-koma.png’) は data-koma属性を追加したもので、これは後述する。 ) そして最後にこのイメージエレメントを <div id=”boardbase”> というエレメントの最後尾に追加する

こうやってスタイルシートを使ってブラウザーにならべさせた将棋の駒を動かすのは実はわりと簡単にできる。

たとえば 例えば先手の銀を7九から6八に動かそうとした場合は

<img src=”….Sgin.png” alt=””> というエレメントをJQueryのクラス名指定($(‘.koma.c7.r9’)で探し出し

classを”koma c6 r8″ と書き換えてやれば、あとはブラウザの描画機能がイメージを動かし銀が動いたようにみせることができる。

ただそうは言っても、駒を取る場合はもともとそこにあった駒を先手後手の判断をして駒台に乗せる必要があるし、駒が成れば、画像ファイル指定も変えてやる必要がある。  場所を示すために動いた升目をハイライトする必要もある。

次回はこのあたりのインプリメンテーションを紹介する。

将棋盤と駒をWEB上に表示する1

まずは静的に将棋盤と駒をWEBページに表示してみる。
HTMLの記述は以下のごとくなる。
当該部分のみ

<link href="boardstyle.css"  rel="stylesheet" >
.
.

<div class="table"> <!-- outer table -->

<div class="row">
<div id="boardbase" class="cell">
<img class="board" src="assets/images/shogiboards/ban_kaya_a.png" alt=""/> <!-- 盤を表示-->
<img class="board" src="assets/images/shogiboards/masu_dot_xy.png" alt=""/><!-- マス目を表示 -->
<img class="koma c5 r9" src="assets/images/shogiboards/Sou.png" alt=""/><!--先手王を 59に -->
<img class="koma c5 r1" src="assets/images/shogiboards/Gou.png" alt=""/><!-- 後手王を 51に-->
<img class="koma c3 r7" src="assets/images/shogiboards/Ggin.png" alt=""/><!--後手銀を 37に -->
<img class="koma c9 r7" src="assets/images/shogiboards/Sfu.png" alt=""/><!--先手の歩を97に-->
<img class="koma c4 r9" src="assets/images/shogiboards/Skin.png" alt=""/><!--先手の金を49に-->
</div>

<div class="table"><!-- inner table -->
<div class="row">
<div id="goteMochigoma" class="komadai cell"> <!-- 後手の駒台-->
<img src="assets/images/shogiboards/Gou.png" alt=""/>
<img src="assets/images/shogiboards/Ggin.png" alt=""/>
<img src="assets/images/shogiboards/Gfu.png" alt=""/>
<img src="assets/images/shogiboards/Gfu.png" alt=""/>
<img src="assets/images/shogiboards/Gkin.png" alt=""/>
</div>
</div>
<div class="row">
<div class="cell empty" ></div>
</div>
<div class="row">
<div id="senteMochigoma" class="komadai cell"><!--先手の駒台-->
<img src="assets/images/shogiboards/Sgin.png" alt=""/>
<img src="assets/images/shogiboards/Sfu.png" alt=""/>
<img src="assets/images/shogiboards/Sfu.png" alt=""/>
<img src="assets/images/shogiboards/Skyo.png" alt=""/>

</div>
</div>
</div><!-- innter table -->
</div><!-- shogirow for the outer css-table-->
</div><!--shogitable-->

一方、boardstyle.cssは

.forSnapshot {position:relative;}
.table {display:table;}
.row {display:table-row;}
.cell {display:table-cell;}
.empty {height:25px;}
#boardbase {position:relative; width: 440px; height:490px;}
#senteMochigoma {vertical-align:bottom;}
.komadai {height:220px; width:160px; background-image:url(assets/images/shogiboards/ban_kaya_a.png);}
.board {position: absolute; left:10px; top:9px; }
.comment {padding-left:10px; width:590px;min-height:120px;}
.koma {position: absolute ; width:43px; height:48px;}
.marker {position:absolute; width:43px; height:48px;}
.lostworld {left:-20000px; top: -20000px;}
.c1 {left:365px;} .c2 {left: 322px;} .c3 {left:279px;}
.c4 {left:236px;} .c5 {left: 193px;} .c6 {left:150px;}
.c7 {left:107px;} .c8 {left: 64px;}  .c9 {left: 23px;}
.r1 {top:20px;}   .r2 {top:68px;}    .r3 {top:116px;}
.r4{top:164px;}   .r5{top:212px;}    .r6 {top:260px;}
.r7{top:308px;}   .r8{top:356px;}    .r9{top:404px;}
画像ファイルはすべてassets/images/shogiboards フォルダーに格納しておく。
ポイントとしては


<div id=boardbase> で将棋板ブロックのコンテクストを作り、この中に画像を放り込む。この画像の位置については koma属性でabsolute とし、列と行の位置をcx, rx でそれぞれ指定する。 r1=20px, c9=23px を起点に cxは横幅43px  lxは 縦高さ48pxづつ変化させている。駒台のブロックには単純に駒のイメージファイルを放り込んでおくと順番に表示される。

html において、将棋板と駒台をアレンジするのに、CSSのdisplay:table 属性を使っている。 htmlの一連のtable tag類 を使うことも考えたが、これはデータをリストアップするのとはちょっと違ったような気がしたので。cssで処理をすることにした。

結果はこちら

 

一応表示できるようになったが、これで将棋の駒40枚をならべるのは大変なので、 次回はたとえば文字列 ”s44g”,”g65p” などの羅列をつくり、これをJavascriptで処理して駒の配置表示する方法をかんがえる。