2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

日本語プログラミング言語「なでしこ」Advent Calendar 2023

Day 8

なでしこさんで「ひろいもの」を作るよ! ② ~拾える石を判定~

Last updated at Posted at 2023-12-07

 拾える石を判定して、クリアとゲームオーバーの処理を追加し、ゲームを完成させます。
 あるいは、なでしこ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対象
        もし進行方向*-1y*列数xならば続ける# 後戻り禁止
        [,]から対象拾える石探索
    ここまで
ここまで

●(座標から方向へ)拾える石探索
    ,座標x,y方向
    もし(+x)また(+y)また(+x(現在データ表列数)-1)また(+y(現在データ表行数)-1)ならば戻る
    もし現在データ[+y][+x]ならば
        拾えるデータ[+y][+x]
    違えば
        [+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が)同配列確認
    もし(AJSONエンコード)=(BJSONエンコード)ならばはい戻る
    いいえ戻る
ここまで

 どうでしょう? いちおうちゃんと「同じです」と言ってもらうことができました☆

終了判定

 ま、そんなこんなでこんな感じ。

●終了判定
    現在データ空データ同じ配列か確認
    もしそれはいならば:
        
「クリア!」言う戻る
    拾えるデータ空データ同じ配列か確認
    もしそれはいならば:
        
「失敗!」言う戻る
ここまで

 簡単ですね☆
 簡単なのに、そんなわけでムダに一番時間かかってしもうた💧

ここまでのコード

#設定
画面幅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列数だけ配列要素作成ここまで

#描画
盤面描画
ゲーム中フラグオン

#マス目を描画
●盤面描画
    ベージュ色塗り色設定線太さ設定
    [0,0,画面幅,画面高さ]四角描画
    線太さ設定
    列数[マス幅*回数,0]から[マス幅*回数,画面高さ]まで線描画ここまで
    行数[0,マス幅*回数]から[画面幅,マス幅*回数]まで線描画ここまで
    問題描画
ここまで

#問題を描画
●問題描画
    白色塗り色設定1.5線太さ設定
    現在データ反復
        描画行マス幅*(対象キー+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対象
        もし進行方向*-1y*列数xならば続ける# 後戻り禁止
        [,]から対象拾える石探索
    ここまで
ここまで

●(座標から方向へ)拾える石探索
    ,座標x,y方向
    もし(+x)また(+y)また(+x(現在データ表列数)-1)また(+y(現在データ表行数)-1)ならば戻る
    もし現在データ[+y][+x]ならば
        拾えるデータ[+y][+x]
    違えば
        [+x,+y]から方向拾える石探索
    ここまで
ここまで

●終了判定
    現在データ空データ同じ配列か確認
    もしそれはいならば:
        
「クリア!」言う
        ゲーム中フラグオフ戻る
    拾えるデータ空データ同じ配列か確認
    もしそれはいならば:
        
「失敗!」言う
        ゲーム中フラグオフ戻る
ここまで

●(AとBの|Bが)同配列確認
    もし(AJSONエンコード)=(BJSONエンコード)ならばはい戻る
    いいえ戻る
ここまで
 できました!  あっ、追加で「ゲーム中フラグ」を用意して、クリアもしくはゲームオーバー後に盤面をクリックしても、反応しないようにしました。

 まるっとエディタに貼れば、動きます☆

 が、これもやはり動作確認をこちらにご用意しました。
 昨日のとは違い、あらぬところを押しても石は拾えなくなっています。

つづきます

 これでもう、いちおうちゃんとゲームが出来るようになったんじゃないですか?
 やったね☆
 でも、相変わらず書くことがぴんちなのでつづきます😅

 次はUIを追加して、違う問題も選択して出来るようにしたい。
 あと戻るボタン付けたい。
 などなど色々と、ぽい感じにしていこうと思います✨

2
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?