目次
- 重複チェック
- 使い切った数字ボタンディセーブル
- クリア判定
- グローバル変数
■重複チェック
数独では縦・横・3×3ブロックには同じ数字を入れてはいけない。 そういう状態になったときは、すぐにわかった方がよいので、数字が重複している場合は、それらを赤色表示することにする。
まずは、下記のように、数字を入れた・消した場合に check_cell_numbers() をコールする。
1 2 3 4 5 6 | func cell_pressed(x, y): # 盤面セルがクリックされた場合 ..... else : # 手がかり数字以外のセルがクリックされた場合 ..... check_cell_numbers() # 重複チェック update_cell_cursor() # 選択数字のセル強調 |
check_cell_numbers() の実装は下記のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | var badNumCount = 0 # 重複エラー数 ..... func is_OK(x, y, n): # 入れた数字に重複が無いかどうかチェック for i in range( 9 ): if i != x && get_cell_number(i, y) == n: return false if i != y && get_cell_number(x, i) == n: return false var x0 = x - x % 3 var y0 = y - y % 3 for v in range( 3 ): for h in range( 3 ): if x != x0 + h && y != y0 + v && get_cell_number(x0+h, y0+v) == n: return false return true func check_cell_numbers(): badNumCount = 0 for y in range( 9 ): for x in range( 9 ): var n = get_cell_number(x, y) if n != 0 : if !is_OK(x, y, n): badNumCount += 1 n += 9 if !is_clue_cell(x, y): set_cell_number(x, y, n) else : set_cell_clue(x, y, n) |
is_OK(x, y, n) は入れた数字に縦・横・3×3ブロック内で重複がないかどうかをチェックする関数だ。 単純に入れた箇所以外全部と比較し、ひとつでも一致があればその時点で false を返し、一致がなければ true を返す。
check_cell_numbers() は全部の箇所に対して is_OK() を呼んでいる。
エラーがあった場合は、後でクリア判定に使用するので badNumCount でエラー数をカウントしておく。 また、赤数字は通常数字の直後にあるので、タイルIDを+9している。
以上を実装し、実行して3を重複位置に入れると、下図のような表示になる。
■使い切った数字ボタンディセーブル
はじめから入っている手がかり数字も含めて、ある数字を9個入れた場合、その数字をそれ以上入れる必要はない。 なので、使い切った数字ボタンはディセーブルするのが親切なUIというものだ。
そのために、usedNums という配列を用意し、そこに各数字を何個使ったかを保持することにする。 なお、usedNums[0] が ‘1’ ボタンに対応する。
下記のように最初に、usedNums を宣言し、手がかり数字を設定する set_quest() で各手がかり数字の数をカウントしておく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var usedNums = [] # 1 ~ 9 の数字を何個使用しているか ..... func set_quest(q): usedNums.resize( 9 ) for i in range( 9 ): usedNums[i] = 0 var ix = 0 for ch in q: if ch != ' ' : # 空白は無視 ..... var n = ch as int if n >= 1 && n <= 9 : usedNums[n- 1 ] += 1 # 使用数字数をカウント ..... |
次に、下記のように cell_pressed() の中で、数字を入れた場合、消した場合に usedNums[] を更新する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | func cell_pressed(x, y): # 盤面セルがクリックされた場合 var n = get_cell_number(x, y) # クリックされたセルに入っていた数字 ..... else : # 手がかり数字以外のセルがクリックされた場合 if n != 0 : usedNums[n- 1 ] -= 1 # 数字使用数を1減らす if get_cell_number(x, y) == cur_numButton: set_cell_number(x, y, 0 ) # 0 for クリア else : usedNums[cur_numButton- 1 ] += 1 # 数字使用数を1増やす set_cell_number(x, y, cur_numButton) check_cell_numbers() # 重複チェック update_cell_cursor() # 選択数字のセル強調 update_numbuttons_disabeled() # 数字ボタンディセーブル状態更新 |
最後に下記に update_numbuttons_disabeled() の実装を示す。 使い切っている場合は set_disabled() によりボタンをディセーブル状態にする。
1 2 3 4 | func update_numbuttons_disabeled(): # 数字ボタンディセーブル状態更新 for i in range( 9 ): var b : bool = usedNums[i] == 9 get_numbutton(i+ 1 ).set_disabled(b) |
そうすると、’1′ を使い切った場合は、下図のように、数字ボタン1がディセーブル表示となる。
■クリア判定
重複エラーがなく、全ての数字を使い切ったら問題クリア状態だと判定できる。
そのコードは下記のように記述できる。
1 2 3 4 5 6 7 | func is_solved(): if badNumCount != 0 : # 重複がある場合 return false ; for i in range( 9 ): if usedNums[i] != 9 : return false ; # 使い切っていない数字が残っている return true ; |
本アプリでは、問題クリア時にその旨を示す確認ダイアログを表示する。
確認ダイアログを表示するクラスは AcceptDialog なので、それをノードツリーに追加する。
AcceptDialog はノードに追加すると、下図のようにノードツリーの右端に「}」を右に90度回転したようなアイコンが表示される。 これはデフォルトでは非表示という意味だ。逆に ◎ のようなアイコンは表示中という意味だ。
ダイアログを実際に表示するには、下記のように、ダイアログタイトル、文言を設定し、popup_centered() をコールするだけだ。
1 2 3 4 5 6 7 8 9 10 | func cell_pressed(x, y): # 盤面セルがクリックされた場合 var n = get_cell_number(x, y) # クリックされたセルに入っていた数字 ..... else : # 手がかり数字以外のセルがクリックされた場合 if is_solved(): print( "solved" ) $AudioSolved.play() $AcceptDialog.window_title = "Congratulations !" $AcceptDialog.dialog_text = "Good Job !, you are great." $AcceptDialog.popup_centered() |
グローバル変数
GDScript にはグローバル変数が無い。 それが何故なのかはよくわからないが、ユーザ得点のようにシーン遷移を行った場合でも情報をシーン間で共有可能にしたいというのは、 当然の要求仕様だ。なので、グローバル変数と同等の機能を実現するための仕組みがある。それが「自動読み込み(AutoLoad)」だ。
まずはグローバル変数のためのシーンを作成する。ルートノードは Node2D とし、シーン名は Global としておく。
プロジェクト > プロジェクト設定… メニューを実行し、「自動読み込み」タブを選ぶ。 パス部分にグローバル変数シーンを指定し、【追加】ボタンを押す。そうすると下図のようになる。
以上で、各シーンにおいて、「Global.メンバ変数名」で Global のメンバ変数にアクセスすることが出来る。
本アプリでは、下記のように問題番号と問題データをグローバル変数として定義している。
1 2 3 4 5 6 7 8 | var qNumber = 1 # 問題番号 [ 1 , 5 ] var quest = [ # 問題データ "008010240 090320061 102805007 039452700 670103092 001679380 900706108 780091020 015030600" , "007643890 009000047 384570102 901065008 405208603 800130709 108027534 590000200 042351900" , "003504900 096000750 040000080 000609000 600030001 000401000 080000020 054000160 007108500" , "100800007 007000050 030907100 504020900 000604000 009070304 001503040 040000600 700009003" , "009060070 000000015 600051000 000007300 706000509 003200000 000980003 890000000 020040800" , ] |
TechProjin Godot入門 関連連載リンク
Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次
標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次