ストレトリ 製作過程

Home PC-6001mkII Program etc

関連リンク ストレトリ (P6)Making「ストレトリ」

画面 更新日:2007/08/25
作成日:2006/08/27

 HSPプログラムコンテスト2006 にエントリしたプログラムの製作過程を綴ってます。

 完成版のページはこちらにあります。

2006/08/25〜27:なにを作ろうか...
 今年は、コンテスト用に何を作るか...。今年も一般部門には見向きもせずショート部門で攻めて行きたい。という思い以外、正直なところ何も考えてなかった。

 とりあえず、現在エントリーされているショート部門のプログラムを参照...う〜む、鑑賞モノのレベルが高いなあ。ちょっと今からではアイディアもないし太刀打ちできそうにないので、なにか遊べる系統のものが良いかな。

 はじめに考えたのは、キーボードシューティングだった。
一発撃つとこちらに向かって襲ってくる敵や非常に小さく見づらい文字の敵、複数の文字が重なって表示される敵、突然画面内に出て襲ってくる敵...などと考えて行くにつれ、ショートプログラムに収まる気がしなくなってきたので、とりあえずお蔵入りにする。

 次に考えたのは、動くジグソーパズル。
これなら、見た目より少ない容量でできそうかもと思ったが、「動くジグソーパズル」を探してみると実に12万件以上もヒット...。類似が多いからダメという訳ではないけど、作るからには何かひとさじ加えたいな、と考えていたが、これといったアイディアが思い浮かばない。というわけでまたお蔵入り。
これは何かアイディアが思い浮かんだら復活させよう。

 とやっているうちに27日になってしまった。
ここで、ふと昔作りかけていたパズルゲームを思い出す。盤面上にある数字から連続した数字を取っていく詰めパズルで、昔P6で途中まで作っていた...ような気がする。
よし、ここで思い出したのも何かの縁(?)だし、これでいこう。

2006/08/27:基本ルールとやりたいこと
 プログラムを作るにあたって、基本的なルールを書きとめておく。
      
   
    
    
      

  1. 盤面(6x6)には、初めに全ての数字が配置されている。数字の追加や移動はなし
  2. 縦横に3つ以上連続した数字の範囲内で連続した数字の組合せになる
    1行目で言うと、[1,3,2]や[6,1,3,2,5,4]はOK。[5,4]はNG。
    3行目で言うと、横の[4,5,6]や縦の[4,5,3]はOK。斜めの[4,3,2]はNG
  3. 同じ場所の数字は2度使えない。
    3行目で横の[4,5,6]を取ると、縦の[4,5,3]は取れない。
  4. どこも取れなくなった時に数字が盤面に残っていたら手詰まり。

 あと、今時点でやりたいこと(できるかどうかは別として)を忘れないうちに列挙しておく。どこまで収まるのか??
  1. 手詰まりの自動判定
  2. 面の選択
  3. やり直し機能
  4. マウス対応(まずはキーボードで作成)
  5. 数字を取れる場合のアニメーション
  6. 数字を取った場合のアニメーション
  7. 一手戻し機能
  8. 面クリア情報の記録
  9. カスタム面の入力

2006/08/29:画面っぽいもの
 面のデータ形式を考える。4KBというサイズの中では面データも無視できない大きさになるのでなるべく小さくしたいが、今の所は1マス1バイト(1面辺り36バイト)で考える。できれば8面くらい入れたいので、8面分のダミーデータを入れておく。

 このデータを元に、簡易的に画面を作成し、カーソル移動のみ実装してみる。

画面

 この時点で、既にstart.axは 2,227バイト...。カーソルを回転させている場合ではなかったか?まだ判定ルーチンを全然入れていないのに大丈夫だろうか?と人ごとのように書いてみる。

2006/09/03:判定
 「縦横に3つ以上連続した数字の範囲内で連続した数字の組合せになる」。当然の事ながら、これをプログラムで表現する必要がある。

 例えば数字の組合せが「3,5,2,4」の場合、これを素直に処理として表現すると、以下のようになるだろうか。
  1. 数字を小さいものから順に並べる(「3,5,2,4」→「2,3,4,5」)
  2. ソート後の1番目の値を見る(この場合は2)
  3. この値に1を足して2番目の値と比較する(3 = 2 + 1なのでOK)
  4. さらに1を足して3番目の値と比較する(4 = 3 + 1なのでOK)
  5. 項番4を、消そうとする数字の数だけ繰り返す
 最後まで判定を行って全てOKなら、これらの数字は取れる組合せである事がわかる。本当は、0〜9の数字の他に空白のデータもあるのでもう少し処理が入るが、ここでは割愛している。

 ただこの方法では最初の並べ替えでプログラムが長くなる(ショートプログラムとしては厳しい)ので、今回は少し特殊に分類を使った処理を模索する。さきほどと同じく数字の組合せが「3,5,2,4」の場合を元に説明する。
  1. 0〜9の分類を入れる領域を用意する
    今回はデータの形式に合わせて文字列で「/0123456789」を用意する。
  2. 数字に対応した位置の文字を「*」に置きかえる。(poke文で直接書き換える)
    「3」なら「/0123456789」「/012*456789」となる。
  3. 数字がここまでの最小値未満なら最小値を更新する
  4. 項番2〜3を、消そうとする数字の数だけ繰り返す (「/01****6789」)
  5. 最小値に対応した文字位置から消そうとする数字の数だけの文字数分の文字列(「****」)と、消そうとする文字数分「*」が連続した文字列(「****」)を比較して、同じならOK
 説明として書くと後者の方が複雑に見えるが、プログラムは短くなる(多分)ので、こちらで書き進める。

 この他に、数字を取る簡易的な処理とカーソルの処理を少しだけ書き加え、ようやく骨格の部分ができあがった。この時点で、start.axは2,796バイト。ここから、骨格部分のシェイプアップに移る。

2006/09/03:シェイプアップ
 プログラムの骨格が出来上がったので、ここで一度 start.axの削減にとりかかる。まずは、以下に挙げる汎用的な項目を中心にサイズの削減を行う。これだけでも数%はプログラムの削減ができる、はずだ。

項目使用前使用後削減備考
変数の初期化 a = 0 ;(12) dim a ;(8) 4 整数型の変数に有効
配列変数の一部だとNG
パラメータの省略 color 0, 0, 0 ;(16) color ;(4) 12  
color 255, 0, 0 ;(16) color 255 ;(8) 8  
color 255, 255, 0 ;(16) color 255, 255 ;(12) 4  
color 255, 0, 255 ;(16) color 255, , 255;(16) 0 途中のパラメータの省略は効果なし
定数の加減算 a = a + 2 ;(20) a += 2 ;(12) 8 -=, *= 等も同様
a = a + 1 ;(20) a+ ;(8) 12 1の加減算のみ
同じ計算式の変数化 a1 = b * c + d ;(28)
a2 = b * c + d ;(28)
a0 = b * c + d ;(28)
a1 = a0 ;(12)
a2 = a0 ;(12)
4 使用個所が多いほど効果あり
a1 = 1.0 ;(20)
a2 = 1.0 ;(20)
a0 = 1.0 ;(20)
a1 = a0 ;(12)
a2 = a0 ;(12)
-4 3箇所以上あれば効果あり

 この先もこれらの基本的な項目や処理内容自身の組替えでプログラムを削減しつつ機能を追加しつつを繰り返す事になる。この辺りが苦しくもあり面白くもありだと思う。

 この時点で、start.axは 2,632バイト。1割はサイズを削減させたかったが、そううまくは行かなかった。

2006/09/05:手詰まれ!
 9/3に作成した判定ルーチンを利用して、手詰まり判定の処理を追加する。

 処理自身は単純で、画面内で縦横に取る全ての組み合わせ全てで判定ルーチンを実行し、最終的に取れない数字があれば手詰まりとなる。

 試しに、手詰まり判定処理後に手詰まりとなる数字を赤く表示させてみた。初めの予定では、取れる数字がなくなったときに手詰まり表示という流れだったが、途中で手詰まりがわかった方が良いような気がしてきた。

画面

 この時点で、start.axは 3,360バイト。面クリアとやり直しを加えて画面レイアウトを変えたらほぼ一杯?マウス操作も入れたいけど...。

2006/09/09:マウス対応
 やり直し部分は、Enterを押したら面の初期設定部分に移動,面クリア処理は残り数が0になったら面クリアのダイアログを表示 と、暫定だが最低限の処理を実装する。

 で、マウス操作だ。これが無いと本作としては やっぱり片手落ちだろう。

 妙なこだわりだが、マウスでプレイする時はキーボードに触らなくても良いようにしたい。という訳で、これまでキーボード用に行った以下操作系処理を全てマウス用に実装する事になる。
  • カーソル移動はマウスカーソルの移動
  • 確定は左ボタンクリック
  • キャンセルは右ボタンクリック
 とここまでは順当に決まる。問題は今回追加したばかりの「やり直し」だ。
マウスでできる操作で比較的少ない処理でできそうな事と言うと...以下のようなマウス左右ボタンの組合せだろうか。
  1. マウス左右ボタン同時押し
  2. マウス左ボタンを押しながらマウス右ボタンを押す
  3. マウス右ボタンを押しながらマウス左ボタンを押す
  4. マウス左右ボタンが押された状態になっている
 何が違うの?と思われそうだが、stick命令の非トリガータイプキー指定によりできる処理とできない処理に分かれる。例えば、マウス右ボタンを非トリガータイプキー指定しないと、3,4でマウス右ボタン押しっぱなし状態は検出できない(もちろんgetkeyで別に取る手段もあるが、その分処理が長くなるので却下)
マウス右ボタンは、後に入るかどうか分からない「一手戻し」の操作でも使う事を見越して、非トリガータイプキーの指定はしたくない。という事で、3,4はとりあえず却下。

1の同時押し(マウス左右ボタンに非トリガータイプキーを指定しない)だと、タイミングが非常にシビアなので、これも却下。という事で、消去法ではあるが、2の処理で進めて行くことにする。

 この時点で、start.axは 3,834バイト。あと262バイトしか使えないが、ここからの空き容量との攻防(?)がプログラム作成の醍醐味となる、と思う。

2006/09/13:減量
 プログラムの空きも少なくなったので、またstart.axのサイズの削減を行う。

 この辺りになると、サイズの削減を行うにつれてリストがだんだん見辛くなってくる。ここでは、リストの見辛さと引き換えにサイズ削減を行う様子をいくつか書きとめておく。
  1. 複数の命令を一つにまとめる

     9/5に面数と残り数を表示する部分を作成したが、この時点では 行数分だけmes命令を実行している。
    これを、ひとつながりの文字列にして、mes命令の分を節約する。
    変更前変更後
    mes "Maze"
    mes "  " + (maze + 1)
    mes "残り"
    mes " " + n_left loop
    mes "Maze\n  " + (maze + 1)
    mes "\n残り\n " + n_left
     これで16バイト削減だ。ちなみに、1行にしてもサイズが変わらなかったので、今回は2行で記述している。

  2. 一旦変数を介していた複数の処理を繋げる

     例えば、面データを配列変数に展開する際に、見やすさも考えて一旦変数に代入後この変数の値を配列変数に入れていたが、サイズ削減のために見やすさを犠牲にする。
    変更前変更後
    tmp_c = peek(a_maze(maze), n) - 47
    a_p(i + cnt) = tmp_c
    a_p(i + cnt) = peek(a_maze(maze), n) - 47
     これで12バイト削減となる。

  3. 値が意図通りに入っている事を見越して、処理を変更する

     例えば、整数が入る配列変数をdim文で確保した場合、最初は必ず0が入る。これを前提とする事で「-1を代入する」処理を「現在の値(0)から1を引く」と処理を置きかえることができる。なぜこんな事をするかというのは、言わずもがな、サイズ削減になるからだ。
    変更前変更後
    dim a_p(56)
    repeat 56
     a_p(cnt) = -1
    loop
    dim a_p(56)
    repeat 56
     a_p(cnt)- ;-1の代入
    loop
    これでさらに6バイト削減できる。ただ この手の変更を行うと、一週間ぶりにリストをみた場合などに何をやりたかったのか思い出すのに時間がかかる(もしくは思い出せない)ので、覚えている内に注釈を入れておきたいところだ。

 その他、この辺りで何度もリストを見直すと出てくるのが、初期値の設定だけして使っていない、いわゆる「ゴミ変数」である。これらも、気付いた時にこまめに消しておいた方が良い。

 この時点で、start.axは 3,702バイト。なんとか132バイト増えた。確証は無いが、何十回かリストを見直せば、後100バイト位は削れると思う。

2006/09/16:詰めこみ
 プログラムの仕上げとして、予定していた処理を入れるだけ入れてみる。
  1. 面の選択
    キーボードの数字(テンキーでない方)を押す事で、数字に対応した面に移動するようにする。マウス操作はどうしようか迷ったが、良い操作が思い浮かばなかったのでキーボード操作のみにする。

  2. 数字が取れるときの表示
    アニメーションは、もう少し余裕がでたら(余裕はないけど)と言う事にして、取れる組合せになった場合にカーソルの色を濃くしてみる。

  3. 一手戻し機能
    操作は、当初の予定通りマウス右クリックor Escキー。ちょっと豪華に複数の手数を戻せるようにする。原理は簡単で、数字を消す毎に面データを丸ごと配列変数に退避している。一手戻す場合は、逆に退避している面データを戻すだけだ。

  4. 画面の色変更
    正直あまり変わっていないけどちょっと見栄えが上がった?

  5. 面クリアの演出を変更
    さすがにダイアログのままだとそっけないので、クリアを実感できる(?)表示に変更する。

 この時点で、start.axは 4,094バイト。V1.0としては満足の出来だ。あとは一番肝心の面作成が残っているが...。

2006/09/18:面作成と名付け
 プログラムの処理とは関係ないが、面の作成を行った。

 現在愛読している縦長の某パズル紙でもそうだが、やはり人間くささのあるパズルは面白い。という事で、各面にテーマを決めて作成し、唯一解になるように数字を調整していく。
 面数順にちゃんと難しくなっていってるかは微妙なところだが、なんとか最初の予定だった8面分を作成した。

 もう一つ、ちゃんと決めるべき事が残っていた。タイトルだ。

 単純に(仮)を取って「ストレート」に決定しようと思っていたが、どうもタイトルが安直過ぎる。どうせなら Googleで検索したときに分かりやすいように、もうちょっとだけ変えておきたい。という事で、いくつか調べてみた。
  • ストレト:562件("ストレト":562件)
  • ストレツ:22,900件("ストレツ":241件)
  • ストレトリ:1,000件("ストレトリ":5件)
 という訳で、「ストレトリ」にタイトルを変更する。


ストレトリ