Puzzlinkで新エディタを開発してみる日記<クロクローン編②>
前回はこちら。
これまでで入力UIは完成して、特にバグらしきバグもなくマウス・キーボードで問題盤面・解答をできるようになりました。
自作で1から作るとここだけで1週間は軽くかかるので、いかに基盤APIが偉大かということがわかります。
さて今回は、作った問題をシェア・保存するための機能である「URL入出力・ファイル入出力」の部分を作成していこうと思います。
余裕があれば解答チェックまで行っちゃおうかな。
URL入出力・ファイル入出力は、それぞれ Encode, FileIO というクラスに重ね書きしていく様子です。
島国スクリプトのままの状態だとこんな。
//---------------------------------------------------------
// URLエンコード/デコード処理
Encode: {
decodePzpr: function(type) {
this.decodeBorder();
this.decodeRoomNumber16();
},
encodePzpr: function(type) {
this.encodeBorder();
this.encodeRoomNumber16();
}
},
//---------------------------------------------------------
FileIO: {
decodeData: function() {
this.decodeAreaRoom();
this.decodeCellQnum();
this.decodeCellAns();
},
encodeData: function() {
this.encodeAreaRoom();
this.encodeCellQnum();
this.encodeCellAns();
}
},
まずは簡単なURLの方から行きましょうか。
これは、decodePzpr/encodePzprメソッドの中で各盤面要素ごとに対応するメソッドを呼び出すだけ。なんとも簡単。
境界線のやつはそのまま引き継ぐとして、あとは「矢印付き数字(数字範囲1~99)」がほしいですね。というわけで例によって yajitatami.js くんへ。
すると、どうも decodeArrowNumber16() が「矢印付き数字」のURL変換をつかさどっているらしい。
ここらへんのメソッド定義一式は `src/variety_common/Encode.js` にあるのですが、名前の16は「16進数を使う」という意味で、数字上限は8192まで行ける様子。ヤジリンもやじかずもこれを使っているのでこれで問題ないでしょう。
なお、ヤジタタミでは「矢印付き数字⇒境界線」の順でURLを出していますが、他の大多数は「部屋境界⇒そのほかの要素」の順で出しているので、今回もこちらに従います。
//---------------------------------------------------------
// URLエンコード/デコード処理
Encode: {
decodePzpr: function(type) {
this.decodeBorder();
this.decodeArrowNumber16();
},
encodePzpr: function(type) {
this.encodeBorder();
this.encodeArrowNumber16();
}
},
http://localhost:8000/p.html?kuroclone/6/6/44ii000f01u0a32h13u44c
前回の例題をエンコードしてみるとこのように。まだ本家にマージされていないのでローカルホストでのURLですが、ローカル環境が整いさえすえば上のURLで実際に遊べます。やったね。
同じくファイル出力のFileIOクラスもやってしまいましょう。
ファイル出力では、問題盤面だけでなく解答盤面の状態も出す必要があります。でも黒マス系のパズルならただ `decodeCellAns` を呼ぶだけっぽい。島国とやじかずがそうやってるんだから多分そう。
//---------------------------------------------------------
FileIO: {
decodeData: function() {
this.decodeAreaRoom();
this.decodeCellDirecQnum();
this.decodeCellAns();
},
encodeData: function() {
this.encodeAreaRoom();
this.encodeCellDirecQnum();
this.encodeCellAns();
}
},
先ほどの例題をファイルエンコード(改行なし)した結果が以下。設定次第ではPencilBox用の出力もできるようですが、まああちらはシャカシャカのりのり時代で時が止まっているツールなので、今後は考える必要はなさそう。
pzprv3/kuroclone/6/6/4/0 0 0 1 1 1 /0 0 0 1 1 1 /2 3 3 3 1 1 /2 3 3 3 1 1 /2 2 2 2 2 2 /2 2 2 2 2 2 /. 3,2 . . . . /. . . . 1,3 . /. . . . . . /. . . . . . /. . . . . . /. . 4,4 . . . /. . . . . . /. . . . . . /. . . . . . /. . . . . . /. . . . . . /. . . . . . /
いよいよ解答チェックへ
さて、URL出力・ファイル出力が思いのほか簡単に出来上がったので、解答チェックまで行ってしまおうかと思います。
さて、解答チェックの部分はざっとこのようになっています。ここに関してはシンプルな1ファイル1パズル構成の「ウソワン」をサンプルで借用。
//---------------------------------------------------------
// 正解判定処理実行部
AnsCheck: {
checklist: [
"checkAdjacentShadeCell", // 黒マス隣接禁止
"checkConnectUnshadeRB", // 白マス分断禁止
"checkLiarCell", // 部屋に嘘数字1つだけ
"doneShadingDecided" // 後述
],
checkLiarCell: function(code) {
this.checkAllBlock(
this.board.roommgr,
function(cell) {
return cell.isLiar();
},
function(w, h, a, n) {
return a === 1;
},
"bkLiarNe1"
);
}
}
このように、AnsCheckクラスの中に checklist でチェック用のメソッド名をリスト指定し、各メソッドを実装していく感じ。
上のサンプルだとリストに4つあるのに「部屋内に嘘1個だけよ」の1つしか実装してませんが、他のメソッド(上の例だといわゆる「連黒分断禁」ルール)は他パズルでも共有できるよう `src/variety_common/Answer.js` にまとまっています。
なお、最後の doneShadingDecided はチェック項目というより、白黒が全マス確定するまで勝手に「正解です」と出ないようにするフラグ的なやつっぽい?(怪しい)。ここはひとまず抜いておき、必要になったらあとで検討しましょうか。
さて、これを書くにあたり、何が必要かチェックリストを作らないといけません。そこで前回掲載のルールからチェックリストを以下のように洗い出します。
また、各チェック項目には1対1対応で「エラーコード」というのが決まっていますので、ついでに `src/res/failcode.json` を元に、エラーコードも一緒に決めてしまいましょう。
・cbShade: 異なる部屋にある黒マスどうしが辺を共有しています。
・anUnitNe.kuroclone: 矢印の1マス先にあるユニットの大きさが正しくありません。
・bkUnitNe2.kuroclone:ユニットの数が2つでない部屋があります。
・bkDifferentShape.kuroclone:部屋内のユニットの形が異なっています。
「矢印マスは黒マスにならない」は入力UI的にそもそも入れられないので無視です。何かしらのバグで入れられちゃう可能性はありますが、ウソワンでもぬりかべでもやってないので無視でいいでしょう。
「cbShade」は島国からそのまま流用してきたものです。
エラーコードは「接頭辞2文字」から始まるなどざっくりルールが決まっているようなので、郷に入れは郷に従えの精神。従わないと動かないわけではないですが、秩序を保ちたい。
既に同じエラーコードがある場合は「.パズル名」をつけて区別可能。
メモ:特に多い接頭辞のリスト
・nm:数字、ar:矢印、an:矢印付き数字
・bk:領域、bd:境界線、ln:線
接尾辞は不等号関係が多くて、例えば Ne(≠)、Lt(<)、Ge3(≧3)みたいな使われ方がされています。
エラーコードの文章は `src/res/failcode.json` へ日本語と英語それぞれに記述します。でもいざ実装しだすと「やっぱ変えた」となりそうなので、ここはまだ FailCodeクラスを作るだけにとどめます。
なおFailCodeクラスは大半のパズルで未定義なのであってもなくても大丈夫なんだと思いますが、あった方が個人的にわかりやすいので書きました。
//---------------------------------------------------------
// 正解判定処理実行部
AnsCheck: {
checklist: [
"checkSideAreaShadeCell"
// "checkArrowNumber",
// "checkUnitsCount",
// "checkUnitsShape"
],
// From shimaguni.js
checkSideAreaShadeCell: function() {
this.checkSideAreaCell(
function(cell1, cell2) {
return cell1.isShade() && cell2.isShade();
},
true,
"cbShade"
);
}
},
FailCode: {
cbShade: "cbShade",
anUnitNe: "anUnitNe.kuroclone",
bkUnitNe2: "bkUnitNe2.kuroclone",
bkDifferentShape: "bkDifferentShape.kuroclone"
}
さて、チェックリストも決まったことなので、次回はいよいよツギハギでなく自前でプログラムを組むところに入ります。
でも今日は眠いのでもう寝ます。おやすみ。