このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
これで 2 つのアルゴリズムが準備できた。
PuzzleLine を改造し、状況によって
アルゴリズムを切り替えて自動的に解答するようにする。
行の解答状況を記憶するために、
以前準備しておいた、m_lProgress フィールドを使用しよう。
この値によって、進捗度を調べることにする。
では、Solve メソッドを書き換えてみる。
Public Function Solve() As Long
Dim eState() As CellStateConstants
Dim oSolver As IPuzzleLineSolver
Dim lProgress As Long
lProgress = m_lProgress
' まず、行の解答状況をバッファにコピーする
ReDim eState(0 To m_lCellCount - 1)
Call CopyStatesOfLine(eState)
' 進捗状況を基にアルゴリズムを選択して解く
' 解けなかった場合、上位のアルゴリズムを使う
If lProgress = 0 Then
Set oSolver = New OverlapDetector
Solve = oSolver.Execute(m_oHints, eState)
If Solve = 0 Then lProgress = 1
End If
If lProgress = 1 Then
Set oSolver = New RaaSolver
Solve = oSolver.Execute(m_oHints, eState)
If Solve = 0 Then lProgress = 2
End If
' 解答できた場合は反映する
If Solve > 0 Then
Call ApplyStatesOfLine(eState)
End If
m_lProgress = lProgress
End Function
進捗度 m_lProgress が 0 ならば、OverlapDetector を使う。
最も高速な処理だからだ。OverlapDetector でだめなら、
m_lProgress を 1 にし、RaaSolver を使う。
RaaSolver もだめなら、m_lProgress を 2 にする。
現在は、2 つのアルゴリズムしかないので、
RaaSolver で解けなければそれ以上の進捗はないが、
発想しだいでは、さらにいくつかのアルゴリズムを
使い分けるように変更できるはずだ。
さて、 PuzzleLine には、もうひとつ考えることがある。
解答欄は縦と横のヒントが必ず交差するため、
どこかの行によってセルの解答が得られれば、
必ずどこかの列の解答にも影響されるはずだ。
例えば、ある列の m_lProgress が 2 となり、
どのアルゴリズムでも解答が得られない状態であるとする。
そこ状態で、他の行によってセルの状態が変更された場合、
この列を再評価すれば、答えが得られるかもしれない。
そのため、行や列は、外部からのセルの変更に対応して、
進捗度をリセットする必要がある。
そのためにはどうすればいいか。
例えば、行が変更した場合に、対応する列に対して、
通知メソッドを呼ぶという方法がある。
しかし、これはあまりスマートではない。
確定と通知を同時に PuzzleLine に実装しなければならない。
よく考えると、確定と通知が常にセットであるということは、
通知に関してもセル管理クラスの方で行えば効率がいい。
そう、PuzzleAnswer の Changed イベントを使えばいいのだ。
PuzzleLine で PuzzleAnswer のイベントを受け取るには、
WithEvents キーワードとともにフィールドを定義する。
Private WithEvents m_oAnswer As PuzzleAnswer
こうすると、オブジェクトボックスに m_oAnswer が出る。
それを選択すると、自動的に Changed イベントが
コードに挿入されるはずだ。
PuzzleLine は、外部によって変更されたセルを調べ、
自分の行や列に該当した場合、進捗度をリセットする。
Private Sub m_oAnswer_Changed(ByVal x As Long, ByVal y As Long, ByVal NewState As CellStateConstants)
' 自分の行列でなければ抜ける
If m_fColumn Then
If x <> m_lLineIndex Then Exit Sub
Else
If y <> m_lLineIndex Then Exit Sub
End If
' 進捗をリセット
m_lProgress = 0
End Sub
進捗の通知は、自分自身でセルを変更した場合も通知される。
そのため、Solve メソッドでは、
m_lProgress を lProgress ローカル変数にコピーし、
ApplyStatesOfLine を呼び出した後に、
改めて m_lProgress に代入している。
それにより、自分自身での変更の場合は、
m_lProgress がリセットされずに済むのだ。
イベントを使う場合、このような注意も必要だ。
さて、コードが正しいか実行しようとすると、
なぜか「メソッドまたはデータメンバーが見つかりません」
のコンパイルエラーが出てしまう。
どうやら、コードに裏技的にデフォルト属性をつけたので、
WithEvents と組み合わせた場合、
Excel が対応していないようだ。
仕方ないので ApplyStatesOfLine の以下の行を書き換える。
修正前: m_oAnswer(x, y) = State(y)
修正後: m_oAnswer.Item(x, y) = State(y)
プロパティ名を明示した場合は問題なさそうだ。