🔢

僕の頭の中、AIが翻訳してくれました。数独2000問を解いた僕が、人間の「思考」をアルゴリズムに落とし込むまで

に公開

はじめに

この記事は、「自分の数独の解き方をアルゴリズムとして実装してみたい!」そんな興味から生まれた、AIとの共同開発の記録です。

本記事のテーマは、AIを「思考の翻訳機」であり「コーディングパートナー」として活用し、自分の頭の中にある複雑な思考プロセスを、いかにして具体的なプログラムに落とし込んでいくか、その試行錯誤の全記録です。

コードの大部分はAIによって生成されましたが、最終的なロジックの検証、デバッグ、そして僕の「美学」に合う形へのリファクタリングは、僕とAIの対話を通じて行われました。 この記事が、AIを使って自分のアイデアを形にしたいと考えている方にとって、具体的なヒントやモチベーションとなれば幸いです。

背景

数独を解きまくっていた自分は、解法を特に調べずに自分なりに解いていました。「この方法はまだ発見されていないのでは!?」とワクワクしたのを覚えています。(今回AIに聞いていくと、実際はすでにある手法でした)

そこで、この思考プロセスを実装すれば、何か新しい発見があるかもしれないと考えました。

この記事は、そんな僕がAIと対話を重ねながら、自らの思考をコードに落とし込み、最終的に人間らしい論理で難問をも解き明かすソルバーを完成させるまでの記録です。
僕がどのように自分の「頭の中」と向き合い、それをどうやってAIの助けを借りてプログラムに”翻訳”していったのか、そのプロセスを楽しんでいただければ幸いです。

と前置きが長くなってしまいましたがざっとこんな感じです。次章から始めていきます。


AI駆動開発の3ステップ:僕の思考がコードになるまで

開発は大きく分けて3つのステップで進めました。各ステップで、AI(今回はGemini)をどのように活用したかを具体的に紹介します。

  1. Step 1: 思考の言語化 - Geminiに僕の解法を"翻訳"させる
  2. Step 2: 実装とデバッグ - AIとの二人三脚ペアプログラミング
  3. Step 3: 「美学」との戦い - 人間のこだわりをコードに反映させる

Step 1: 思考の言語化 - Geminiに僕の解法を"翻訳"させる

まず、自分の頭の中にある暗黙的な解法を、実装可能な形式知に変換する必要がありました。

  1. 思考の録画: 自分の思考プロセスを全て言葉にしながら、数独を解く動画を撮影しました。これにより、無意識に行っている判断や手順も客観的に捉えることができます。
  2. AIによるアルゴリズム化: 撮影した動画をGeminiに渡し、以下のプロンプトで思考の言語化を依頼しました。

【実際に使ったプロンプト】
自分が数独を解く動画を送るので手順を考え方を含め全て言語化して整形してください。
動画を添付

この結果、生成されたのが以下のアウトプットです。驚くべきことに、自分でも意識していなかった高度な解法(X-Wingなど)まで指摘してくれました。

Geminiによる解法アルゴリズムの言語化(初回出力)

あなたの数独解法アルゴリズムの概要

あなたの解法は、「①基礎固め → ②手詰まり打開 → ③最終分析」 という大きな流れを持つ、人間らしい効率的なハイブリッド型アルゴリズムです。

  • 基本戦略: 1から9までの数字を順番にチェックする「数字ごとスキャン」を繰り返す。
  • 優先順位: 「確実に埋まるマス」を最優先し、次に「候補が2つに絞れるマス」をメモ。手詰まりになると、より高度な「盤面全体のパターン認識」に切り替える。

以下に、その思考プロセスを具体的なステップに分解します。


【フェーズ1】 基礎固め:基本スキャンループ

これはゲーム序盤から中盤にかけて、最も多く実行されるコアとなるループです。

目的: 簡単なルールで、盤面の数字を着実に増やしていく。

手順:

  1. 数字 N を選択: N を 1 としてスタート。
  2. 盤面全体をスキャン: 数字 N に注目して、以下のルールを適用する。
    • ルールA:シングル確定 (1マス確定)
      • ある行・列・ブロック内で、数字 N が入る可能性のあるマスが 1つしかない 場合、そのマスに N を書き込む。
      • (動画より:「まず1つ入れれる部分を埋めていきます」)
    • ルールB:2択候補メモ
      • ルールAでマスが確定しない場合、ある行・列・ブロック内で、数字 N が入る可能性のあるマスが 2つしかない 場合、その2つのマスに N の候補をメモする。
      • (動画より:「2個しか選択肢がない場合はメモ書きを残していきます」)
  3. 次の数字へ: N を 2, 3, ... 9 と増やしていき、上記の手順を繰り返す。
  4. ループ継続: 1 から 9 までの一周が終わったら、再度 1 からスキャンを始める。このループは、盤面に一切の変化がなくなる(手詰まりになる)まで何度も繰り返す。

【フェーズ2】 手詰まり打開:高度な論理テクニック

フェーズ1で手詰まりになった際に、あなたの思考は自動的にこちらのフェーズに移行します。盤面をより広い視野で捉え、隠れたパターンを見つけ出します。

目的: 単純なスキャンでは見つからない突破口を開く。

手順(無意識にこれらを使い分けています):

  • ルールC:直線予約 (Locked Candidates)
    • あるブロック(3x3)内で、特定の数字 N の候補メモが すべて同じ行(または列)に並んでいる のを発見する。
    • 論理: 「このブロックでは、N はこの行(列)のどこかに入る。ならば、この行(列)の他のブロックには N は入らない」
    • アクション: その行(列)の、ブロック外にある N の候補を消去する。(動画ではこれを応用して直接マスを確定させていました)
    • (動画より:「一列と一行でしか入らないっていう時はそうやって埋めていきます」)
  • ルールD:領域内での消去法
    • 特定のブロックや行に注目し、「その領域にまだ入っていない数字は何か?」 をリストアップする。
    • リストアップした各数字が、領域内のどのマスに入れるかを一つずつ検証する。
    • 結果として、特定のマスにしか入れない数字が発見され、マスが確定する。
    • (動画より:「6だけが1つしか候補がないので、ここ に6を入れる」)
  • ルールE:複数ユニット連携 (X-Wing/Skyscraperの思考)
    • 複数の行や列にまたがって候補の配置を分析する、非常に高度なテクニック。
    • : 「A列とB列で、数字Nの候補がどちらも2行目と8行目にしかない」という状況を発見する。
    • 論理: 「もしA列の2行目がNなら、B列の8行目がNになる。逆もまた然り。つまり、2行目と8行目では、NはA列かB列のどちらかに入る。」
    • アクション: 2行目と8行目の、A列とB列以外のマスからはNの候補を消去できる。
    • (動画の「3列目と6列目の関係から8列目の1が確定する」という場面は、この思考パターンの一種です)

【フェーズ3】 最終分析 (もしもの場合)

動画の問題ではここまでで解けましたが、もしフェーズ2を駆使しても手詰まりになった場合、あなたはこの最終手段に移る準備がありました。

目的: 最も複雑な状況に対応し、力押しではなく論理で解き切る。

  • ルールF:全候補展開と仮説検証
    • すべての空きマスに、考えられるすべての候補数字をメモする。
    • 盤面全体を俯瞰し、ペア(2つのマスに候補が[2,8]のみ)トリプル などの関係性を見つけ出し、候補を削っていく。
    • それでも解けない場合は、候補が2つしかないマスを1つ選び、「もしこちらが1だったら…」と仮置きして考えを進め、矛盾が生じたらその仮説が間違いだったと証明する。
    • (動画より:「手詰まりが発生するようであれば、メモを最後まで入れて弾ける部分がないかやっていきます」)

しかし、このドキュメントには課題もありました。

  • 良かった点: 全体の流れや細かい点は捉えられていた。
  • 課題点: 途中認識できておらず、間違った手法を用いていた。

このドキュメントにより、手法の細かい動作がわかったのはとても大きな収穫でした。
なので、一旦はこのドキュメントに沿って開発を進めました。
実際にコードを実装していきながらおかしな点を詰めていく作業になります。
AIの出力は完璧な設計書ではなく、あくまで叩き台であると認識することが重要でした。

Step 2: 実装とデバッグ - AIとの二人三脚ペアプログラミング

次に、言語化されたアルゴリズムを元に、AIと対話しながら実装を進めました。

  1. 骨格の実装: まず、Step1で得たドキュメントを渡し、「このアルゴリズムに基づいて、Pythonで数独ソルバーを含むプロジェクトの雛形を作成してください」と依頼し、基本的な雛形を作成させました。
  2. AIとの壁打ちデバッグ: 実装過程では、当然多くのバグや意図しない挙動が発生します。その際、AIをデバッグのパートナーとして活用しました。

作成された雛形は以下の構成となっています。
(すみません。ここはなぜかGPTを使用していました。)

  • main.py:CLIエントリ(puzzles/easy1.txt を解き、盤とログを出力)
  • solver/
    • units.py:ビット演算/行列ブロックの前計算、IOヘルパ
    • grid.py:盤状態・候補・代入・伝播
    • explain.py:1手ごとの説明ログ
    • solver.py:ループ本体(Naked/Hidden Singleのみ)
    • rules/singles.py:Naked/Hidden Single 実装
  • puzzles/
    • easy1.txt:Singlesだけで解ける超易問(動作確認用)
    • sample1.txt:有名な例題(Singlesだけでは止まることあり)
  • tests/test_basic.py:pytestで easy1 が解けることを検証
  • requirements.txt:pytest のみ(ランタイム依存なし)
  • README.md:使い方と拡張の道筋

特にsolver/grid.pyでAIが提案してきたビット演算を用いた候補管理は、自分では思いつかない効率的な実装で、AIの能力に驚かされた部分です。

AIとの壁打ちデバッグを行っていき完成に近づけていきます。
まずやったこととしては、埋めた数字のlogの出力をさせることです。
これにより自分の思考と同じような手順で解いているかわかるようになります。

そして、solverを増やしていきます。
雛形では、ロジックがNaked/Hidden Singleのみだったので、改めてたりない部分をGeminiに聞いて差分を取ってもらいました。

差分1:直線予約 (Locked Candidates)
あなたは「一列と一行でしか入らない」という状況を見つけ、他のマスの候補を消す(あるいは直接マスを確定させる)という思考を使っていました。これはコードにはありません。
差分2:領域内での消去法
あなたはブロックに注目し、「6だけが1つしか候補がない」と判断してマスを埋めました。これは、複数の数字の候補を同時に考慮して消去法を行う高度なテクニックです。現在のコードは一度に一つの数字しか見ていません。
差分3:複数ユニット連携 (X-Wingなどの思考)
あなたは「3列目と6列目の関係から8列目の1が確定する」という、複数の離れた行や列の関係性を読み解く非常に高度な分析を行いました。これは現在のコードの単純なループ構造では実現できません。
差分4:候補のメモ機能
あなたは「2択の候補をメモする」という戦略を序盤から使っていました。現在のコード(grid.py)は内部的に全候補をビットマスクで保持していますが、それをペアやトリプルの発見に活用するロジック(まさにあなたの解法)がありません。

この差分を元に実装してもらいました。
一発では、やはりうまくいかないものでした。
これは他のファイルも参照してもらうことで解決するバグだったので全体を参照してメソッドを使用している部分も確認しながら実装してということで簡単にクリアできました!

Step 3: 「美学」との戦い - 人間のこだわりをコードに反映させる

僕の開発には大きなテーマがありました。それはいかにして安易な『仮置き(バックトラッキング)』を使わずに、人間らしい論理だけで解き切るかです。

多くのソルバーは、手詰まりになると数字を仮置きし、矛盾したら戻るという力任せの探索を行います。しかし、僕が数独を解く喜びは、盤面を支配する美しい論理を見つけ出す「ひらめき」の瞬間にあります。「仮置きして間違えたら戻る」という方法は、僕の美学が許しませんでした。

この「美学」をAIに理解させ、実装に反映させるのが最も困難な挑戦でした。

結果として、ほとんどの難問も論理だけで解けるようになりましたが、どうしても論理だけでは解けない「最難関問題」も存在しました。それらは、僕自身も無意識に「もしここがXだったら…」と仮説を立て、矛盾を見つけることで解いていたのです。

それは、力任せの全探索とは違う、論理の延長線上にある間接証明としての仮置き。僕の美学が許す、最後の切り札です。最終的に、この「制限付きの仮説検証」ロジックもAIと相談しながらコードに組み込むことで、ソルバーはついに完成しました。

これは最後にどうしても解けない問題は、行き詰まった際は残りのメモで仮置き→矛盾で否定(間接証明)をして解くように変更したいと言って実装してもらいました。
「美学」との間で葛藤もありましたが、これは単なる全探索ではなく、あくまで論理の延長線上にある最後の手段と判断しました。この決断により、ソルバーは世界で一番難しいとされる問題さえも解き明かす、真の完成度に至りました。

完成したソルバーのソースコードは、以下のGitHubリポジトリで公開しています。
コミットなど適当ですが多めにみてください。。
https://github.com/koupro0204/sudoku-solver


完成したソルバーのアルゴリズム解説

全体の流れは、solver/solver.pyrunメソッドに集約されており、AIとの対話を経て洗練された以下のフェーズを繰り返すことで解を探します。

1. 基礎固め:数字を確定させる(Placing Rules)

まず、ソルバーは最も基本的で確実な手筋だけを試します。人間が簡単なマスから埋めていく思考と同じです。

  1. Naked Singleの探索: 盤面全体をスキャンし、「候補が1つしかないマス」を探して確定させます。
  2. Hidden Singleの探索: 次に、「ある行・列・ブロックの中で、特定の数字が1つのマスにしか入れない」状況を探して確定させます。

重要なポイント: このフェーズで1つでもマスが埋まると、ループは最初からやり直されます。1つの数字が確定すると、連鎖的に別のSingleが見つかる可能性があるからです。


2. 論理の飛躍:候補を削減する(Elimination Rules)

フェーズ1で手詰まりになると、ソルバーはまず人間がするように一度立ち止まって盤面をじっと観察し、次の手のヒントを探すスキャン思考に入ります。

そして、そのスキャンで見つけたヒントを元に、より高度な論理(Elimination Rules)を展開して局面を打開します。これらのルールは直接数字を置くのではなく、このマスに、この数字は絶対に入らないという事実を見つけ出し、候補を消していく作業です。

  1. Pointingの適用: ブロック内で候補が1行/1列に並んでいる場合、その行/列の他のブロックからその候補を削除します。
  2. Naked Subsetの適用: これがソルバーの強力な武器です。「k個のセルがk種類の候補を独占している」パターン(Pair, Tripleなど)を見つけ、そのユニット(行・列・ブロック)の他のセルからそれらの候補を削除します。

重要なポイント: このフェーズで1つでも候補が削除されると、ループは【フェーズ1】の最初からやり直されます。候補が減ったことで、新たなSingleが見つかる可能性が生まれるからです。


最終. 論理の限界を超えて:間接証明による探索

全ての論理ルールを尽くしても盤面が進まなくなった場合、最終手段に移行します。これは、僕がこだわった「人間らしい仮説検証」の思考です。

  1. 最も有望なマスを選択(MRVヒューリスティック): 盤面上で最も候補の数字が少ないマスを一つ選びます。これにより、闇雲に試すのではなく、最も効率的に矛盾を発見できる可能性の高い場所から攻めます。
  2. 仮説と検証: 選んだマスの候補の一つを「仮に正しい」と置いてみて、その新しい盤面に対して、【フェーズ1】からの全ての解法プロセスを再帰的に実行します。
  3. 結果の判断:
    • 解決すれば: 仮説は正しかったことになり、成功を返します。
    • 矛盾すれば: 仮説は「間違いだった」ことが証明されます(間接証明)。
  4. バックトラック: 仮説が間違いだと証明されたら、盤面を元に戻し、同じマスの別の候補を試します。

このプロセスにより、ソルバーは論理的な美しさを保ちつつ、あらゆる数独問題を解き明かす能力を手に入れたのです。


AI駆動開発を終えて得られた知見と学び

「自分の解き方は、まだ発見されていないのでは?」

そんな好奇心から始まったプロジェクトは、AIとの対話を通じて、自分の頭の中にある思考の地図を解き明かす冒険になりました。

この経験から得られた、AI駆動開発を成功させるための学びは3つです。

  1. AIは「壁打ち相手」である: AIは完璧な答えをくれる魔法の箱ではありません。自分の考えをぶつけ、フィードバックをもらい、議論を深めることで真価を発揮します。開発の主導権は常に人間が握るべきです。
  2. 「問いの質」が「出力の質」を決める: 曖昧な質問からは、曖昧な答えしか返ってきません。今回のように、動画や具体的なコード、エラーメッセージを提示することで、AIは的確なサポートを提供してくれます。プロンプトエンジニアリングのスキルは非常に重要です。
  3. 自分の思考を言語化する訓練になる: AIに何かをさせようとすると、自分がいかに物事を曖昧に理解しているかに気づかされます。AIに指示を出すプロセス自体が、自身の思考を整理し、解像度を上げる最高のトレーニングになりました。

まさかのすでに存在していた手法ではありましたが、この開発プロセスを通じて、数独の知識を深め、自分の思考を客観的に理解するという、期待以上の収穫がありました。

展望

僕のソルバーは、僕が解ける問題はすべて解けるようになりました。しかし、世界には「Swordfish」「Jellyfish」といった、僕もまだ知らないさらに高度な解法が存在するようです。
このプロジェクトで得た知識と経験を武器に、僕の挑戦は続きます。
最後までお読みいただき、ありがとうございました!


おまけ:僕が思う、数独を一番速く解く方法

友達の解き方を見ていて気づいたのですが、多くの人は「この空いているマスには何が入るだろう?」と考えます。これは、一つのマスに注目して候補を絞っていく方法で、時間がかかりがちです。

僕が提案するのは、視点を逆転させた数字に注目する方法です。

「1は、盤面のどこに入るだろう?」と考えて、1を置ける場所だけを一気に探していくのです。
この方法だと、他の数字のことは一旦忘れていいので、頭のリソースを大幅に節約できます。簡単な問題であれば、1から9までを順番に見ていくだけで、驚くほどスピーディーに解けるはずです。

実はこれこそが、僕のソルバーが最初に実行するNaked SingleやHidden Singleの探索ロジックそのもの。ぜひ、試してみてください!

Discussion