将棋盤UIの設計方針を再整理する(スクラップ&リビルド)

この記事の所要時間は約 6 分 です。

将棋盤UI実装への道』と題して、ここまで、思いついたロジックをそのまま晒してきたわけだが、プログラムを書いているうちに、いろいろと『気付き』もあり、この将棋盤UIのプログラムの構造を何度か見直している。部分としてのロジックは書いたものが活かせても、書き進めるうちに、改善点が見えてくる。

未完成のプログラムであるため、この後、さらに見直しが入るとは思うが、一旦、現在、考えている設計方針をまとめてみたい。

プログラムを3つの部分に分割する

ここまで書いてきたプログラムコードは、2種類に分類できることに気がついた。

「将棋の盤面情報を操作するプログラム群」と「将棋盤をブラウザ上に再現するプログラム群」だ。

sfen形式の文字列を盤配列に格納する関数は、前者であるし、その盤配列を元に、将棋盤を再現するshowBoard関数等は、後者にあたる。

この2種類に分類されたプログラム群に、この後、実装する可動域判定関数や駒の成不成の判定などのルール周りを扱うプログラム群を加えて、それぞれ、3つのオブジェクトの中に、さまざまな配列や関数を格納することにした。

shogiui_policy

上記の図で示した通り、HTMLから呼ばれるのは、UIControllerのみとして、このUIControllerが(局面情報を保持する)Shogiオブジェクトや(ルールを担当する)RuleCheckerオブジェクトを利用して、ブラウザ上に将棋盤を表現するのが、今回、採用したい方針だ。

ちなみに、HTMLからShogiに点線が引かれているのは、駒落ちや指定局面からの対局を実装する際に、HTMLからその情報を直接局面として取り込むことも想定されるからである。

各オブジェクトの構造を考える

Shogiオブジェクト(局面の保持を担当する)

再プログラミング前に考えていたのは、以下のような構造だった。実際に、コーディングを進めていく中で、だいぶ見直しが入っていることは述べておく必要があるが、基本的には、局面に関する情報を一箇所に集めた。

  • board[]
  • capture_black
    • piece_array[]
    • put()
    • push()
  • capture_white
    • piece_array[]
    • put()
    • push()
  • turn
  • sfen
    • sfen_split()
    • sfen2board()
    • sfen2turn()
    • sfen2capture()
  • number_of_moves
  • turn_change()
  • move_piece()
  • sfen_set()

UIController(将棋盤の表示制御)

UIControllerの役割は、将棋盤のHTML表示に関わるDOM操作全般だ。駒を選択した時に、選択された駒の背景を変えたり、その駒の移動可能なマス目を表示するのは、このUIControllerに任せた。ただし、移動可能なマス目を計算するのは、後述するRuleCheckerに担わせている。

  • game_definition
    • rank_min
    • rank_max
    • file_min
    • file_max
  • size
    • board_size
      • padding_top
      • padding_left
    • piece_size
      • width
      • height
    • capture_board_size
      • padding_top
      • padding_left
      • width
      • height
  • elements
    • b
    • cw
    • cb
    • piece[]
  • initialize()
  • show_board()
  • show_capture()
  • select_piece()
  • unselect_piece()
  • show_movable_squares()
  • show_promotion_window()

ちなみに、コーディングを進めていくうちに、「盤上の駒の移動」と「持ち駒を打つ」という操作で関数を分けておく必要性を感じたので、”show_movable_squares()”だけではなく、”show_droppable_squares()”という関数も実装した。二歩の制御が大変だった。

なお、当初は、elementsの中の各オブジェクトにHTMLからgetElementsByIdしてくる要素を格納して保持させる想定であったが、コードを書いていくうちに、都度、HTMLからgetしてくる方がソースコードの可読性が上がる、かつ、保持し続けるべき情報ではない、ということに気がつき、ここの方針は見直した。

RuleChecker(将棋ルールの実装)

ここに駒の移動や持ち駒を打てるマス目、強制成などのチェックを任せる予定だ。当初、考えていた関数群は、以下の通り。

  • check_movable_squares()
  • check_must_promotion()
  • check_pin()

前述の通り、ここにも、”check_droppable_squares()”を追加で実装している。

その他、グローバルに残したもの

駒の定義情報など、汎用的に使う定数の類は、グローバルに残した。

また、配列のインデックスを段や筋に変換する関数など汎用的なものは、一旦、グローバルに残すことにした。

まとめ

プログラムコードを数百ステップ書いてからの見直しは骨が折れるが、何度もプログラムを書き直す中で、少しずつではあるが、ソースコードが改善されている。

更には、私自身、ほぼ初めてJavaScriptプログラミングだったわけだが、実装力が向上していることが実感できているので、このスクラップ&リビルドな書き直しは、必要なステップだったような気がする。

現在の進捗としては、駒の選択処理や持ち駒を打つ動作、駒を移動させる動作については、ほぼ実装できている。このあたりは、次回以降で解説してみたい。

shogiui_kaku_move

なお、駒が成る際の挙動全般、王手絡みで移動してはならない駒移動の制限(チェスでいうピンや自殺手)が残課題だ。駒の成り関連はともかくとして、ピンや自殺手の制限は、なかなか難しそうだ。