Javascriptを使用して数独を解く方法

出版日: 2024年5月27日

Javascriptを使用して数独を解く方法

背景

以前のブログ記事では、以下のように完全な数独グリッドを作成する方法について書きました。(この数独グリッドはリアルタイムで作成されるため、このページを訪れるたびにグリッドが変わります)

1
5
8
3
2
4
6
7
9
4
9
7
1
8
6
5
2
3
6
3
2
9
5
7
1
8
4
3
1
4
7
6
8
2
9
5
5
8
9
4
1
2
3
6
7
2
7
6
5
3
9
8
4
1
7
2
1
8
9
3
4
5
6
8
4
5
6
7
1
9
3
2
9
6
3
2
4
5
7
1
8

これは続編の投稿であり、このグリッドから数独ゲームを作成するためにいくつかの数字を削除し、それを解くアルゴリズムを開発します。

数独データ生成

数独ゲームを作成して解く最も難しい部分は、完全なグリッドを作成することです。これはすでに解決済みです。グリッドには81個の数字があり、これらの数字をサイズ81の配列に格納します。生成されたデータは以下のようになります:

let grid = [{ "row": 0, "col": 0, "block": "0-0", "value": 1 },

{ "row": 0, "col": 1, "block": "0-0", "value": 5 },

{ "row": 0, "col": 2, "block": "0-0", "value": 8 },

{ "row": 0, "col": 3, "block": "0-1", "value": 3 },

{ "row": 0, "col": 4, "block": "0-1", "value": 2 },

{ "row": 0, "col": 5, "block": "0-1", "value": 4 },

{ "row": 0, "col": 6, "block": "0-2", "value": 6 },

{ "row": 0, "col": 7, "block": "0-2", "value": 7 },

{ "row": 0, "col": 8, "block": "0-2", "value": 9 },

{ "row": 1, "col": 0, "block": "0-0", "value": 4 },

{ "row": 1, "col": 1, "block": "0-0", "value": 9 },

{ "row": 1, "col": 2, "block": "0-0", "value": 7 },

{ "row": 1, "col": 3, "block": "0-1", "value": 1 },

{ "row": 1, "col": 4, "block": "0-1", "value": 8 },

{ "row": 1, "col": 5, "block": "0-1", "value": 6 },

{ "row": 1, "col": 6, "block": "0-2", "value": 5 },

{ "row": 1, "col": 7, "block": "0-2", "value": 2 },

{ "row": 1, "col": 8, "block": "0-2", "value": 3 },

{ "row": 2, "col": 0, "block": "0-0", "value": 6 },

{ "row": 2, "col": 1, "block": "0-0", "value": 3 },

{ "row": 2, "col": 2, "block": "0-0", "value": 2 },

{ "row": 2, "col": 3, "block": "0-1", "value": 9 },

{ "row": 2, "col": 4, "block": "0-1", "value": 5 },

{ "row": 2, "col": 5, "block": "0-1", "value": 7 },

{ "row": 2, "col": 6, "block": "0-2", "value": 1 },

{ "row": 2, "col": 7, "block": "0-2", "value": 8 },

{ "row": 2, "col": 8, "block": "0-2", "value": 4 },

{ "row": 3, "col": 0, "block": "1-0", "value": 3 },

{ "row": 3, "col": 1, "block": "1-0", "value": 1 },

{ "row": 3, "col": 2, "block": "1-0", "value": 4 },

{ "row": 3, "col": 3, "block": "1-1", "value": 7 },

{ "row": 3, "col": 4, "block": "1-1", "value": 6 },

{ "row": 3, "col": 5, "block": "1-1", "value": 8 },

{ "row": 3, "col": 6, "block": "1-2", "value": 2 },

{ "row": 3, "col": 7, "block": "1-2", "value": 9 },

{ "row": 3, "col": 8, "block": "1-2", "value": 5 },

{ "row": 4, "col": 0, "block": "1-0", "value": 5 },

{ "row": 4, "col": 1, "block": "1-0", "value": 8 },

{ "row": 4, "col": 2, "block": "1-0", "value": 9 },

{ "row": 4, "col": 3, "block": "1-1", "value": 4 },

{ "row": 4, "col": 4, "block": "1-1", "value": 1 },

{ "row": 4, "col": 5, "block": "1-1", "value": 2 },

{ "row": 4, "col": 6, "block": "1-2", "value": 3 },

{ "row": 4, "col": 7, "block": "1-2", "value": 6 },

{ "row": 4, "col": 8, "block": "1-2", "value": 7 },

{ "row": 5, "col": 0, "block": "1-0", "value": 2 },

{ "row": 5, "col": 1, "block": "1-0", "value": 7 },

{ "row": 5, "col": 2, "block": "1-0", "value": 6 },

{ "row": 5, "col": 3, "block": "1-1", "value": 5 },

{ "row": 5, "col": 4, "block": "1-1", "value": 3 },

{ "row": 5, "col": 5, "block": "1-1", "value": 9 },

{ "row": 5, "col": 6, "block": "1-2", "value": 8 },

{ "row": 5, "col": 7, "block": "1-2", "value": 4 },

{ "row": 5, "col": 8, "block": "1-2", "value": 1 },

{ "row": 6, "col": 0, "block": "2-0", "value": 7 },

{ "row": 6, "col": 1, "block": "2-0", "value": 2 },

{ "row": 6, "col": 2, "block": "2-0", "value": 1 },

{ "row": 6, "col": 3, "block": "2-1", "value": 8 },

{ "row": 6, "col": 4, "block": "2-1", "value": 9 },

{ "row": 6, "col": 5, "block": "2-1", "value": 3 },

{ "row": 6, "col": 6, "block": "2-2", "value": 4 },

{ "row": 6, "col": 7, "block": "2-2", "value": 5 },

{ "row": 6, "col": 8, "block": "2-2", "value": 6 },

{ "row": 7, "col": 0, "block": "2-0", "value": 8 },

{ "row": 7, "col": 1, "block": "2-0", "value": 4 },

{ "row": 7, "col": 2, "block": "2-0", "value": 5 },

{ "row": 7, "col": 3, "block": "2-1", "value": 6 },

{ "row": 7, "col": 4, "block": "2-1", "value": 7 },

{ "row": 7, "col": 5, "block": "2-1", "value": 1 },

{ "row": 7, "col": 6, "block": "2-2", "value": 9 },

{ "row": 7, "col": 7, "block": "2-2", "value": 3 },

{ "row": 7, "col": 8, "block": "2-2", "value": 2 },

{ "row": 8, "col": 0, "block": "2-0", "value": 9 },

{ "row": 8, "col": 1, "block": "2-0", "value": 6 },

{ "row": 8, "col": 2, "block": "2-0", "value": 3 },

{ "row": 8, "col": 3, "block": "2-1", "value": 2 },

{ "row": 8, "col": 4, "block": "2-1", "value": 4 },

{ "row": 8, "col": 5, "block": "2-1", "value": 5 },

{ "row": 8, "col": 6, "block": "2-2", "value": 7 },

{ "row": 8, "col": 7, "block": "2-2", "value": 1 },

{ "row": 8, "col": 8, "block": "2-2", "value": 8 }]

数字を削除して数独ゲームを作成する

今度はランダムにいくつかの数字を削除して、新しいグリッドを作成しましょう。これが私たちの数独ボードになります。

以下のフィールドで削除するセルの数を選択できます。10〜50セルの間で削除できます。

1
5
8
3
2
4
6
7
9
7
1
8
3
6
3
2
5
7
1
8
4
3
4
7
6
2
9
5
8
4
3
6
2
7
6
5
3
9
4
1
7
1
3
4
5
6
8
4
5
6
7
1
9
3
2
9
3
2
5
7
8

セルを削除するコードは以下にあります:

let dropCount = 20;

function drop(dC) {
	let indicesToBeDropped = Array.from({ length: grid.length }, (_, index) => index)
		.map((value) => ({ value, sort: Math.random() }))
		.sort((a, b) => a.sort - b.sort)
		.map(({ value }) => value)
		.slice(0, dC);

	let nG = grid.map((number, index) => {
		let newNumber = { ...number };
		if (indicesToBeDropped.includes(index)) {
			newNumber.value = '';
			newNumber.empty = true;
		}
		return newNumber;
	});

	return nG;
}

let newGrid = drop(dropCount);

数独ゲームを解く

ここが興味深い部分です。ボードを解いて、完全な状態に戻します。できれば。

ゲームが実際に解けるかどうかは確かではないので、解いてみましょう。データ構造がこのゲームを解くのを非常に簡単にします。各数字には関連する行、列、およびブロック番号があります。同じ行、列、ブロックを共有するデータをフィルタリングし、空のセルに適合する数字を見つけるだけです。

以下はグリッドを解くコードです。ロジックはコード内で説明されていますが、質問がある場合はコメントで教えてください。

// 以前のグリッドをコピーして、修正しないようにする
// $ 記号は、svelteで新しいグリッドを以前のグリッドに基づいてリアクティブにします
$: gameGrid = newGrid.map((n) => {
	return { ...n };
});

// 各番号が埋められるときにスリープするために
// ユーザーがアルゴリズムがステップバイステップで動作しているのを見えるように
let timeout;
function sleep(time) {
	return new Promise((resolve) => {
		timeout = setTimeout(resolve, time);
		return timeout;
	});
}

// 1000回の試行を超えた場合、試行を停止するためのカウントを保持します
let count = 0;

// 解決状況を追跡してメッセージを表示できるようにする
let solved = false;

// ソルバ関数
async function solve() {
	solved = false;

	// 値が ""(つまり空のセル)であるセルがなくなるまで反復を続けます(最大1000回の試行)
	while (gameGrid.filter((n) => n.value === '').length !== 0 && count < 1000) {
		count++;
		// すべてのセルをループします
		for (let cellIndex = 0; cellIndex < gameGrid.length; cellIndex++) {
			let cell = gameGrid[cellIndex];
			// 空のセルの場合
			if (cell.value === '') {
				// このセルと同じ行、列、およびブロックにある数字を見つけます
				let rowNumbers = gameGrid.filter((i) => i.row === cell.row).map((i) => i.value);
				let colNumbers = gameGrid.filter((i) => i.col === cell.col).map((i) => i.value);
				let blockNumbers = gameGrid.filter((i) => i.block === cell.block).map((i) => i.value);

				// 行、列、およびブロックに存在しないこのセルに入れられる数字を見つけます
				let rowPossibleNumbers = numbers.filter((i) => !rowNumbers.includes(i));
				let colPossibleNumbers = numbers.filter((i) => !colNumbers.includes(i));
				let blockPossibleNumbers = numbers.filter((i) => !blockNumbers.includes(i));

				// このセルに対するすべての可能な数字の交差を見つけます
				let possibleNumbers = [rowPossibleNumbers, colPossibleNumbers, blockPossibleNumbers];
				possibleNumbers = possibleNumbers.reduce((a, b) => a.filter((c) => b.includes(c)));

				// 可能な数字が1つだけの場合、正しい数字を見つけました!
				// それを埋めてスリープします
				if (possibleNumbers.length === 1) {
					cell.value = possibleNumbers[0];

					// gameGridを更新してHTML DOMに表示します
					gameGrid = [...gameGrid];
					await sleep(100);
					break;
				}
			}
		}
	}
	clearTimeout(timeout);
	solved = true;
}

アルゴリズムが期待通りに動作するかどうかを確認するには、以下のボタンをクリックしてください:

1
5
8
3
2
4
6
7
9
7
1
8
3
6
3
2
5
7
1
8
4
3
4
7
6
2
9
5
8
4
3
6
2
7
6
5
3
9
4
1
7
1
3
4
5
6
8
4
5
6
7
1
9
3
2
9
3
2
5
7
8

試行回数: 0

上記の入力フィールドで削除するセルの数を制御できることを試してみてください。50セルを削除するに近づくにつれて、1000回の試行内でボードを解くのが非常に難しくなることがわかるでしょう。

結論

このブログ記事では、完全なボードから数独ゲームを作成する方法を考案しました。完全なボード作成のトピックは以前のブログで取り上げました。

ユーザー入力に基づいて特定の数のセルを削除した後、行、列、ブロックの条件に対して数字が適切かどうかを確認することでセルを埋めるブルートフォースアプローチを使用しました。

質問がある場合は、以下のコメントに残してください。

それでは、ハッキングを楽しんでください :)

このブログは英語からChatGPTによって翻訳されました。不明な点がある場合は、お問い合わせページからご連絡ください。

コメントを残す

コメント

その他のブログ

SvelteとJavaScriptを使用してシンプルで動的なツールチップを作成する

2024/06/19

SvelteとJavaScriptを使用してシンプルで動的なツールチップを作成する

JavaScriptSvelteTooltip動的シンプルツールチップフロントエンド
JavaScriptを用いて東京都のインタラクティブな地図を作成する

2024/06/17

JavaScriptを用いて東京都のインタラクティブな地図を作成する

SvelteSVGJavaScriptTailwindインタラクティブな地図東京市区町村23区地図
Matplotlibで日本語文字化けを解決できる簡単な方法

2024/06/14

Matplotlibで日本語文字化けを解決できる簡単な方法

MatplotlibグラフチャートPython日本語文字化け問題バグ
書評 | トーキング・トゥ・ストレンジャーズ 「よく知らない人」について私たちが知っておくべきこと by マルコム・グラッドウェル

2024/06/13

書評 | トーキング・トゥ・ストレンジャーズ 「よく知らない人」について私たちが知っておくべきこと by マルコム・グラッドウェル

書評トーキング・トゥ・ストレンジャーズ「よく知らない人」について私たちが知っておくべきことマルコム・グラッドウェル
日本語で最もよく使われる3000字の漢字

2024/06/07

日本語で最もよく使われる3000字の漢字

3000よく使う準漢字使用回数漢字日本語漢字リスト漢字普及率日本語能力試験独学勉強単語
VSCodeでRegexを使用してReplaceする方法

2024/06/07

VSCodeでRegexを使用してReplaceする方法

VSCodeRegex検索置き換える条件付き置換FindReplaceConditional Replace
SvelteではReadable Storeを使用するな

2024/06/06

SvelteではReadable Storeを使用するな

SvelteReadableWritableステート管理ストアStore速度メモリファイルサイズ
GzipとPakoでデータを圧縮してWebサイトのローディング速度を上げる方法

2024/06/05

GzipとPakoでデータを圧縮してWebサイトのローディング速度を上げる方法

Gzip圧縮PakoWebサイトローディング速度SvelteKit
JavaScriptを使用してWebページ上でマウスが指している単語を特定する

2024/05/31

JavaScriptを使用してWebページ上でマウスが指している単語を特定する

JavascriptマウスPointerHoverWeb開発
SvelteとSVGを用いてインタラクティブな地図を作成する

2024/05/29

SvelteとSVGを用いてインタラクティブな地図を作成する

SvelteSVGインタラクティブな地図フロントエンド
書評 | Originals 誰もが「人と違うこと」ができる時代 by アダム・グラント & シェリル・サンドバーグ

2024/05/28

書評 | Originals 誰もが「人と違うこと」ができる時代 by アダム・グラント & シェリル・サンドバーグ

書評Originals誰もが「人と違うこと」ができる時代アダム・グラント & シェリル・サンドバーグ
ウェブサイトへのトラフィックを1か月で10倍に増やした方法

2024/05/26

ウェブサイトへのトラフィックを1か月で10倍に増やした方法

ウェブサイトへのトラフィック増加クリックインプレッションGoogle Search Console
人生はサイクリングに似ている

2024/05/24

人生はサイクリングに似ている

サイクリング人生哲学成功
JavaScriptでバックトラッキング・アルゴリズムを用いて完全な数独グリッドを生成する

2024/05/19

JavaScriptでバックトラッキング・アルゴリズムを用いて完全な数独グリッドを生成する

数独バックトラッキング・アルゴリズム完全なグリッドJavaScript
Tailwindが素晴らしい理由とWeb開発をいかに楽にするか

2024/05/16

Tailwindが素晴らしい理由とWeb開発をいかに楽にするか

Tailwind素晴らしいフロントエンドWeb開発
PythonとGitフックを使用してサイトマップを自動的に生成する

2024/05/15

PythonとGitフックを使用してサイトマップを自動的に生成する

GitフックPythonサイトマップSvelteKit
書評 | Range (レンジ) 知識の「幅」が最強の武器になる by デイビッド・エプスタイン

2024/05/14

書評 | Range (レンジ) 知識の「幅」が最強の武器になる by デイビッド・エプスタイン

書評Range (レンジ)David Epstein (デイビッド・エプスタイン)知識の「幅」が最強の武器になる
SvelteとSvelteKitはなんですか?

2024/05/13

SvelteとSvelteKitはなんですか?

SvelteSvelteKitFront-endVite
SvelteKitで国際化(多言語化)

2024/05/12

SvelteKitで国際化(多言語化)

国際化多言語SvelteKitI18N
SvelteでCachingを用いてDeploy時間を短縮する方法

2024/05/11

SvelteでCachingを用いてDeploy時間を短縮する方法

SvelteEnhanced ImageCachingDeploy Time
SvelteとIntersection Oberverによるレイジーローディング

2024/05/10

SvelteとIntersection Oberverによるレイジーローディング

レイジーローディングウェブサイト速度の最適化SvelteIntersection Observer
遺伝的アルゴリズムで最適な株式ポートフォリオを作る方法

2024/05/10

遺伝的アルゴリズムで最適な株式ポートフォリオを作る方法

株式書状ポートフォリ最適化遺伝的アルゴリズムPython
Pythonを用いてShapeFileをSVGに変換できる方法

2024/05/09

Pythonを用いてShapeFileをSVGに変換できる方法

ShapeFileSVGPythonGeoJSON
Svelteの反応性:変数、バインディング、およびキー関数

2024/05/08

Svelteの反応性:変数、バインディング、およびキー関数

Svelte反応性バインディングキー関数
書評 | 孫子の兵法

2024/05/07

書評 | 孫子の兵法

書評The Art Of War (兵法)Sun Tzu (孫子)Thomas Cleary
スペシャリストは終了。ゼネラリスト万歳!

2024/05/06

スペシャリストは終了。ゼネラリスト万歳!

専門家ジェネラリストパラダイムシフトソフトウエア・エンジニアリング
トルコ人の有権者の投票行動をPythonでの分析

2024/05/03

トルコ人の有権者の投票行動をPythonでの分析

トルコ投票者年齢分析国家投票有権者行動分析
Seleniumを用いてトルコ投票データベースを作る方法

2024/05/01

Seleniumを用いてトルコ投票データベースを作る方法

PythonSeleniumWeb Scrapingトルコ国家投票
SvelteとTailwindを使用してInfinite Scrollできる方法

2024/04/30

SvelteとTailwindを使用してInfinite Scrollできる方法

SvelteTailwindInfinite ScrollFront-end
1年間以内で日本語を駆使できるようになるための方法

2024/04/29

1年間以内で日本語を駆使できるようになるための方法

日本語短時間言語学習日本語能力試験ビジネス日本語
SvelteとTailwindを用いたWebサイトテンプレート

2024/04/25

SvelteとTailwindを用いたWebサイトテンプレート

Web開発フロントエンドSvelteTailwind
怠惰なエンジニアとひどいデザイン

2024/01/29

怠惰なエンジニアとひどいデザイン

怠け者エンジニア質の悪い製品StarbucksSBI証券
偉大さについて

2024/01/28

偉大さについて

雄大さ人生の意味満足できる人生目的
MacBook で PDF を PNG に変換する

2024/01/28

MacBook で PDF を PNG に変換する

PDFPNGMacBookAutomator
2023年振り返り:24冊の読んだ本のまとめ

2023/12/31

2023年振り返り:24冊の読んだ本のまとめ

読書 2023振り返り
Python PILを使用して写真コラージュを作成する方法

2023/12/30

Python PILを使用して写真コラージュを作成する方法

PythonPIL画像処理コラージュ
ウェブサイトの訪問者のデバイスとブラウザを検出する方法

2024/01/09

ウェブサイトの訪問者のデバイスとブラウザを検出する方法

Javascript端末検知ブラウザ検知Website分析
ChatGPT回答の解析

2024/01/19

ChatGPT回答の解析

ChatGPT大規模言語モデル機械学習生成AI