Firebaseを駆使して数独を解くWebアプリを作ってみた　後編

#概要
こんにちは。Aidemyの刀根です。
Firebaseを駆使して数独を解くwebアプリを作成します。

今回は後編です。[前編はこちら](https://qiita.com/Swordroot_M/items/7774aff2bfabd60b63ba)

ソースコードの全容はGithubの方で見てください。この記事に載せちゃうと記事の長さがえげつないことになるので。。。
[Githubはこちら](https://github.com/Swordroot/Sudoku)

また、今回作成したwebアプリは[こちら](https://sudoku-fa55e.firebaseapp.com/)で公開しています。

#前回のあらすじ
フロント側の実装を終え、バックエンドの回答アルゴリズムの実装を開始。
とりあえずルールに則り、「縦横・ブロックに同じ数字が入らない」というルールに従い候補を絞るところまで実装しました。

これだけでも簡単な問題であれば解けてしまうのですが、以下の画像のように難しい問題は解けません。
(ちなみにこの問題は[こちらのページ](http://funahashi.kids.coocan.jp/game/game651.html)で生成させていただきました。)
![スクリーンショット 2018-12-12 17.19.15.png](https://qiita-image-store.s3.amazonaws.com/0/153926/248d53d5-4c59-b291-ce8b-ec68c216d4fb.png)

そこで、さらに回答アルゴリズムを洗練していきます。

#人が解く方法の続きを実装する前に・・・
まずはとにかく解けるようにしていきたいので、先に総当たりのアルゴリズムを実装していきます。

##バックトラック
バックトラック法とは総当たり方法の一種で、とりあえず適当に行けるところまで行ってみて、ダメだったら戻ってやり直す方法のことです。[詳しくはこちら。](http://www.ss.cs.meiji.ac.jp/CCP030.html)
数独の場合は、片っ端から候補の数字を入れていき、矛盾が生じたらまた別の数字を入れて・・・という過程を繰り返すことで総当たりしていきます。

これを左上のますから走査する中で行なっていきます。
各ますでの処理の流れは以下の通りです。

1. 候補として残っている数字を今見ているますに入れる
2. ルールに反していないかチェックする
3. ルールに反していなければ次のますへ行く
4. ルールに反している場合は別の候補を今見ているますに入れる
5. 全ての候補がルールに反してしまう場合は、１ます戻る

これをまず一旦実装します。
詳細な実装はGitHubをみてください。
[byBackTrackHelper.ts](https://github.com/Swordroot/Sudoku/blob/master/functions/src/helpers/byBackTrackHelper.ts)


## いざ、実行！！
先ほど解けなかった問題を解かせてみました。
![スクリーンショット 2018-12-12 18.09.26.png](https://qiita-image-store.s3.amazonaws.com/0/153926/1375b51d-da48-050a-7864-9d5ae53f62f7.png)
解けました。

## もっと難しい問題だとどうなる？
実はこれ、かなり予想外の展開です。本当はここでtimeoutして、もっと候補を少なくする必要があるという流れにしたかったのですが・・・
そこで、さらに難しそうな問題を[こちらのページ](http://www.sudokugame.org/extremesudoku.php)からお借りして解かせてみることにしました。
その結果・・・
![スクリーンショット 2018-12-12 18.57.12.png](https://qiita-image-store.s3.amazonaws.com/0/153926/cd6631d6-9b55-5132-1604-72354540e9c6.png)
ちゃんとtimeoutしてくれました。（画像はfirebase cloud function側のログ）
**やっぱりもっと候補を少なくする必要がありそうですね！**

#人が解くアルゴリズムをさらに実装
ということで、人が解くアルゴリズムをさらに実装していきます。

##行or列orブロックの中で１箇所しか入る場所がないパターン
例えばこのように候補が残っている行があった場合を考えます。
![スクリーンショット 2018-12-12 19.12.25.png](https://qiita-image-store.s3.amazonaws.com/0/153926/d8f28aa5-70a6-433e-0952-3e14fda76a5c.png)
この行では、3が入る場所が１箇所(左から3列目)しか残っていないのでそこに3を入れることができます。
同様のパターンを行・ブロックでも処理できるように実装します。
[byLastOneHelper.ts](https://github.com/Swordroot/Sudoku/blob/master/functions/src/helpers/byLastOneHelper.ts)

##行or列の中で2~3箇所しか入る場所がなく、それが全て同じブロックのパターン
例えば、上3行の候補が画像のように残っている場合を考えます。
![スクリーンショット 2018-12-12 20.29.41.png](https://qiita-image-store.s3.amazonaws.com/0/153926/fdb89eb9-b8e0-a5ce-4585-c5707b7045ee.png)

この時画像の上から３行目に注目すると、1が入るマスは２箇所しかなく、かつ同じブロック内であることがわかります。
つまり、画像内の右のブロックにおいては１が入るのはその２マスだけで、他のマスには入らないことがわかります。
これも実装します。
[byLast2Or3InSameAreaHelper.ts](https://github.com/Swordroot/Sudoku/blob/master/functions/src/helpers/byLast2Or3InSameAreaHelper.ts)

##ブロックの中で2~3箇所しか入る場所がなく、それが全て同じ行or列のパターン
これは直前で述べた奴の逆パターンです。
例えば、右3列の候補が画像のように残っている場合を考えます。
![スクリーンショット 2018-12-12 20.35.38.png](https://qiita-image-store.s3.amazonaws.com/0/153926/8d1f8526-9576-32ea-d38e-99b86002fa4b.png)

この時画像の下ブロックに注目すると、1が入るマスは２箇所しかなく、かつ同じ列であることがわかります。
つまり、画像内の右から２列目においては１が入るのはその２マスだけで、他のマスには入らないことがわかります。
これも実装します。
[byLast2Or3InSameAreaHelper.ts](https://github.com/Swordroot/Sudoku/blob/master/functions/src/helpers/byLast2Or3InSameAreaHelper.ts)

#ここまでやったら・・・
あとはバックトラックでもそこまで時間がかからないので、バックトラックを利用します。
だいぶ候補を絞ったのでそれなりの速さで解けるようになっています。

#まとめ
今回はアドベントカレンダーの一環で数独を解くwebアプリを一から作成しました。
もし今後も更新するなら、バックトラックなしでも解けるようにしていきたいですね。