2006 年 6 月 5 日 18 時 58 分

PuzzleSolver #5: 解けるまで解く


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


さて、PuzzleSolver はテスト仕様であり、
今までは縦横のヒントを一度実行するだけだった。

このままでは、一度行列のスキャンを行うだけで、
PuzzleSolver や PuzzleLine などのオブジェクトが
解体されてしまい、昨日作った進捗管理が意味を成さない。

そこでこれを改造し、PuzzleSolver の Solve 呼び出しで、
一気に答えが出るところまでもっていこう。

中には、これらのアルゴリズムで解けない問題もあるため、
終了条件としては、1 セルも解けなくなるか、
エラー(矛盾)が発生するまでということにしよう。

Solve メソッドは、ユーザの呼び出しインタフェースなので、
矛盾が生じた場合には、戻り値などで返すのではなく、
エラーを発生させて通知するほうが好ましい。

ということで、まずはエラーコードを定義しよう。

VB のエラーは、COM で HRESULT と呼ばれる定数に該当し、
カスタムエラーを定義する場合、
vbObjectError という定数以降を使う必要がある。
vbObjectError は、&H80040000 の値である。

詳しくは述べないが、HRESULT のエラーは、
上位 16 ビットは意味が決まっており、
下位 16 ビットを自由に使うことができる。

ほかのアプリケーションとのバッティングを防ぎたいので、
アプリが使用するエラーは、中途半端な場所からにしよう。

Private Const ERROR_STATUS_BASE = vbObjectError Or &H7700

では、エラー定数を定義しよう。

Public Enum LogicPuzzleErrorConstants
    NONOGRAM_E_BADFORMAT = ERROR_STATUS_BASE
    NONOGRAM_E_INCONSISTENT = ERROR_STATUS_BASE + 1
End Enum

変わった名前のつけ方ではあるが、
これは、HRESULT の定数の命名規則に準じている。

ついでに、エラーメッセージも定義しておこう。

Private Const ERRMSG_NONOGRAM_E_INCONSISTENT = _
    "塗り潰しパターンとヒントの数値が矛盾しています。"


よし、Solve メソッドを実装しよう。

Public Sub Solve()

    Dim c As Long
    Dim r As Long
   
    Dim lTotal  As Long
    Dim lResult As Long
   
    Do
        lTotal = 0

        For c = 0 To m_oAnswer.Width - 1
            lResult = m_oColumns(c).Solve()
            If lResult = -1 Then
                Call Err.Raise(NONOGRAM_E_INCONSISTENT, _
                        , ERRMSG_NONOGRAM_E_INCONSISTENT)
            End If
            lTotal = lTotal + lResult
        Next
       
        For r = 0 To m_oAnswer.Height - 1
            lResult = m_oRows(r).Solve()
            If lResult = -1 Then
                Call Err.Raise(NONOGRAM_E_INCONSISTENT, _
                        , ERRMSG_NONOGRAM_E_INCONSISTENT)
            End If
            lTotal = lTotal + lResult
        Next
         
    Loop While lTotal > 0
   
End Sub


全体をループで挟み、
行列の精査で解答を得られたセル数を合計、
合計が 0 であればこれ以上進まないので終了だ。

もし、行の Solve で -1 が返れば矛盾が発生したので、
Err.Raise() を使って、エラーを発生させる。
Err.Raise の第 3 引数は説明文だ。
名前つき引数を使ってもいいのだが、
個人的に嫌いなので、第 2 引数を省略することで書く。

よし、ではこれで実行してみよう。

解答欄を選択してクリアし、F8 でマクロを実行する。
一発で答えが画面に表示された。
これが難しい問題であれば、おそらく一部だけか、
あるいはまったく解けない状態でとまるはずだ。

例えば、以下の「参」の文字だが、
この問題、実は重解となっているため、
どうしても途中までしか確定できない。
これを実行すれば、途中までの解等が表示された。

http://img1.mixi.jp/photo/bbs_comm/28/98/72122898_45.jpg

後は、最初の問題にもどり、
明らかに矛盾している解答状態にした上でマクロを実行。

----------------------------------------------
実行時エラー'-2147191039 (80047701)':

塗り潰しパターンとヒントの数値が矛盾しています。
----------------------------------------------

よし、エラーが出た。
しかし、エラー処理をしていないため、
ユーザにとっては迷惑なダイアログではある。

とりあえずは良好に動いているようだ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.