これまでの実装とこれからの展望

上のトピックで「不必要なクラス」についてちょっと言及しましたが、
これまでどういう設計になっていたのか少しだけ書いておきます。

それから、(まだ不必要なクラスの完全排除には至っていませんが)
修正が完了したあとどうするのか、についても少し。
githubのIssuesにもまとめてあったりしますが、きっと誰も読んでないので/(^o^)\
まず、マス目を表現するクラス Cell があって、
それを 3x3 の二次元配列で保持しておく Box クラス、
さらにそれを 3x3 の二次元配列で保持する NumberPlace クラスがありました。

NumberPlace → Box[1]
               Box[2]
               ...
               Box[9] → Cell[1]
                         Cell[2]
                         ...
                         Cell[9]

という感じで、Cellは合計81個になる、という構成。
人間のロジックを実装するために視覚的(?)なメソッドを用意していて、
Box#top で自分の真上にある Box オブジェクトを返すようにしてみたりとか、
メソッドチェインと呼び出しの便利さを実現するために、
低レベルな部分ではかなりヘンテコで気色悪い処理をしたりしてました。

そうして「人間の解き方」をプログラムに組み込めたのか、
というと、ぶっちゃけできなかったんですね。
後で調べてわかったんですが、人間のやることに近いほど、
プログラミングで(綺麗に)実装するのは難しい。

この場合はナンプレに対する弱いAIを作ろうとしていたわけです。
考えてみればそりゃ確かに難しくて当然なんですが、
やってる最中、あるいは思いついた当初は気付かないものですねー。

現在はまさに機械的に探索して候補を絞っていくだけなので、
人間の解法のために用意されたBoxクラスは必要ないわけです。
これを排除してしまえば、なんやかんややってた気色悪い処理も一層できる。
そんなわけでブランチを作成して、現在テストが真っ赤です。

これが終わったら Enumerable を導入しようかと思っています。
あとは外部からの解ロジックの追加とか。趣味で。

nps = NPSolver::NumberPlace.new
nps.add_solver :in_better do |nps|
  # do solving
end
nps.solve! :in_better

みたいなね。
完全に趣味ですけどね。
むしろこんなの実装するくらいならBoxあったほうが自由度広がるよね。

Enumerableについては導入してみたときにまとめてみるつもりですが、
二次元配列であろうとeachを用意できる以上コレクションなので、
これまで自分で実装してきたメソッドの代わりにEnumerableのメソッドを
使ったほうが精神衛生的にも車輪の再開発的な意味でも良いことです。
何より、 NumberPlace#each で # が返ってくるのが楽しそうだよね!

というわけでBoxの排除をしこしこ進めていきます……。