ナンバープレイスを自動で解くことができる、ナンプレ解析モジュールを作ってみました。 高度な解き方には対応していませんが、初級レベルの問題なら大丈夫と思います。
ナンバープレイスを自動で解かせる、外部モジュールです。基本的な解法である、レーザー解法と二国同盟に対応しています。 それ以上の解法には対応していないので、上級レベルの問題は解けない可能性が高いです。
ナンバープレイス解析関数(solve_numberplace str p1, int p2)
str p1: 81文字の数字文字列を受け取る
int p2: 解けたら1、 解けなければ0が入る
return: 81文字の解析結果文字列を返す
下のリンクからファイルをダウンロードできます。
※ZIP形式で圧縮されてるので、使う前に解凍してください!
solve_np.zip
#module solve_np_mod
; =========================================================
; ナンバープレイス解析関数(solve_numberplace str p1, int p2)
; str _question: 81文字の数字文字列
; var resultFlag: 解けたら1, 解けなければ0
; return question: 81文字の解析結果文字列を返す
; =========================================================
#defcfunc solve_numberplace str _question, var resultFlag
sdim question, 100
question = _question
dim board, 9, 9
dim notes, 9, 9, 10
; --- 盤面初期化 ---
repeat 81
board(cnt \ 9, cnt / 9) = int(strmid(question, cnt, 1))
loop
; --- 解析ループ ---
gosub *update_notes ; 候補リストの更新
repeat
changed = 0
; 1. 基本解法
gosub *solve_basic
if changed : continue
; 2. レーザー解法
gosub *check_laser
if changed : continue
; 3. 二国同盟
gosub *check_naked_pairs
if changed : continue
if changed == 0 : break ; どの手法でも変化がなければ終了
loop
; --- 結果判定 ---
question = ""
repeat 81
sx = cnt \ 9 : sy = cnt / 9
question += str(board(sx, sy))
loop
resultFlag = 1
repeat 81
if board(cnt \ 9, cnt / 9) == 0 : resultFlag = 0 : break
loop
return question
; ---------------------------------------------------------
; 候補リスト更新
; ---------------------------------------------------------
*update_notes
repeat 81
tx = cnt \ 9 : ty = cnt / 9
repeat 10, 1 : notes(tx, ty, cnt) = (board(tx, ty) == 0) : loop
loop
repeat 81
tx = cnt \ 9 : ty = cnt / 9 : val = board(tx, ty)
if val == 0 : continue
repeat 9
notes(cnt, ty, val) = 0
notes(tx, cnt, val) = 0
bx = (tx / 3) * 3 : by = (ty / 3) * 3
notes(bx + (cnt \ 3), by + (cnt / 3), val) = 0
loop
loop
return
; ---------------------------------------------------------
; 数字を確定させ、周囲の候補リストからその数字を消去する
; ---------------------------------------------------------
*set_and_clear_notes
board(tx, ty) = val
changed = 1
repeat 10, 1 : notes(tx, ty, cnt) = 0 : loop
repeat 9
notes(cnt, ty, val) = 0 ; 行
notes(tx, cnt, val) = 0 ; 列
; ブロック
bx = (tx / 3) * 3 : by = (ty / 3) * 3
notes(bx + (cnt \ 3), by + (cnt / 3), val) = 0
loop
return
; ---------------------------------------------------------
; 基本解法 (Single Candidate)
; ---------------------------------------------------------
*solve_basic
repeat 81 : tx = cnt \ 9 : ty = cnt / 9
if board(tx, ty) != 0 : continue
c_cnt = 0 : last_n = 0
repeat 9, 1
if notes(tx, ty, cnt) : c_cnt++ : last_n = cnt
loop
if c_cnt == 1 {
val = last_n : gosub *set_and_clear_notes
}
loop
return
; ---------------------------------------------------------
; レーザー (Hidden Single)
; ---------------------------------------------------------
*check_laser
repeat 9, 1 : num = cnt ; チェックする数字(1~9)
; type 0:行, 1:列, 2:ブロック
repeat 3 : type = cnt
repeat 9 : unit_idx = cnt
; --- ユニットの座標抽出 ---
dim u_x, 9 : dim u_y, 9
repeat 9 : i = cnt
if type == 0 { u_x(i) = i : u_y(i) = unit_idx }
if type == 1 { u_x(i) = unit_idx : u_y(i) = i }
if type == 2 {
u_x(i) = (unit_idx \ 3) * 3 + (i \ 3)
u_y(i) = (unit_idx / 3) * 3 + (i / 3)
}
loop
; --- 隠れたシングル判定 ---
found = 0 : can_pc = 0 : target_idx = -1
repeat 9 : i = cnt
tx = u_x(i) : ty = u_y(i)
if board(tx, ty) == num { found = 1 : break } ; 既に配置済み
if (board(tx, ty) == 0) & (notes(tx, ty, num)) {
can_pc++ : target_idx = i
}
loop
; そのユニット内で、その数字を置けるのが1箇所しかなければ確定
if (found == 0) & (can_pc == 1) {
tx = u_x(target_idx) : ty = u_y(target_idx)
val = num : gosub *set_and_clear_notes
}
loop
loop
loop
return
; ---------------------------------------------------------
; 二国同盟 (Naked Pair)
; ---------------------------------------------------------
*check_naked_pairs
; type 0:行, 1:列, 2:ブロック
repeat 3 : type = cnt
repeat 9 : unit_idx = cnt ; 何番目の行/列/ブロックか
; --- そのユニットに属する9マスの座標を抽出 ---
dim u_x, 9 : dim u_y, 9
repeat 9 : i = cnt
if type == 0 { u_x(i) = i : u_y(i) = unit_idx } ; 行
if type == 1 { u_x(i) = unit_idx : u_y(i) = i } ; 列
if type == 2 { ; ブロック
u_x(i) = (unit_idx \ 3) * 3 + (i \ 3)
u_y(i) = (unit_idx / 3) * 3 + (i / 3)
}
loop
; --- 抽出した9マス(u_x, u_y)に対して二国同盟のロジックを実行 ---
repeat 9 : i1 = cnt
tx1 = u_x(i1) : ty1 = u_y(i1)
if board(tx1, ty1) != 0 : continue
cur_x = tx1 : cur_y = ty1 : gosub *get_c_info
if res != 2 : continue
c1_v1 = v1 : c1_v2 = v2
repeat 9 : i2 = cnt
if i1 == i2 : continue
tx2 = u_x(i2) : ty2 = u_y(i2)
if board(tx2, ty2) != 0 : continue
cur_x = tx2 : cur_y = ty2 : gosub *get_c_info
if (res == 2) & (v1 == c1_v1) & (v2 == c1_v2) {
; ペア発見。他の7マスから候補を削る
repeat 9 : i3 = cnt
if (i3 == i1) | (i3 == i2) : continue
tx3 = u_x(i3) : ty3 = u_y(i3)
if board(tx3, ty3) != 0 : continue
if notes(tx3, ty3, c1_v1) { notes(tx3, ty3, c1_v1) = 0 : changed = 1 }
if notes(tx3, ty3, c1_v2) { notes(tx3, ty3, c1_v2) = 0 : changed = 1 }
loop
}
loop
loop
loop
loop
return
; 補助用:cur_x, cur_y を指定して呼び出す
*get_c_info
res = 0 : v1 = 0 : v2 = 0
repeat 9, 1
if notes(cur_x, cur_y, cnt) {
res++
if v1 == 0 { v1 = cnt } else { v2 = cnt }
}
loop
return
#global
|