拾える石を判定して、クリアとゲームオーバーの処理を追加し、ゲームを完成させます。
あるいは、なでしこ3で、配列の比較をすることについて。
判定について考える
とりあえず今クリックしたとこの石が拾えるかどうかの判定について考える
1回目は石があるとこどこでも取れるワケだからイイとして・・・
まず、
- 一つ前に取った石と上下左右で隣り合った場所ならば、取れます。
とゆうことは、石を取った場所を覚えておかなきゃなのだ。
まあ、石を取った場所はもれなく覚えておきたいよね~。
「あっ、間違えた!」って言って戻したりしたい場合もあるだろうから、後には一手戻すボタンも作りたいと思うし。
これは、拾った履歴の配列を用意して、石を消す処理を行ったらその行と列を配列追加していけば良さそう?
最後に追加された末尾のデータが、前回石を拾った座標ということになるので、今押した座標が前の座標の上下左右に当たるか、順に確認すればいいですね。
でも、石の取れる条件はこれだけではありませんでした。
- 石の無いところは次の石まで直進!
コレです!
曲がったり後戻りは出来ない。
とゆうことは、前々回の位置と前回の位置とで進行方向を調べる必要があって?
進行方向に合致していたらつながっていなくてもOKだけど、間に別の石があったらダメだからいっこいっこ確認しなきゃならないっぽい。
お得意の再帰関数かな?
進行方向は、拾った履歴に記録していけば良いかな?
あっ、まって!
その前に、クリアとゲームオーバーの判定についても考えてみます。
クリアとゲームオーバーの判定について考える
クリアは、画面上の碁石が全て無くなった時です。
もちろん、取れる石を正しく取っていった場合に限ります。
石が拾えるかどうかの判定が出来るようになれば、問題データ(というか現在データ)が全て0になっていればクリアとすることができます。
ゲームオーバーは、石がまだ残っているのに取れる石が無くなってしまった場合です。
つまり、ゲームオーバーの判定のことを考えると、石が拾えるかどうかの判定は、マウスを押した場所に対して行うのではなく、あらかじめ取ることが出来る位置を総当たりでチェックする必要がありそうです。
取ることが出来る石が一つも無ければゲームオーバーです。
拾える石を探索して拾える石のデータを作成し、拾える石のデータが全て0だったらゲームオーバー。そして、マウスを押した時にも、この拾えるデータを確認すれば良いということになります。
拾える石の判定
データを作成
「どこから取り始めても良い」ので、一回目の「拾えるデータ」は問題データと同一です。
配列複製
については後に書きます。
#初期化
現在データ=中之字を行数と列数に碁石データ整形。
拾えるデータ=現在データを配列複製。
拾った履歴=空配列。
問題データですが、石を拾うごとに書き換えて行くので「現在データ」に改名しました。
配列複製して元の問題データをキープしておくか考え中ですが、今のところ使わないのでとりあえず。
進行方向
斜めには進めないので、上下左右のどれかです。
今の行と前の行、今の列と前の列を比較するだけ。
●(座標の)進行方向更新
列,行=座標。進行方向=空。
上,下,左,右=[列数*-1,列数,-1,1]。
もし、(拾った履歴の要素数)≠0ならば、
前列,前行=拾った履歴[(拾った履歴の要素数)-1]。
もし、(行<前行)ならば、進行方向=上
もし、(行>前行)ならば、進行方向=下
もし、(列<前列)ならば、進行方向=左
もし、(列>前列)ならば、進行方向=右
ここまで。
進行方向を戻す。
ここまで。
拾えるデータ
二回目以降の拾えるデータの初期値は石が一つも無い状態。
繰り返しでひたすら0をセットしていけばいいんだけど、配列要素作成
という命令があったので、使ってみる。
👉 なでしこさん マニュアル > plugin_system/配列要素作成
進行方向の逆以外の三方向へ再帰でどんどこ石を探索。
再帰とは、関数の中で自分自身を呼び出す的な処理のことです。
👉 なでしこさん マニュアル > 文法/関数
石を発見したら拾えるデータのその位置に1をセットして、その方向は終了。
空データ=空配列。
行数回。空データ[回数-1]=0を列数だけ配列要素作成。ここまで。
●(進行方向と座標から)拾えるデータ更新
列,行=座標。
拾えるデータ=空データを配列複製。
上下左右=[[0,-1],[0,1],[-1,0],[1,0]]。
上下左右を反復
x,y=対象。
もし、進行方向*-1=y*列数+xならば、続ける。# 後戻り禁止
[列,行]から対象へ拾える石探索。
ここまで。
ここまで。
●(座標から方向へ)拾える石探索
列,行=座標。x,y=方向。
もし、(列+x<0)または(行+y<0)または(列+x>(現在データの表列数)-1)または(行+y>(現在データの表行数)-1)ならば、戻る。
もし、現在データ[行+y][列+x]=1ならば、
拾えるデータ[行+y][列+x]=1。
違えば、
[列+x,行+y]から方向へ拾える石探索。
ここまで。
ここまで。
マウスを押した時には、拾えるデータを確認して石を取る。
取った石の位置で進行方向を更新し、拾った履歴を記入して、次の拾えるデータを作る。
#イベント
描画中キャンバスをマウス押した時には、
列,行=マウス位置取得。
もし、拾えるデータ[行][列]=1ならば、
現在データ[行][列]=0。
進行方向=[列,行]の進行方向更新。
拾った履歴に[列,行,進行方向]を配列追加。
進行方向と[列,行]から拾えるデータ更新。
ここまで。
盤面描画。
ここまで。
終了判定
配列の比較でハマった!
クリアの判定にしろゲームオーバーの判定にしろ、全て同じ盤面のサイズの二次元配列で、クリアならば現在データの値が全て0、ゲームオーバーなら拾えるデータの値が全て0であるかを調べたいわけです。
素直に全部反復して値が0か調べてもできるけど、ものすごく無駄っぽい気がする💧
全ての値が0の石が無い状態のデータを作っておいて、それと同じか比較すれば良いんじゃないの? と思ったんです。
幸いそれは、拾えるデータの初期状態として作成済みです。
ところが!
v1ならばそれで出来るのですが、v3では出来ませんでした。
配列A=「1,2,3」を「,」で区切る。
配列B=「1,2,3」を「,」で区切る。
もし、配列A=配列Bならば、「同じです」を言う。
違えば、「違います」を言う。
これは、v1で実行すれば「同じです」となりますが、v3では「違います」と返ってきます。
なんですと~⁉️
v1とv3では配列の仕様が結構変わっています。
まず、v1では配列A=配列B
で、配列Aと内容の同一な別の配列Bを作ることができましたが、v3ではそうはならない。
参照ということになって、Bを変更するとAも変わってしまうんだよね~。
配列A=「1,2,3」を「,」で区切る。
配列B=配列A。
配列Bに「4」を配列追加。
配列Aを表示。# 1,2,3,4
配列Bを表示。# 1,2,3,4
なんてこった❗️
というわけで、v3では、内容の同一な別の配列Bを作るためには、配列複製
をする必要があります。
👉 なでしこさん マニュアル > plugin_system/配列複製
こんな感じ?
配列A=「1,2,3」を「,」で区切る。
配列B=配列Aを配列複製。
配列Bに「4」を配列追加。
配列Aを表示。# 1,2,3
配列Bを表示。# 1,2,3,4
でででっ‼️
比較の際も普通に「=」で比較して同一と判定されるのは、同じ配列を参照している配列だけ。たとえ値が全く同じでも、たった今配列複製したばっかでも、それは別のものとゆう判定になるらしい。
どーしたらいいんだ⁉️
これはなでしこさんというよりかJavascriptの仕様なので、検索するとなんだか色々な方法が出てきて、結局どれを使えばいいんだよ? となりましたが、なでしこの命令だけで出来てカンタンなのは、JSONエンコードして比較することではないかと思いました。
配列A=「1,2,3」を「,」で区切る。
配列B=「1,2,3」を「,」で区切る。
配列Aと配列Bが同じ配列か確認。
もし、それ=はいならば、「同じです」を言う。
違えば、「違います」を言う。
●(AとBの|Bが)同配列確認
もし、(AをJSONエンコード)=(BをJSONエンコード)ならば、はいで戻る。
いいえで戻る。
ここまで。
どうでしょう? いちおうちゃんと「同じです」と言ってもらうことができました☆
終了判定
ま、そんなこんなでこんな感じ。
●終了判定
現在データと空データが同じ配列か確認。
もし、それ=はいならば、:
「クリア!」と言う。戻る。
拾えるデータと空データが同じ配列か確認。
もし、それ=はいならば、:
「失敗!」と言う。戻る。
ここまで。
簡単ですね☆
簡単なのに、そんなわけでムダに一番時間かかってしもうた💧
ここまでのコード
#設定できました! あっ、追加で「ゲーム中フラグ」を用意して、クリアもしくはゲームオーバー後に盤面をクリックしても、反応しないようにしました。
画面幅=300。画面高さ=360。マス幅=30。
行数=画面高さ/マス幅-1。列数=画面幅/マス幅-1。
#ゲーム画面作成
ゲーム画面=[画面幅,画面高さ]のキャンバス作成。
#問題
中之字=[
[0,1,1,0,0],
[0,0,1,0,0],
[1,1,1,1,1],
[1,0,1,0,1],
[1,1,1,1,1],
[0,0,1,0,0],
[0,0,1,0,0],
[0,0,1,0,0],
[0,0,1,0,0]
]
#データの作成
現在データ=中之字を行数と列数に碁石データ整形。
拾えるデータ=現在データを配列複製。
拾った履歴=空配列。
空データ=空配列。
行数回。空データ[回数-1]=0を列数だけ配列要素作成。ここまで。
#描画
盤面描画。
ゲーム中フラグ=オン。
#マス目を描画
●盤面描画
ベージュ色に塗り色設定。5に線太さ設定。
[0,0,画面幅,画面高さ]へ四角描画。
1に線太さ設定。
列数回。[マス幅*回数,0]から[マス幅*回数,画面高さ]まで線描画。ここまで。
行数回。[0,マス幅*回数]から[画面幅,マス幅*回数]まで線描画。ここまで。
問題描画。
ここまで。
#問題を描画
●問題描画
白色に塗り色設定。1.5に線太さ設定。
現在データを反復
描画行=マス幅*(対象キー+1)。
対象を反復
描画列=マス幅*(対象キー+1)。
もし、対象=1ならば[描画列,描画行]へマス幅/2-1の円描画。
ここまで。
ここまで。
ここまで。
#問題を盤面の中央に配置できるようデータ整形
●(問題を行数と列数に)碁石データ整形
追加行数=行数-(問題の表行数)。
追加列数=列数-(問題の表列数)。
上追加行数=追加行数/2を切り捨て。
下追加行数=追加行数/2を切り上げ。
左追加列数=追加列数/2を切り捨て。
右追加列数=追加列数/2を切り上げ。
追加行=空配列。
問題の表列数回。追加行に0を配列追加。ここまで。
上追加行数回。問題の0に追加行を配列挿入。ここまで。
下追加行数回。問題に追加行を配列追加。ここまで。
追加列=空配列。
問題の表行数回。追加列に0を配列追加。ここまで。
左追加列数回。問題の0に追加列を表列挿入して問題に代入。ここまで。
右追加列数回。問題の(問題の表列数)に追加列を表列挿入して問題に代入。ここまで。
問題を戻す。
ここまで。
#イベント
描画中キャンバスをマウス押した時には、
もし、ゲーム中フラグ=オフならば、戻る。
列,行=マウス位置取得。
もし、拾えるデータ[行][列]=1ならば、
現在データ[行][列]=0。
進行方向=[列,行]の進行方向更新。
拾った履歴に[列,行,進行方向]を配列追加。
進行方向と[列,行]から拾えるデータ更新。
ここまで。
盤面描画。0.1秒待つ。終了判定。
ここまで。
●マウス位置取得
列=((マウスX-マス幅/2)/マス幅)の整数部分。
行=((マウスY-マス幅/2)/マス幅)の整数部分。
[列,行]を戻す。
ここまで。
●(座標の)進行方向更新
列,行=座標。進行方向=空。
上,下,左,右=[列数*-1,列数,-1,1]。
もし、(拾った履歴の要素数)≠0ならば、
前列,前行=拾った履歴[(拾った履歴の要素数)-1]。
もし、(行<前行)ならば、進行方向=上
もし、(行>前行)ならば、進行方向=下
もし、(列<前列)ならば、進行方向=左
もし、(列>前列)ならば、進行方向=右
ここまで。
進行方向を戻す。
ここまで。
●(進行方向と座標から)拾えるデータ更新
列,行=座標。
拾えるデータ=空データを配列複製。
上下左右=[[0,-1],[0,1],[-1,0],[1,0]]。
上下左右を反復
x,y=対象。
もし、進行方向*-1=y*列数+xならば、続ける。# 後戻り禁止
[列,行]から対象へ拾える石探索。
ここまで。
ここまで。
●(座標から方向へ)拾える石探索
列,行=座標。x,y=方向。
もし、(列+x<0)または(行+y<0)または(列+x>(現在データの表列数)-1)または(行+y>(現在データの表行数)-1)ならば、戻る。
もし、現在データ[行+y][列+x]=1ならば、
拾えるデータ[行+y][列+x]=1。
違えば、
[列+x,行+y]から方向へ拾える石探索。
ここまで。
ここまで。
●終了判定
現在データと空データが同じ配列か確認。
もし、それ=はいならば、:
「クリア!」と言う。
ゲーム中フラグ=オフ。戻る。
拾えるデータと空データが同じ配列か確認。
もし、それ=はいならば、:
「失敗!」と言う。
ゲーム中フラグ=オフ。戻る。
ここまで。
●(AとBの|Bが)同配列確認
もし、(AをJSONエンコード)=(BをJSONエンコード)ならば、はいで戻る。
いいえで戻る。
ここまで。
まるっとエディタに貼れば、動きます☆
が、これもやはり動作確認をこちらにご用意しました。
昨日のとは違い、あらぬところを押しても石は拾えなくなっています。
つづきます
これでもう、いちおうちゃんとゲームが出来るようになったんじゃないですか?
やったね☆
でも、相変わらず書くことがぴんちなのでつづきます😅
次はUIを追加して、違う問題も選択して出来るようにしたい。
あと戻るボタン付けたい。
などなど色々と、ぽい感じにしていこうと思います✨