本文へスキップ

Clamini library

ナンプレ解析モジュール

 ナンバープレイスを自動で解くことができる、ナンプレ解析モジュールを作ってみました。 高度な解き方には対応していませんが、初級レベルの問題なら大丈夫と思います。

ナンプレ解析モジュールの仕様


 ナンバープレイスを自動で解かせる、外部モジュールです。基本的な解法である、レーザー解法と二国同盟に対応しています。 それ以上の解法には対応していないので、上級レベルの問題は解けない可能性が高いです。

モジュールの使い方


 ナンバープレイス解析関数(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