diff --git a/001game/algorithms/backtracking.js b/001game/algorithms/backtracking.js
new file mode 100644
index 0000000000..43e181f535
--- /dev/null
+++ b/001game/algorithms/backtracking.js
@@ -0,0 +1,555 @@
+const BacktrackingAlgorithms = [
+ {
+ id: 'n-queens',
+ name: 'N皇后问题',
+ description: '在 N×N 的棋盘上放置 N 个皇后,使其不能互相攻击(同行、同列、同对角线不能有两个皇后)。',
+ timeComplexity: 'O(N!)',
+ spaceComplexity: 'O(N)',
+ difficulty: 2,
+ init: function() {
+ this.n = 6;
+ this.board = Array(this.n).fill(null).map(() => Array(this.n).fill(0));
+ this.renderBoard();
+ },
+ renderBoard: function() {
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+ ${this.board.flat().map((_, i) => {
+ const row = Math.floor(i / this.n);
+ const col = i % this.n;
+ const isLight = (row + col) % 2 === 0;
+ return `
`;
+ }).join('')}
+
+ `;
+ },
+ run: function() {
+ const n = this.n;
+ const board = Array(n).fill(null).map(() => Array(n).fill(0));
+ const solutions = [];
+
+ const isSafe = (row, col) => {
+ for (let i = 0; i < row; i++) {
+ if (board[i][col]) return false;
+ }
+ for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
+ if (board[i][j]) return false;
+ }
+ for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
+ if (board[i][j]) return false;
+ }
+ return true;
+ };
+
+ const solve = (row) => {
+ if (row === n) {
+ solutions.push(board.map(r => [...r]));
+ return true;
+ }
+
+ for (let col = 0; col < n; col++) {
+ const currentRow = row;
+ const currentCol = col;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('.queen-cell').forEach(cell => {
+ cell.classList.remove('has-queen', 'conflict');
+ });
+ for (let r = 0; r < currentRow; r++) {
+ for (let c = 0; c < n; c++) {
+ if (board[r][c]) {
+ document.getElementById(`queen-${r}-${c}`).classList.add('has-queen');
+ document.getElementById(`queen-${r}-${c}`).textContent = '♛';
+ }
+ }
+ }
+ document.getElementById(`queen-${currentRow}-${currentCol}`).classList.add('has-queen');
+ document.getElementById('step-info').textContent =
+ `尝试在第${currentRow}行第${currentCol}列放置皇后`;
+ }
+ });
+
+ if (isSafe(row, col)) {
+ board[row][col] = 1;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`queen-${currentRow}-${currentCol}`).textContent = '♛';
+ document.getElementById('step-info').textContent =
+ `在(${currentRow}, ${currentCol})放置皇后成功`;
+ }
+ });
+
+ if (solve(row + 1)) {
+ return true;
+ }
+
+ board[row][col] = 0;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`queen-${currentRow}-${currentCol}`).textContent = '';
+ document.getElementById(`queen-${currentRow}-${currentCol}`).classList.add('conflict');
+ document.getElementById('step-info').textContent =
+ `回溯: 移除(${currentRow}, ${currentCol})的皇后`;
+ }
+ });
+ } else {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`queen-${currentRow}-${currentCol}`).classList.add('conflict');
+ document.getElementById('step-info').textContent =
+ `位置(${currentRow}, ${currentCol})不安全,尝试下一个位置`;
+ }
+ });
+ }
+ }
+
+ return false;
+ };
+
+ solve(0);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `找到解决方案!成功放置${n}个皇后`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'N皇后问题的时间复杂度是多少?',
+ options: ['O(N²)', 'O(N³)', 'O(N!)', 'O(2^N)'],
+ correct: 2
+ }
+ },
+ {
+ id: 'sudoku',
+ name: '数独求解',
+ description: '填充9×9的数独网格,使每行、每列、每个3×3宫格内的数字1-9各出现一次。',
+ timeComplexity: 'O(9^(n*n))',
+ spaceComplexity: 'O(n²)',
+ difficulty: 2,
+ init: function() {
+ this.board = [
+ [5, 3, 0, 0, 7, 0, 0, 0, 0],
+ [6, 0, 0, 1, 9, 5, 0, 0, 0],
+ [0, 9, 8, 0, 0, 0, 0, 6, 0],
+ [8, 0, 0, 0, 6, 0, 0, 0, 3],
+ [4, 0, 0, 8, 0, 3, 0, 0, 1],
+ [7, 0, 0, 0, 2, 0, 0, 0, 6],
+ [0, 6, 0, 0, 0, 0, 2, 8, 0],
+ [0, 0, 0, 4, 1, 9, 0, 0, 5],
+ [0, 0, 0, 0, 8, 0, 0, 7, 9]
+ ];
+ this.renderSudoku();
+ },
+ renderSudoku: function() {
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+ ${this.board.flat().map((val, i) => {
+ const row = Math.floor(i / 9);
+ const col = i % 9;
+ return `
${val || ''}
`;
+ }).join('')}
+
+ `;
+ },
+ run: function() {
+ const board = this.board.map(row => [...row]);
+
+ const isValid = (row, col, num) => {
+ for (let i = 0; i < 9; i++) {
+ if (board[row][i] === num || board[i][col] === num) return false;
+ }
+ const boxRow = Math.floor(row / 3) * 3;
+ const boxCol = Math.floor(col / 3) * 3;
+ for (let i = 0; i < 3; i++) {
+ for (let j = 0; j < 3; j++) {
+ if (board[boxRow + i][boxCol + j] === num) return false;
+ }
+ }
+ return true;
+ };
+
+ const solve = () => {
+ for (let row = 0; row < 9; row++) {
+ for (let col = 0; col < 9; col++) {
+ if (board[row][col] === 0) {
+ for (let num = 1; num <= 9; num++) {
+ const currentRow = row;
+ const currentCol = col;
+ const currentNum = num;
+
+ if (isValid(row, col, num)) {
+ board[row][col] = num;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const cell = document.getElementById(`sudoku-${currentRow}-${currentCol}`);
+ cell.textContent = currentNum;
+ cell.classList.add('solving');
+ document.getElementById('step-info').textContent =
+ `尝试在(${currentRow}, ${currentCol})放置${currentNum}`;
+ }
+ });
+
+ if (solve()) return true;
+
+ board[row][col] = 0;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const cell = document.getElementById(`sudoku-${currentRow}-${currentCol}`);
+ cell.textContent = '';
+ cell.classList.remove('solving');
+ document.getElementById('step-info').textContent =
+ `回溯: 清除(${currentRow}, ${currentCol})`;
+ }
+ });
+ }
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ solve();
+
+ for (let row = 0; row < 9; row++) {
+ for (let col = 0; col < 9; col++) {
+ const currentRow = row;
+ const currentCol = col;
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const cell = document.getElementById(`sudoku-${currentRow}-${currentCol}`);
+ cell.classList.remove('solving');
+ cell.classList.add('solved');
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent = '数独求解完成!';
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '数独求解使用的算法思想是什么?',
+ options: ['贪心算法', '动态规划', '回溯算法', '分治算法'],
+ correct: 2
+ }
+ },
+ {
+ id: 'maze-solver',
+ name: '迷宫求解',
+ description: '给定一个迷宫,找到从起点到终点的路径。使用回溯算法探索所有可能的路径。',
+ timeComplexity: 'O(4^(m×n))',
+ spaceComplexity: 'O(m×n)',
+ difficulty: 2,
+ init: function() {
+ this.maze = [
+ [0, 1, 0, 0, 0, 0, 0, 0],
+ [0, 1, 0, 1, 1, 1, 1, 0],
+ [0, 0, 0, 0, 0, 0, 1, 0],
+ [1, 1, 1, 1, 1, 0, 1, 0],
+ [0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 1, 1, 1, 1, 1, 1, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1, 1, 0]
+ ];
+ this.start = { row: 0, col: 0 };
+ this.end = { row: 7, col: 7 };
+ this.renderMaze();
+ },
+ renderMaze: function() {
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+ ${this.maze.flat().map((val, i) => {
+ const row = Math.floor(i / 8);
+ const col = i % 8;
+ let className = val ? 'wall' : '';
+ if (row === this.start.row && col === this.start.col) className = 'start';
+ if (row === this.end.row && col === this.end.col) className = 'end';
+ return `
`;
+ }).join('')}
+
+ `;
+ },
+ run: function() {
+ const maze = this.maze.map(row => [...row]);
+ const start = this.start;
+ const end = this.end;
+ const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
+ const path = [];
+
+ const solve = (row, col) => {
+ if (row === end.row && col === end.col) {
+ path.push([row, col]);
+ return true;
+ }
+
+ if (row < 0 || row >= 8 || col < 0 || col >= 8 || maze[row][col] === 1 || maze[row][col] === 2) {
+ return false;
+ }
+
+ maze[row][col] = 2;
+ path.push([row, col]);
+
+ const currentRow = row;
+ const currentCol = col;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ if (!(currentRow === start.row && currentCol === start.col)) {
+ const cell = document.getElementById(`maze-${currentRow}-${currentCol}`);
+ cell.classList.add('current');
+ setTimeout(() => cell.classList.replace('current', 'path'), 100);
+ }
+ document.getElementById('step-info').textContent =
+ `探索位置 (${currentRow}, ${currentCol})`;
+ }
+ });
+
+ for (const [dr, dc] of directions) {
+ if (solve(row + dr, col + dc)) {
+ return true;
+ }
+ }
+
+ path.pop();
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const cell = document.getElementById(`maze-${currentRow}-${currentCol}`);
+ cell.classList.remove('path', 'current');
+ document.getElementById('step-info').textContent =
+ `回溯: 离开位置 (${currentRow}, ${currentCol})`;
+ }
+ });
+
+ return false;
+ };
+
+ solve(start.row, start.col);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `找到路径!路径长度: ${path.length} 步`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '迷宫求解问题中,回溯算法的特点是什么?',
+ options: ['找到一条路径就停止', '找到所有可能的路径', '只找最短路径', '随机选择路径'],
+ correct: 0
+ }
+ },
+ {
+ id: 'generate-parentheses',
+ name: '括号生成',
+ description: '给定 n 对括号,生成所有有效的括号组合。',
+ timeComplexity: 'O(4^n / √n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 2,
+ init: function() {
+ this.n = 3;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
生成 ${this.n} 对括号的所有有效组合
+
+
+
+ `;
+ },
+ run: function() {
+ const n = this.n;
+ const results = [];
+ let currentStr = '';
+ let depth = 0;
+
+ const container = document.getElementById('parentheses-tree');
+ const resultsContainer = document.getElementById('parentheses-results');
+
+ const generate = (open, close, str, level) => {
+ if (str.length === n * 2) {
+ results.push(str);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.style.cssText = 'background: var(--success-color); padding: 10px 15px; border-radius: 8px; animation: fadeIn 0.3s;';
+ div.textContent = str;
+ resultsContainer.appendChild(div);
+ document.getElementById('step-info').textContent = `找到有效组合: ${str}`;
+ }
+ });
+ return;
+ }
+
+ const currentStr = str;
+
+ if (open < n) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ container.innerHTML = `${' '.repeat(level)}${currentStr}(`;
+ document.getElementById('step-info').textContent =
+ `添加左括号: ${currentStr}(`;
+ }
+ });
+ generate(open + 1, close, str + '(', level + 1);
+ }
+
+ if (close < open) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ container.innerHTML = `${' '.repeat(level)}${currentStr})`;
+ document.getElementById('step-info').textContent =
+ `添加右括号: ${currentStr})`;
+ }
+ });
+ generate(open, close + 1, str + ')', level + 1);
+ }
+ };
+
+ generate(0, 0, '', 0);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `共生成 ${results.length} 个有效括号组合: ${results.join(', ')}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '生成有效括号时,什么条件下可以添加右括号?',
+ options: ['任何时候都可以', '右括号数量小于n', '右括号数量小于左括号数量', '左括号数量大于0'],
+ correct: 2
+ }
+ },
+ {
+ id: 'permutations',
+ name: '全排列',
+ description: '给定一组不重复的数字,返回所有可能的排列组合。',
+ timeComplexity: 'O(n! × n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 2,
+ init: function() {
+ this.nums = [1, 2, 3];
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
生成 [${this.nums.join(', ')}] 的所有排列
+
+
+
+ `;
+ },
+ run: function() {
+ const nums = [...this.nums];
+ const results = [];
+ const used = Array(nums.length).fill(false);
+ const current = [];
+
+ const permute = () => {
+ if (current.length === nums.length) {
+ results.push([...current]);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const container = document.getElementById('perm-results');
+ const div = document.createElement('div');
+ div.style.cssText = 'background: var(--success-color); padding: 10px 15px; border-radius: 8px;';
+ div.textContent = `[${current.join(', ')}]`;
+ container.appendChild(div);
+ document.getElementById('step-info').textContent = `找到排列: [${current.join(', ')}]`;
+ }
+ });
+ return;
+ }
+
+ for (let i = 0; i < nums.length; i++) {
+ if (used[i]) continue;
+
+ used[i] = true;
+ current.push(nums[i]);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('perm-current').textContent =
+ `当前: [${current.join(', ')}]`;
+ document.getElementById('step-info').textContent =
+ `选择 ${nums[i]}, 当前排列: [${current.join(', ')}]`;
+ }
+ });
+
+ permute();
+
+ current.pop();
+ used[i] = false;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `回溯: 移除 ${nums[i]}`;
+ }
+ });
+ }
+ };
+
+ permute();
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `共生成 ${results.length} 个排列`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'n个不同元素的排列数量是多少?',
+ options: ['n', 'n²', 'n!', '2^n'],
+ correct: 2
+ }
+ }
+];
diff --git a/001game/algorithms/cipher.js b/001game/algorithms/cipher.js
new file mode 100644
index 0000000000..9e0f7cb526
--- /dev/null
+++ b/001game/algorithms/cipher.js
@@ -0,0 +1,451 @@
+const CipherAlgorithms = [
+ {
+ id: 'caesar-cipher',
+ name: '凯撒密码',
+ description: '凯撒密码是一种替换加密技术,将字母表中的每个字母替换为固定位置后的字母。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 1,
+ init: function() {
+ this.text = 'HELLO WORLD';
+ this.shift = 3;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
原文: ${this.text}
+
位移: ${this.shift}
+
+ ${'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(c => `
+
${c}
+ `).join('')}
+
+
+
+ `;
+ },
+ run: function() {
+ const text = this.text;
+ const shift = this.shift;
+ const container = document.getElementById('cipher-result');
+
+ let result = '';
+
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+ const currentI = i;
+
+ if (char === ' ') {
+ result += ' ';
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.textContent = '␣';
+ div.style.background = 'var(--bg-lighter)';
+ container.appendChild(div);
+ document.getElementById('step-info').textContent = '空格保持不变';
+ }
+ });
+ } else {
+ const code = char.charCodeAt(0);
+ const shifted = ((code - 65 + shift) % 26) + 65;
+ const newChar = String.fromCharCode(shifted);
+ result += newChar;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.innerHTML = `${char}
${newChar}`;
+ div.style.background = 'var(--primary-color)';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `${char} → ${newChar} (位移${shift}位)`;
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `加密结果: ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '凯撒密码有多少种可能的密钥?',
+ options: ['1种', '25种', '26种', '无限种'],
+ correct: 1
+ }
+ },
+ {
+ id: 'morse-code',
+ name: '摩尔斯电码',
+ description: '摩尔斯电码使用点(.)和划(-)的组合来表示字母和数字。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 1,
+ init: function() {
+ this.text = 'SOS';
+ this.morseCode = {
+ 'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
+ 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
+ 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
+ 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
+ 'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---',
+ '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
+ '8': '---..', '9': '----.'
+ };
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
将 "${this.text}" 转换为摩尔斯电码
+
+
+
+ `;
+ },
+ run: function() {
+ const text = this.text.toUpperCase();
+ const morseCode = this.morseCode;
+ const container = document.getElementById('morse-result');
+ const display = document.getElementById('morse-display');
+
+ let fullMorse = '';
+
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+ const code = morseCode[char];
+ fullMorse += code + ' ';
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.style.cssText = 'background: var(--bg-lighter); padding: 15px 20px; border-radius: 8px; text-align: center;';
+ div.innerHTML = `
+ ${char}
+ ${code}
+ `;
+ container.appendChild(div);
+
+ display.textContent = fullMorse.trim();
+ document.getElementById('step-info').textContent =
+ `${char} → ${code}`;
+ }
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `摩尔斯电码: ${fullMorse.trim()}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'SOS 在摩尔斯电码中是什么?',
+ options: ['... --- ...', '.- .- .-', '-.. -.. -..', '. . .'],
+ correct: 0
+ }
+ },
+ {
+ id: 'vigenere-cipher',
+ name: '维吉尼亚密码',
+ description: '维吉尼亚密码使用关键词对明文进行多表替换加密,比凯撒密码更安全。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 2,
+ init: function() {
+ this.text = 'HELLO';
+ this.key = 'KEY';
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
原文: ${this.text}
+
密钥: ${this.key}
+
+
+
+ `;
+
+ this.renderTable();
+ },
+ renderTable: function() {
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ document.getElementById('vigenere-table').innerHTML = `
+
+
+ |
+ ${alphabet.split('').map(c =>
+ `${c} | `
+ ).join('')}
+
+ ${alphabet.split('').map((row, i) => `
+
+ | ${row} |
+ ${alphabet.split('').map((_, j) => {
+ const shifted = (i + j) % 26;
+ return `${alphabet[shifted]} | `;
+ }).join('')}
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const text = this.text.toUpperCase();
+ const key = this.key.toUpperCase();
+ const container = document.getElementById('vigenere-result');
+
+ let result = '';
+
+ for (let i = 0; i < text.length; i++) {
+ const textChar = text[i];
+ const keyChar = key[i % key.length];
+
+ const textCode = textChar.charCodeAt(0) - 65;
+ const keyCode = keyChar.charCodeAt(0) - 65;
+ const encryptedCode = (textCode + keyCode) % 26;
+ const encryptedChar = String.fromCharCode(encryptedCode + 65);
+
+ result += encryptedChar;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.style.cssText = 'background: var(--bg-lighter); padding: 15px; border-radius: 8px; text-align: center;';
+ div.innerHTML = `
+
+
+
+
+
+
=
+
+
密文
+
${encryptedChar}
+
+
+ `;
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `${textChar}(${textCode}) + ${keyChar}(${keyCode}) = ${encryptedChar}(${encryptedCode})`;
+ }
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `加密结果: ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '维吉尼亚密码相比凯撒密码的优势是什么?',
+ options: ['更快', '使用多表替换更安全', '更简单', '不需要密钥'],
+ correct: 1
+ }
+ },
+ {
+ id: 'rot13',
+ name: 'ROT13 加密',
+ description: 'ROT13 是凯撒密码的特例,位移固定为13位。加密和解密使用相同的操作。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 1,
+ init: function() {
+ this.text = 'HELLO WORLD';
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
原文: ${this.text}
+
+
+
原始字母表
+
+ ${'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(c =>
+ `
${c}
`
+ ).join('')}
+
+
+
⇄
+
+
ROT13 字母表
+
+ ${'NOPQRSTUVWXYZABCDEFGHIJKLM'.split('').map(c =>
+ `
${c}
`
+ ).join('')}
+
+
+
+
+
+ `;
+ },
+ run: function() {
+ const text = this.text;
+ const container = document.getElementById('rot13-result');
+
+ let result = '';
+
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+
+ if (char === ' ') {
+ result += ' ';
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.textContent = '␣';
+ div.style.background = 'var(--bg-lighter)';
+ container.appendChild(div);
+ document.getElementById('step-info').textContent = '空格保持不变';
+ }
+ });
+ } else {
+ const code = char.charCodeAt(0);
+ const shifted = ((code - 65 + 13) % 26) + 65;
+ const newChar = String.fromCharCode(shifted);
+ result += newChar;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.innerHTML = `${char}
${newChar}`;
+ div.style.background = 'var(--primary-color)';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `${char} → ${newChar}`;
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `加密结果: ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'ROT13 的一个有趣特性是什么?',
+ options: ['只能加密不能解密', '加密和解密是同一操作', '需要两个密钥', '只能处理数字'],
+ correct: 1
+ }
+ },
+ {
+ id: 'atbash-cipher',
+ name: '埃特巴什密码',
+ description: '埃特巴什密码将字母表反转:A↔Z, B↔Y, C↔X,以此类推。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 1,
+ init: function() {
+ this.text = 'HELLO';
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ this.reversed = alphabet.split('').reverse().join('');
+
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
原文: ${this.text}
+
+
+
原始
+
+ ${alphabet.split('').map(c =>
+ `
${c}
`
+ ).join('')}
+
+
+
⇄
+
+
反转
+
+ ${this.reversed.split('').map(c =>
+ `
${c}
`
+ ).join('')}
+
+
+
+
+
+ `;
+ },
+ run: function() {
+ const text = this.text;
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ const reversed = this.reversed;
+ const container = document.getElementById('atbash-result');
+
+ let result = '';
+
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+ const index = alphabet.indexOf(char);
+ const newChar = reversed[index];
+ result += newChar;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.innerHTML = `${char}
${newChar}`;
+ div.style.background = 'var(--primary-color)';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `${char} (位置${index}) → ${newChar}`;
+ }
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `加密结果: ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '埃特巴什密码的加密和解密有什么关系?',
+ options: ['完全不同的操作', '同一操作', '需要密钥', '只能加密'],
+ correct: 1
+ }
+ }
+];
diff --git a/001game/algorithms/dynamic-programming.js b/001game/algorithms/dynamic-programming.js
new file mode 100644
index 0000000000..bc6a92cc4a
--- /dev/null
+++ b/001game/algorithms/dynamic-programming.js
@@ -0,0 +1,465 @@
+const DPAlgorithms = [
+ {
+ id: 'fibonacci',
+ name: '斐波那契数列',
+ description: '斐波那契数列定义为 F(n) = F(n-1) + F(n-2),使用动态规划可以避免重复计算。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n) 或 O(1)',
+ difficulty: 1,
+ init: function() {
+ this.n = 15;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+ ${Array(this.n + 1).fill(0).map((_, i) => `
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const n = this.n;
+ const dp = [0, 1];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const box = document.getElementById('fib-0');
+ box.classList.add('active');
+ box.querySelector('.value').textContent = '0';
+ document.getElementById('step-info').textContent = 'F(0) = 0';
+ }
+ });
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const box = document.getElementById('fib-1');
+ box.classList.add('active');
+ box.querySelector('.value').textContent = '1';
+ document.getElementById('step-info').textContent = 'F(1) = 1';
+ }
+ });
+
+ for (let i = 2; i <= n; i++) {
+ dp[i] = dp[i - 1] + dp[i - 2];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('.fib-box').forEach(b => b.classList.remove('active'));
+
+ const prev1 = document.getElementById(`fib-${i - 1}`);
+ const prev2 = document.getElementById(`fib-${i - 2}`);
+ const current = document.getElementById(`fib-${i}`);
+
+ prev1.style.background = 'var(--warning-color)';
+ prev2.style.background = 'var(--secondary-color)';
+ current.classList.add('active');
+ current.querySelector('.value').textContent = dp[i];
+
+ document.getElementById('step-info').textContent =
+ `F(${i}) = F(${i - 1}) + F(${i - 2}) = ${dp[i - 1]} + ${dp[i - 2]} = ${dp[i]}`;
+ }
+ });
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '斐波那契数列的递归解法时间复杂度是多少?',
+ options: ['O(n)', 'O(n²)', 'O(2^n)', 'O(log n)'],
+ correct: 2
+ }
+ },
+ {
+ id: 'climbing-stairs',
+ name: '爬楼梯',
+ description: '有 n 级楼梯,每次可以爬 1 或 2 级,问有多少种方法爬到顶?',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.n = 10;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
+ ${Array(this.n + 1).fill(0).map((_, i) => `
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const n = this.n;
+ const dp = [1, 1];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const box = document.getElementById('stair-0');
+ box.classList.add('active');
+ box.querySelector('.value').textContent = '1';
+ document.getElementById('step-info').textContent = '第0级: 1种方法(起点)';
+ }
+ });
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const box = document.getElementById('stair-1');
+ box.classList.add('active');
+ box.querySelector('.value').textContent = '1';
+ document.getElementById('step-info').textContent = '第1级: 1种方法(爬1级)';
+ }
+ });
+
+ for (let i = 2; i <= n; i++) {
+ dp[i] = dp[i - 1] + dp[i - 2];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('.fib-box').forEach(b => b.classList.remove('active'));
+
+ const prev1 = document.getElementById(`stair-${i - 1}`);
+ const prev2 = document.getElementById(`stair-${i - 2}`);
+ const current = document.getElementById(`stair-${i}`);
+
+ prev1.style.background = 'var(--warning-color)';
+ prev2.style.background = 'var(--secondary-color)';
+ current.classList.add('active');
+ current.querySelector('.value').textContent = dp[i];
+
+ document.getElementById('step-info').textContent =
+ `第${i}级: 从第${i - 1}级爬1级 + 从第${i - 2}级爬2级 = ${dp[i - 1]} + ${dp[i - 2]} = ${dp[i]}种方法`;
+ }
+ });
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '爬楼梯问题与哪个经典问题等价?',
+ options: ['背包问题', '斐波那契数列', '最长公共子序列', '编辑距离'],
+ correct: 1
+ }
+ },
+ {
+ id: 'coin-change',
+ name: '零钱兑换',
+ description: '给定不同面额的硬币和一个总金额,计算可以凑成总金额的最少硬币数量。',
+ timeComplexity: 'O(amount × n)',
+ spaceComplexity: 'O(amount)',
+ difficulty: 2,
+ init: function() {
+ this.coins = [1, 2, 5];
+ this.amount = 11;
+
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
硬币面额: ${this.coins.join(', ')} | 目标金额: ${this.amount}
+
+
+ ${Array(this.amount + 1).fill(0).map((_, i) => `
+
+
金额${i}
+
${i === 0 ? '0' : '∞'}
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const coins = this.coins;
+ const amount = this.amount;
+ const dp = Array(amount + 1).fill(Infinity);
+ dp[0] = 0;
+
+ for (let i = 1; i <= amount; i++) {
+ for (const coin of coins) {
+ if (coin <= i && dp[i - coin] !== Infinity) {
+ const newCount = dp[i - coin] + 1;
+ if (newCount < dp[i]) {
+ dp[i] = newCount;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('.fib-box').forEach(b => b.classList.remove('active'));
+
+ const current = document.getElementById(`coin-${i}`);
+ const prev = document.getElementById(`coin-${i - coin}`);
+
+ prev.style.background = 'var(--secondary-color)';
+ current.classList.add('active');
+ current.querySelector('.value').textContent = dp[i];
+
+ document.getElementById('step-info').textContent =
+ `金额${i}: 使用${coin}面额硬币, 从金额${i - coin}转移, 需要${dp[i]}枚硬币`;
+ }
+ });
+ }
+ }
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const result = dp[amount] === Infinity ? '无法兑换' : `${dp[amount]}枚硬币`;
+ document.getElementById('step-info').textContent = `最少需要 ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '零钱兑换问题属于哪类动态规划?',
+ options: ['线性DP', '区间DP', '背包问题', '树形DP'],
+ correct: 2
+ }
+ },
+ {
+ id: 'knapsack',
+ name: '0-1 背包问题',
+ description: '有 n 个物品,每个物品有重量和价值,在背包容量有限的情况下,选择物品使总价值最大。',
+ timeComplexity: 'O(n × W)',
+ spaceComplexity: 'O(n × W)',
+ difficulty: 2,
+ init: function() {
+ this.items = [
+ { weight: 2, value: 3 },
+ { weight: 3, value: 4 },
+ { weight: 4, value: 5 },
+ { weight: 5, value: 8 },
+ { weight: 9, value: 10 }
+ ];
+ this.capacity = 15;
+
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
背包容量: ${this.capacity}
+
+ ${this.items.map((item, i) => `
+
+
物品${i + 1}
+
+ 重量:${item.weight} 价值:${item.value}
+
+
+ `).join('')}
+
+
+
+ `;
+
+ this.renderDpTable();
+ },
+ renderDpTable: function() {
+ const n = this.items.length;
+ const w = this.capacity;
+
+ document.getElementById('dp-table').innerHTML = `
+
+
+ | 物品\\容量 |
+ ${Array(w + 1).fill(0).map((_, i) =>
+ `${i} | `
+ ).join('')}
+
+ ${Array(n + 1).fill(0).map((_, i) => `
+
+ |
+ ${i === 0 ? '无' : `物品${i}`}
+ |
+ ${Array(w + 1).fill(0).map((_, j) =>
+ `0 | `
+ ).join('')}
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const items = this.items;
+ const n = items.length;
+ const W = this.capacity;
+ const dp = Array(n + 1).fill(null).map(() => Array(W + 1).fill(0));
+
+ for (let i = 1; i <= n; i++) {
+ const { weight, value } = items[i - 1];
+
+ for (let w = 0; w <= W; w++) {
+ if (weight <= w) {
+ dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weight] + value);
+ } else {
+ dp[i][w] = dp[i - 1][w];
+ }
+
+ const currentI = i;
+ const currentW = w;
+ const currentVal = dp[i][w];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('#dp-table td').forEach(td => {
+ td.style.background = '';
+ });
+
+ const cell = document.getElementById(`dp-${currentI}-${currentW}`);
+ cell.textContent = currentVal;
+ cell.style.background = 'var(--primary-color)';
+
+ if (weight <= currentW && dp[currentI - 1][currentW - weight] + value === currentVal) {
+ const prevCell = document.getElementById(`dp-${currentI - 1}-${currentW - weight}`);
+ if (prevCell) prevCell.style.background = 'var(--secondary-color)';
+ }
+
+ document.getElementById('step-info').textContent =
+ `考虑物品${currentI}(重${weight}, 值${value}), 容量${currentW}: 最大价值${currentVal}`;
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `最大价值: ${dp[n][W]}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '0-1背包问题中,每个物品可以选择几次?',
+ options: ['0次', '1次', '无限次', '取决于容量'],
+ correct: 1
+ }
+ },
+ {
+ id: 'lcs',
+ name: '最长公共子序列',
+ description: '给定两个字符串,找出它们的最长公共子序列的长度。',
+ timeComplexity: 'O(m × n)',
+ spaceComplexity: 'O(m × n)',
+ difficulty: 2,
+ init: function() {
+ this.str1 = 'ABCBDAB';
+ this.str2 = 'BDCABA';
+
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
字符串1: ${this.str1}
+
字符串2: ${this.str2}
+
+
+ `;
+
+ this.renderLcsTable();
+ },
+ renderLcsTable: function() {
+ const m = this.str1.length;
+ const n = this.str2.length;
+
+ document.getElementById('lcs-table').innerHTML = `
+
+
+ |
+ |
+ ${this.str2.split('').map(c =>
+ `${c} | `
+ ).join('')}
+
+
+ |
+ ${Array(n + 1).fill(0).map((_, j) =>
+ `0 | `
+ ).join('')}
+
+ ${this.str1.split('').map((c, i) => `
+
+ | ${c} |
+ ${Array(n + 1).fill(0).map((_, j) =>
+ `0 | `
+ ).join('')}
+
+ `).join('')}
+
+ `;
+ },
+ run: function() {
+ const str1 = this.str1;
+ const str2 = this.str2;
+ const m = str1.length;
+ const n = str2.length;
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
+
+ for (let i = 1; i <= m; i++) {
+ for (let j = 1; j <= n; j++) {
+ if (str1[i - 1] === str2[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+
+ const currentI = i;
+ const currentJ = j;
+ const currentVal = dp[i][j];
+ const match = str1[i - 1] === str2[j - 1];
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('#lcs-table td').forEach(td => {
+ td.style.background = '';
+ });
+
+ const cell = document.getElementById(`lcs-${currentI}-${currentJ}`);
+ cell.textContent = currentVal;
+ cell.style.background = match ? 'var(--success-color)' : 'var(--primary-color)';
+
+ if (match) {
+ const prevCell = document.getElementById(`lcs-${currentI - 1}-${currentJ - 1}`);
+ if (prevCell) prevCell.style.background = 'var(--secondary-color)';
+ }
+
+ document.getElementById('step-info').textContent =
+ `比较 '${str1[currentI - 1]}' 和 '${str2[currentJ - 1]}': ${match ? '匹配!' : '不匹配'}, LCS长度: ${currentVal}`;
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `最长公共子序列长度: ${dp[m][n]}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '最长公共子序列与最长公共子串的区别是什么?',
+ options: ['完全相同', '子序列可以不连续,子串必须连续', '子序列必须连续,子串可以不连续', '没有区别'],
+ correct: 1
+ }
+ }
+];
diff --git a/001game/algorithms/graph.js b/001game/algorithms/graph.js
new file mode 100644
index 0000000000..860e498621
--- /dev/null
+++ b/001game/algorithms/graph.js
@@ -0,0 +1,405 @@
+const GraphAlgorithms = [
+ {
+ id: 'bfs',
+ name: '广度优先搜索 (BFS)',
+ description: 'BFS 从起点开始,逐层向外扩展搜索,使用队列存储待访问节点。常用于寻找最短路径。',
+ timeComplexity: 'O(V + E)',
+ spaceComplexity: 'O(V)',
+ difficulty: 2,
+ init: function() {
+ this.grid = createGrid(10, 10);
+ this.start = { row: 0, col: 0 };
+ this.end = { row: 9, col: 9 };
+
+ for (let i = 0; i < 20; i++) {
+ const row = Math.floor(Math.random() * 10);
+ const col = Math.floor(Math.random() * 10);
+ if (!(row === 0 && col === 0) && !(row === 9 && col === 9)) {
+ this.grid[row][col] = 1;
+ updateGridCell(row, col, 'wall');
+ }
+ }
+
+ updateGridCell(0, 0, 'start', 'S');
+ updateGridCell(9, 9, 'end', 'E');
+ },
+ run: function() {
+ const grid = this.grid.map(row => [...row]);
+ const start = this.start;
+ const end = this.end;
+ const rows = 10, cols = 10;
+ const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
+
+ const queue = [[start.row, start.col, [[start.row, start.col]]]];
+ const visited = new Set();
+ visited.add(`${start.row},${start.col}`);
+
+ while (queue.length > 0) {
+ const [row, col, path] = queue.shift();
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ if (!(row === start.row && col === start.col) &&
+ !(row === end.row && col === end.col)) {
+ updateGridCell(row, col, 'visited');
+ }
+ document.getElementById('step-info').textContent =
+ `访问节点 (${row}, ${col}), 队列长度: ${queue.length}`;
+ }
+ });
+
+ if (row === end.row && col === end.col) {
+ for (const [r, c] of path) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ if (!(r === start.row && c === start.col) &&
+ !(r === end.row && c === end.col)) {
+ updateGridCell(r, c, 'path');
+ }
+ }
+ });
+ }
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `找到路径!路径长度: ${path.length}`;
+ }
+ });
+ break;
+ }
+
+ for (const [dr, dc] of directions) {
+ const newRow = row + dr;
+ const newCol = col + dc;
+ const key = `${newRow},${newCol}`;
+
+ if (newRow >= 0 && newRow < rows &&
+ newCol >= 0 && newCol < cols &&
+ !visited.has(key) && grid[newRow][newCol] !== 1) {
+ visited.add(key);
+ queue.push([newRow, newCol, [...path, [newRow, newCol]]]);
+ }
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'BFS 使用什么数据结构来存储待访问节点?',
+ options: ['栈', '队列', '堆', '数组'],
+ correct: 1
+ }
+ },
+ {
+ id: 'dfs',
+ name: '深度优先搜索 (DFS)',
+ description: 'DFS 从起点开始,沿着一条路径尽可能深入,直到无法继续再回溯。使用栈或递归实现。',
+ timeComplexity: 'O(V + E)',
+ spaceComplexity: 'O(V)',
+ difficulty: 2,
+ init: function() {
+ this.grid = createGrid(10, 10);
+ this.start = { row: 0, col: 0 };
+ this.end = { row: 9, col: 9 };
+
+ for (let i = 0; i < 20; i++) {
+ const row = Math.floor(Math.random() * 10);
+ const col = Math.floor(Math.random() * 10);
+ if (!(row === 0 && col === 0) && !(row === 9 && col === 9)) {
+ this.grid[row][col] = 1;
+ updateGridCell(row, col, 'wall');
+ }
+ }
+
+ updateGridCell(0, 0, 'start', 'S');
+ updateGridCell(9, 9, 'end', 'E');
+ },
+ run: function() {
+ const grid = this.grid.map(row => [...row]);
+ const start = this.start;
+ const end = this.end;
+ const rows = 10, cols = 10;
+ const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
+
+ const stack = [[start.row, start.col, [[start.row, start.col]]]];
+ const visited = new Set();
+
+ while (stack.length > 0) {
+ const [row, col, path] = stack.pop();
+ const key = `${row},${col}`;
+
+ if (visited.has(key)) continue;
+ visited.add(key);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ if (!(row === start.row && col === start.col) &&
+ !(row === end.row && col === end.col)) {
+ updateGridCell(row, col, 'visited');
+ }
+ document.getElementById('step-info').textContent =
+ `访问节点 (${row}, ${col}), 栈深度: ${stack.length}`;
+ }
+ });
+
+ if (row === end.row && col === end.col) {
+ for (const [r, c] of path) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ if (!(r === start.row && c === start.col) &&
+ !(r === end.row && c === end.col)) {
+ updateGridCell(r, c, 'path');
+ }
+ }
+ });
+ }
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `找到路径!路径长度: ${path.length}`;
+ }
+ });
+ break;
+ }
+
+ for (const [dr, dc] of directions) {
+ const newRow = row + dr;
+ const newCol = col + dc;
+ const newKey = `${newRow},${newCol}`;
+
+ if (newRow >= 0 && newRow < rows &&
+ newCol >= 0 && newCol < cols &&
+ !visited.has(newKey) && grid[newRow][newCol] !== 1) {
+ stack.push([newRow, newCol, [...path, [newRow, newCol]]]);
+ }
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'DFS 使用什么数据结构来存储待访问节点?',
+ options: ['栈', '队列', '堆', '链表'],
+ correct: 0
+ }
+ },
+ {
+ id: 'dijkstra',
+ name: 'Dijkstra 最短路径',
+ description: 'Dijkstra 算法用于计算带权图中单源最短路径,使用优先队列选择当前最短路径节点。',
+ timeComplexity: 'O((V + E) log V)',
+ spaceComplexity: 'O(V)',
+ difficulty: 3,
+ init: function() {
+ const container = document.getElementById('visualization-area');
+ this.nodes = [
+ { id: 0, x: 100, y: 100 },
+ { id: 1, x: 250, y: 50 },
+ { id: 2, x: 400, y: 100 },
+ { id: 3, x: 150, y: 200 },
+ { id: 4, x: 300, y: 200 },
+ { id: 5, x: 450, y: 200 },
+ { id: 6, x: 200, y: 300 },
+ { id: 7, x: 400, y: 300 }
+ ];
+
+ this.edges = [
+ { from: 0, to: 1, weight: 4 },
+ { from: 0, to: 3, weight: 2 },
+ { from: 1, to: 2, weight: 3 },
+ { from: 1, to: 4, weight: 5 },
+ { from: 2, to: 5, weight: 2 },
+ { from: 3, to: 4, weight: 1 },
+ { from: 3, to: 6, weight: 4 },
+ { from: 4, to: 5, weight: 3 },
+ { from: 4, to: 6, weight: 2 },
+ { from: 5, to: 7, weight: 1 },
+ { from: 6, to: 7, weight: 3 }
+ ];
+
+ container.innerHTML = `
+
+
+ ${this.nodes.map(n => `
+
+ ${n.id}
+
+ `).join('')}
+
+ `;
+
+ document.getElementById('step-info').textContent = '从节点 0 开始寻找到其他节点的最短路径';
+ },
+ run: function() {
+ const n = this.nodes.length;
+ const adj = Array(n).fill(null).map(() => []);
+
+ for (const edge of this.edges) {
+ adj[edge.from].push([edge.to, edge.weight]);
+ adj[edge.to].push([edge.from, edge.weight]);
+ }
+
+ const dist = Array(n).fill(Infinity);
+ const visited = new Set();
+ dist[0] = 0;
+
+ for (let i = 0; i < n; i++) {
+ let minDist = Infinity;
+ let minNode = -1;
+
+ for (let j = 0; j < n; j++) {
+ if (!visited.has(j) && dist[j] < minDist) {
+ minDist = dist[j];
+ minNode = j;
+ }
+ }
+
+ if (minNode === -1) break;
+ visited.add(minNode);
+
+ const currentDist = dist[minNode];
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const node = document.getElementById(`node-${minNode}`);
+ node.classList.add('visited');
+ document.getElementById('step-info').textContent =
+ `选择节点 ${minNode}, 当前距离: ${currentDist}`;
+ }
+ });
+
+ for (const [neighbor, weight] of adj[minNode]) {
+ if (!visited.has(neighbor)) {
+ const newDist = dist[minNode] + weight;
+ if (newDist < dist[neighbor]) {
+ dist[neighbor] = newDist;
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `更新节点 ${neighbor} 的距离: ${newDist}`;
+ }
+ });
+ }
+ }
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `最短距离: ${dist.map((d, i) => `到${i}: ${d}`).join(', ')}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: 'Dijkstra 算法可以处理负权边吗?',
+ options: ['可以', '不可以', '只能处理部分情况', '取决于实现'],
+ correct: 1
+ }
+ },
+ {
+ id: 'number-of-islands',
+ name: '岛屿数量',
+ description: '给定一个由 \'1\'(陆地)和 \'0\'(水)组成的二维网格,计算岛屿的数量。岛屿被水包围,由相邻的陆地连接而成。',
+ timeComplexity: 'O(m × n)',
+ spaceComplexity: 'O(m × n)',
+ difficulty: 2,
+ init: function() {
+ this.grid = [];
+ for (let i = 0; i < 8; i++) {
+ this.grid[i] = [];
+ for (let j = 0; j < 8; j++) {
+ this.grid[i][j] = Math.random() > 0.5 ? 1 : 0;
+ }
+ }
+
+ createGrid(8, 8, this.grid);
+
+ const cells = document.querySelectorAll('.grid-cell');
+ cells.forEach((cell, i) => {
+ const row = Math.floor(i / 8);
+ const col = i % 8;
+ if (this.grid[row][col] === 1) {
+ cell.classList.add('wall');
+ cell.textContent = '';
+ }
+ });
+ },
+ run: function() {
+ const grid = this.grid.map(row => [...row]);
+ const rows = 8, cols = 8;
+ const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
+ let islandCount = 0;
+
+ const dfs = (row, col, islandId) => {
+ if (row < 0 || row >= rows || col < 0 || col >= cols || grid[row][col] !== 1) {
+ return;
+ }
+
+ grid[row][col] = 2;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ updateGridCell(row, col, 'visited', islandId);
+ document.getElementById('step-info').textContent =
+ `标记岛屿 ${islandId} 的陆地 (${row}, ${col})`;
+ }
+ });
+
+ for (const [dr, dc] of directions) {
+ dfs(row + dr, col + dc, islandId);
+ }
+ };
+
+ for (let i = 0; i < rows; i++) {
+ for (let j = 0; j < cols; j++) {
+ if (grid[i][j] === 1) {
+ islandCount++;
+ dfs(i, j, islandCount);
+ }
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `总共发现 ${islandCount} 个岛屿!`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '解决岛屿数量问题通常使用什么算法?',
+ options: ['动态规划', '深度优先搜索', '贪心算法', '分治算法'],
+ correct: 1
+ }
+ }
+];
diff --git a/001game/algorithms/math.js b/001game/algorithms/math.js
new file mode 100644
index 0000000000..3069aeaca4
--- /dev/null
+++ b/001game/algorithms/math.js
@@ -0,0 +1,430 @@
+const MathAlgorithms = [
+ {
+ id: 'sieve-of-eratosthenes',
+ name: '埃拉托斯特尼筛法',
+ description: '用于找出小于等于 n 的所有素数。通过标记非素数的方式筛选。',
+ timeComplexity: 'O(n log log n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 1,
+ init: function() {
+ this.n = 30;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
找出小于等于 ${this.n} 的所有素数
+
+
+ ${Array(this.n + 1).fill(0).map((_, i) => `
+
+ ${i}
+
+ `).join('')}
+
+ `;
+
+ document.getElementById('prime-0').style.background = 'var(--danger-color)';
+ document.getElementById('prime-1').style.background = 'var(--danger-color)';
+ },
+ run: function() {
+ const n = this.n;
+ const isPrime = Array(n + 1).fill(true);
+ isPrime[0] = isPrime[1] = false;
+
+ for (let i = 2; i * i <= n; i++) {
+ if (isPrime[i]) {
+ const currentI = i;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`prime-${currentI}`).style.background = 'var(--secondary-color)';
+ document.getElementById('step-info').textContent =
+ `发现素数 ${currentI},开始筛除其倍数`;
+ }
+ });
+
+ for (let j = i * i; j <= n; j += i) {
+ if (isPrime[j]) {
+ isPrime[j] = false;
+ const currentJ = j;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`prime-${currentJ}`).style.background = 'var(--danger-color)';
+ document.getElementById('step-info').textContent =
+ `标记 ${currentJ} 为非素数 (${currentJ} = ${currentI} × ${currentJ / currentI})`;
+ }
+ });
+ }
+ }
+ }
+ }
+
+ const primes = [];
+ for (let i = 2; i <= n; i++) {
+ if (isPrime[i]) {
+ primes.push(i);
+ const currentI = i;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById(`prime-${currentI}`).style.background = 'var(--success-color)';
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `素数列表: [${primes.join(', ')}],共 ${primes.length} 个`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '埃拉托斯特尼筛法的时间复杂度是多少?',
+ options: ['O(n)', 'O(n log n)', 'O(n log log n)', 'O(n²)'],
+ correct: 2
+ }
+ },
+ {
+ id: 'gcd',
+ name: '最大公约数 (GCD)',
+ description: '使用欧几里得算法计算两个数的最大公约数。',
+ timeComplexity: 'O(log(min(a, b)))',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.a = 48;
+ this.b = 18;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
计算 GCD(${this.a}, ${this.b})
+
+
+ `;
+ },
+ run: function() {
+ let a = this.a;
+ let b = this.b;
+ let step = 0;
+
+ while (b !== 0) {
+ const currentA = a;
+ const currentB = b;
+ const quotient = Math.floor(a / b);
+ const remainder = a % b;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const container = document.getElementById('gcd-steps');
+ const div = document.createElement('div');
+ div.innerHTML = `步骤 ${step + 1}: GCD(${currentA}, ${currentB}) = GCD(${currentB}, ${remainder})
+
+ ${currentA} = ${currentB} × ${quotient} + ${remainder}
+ `;
+ div.style.cssText = 'background: var(--bg-lighter); padding: 15px; border-radius: 8px; margin: 5px;';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `${currentA} ÷ ${currentB} = ${quotient} 余 ${remainder}`;
+ }
+ });
+
+ a = b;
+ b = remainder;
+ step++;
+ }
+
+ const result = a;
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const container = document.getElementById('gcd-steps');
+ const div = document.createElement('div');
+ div.innerHTML = `GCD(${this.a}, ${this.b}) = ${result}`;
+ div.style.cssText = 'background: var(--success-color); padding: 15px; border-radius: 8px; margin: 5px;';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `最大公约数是 ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '欧几里得算法基于什么原理?',
+ options: ['GCD(a, b) = GCD(a-b, b)', 'GCD(a, b) = GCD(b, a mod b)', 'GCD(a, b) = a × b', 'GCD(a, b) = a + b'],
+ correct: 1
+ }
+ },
+ {
+ id: 'factorial',
+ name: '阶乘计算',
+ description: '计算 n! = n × (n-1) × ... × 2 × 1。展示递归和迭代两种方式。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(n) 递归 / O(1) 迭代',
+ difficulty: 1,
+ init: function() {
+ this.n = 7;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
计算 ${this.n}!
+
+ ${Array(this.n).fill(0).map((_, i) => `
+
+ `).join('')}
+
+
+
+ `;
+ },
+ run: function() {
+ const n = this.n;
+ let result = 1;
+
+ for (let i = 1; i <= n; i++) {
+ result *= i;
+ const currentI = i;
+ const currentResult = result;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.querySelectorAll('.array-item').forEach(item => {
+ item.style.background = '';
+ });
+
+ const cell = document.getElementById(`fact-${currentI}`);
+ cell.style.background = 'var(--primary-color)';
+ document.getElementById(`fact-val-${currentI}`).textContent = currentResult;
+
+ const prev = currentI > 1 ? document.getElementById(`fact-${currentI - 1}`) : null;
+ if (prev) prev.style.background = 'var(--secondary-color)';
+
+ document.getElementById('step-info').textContent =
+ `${currentI}! = ${currentI} × ${currentResult / currentI} = ${currentResult}`;
+ }
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('fact-result').innerHTML =
+ `${n}! = ${result}`;
+ document.getElementById('step-info').textContent =
+ `${n}! = ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '阶乘函数增长速度如何?',
+ options: ['线性增长', '指数增长', '比指数增长更快', '对数增长'],
+ correct: 2
+ }
+ },
+ {
+ id: 'power',
+ name: '快速幂运算',
+ description: '使用二进制分解快速计算 x^n,将时间复杂度从 O(n) 降低到 O(log n)。',
+ timeComplexity: 'O(log n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 2,
+ init: function() {
+ this.base = 2;
+ this.exponent = 13;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
计算 ${this.base}^${this.exponent}
+
+
+ `;
+ },
+ run: function() {
+ const base = this.base;
+ let exp = this.exponent;
+ let result = 1;
+ let currentBase = base;
+ let step = 0;
+
+ const container = document.getElementById('power-steps');
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ container.innerHTML = `初始: result = 1, base = ${base}, exp = ${exp}
`;
+ document.getElementById('step-info').textContent =
+ `开始计算 ${base}^${exp}`;
+ }
+ });
+
+ while (exp > 0) {
+ const currentExp = exp;
+ const currentResult = result;
+ const currentBaseVal = currentBase;
+ const isOdd = exp % 2 === 1;
+
+ if (isOdd) {
+ result *= currentBase;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.innerHTML = `步骤 ${step + 1}: exp = ${currentExp} (奇数)
+ result = ${currentResult} × ${currentBaseVal} = ${result}`;
+ div.style.cssText = 'background: var(--warning-color); padding: 10px; border-radius: 8px; margin: 5px;';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `exp 是奇数,result = ${result}`;
+ }
+ });
+ } else {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.innerHTML = `步骤 ${step + 1}: exp = ${currentExp} (偶数)
+ result 保持不变 = ${currentResult}`;
+ div.style.cssText = 'background: var(--bg-lighter); padding: 10px; border-radius: 8px; margin: 5px;';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `exp 是偶数,result 保持不变`;
+ }
+ });
+ }
+
+ currentBase *= currentBase;
+ exp = Math.floor(exp / 2);
+ step++;
+
+ if (exp > 0) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.innerHTML = `base = ${currentBaseVal}² = ${currentBase}, exp = ${currentExp}/2 = ${exp}`;
+ div.style.cssText = 'color: var(--text-secondary); padding: 5px;';
+ container.appendChild(div);
+ }
+ });
+ }
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.innerHTML = `${base}^${this.exponent} = ${result}`;
+ div.style.cssText = 'background: var(--success-color); padding: 15px; border-radius: 8px; margin: 10px;';
+ container.appendChild(div);
+
+ document.getElementById('step-info').textContent =
+ `结果: ${base}^${this.exponent} = ${result}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '快速幂算法的时间复杂度是多少?',
+ options: ['O(n)', 'O(log n)', 'O(n²)', 'O(1)'],
+ correct: 1
+ }
+ },
+ {
+ id: 'collatz',
+ name: '考拉兹猜想',
+ description: '从任意正整数开始,如果是偶数则除以2,如果是奇数则乘3加1,最终会回到1。',
+ timeComplexity: '未知',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.n = 27;
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+
考拉兹序列 (从 ${this.n} 开始)
+
+
+ `;
+ },
+ run: function() {
+ let n = this.n;
+ const sequence = [n];
+ const container = document.getElementById('collatz-sequence');
+
+ while (n !== 1) {
+ const currentN = n;
+
+ if (n % 2 === 0) {
+ n = n / 2;
+ } else {
+ n = 3 * n + 1;
+ }
+
+ sequence.push(n);
+
+ const nextN = n;
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const div = document.createElement('div');
+ div.className = 'array-item';
+ div.style.cssText = 'animation: fadeIn 0.3s;';
+ div.textContent = nextN;
+
+ if (nextN === 1) {
+ div.style.background = 'var(--success-color)';
+ } else if (nextN % 2 === 0) {
+ div.style.background = 'var(--secondary-color)';
+ } else {
+ div.style.background = 'var(--warning-color)';
+ }
+
+ container.appendChild(div);
+
+ const operation = currentN % 2 === 0
+ ? `${currentN} ÷ 2 = ${nextN}`
+ : `${currentN} × 3 + 1 = ${nextN}`;
+ document.getElementById('step-info').textContent = operation;
+ }
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `序列长度: ${sequence.length}, 最大值: ${Math.max(...sequence)}`;
+ }
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '考拉兹猜想是否已被证明?',
+ options: ['已证明成立', '已证明不成立', '尚未被证明', '已被证伪'],
+ correct: 2
+ }
+ }
+];
diff --git a/001game/algorithms/searching.js b/001game/algorithms/searching.js
new file mode 100644
index 0000000000..2a665784a7
--- /dev/null
+++ b/001game/algorithms/searching.js
@@ -0,0 +1,274 @@
+const SearchingAlgorithms = [
+ {
+ id: 'linear-search',
+ name: '线性搜索',
+ description: '线性搜索从数组的第一个元素开始,逐个检查每个元素,直到找到目标值或遍历完整个数组。',
+ timeComplexity: 'O(n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.array = generateRandomArray(10, 50);
+ this.target = this.array[Math.floor(Math.random() * this.array.length)];
+ createArrayDisplay(this.array);
+ document.getElementById('step-info').textContent = `查找目标: ${this.target}`;
+ },
+ run: function() {
+ const arr = this.array;
+ const target = this.target;
+
+ for (let i = 0; i < arr.length; i++) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching', 'found'));
+ items[i].classList.add('searching');
+ document.getElementById('step-info').textContent = `检查位置 ${i}: ${arr[i]} ${arr[i] === target ? '= 目标!' : '≠ 目标'}`;
+ }
+ });
+
+ if (arr[i] === target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items[i].classList.remove('searching');
+ items[i].classList.add('found');
+ document.getElementById('step-info').textContent = `找到目标 ${target} 在位置 ${i}!`;
+ }
+ });
+ break;
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '线性搜索最坏情况下需要比较多少次?',
+ options: ['1次', 'n/2次', 'n次', 'log n次'],
+ correct: 2
+ }
+ },
+ {
+ id: 'binary-search',
+ name: '二分搜索',
+ description: '二分搜索在有序数组中查找目标值,每次将搜索范围缩小一半。',
+ timeComplexity: 'O(log n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.array = Array.from({ length: 15 }, (_, i) => (i + 1) * 3);
+ this.target = this.array[Math.floor(Math.random() * this.array.length)];
+ createArrayDisplay(this.array);
+ document.getElementById('step-info').textContent = `查找目标: ${this.target}`;
+ },
+ run: function() {
+ const arr = this.array;
+ const target = this.target;
+ let left = 0, right = arr.length - 1;
+
+ while (left <= right) {
+ const mid = Math.floor((left + right) / 2);
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching', 'found'));
+ for (let i = left; i <= right; i++) {
+ items[i].classList.add('highlight');
+ }
+ items[mid].classList.remove('highlight');
+ items[mid].classList.add('searching');
+ document.getElementById('step-info').textContent =
+ `搜索范围: [${left}, ${right}], 中间位置: ${mid}, 值: ${arr[mid]}`;
+ }
+ });
+
+ if (arr[mid] === target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching'));
+ items[mid].classList.add('found');
+ document.getElementById('step-info').textContent = `找到目标 ${target} 在位置 ${mid}!`;
+ }
+ });
+ break;
+ } else if (arr[mid] < target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `${arr[mid]} < ${target}, 在右半部分继续搜索`;
+ }
+ });
+ left = mid + 1;
+ } else {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `${arr[mid]} > ${target}, 在左半部分继续搜索`;
+ }
+ });
+ right = mid - 1;
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '二分搜索的前提条件是什么?',
+ options: ['数组必须有序', '数组长度为偶数', '数组元素唯一', '数组元素为整数'],
+ correct: 0
+ }
+ },
+ {
+ id: 'jump-search',
+ name: '跳跃搜索',
+ description: '跳跃搜索在有序数组中按固定步长跳跃查找,找到范围后再进行线性搜索。',
+ timeComplexity: 'O(√n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 2,
+ init: function() {
+ this.array = Array.from({ length: 20 }, (_, i) => (i + 1) * 2);
+ this.target = this.array[Math.floor(Math.random() * this.array.length)];
+ createArrayDisplay(this.array);
+ document.getElementById('step-info').textContent = `查找目标: ${this.target}`;
+ },
+ run: function() {
+ const arr = this.array;
+ const target = this.target;
+ const n = arr.length;
+ const step = Math.floor(Math.sqrt(n));
+ let prev = 0;
+ let curr = step;
+
+ while (curr < n && arr[curr] < target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching'));
+ items[curr].classList.add('searching');
+ document.getElementById('step-info').textContent =
+ `跳跃到位置 ${curr}: ${arr[curr]} < ${target}, 继续跳跃`;
+ }
+ });
+ prev = curr;
+ curr += step;
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ document.getElementById('step-info').textContent =
+ `目标可能在范围 [${prev}, ${Math.min(curr, n - 1)}] 内,开始线性搜索`;
+ }
+ });
+
+ for (let i = prev; i < Math.min(curr, n); i++) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('searching'));
+ items[i].classList.add('searching');
+ document.getElementById('step-info').textContent =
+ `检查位置 ${i}: ${arr[i]}`;
+ }
+ });
+
+ if (arr[i] === target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items[i].classList.remove('searching');
+ items[i].classList.add('found');
+ document.getElementById('step-info').textContent =
+ `找到目标 ${target} 在位置 ${i}!`;
+ }
+ });
+ break;
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '跳跃搜索的最优步长是多少?',
+ options: ['n/2', '√n', 'log n', 'n/4'],
+ correct: 1
+ }
+ },
+ {
+ id: 'interpolation-search',
+ name: '插值搜索',
+ description: '插值搜索是二分搜索的改进版,根据目标值的大小估计其位置,适用于均匀分布的数据。',
+ timeComplexity: 'O(log log n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 2,
+ init: function() {
+ this.array = Array.from({ length: 20 }, (_, i) => (i + 1) * 5);
+ this.target = this.array[Math.floor(Math.random() * this.array.length)];
+ createArrayDisplay(this.array);
+ document.getElementById('step-info').textContent = `查找目标: ${this.target}`;
+ },
+ run: function() {
+ const arr = this.array;
+ const target = this.target;
+ let left = 0, right = arr.length - 1;
+
+ while (left <= right && target >= arr[left] && target <= arr[right]) {
+ const pos = left + Math.floor(
+ ((target - arr[left]) * (right - left)) / (arr[right] - arr[left])
+ );
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching', 'found'));
+ for (let i = left; i <= right; i++) {
+ items[i].classList.add('highlight');
+ }
+ items[pos].classList.remove('highlight');
+ items[pos].classList.add('searching');
+ document.getElementById('step-info').textContent =
+ `插值估计位置: ${pos}, 值: ${arr[pos]}`;
+ }
+ });
+
+ if (arr[pos] === target) {
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const items = document.querySelectorAll('.array-item');
+ items.forEach(item => item.classList.remove('highlight', 'searching'));
+ items[pos].classList.add('found');
+ document.getElementById('step-info').textContent =
+ `找到目标 ${target} 在位置 ${pos}!`;
+ }
+ });
+ break;
+ }
+
+ if (arr[pos] < target) {
+ left = pos + 1;
+ } else {
+ right = pos - 1;
+ }
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '插值搜索在什么情况下性能最好?',
+ options: ['数据随机分布', '数据均匀分布', '数据完全逆序', '数据有重复'],
+ correct: 1
+ }
+ }
+];
diff --git a/001game/algorithms/sorting.js b/001game/algorithms/sorting.js
new file mode 100644
index 0000000000..36704ee28b
--- /dev/null
+++ b/001game/algorithms/sorting.js
@@ -0,0 +1,438 @@
+const SortingAlgorithms = [
+ {
+ id: 'bubble-sort',
+ name: '冒泡排序',
+ description: '冒泡排序是一种简单的排序算法,它重复地遍历要排序的列表,比较相邻元素并交换顺序错误的元素。',
+ timeComplexity: 'O(n²)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+ const n = arr.length;
+
+ for (let i = 0; i < n - 1; i++) {
+ for (let j = 0; j < n - i - 1; j++) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [j, j + 1],
+ values: [arr[j], arr[j + 1]]
+ });
+
+ if (arr[j] > arr[j + 1]) {
+ [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [j, j + 1],
+ values: [arr[j], arr[j + 1]]
+ });
+ }
+ }
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: n - i - 1
+ });
+ }
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: 0
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '冒泡排序的时间复杂度是多少?',
+ options: ['O(n)', 'O(n²)', 'O(n log n)', 'O(log n)'],
+ correct: 1
+ }
+ },
+ {
+ id: 'selection-sort',
+ name: '选择排序',
+ description: '选择排序每次从未排序部分选择最小元素,放到已排序部分的末尾。',
+ timeComplexity: 'O(n²)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+ const n = arr.length;
+
+ for (let i = 0; i < n - 1; i++) {
+ let minIdx = i;
+
+ for (let j = i + 1; j < n; j++) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [minIdx, j],
+ values: [arr[minIdx], arr[j]]
+ });
+
+ if (arr[j] < arr[minIdx]) {
+ minIdx = j;
+ }
+ }
+
+ if (minIdx !== i) {
+ [arr[i], arr[minIdx]] = [arr[minIdx], arr[i]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [i, minIdx],
+ values: [arr[i], arr[minIdx]]
+ });
+ }
+
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: i
+ });
+ }
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: n - 1
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '选择排序是稳定的排序算法吗?',
+ options: ['是', '否', '取决于数据', '不确定'],
+ correct: 1
+ }
+ },
+ {
+ id: 'insertion-sort',
+ name: '插入排序',
+ description: '插入排序将数组分为已排序和未排序两部分,每次将未排序部分的第一个元素插入到已排序部分的正确位置。',
+ timeComplexity: 'O(n²)',
+ spaceComplexity: 'O(1)',
+ difficulty: 1,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+ const n = arr.length;
+
+ for (let i = 1; i < n; i++) {
+ let key = arr[i];
+ let j = i - 1;
+
+ GameState.animationSteps.push({
+ type: 'info',
+ message: `选择元素 ${key} 进行插入`
+ });
+
+ while (j >= 0 && arr[j] > key) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [j, j + 1],
+ values: [arr[j], key]
+ });
+
+ arr[j + 1] = arr[j];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [j, j + 1],
+ values: [arr[j], arr[j + 1]]
+ });
+ j--;
+ }
+
+ arr[j + 1] = key;
+ GameState.animationSteps.push({
+ type: 'info',
+ message: `将 ${key} 插入到位置 ${j + 1}`
+ });
+ }
+
+ for (let i = 0; i < n; i++) {
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: i
+ });
+ }
+
+ runAnimation();
+ },
+ challenge: {
+ question: '插入排序在什么情况下性能最好?',
+ options: ['数组完全逆序', '数组已经有序', '数组随机分布', '数组很大'],
+ correct: 1
+ }
+ },
+ {
+ id: 'quick-sort',
+ name: '快速排序',
+ description: '快速排序使用分治策略,选择一个基准元素,将数组分为小于和大于基准的两部分,然后递归排序。',
+ timeComplexity: 'O(n log n)',
+ spaceComplexity: 'O(log n)',
+ difficulty: 2,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+ const sortedIndices = new Set();
+
+ const quickSort = (low, high) => {
+ if (low < high) {
+ const pivotIdx = partition(low, high);
+ quickSort(low, pivotIdx - 1);
+ quickSort(pivotIdx + 1, high);
+ } else if (low === high) {
+ sortedIndices.add(low);
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: low
+ });
+ }
+ };
+
+ const partition = (low, high) => {
+ const pivot = arr[high];
+ let i = low - 1;
+
+ GameState.animationSteps.push({
+ type: 'info',
+ message: `选择基准元素: ${pivot}`
+ });
+
+ for (let j = low; j < high; j++) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [j, high],
+ values: [arr[j], pivot]
+ });
+
+ if (arr[j] < pivot) {
+ i++;
+ [arr[i], arr[j]] = [arr[j], arr[i]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [i, j],
+ values: [arr[i], arr[j]]
+ });
+ }
+ }
+
+ [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [i + 1, high],
+ values: [arr[i + 1], arr[high]]
+ });
+
+ sortedIndices.add(i + 1);
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: i + 1
+ });
+
+ return i + 1;
+ };
+
+ quickSort(0, arr.length - 1);
+ runAnimation();
+ },
+ challenge: {
+ question: '快速排序的平均时间复杂度是多少?',
+ options: ['O(n)', 'O(n²)', 'O(n log n)', 'O(log n)'],
+ correct: 2
+ }
+ },
+ {
+ id: 'merge-sort',
+ name: '归并排序',
+ description: '归并排序将数组分成两半,递归排序后再合并。是一种稳定的排序算法。',
+ timeComplexity: 'O(n log n)',
+ spaceComplexity: 'O(n)',
+ difficulty: 2,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+
+ const mergeSort = (left, right) => {
+ if (left < right) {
+ const mid = Math.floor((left + right) / 2);
+ mergeSort(left, mid);
+ mergeSort(mid + 1, right);
+ merge(left, mid, right);
+ }
+ };
+
+ const merge = (left, mid, right) => {
+ const leftArr = arr.slice(left, mid + 1);
+ const rightArr = arr.slice(mid + 1, right + 1);
+
+ let i = 0, j = 0, k = left;
+
+ while (i < leftArr.length && j < rightArr.length) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [left + i, mid + 1 + j],
+ values: [leftArr[i], rightArr[j]]
+ });
+
+ if (leftArr[i] <= rightArr[j]) {
+ arr[k] = leftArr[i];
+ i++;
+ } else {
+ arr[k] = rightArr[j];
+ j++;
+ }
+
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const bars = document.querySelectorAll('.bar');
+ bars[k].style.height = `${arr[k] * 3}px`;
+ bars[k].querySelector('.bar-value').textContent = arr[k];
+ }
+ });
+ k++;
+ }
+
+ while (i < leftArr.length) {
+ arr[k] = leftArr[i];
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const bars = document.querySelectorAll('.bar');
+ bars[k].style.height = `${arr[k] * 3}px`;
+ bars[k].querySelector('.bar-value').textContent = arr[k];
+ }
+ });
+ i++;
+ k++;
+ }
+
+ while (j < rightArr.length) {
+ arr[k] = rightArr[j];
+ GameState.animationSteps.push({
+ type: 'custom',
+ action: () => {
+ const bars = document.querySelectorAll('.bar');
+ bars[k].style.height = `${arr[k] * 3}px`;
+ bars[k].querySelector('.bar-value').textContent = arr[k];
+ }
+ });
+ j++;
+ k++;
+ }
+
+ for (let idx = left; idx <= right; idx++) {
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: idx
+ });
+ }
+ };
+
+ mergeSort(0, arr.length - 1);
+ runAnimation();
+ },
+ challenge: {
+ question: '归并排序是稳定的排序算法吗?',
+ options: ['是', '否', '取决于实现', '不确定'],
+ correct: 0
+ }
+ },
+ {
+ id: 'heap-sort',
+ name: '堆排序',
+ description: '堆排序利用堆这种数据结构来进行排序。首先构建最大堆,然后依次取出堆顶元素。',
+ timeComplexity: 'O(n log n)',
+ spaceComplexity: 'O(1)',
+ difficulty: 2,
+ init: function() {
+ this.array = generateRandomArray(15, 50);
+ createBars(this.array);
+ },
+ run: function() {
+ const arr = [...this.array];
+ const n = arr.length;
+
+ const heapify = (size, root) => {
+ let largest = root;
+ const left = 2 * root + 1;
+ const right = 2 * root + 2;
+
+ if (left < size) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [largest, left],
+ values: [arr[largest], arr[left]]
+ });
+ if (arr[left] > arr[largest]) {
+ largest = left;
+ }
+ }
+
+ if (right < size) {
+ GameState.animationSteps.push({
+ type: 'compare',
+ indices: [largest, right],
+ values: [arr[largest], arr[right]]
+ });
+ if (arr[right] > arr[largest]) {
+ largest = right;
+ }
+ }
+
+ if (largest !== root) {
+ [arr[root], arr[largest]] = [arr[largest], arr[root]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [root, largest],
+ values: [arr[root], arr[largest]]
+ });
+ heapify(size, largest);
+ }
+ };
+
+ for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
+ heapify(n, i);
+ }
+
+ for (let i = n - 1; i > 0; i--) {
+ [arr[0], arr[i]] = [arr[i], arr[0]];
+ GameState.animationSteps.push({
+ type: 'swap',
+ indices: [0, i],
+ values: [arr[0], arr[i]]
+ });
+
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: i
+ });
+
+ heapify(i, 0);
+ }
+
+ GameState.animationSteps.push({
+ type: 'sorted',
+ index: 0
+ });
+
+ runAnimation();
+ },
+ challenge: {
+ question: '堆排序使用的是什么数据结构?',
+ options: ['栈', '队列', '堆', '树'],
+ correct: 2
+ }
+ }
+];
diff --git a/001game/app.js b/001game/app.js
new file mode 100644
index 0000000000..525c4a7a80
--- /dev/null
+++ b/001game/app.js
@@ -0,0 +1,474 @@
+const GameState = {
+ score: 0,
+ currentLevel: 1,
+ currentCategory: null,
+ currentAlgorithm: null,
+ isRunning: false,
+ isPaused: false,
+ speed: 1,
+ completedLevels: new Set(),
+ startTime: null,
+ animationSteps: [],
+ currentStep: 0,
+ maxBarValue: 100,
+ currentArray: []
+};
+
+const speedLevels = [1, 2, 4, 8];
+let currentSpeedIndex = 0;
+
+const categories = [
+ {
+ id: 'sorting',
+ name: '排序算法',
+ icon: '📊',
+ description: '学习各种经典排序算法的工作原理',
+ algorithms: []
+ },
+ {
+ id: 'searching',
+ name: '搜索算法',
+ icon: '🔍',
+ description: '掌握二分搜索、线性搜索等搜索技巧',
+ algorithms: []
+ },
+ {
+ id: 'graph',
+ name: '图算法',
+ icon: '🕸️',
+ description: '探索BFS、DFS、最短路径等图算法',
+ algorithms: []
+ },
+ {
+ id: 'dynamic-programming',
+ name: '动态规划',
+ icon: '📈',
+ description: '理解斐波那契、背包问题等经典DP问题',
+ algorithms: []
+ },
+ {
+ id: 'backtracking',
+ name: '回溯算法',
+ icon: '🔄',
+ description: '解决N皇后、数独、迷宫等问题',
+ algorithms: []
+ },
+ {
+ id: 'math',
+ name: '数学算法',
+ icon: '🔢',
+ description: '素数筛、最大公约数、阶乘等数学计算',
+ algorithms: []
+ },
+ {
+ id: 'cipher',
+ name: '密码算法',
+ icon: '🔐',
+ description: '凯撒密码、摩尔斯电码等加密解密',
+ algorithms: []
+ }
+];
+
+function init() {
+ loadProgress();
+ initializeAlgorithms();
+ renderCategories();
+ updateStats();
+}
+
+function initializeAlgorithms() {
+ if (typeof SortingAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'sorting').algorithms = SortingAlgorithms;
+ }
+ if (typeof SearchingAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'searching').algorithms = SearchingAlgorithms;
+ }
+ if (typeof GraphAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'graph').algorithms = GraphAlgorithms;
+ }
+ if (typeof DPAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'dynamic-programming').algorithms = DPAlgorithms;
+ }
+ if (typeof BacktrackingAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'backtracking').algorithms = BacktrackingAlgorithms;
+ }
+ if (typeof MathAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'math').algorithms = MathAlgorithms;
+ }
+ if (typeof CipherAlgorithms !== 'undefined') {
+ categories.find(c => c.id === 'cipher').algorithms = CipherAlgorithms;
+ }
+}
+
+function loadProgress() {
+ const saved = localStorage.getItem('algorithmGameProgress');
+ if (saved) {
+ const progress = JSON.parse(saved);
+ GameState.score = progress.score || 0;
+ GameState.completedLevels = new Set(progress.completedLevels || []);
+ }
+}
+
+function saveProgress() {
+ localStorage.setItem('algorithmGameProgress', JSON.stringify({
+ score: GameState.score,
+ completedLevels: Array.from(GameState.completedLevels)
+ }));
+}
+
+function updateStats() {
+ document.getElementById('score').textContent = GameState.score;
+ document.getElementById('level').textContent = GameState.currentLevel;
+}
+
+function showScreen(screenId) {
+ document.querySelectorAll('.screen').forEach(screen => {
+ screen.classList.remove('active');
+ });
+ document.getElementById(screenId).classList.add('active');
+}
+
+function showHome() {
+ showScreen('home-screen');
+ renderCategories();
+}
+
+function showLevelSelect() {
+ if (GameState.currentCategory) {
+ showCategory(GameState.currentCategory.id);
+ } else {
+ showHome();
+ }
+}
+
+function renderCategories() {
+ const grid = document.getElementById('category-grid');
+ grid.innerHTML = categories.map(category => {
+ const completedCount = category.algorithms.filter(a =>
+ GameState.completedLevels.has(`${category.id}-${a.id}`)
+ ).length;
+ const progress = category.algorithms.length > 0
+ ? (completedCount / category.algorithms.length) * 100
+ : 0;
+
+ return `
+
+
${category.icon}
+
${category.name}
+
${category.description}
+
+
${completedCount}/${category.algorithms.length} 已完成
+
+ `;
+ }).join('');
+}
+
+function showCategory(categoryId) {
+ GameState.currentCategory = categories.find(c => c.id === categoryId);
+ document.getElementById('category-title').textContent = GameState.currentCategory.name;
+ renderLevels();
+ showScreen('level-select');
+}
+
+function renderLevels() {
+ const grid = document.getElementById('level-grid');
+ const algorithms = GameState.currentCategory.algorithms;
+
+ grid.innerHTML = algorithms.map((algo, index) => {
+ const levelId = `${GameState.currentCategory.id}-${algo.id}`;
+ const isCompleted = GameState.completedLevels.has(levelId);
+ const isLocked = index > 0 && !GameState.completedLevels.has(
+ `${GameState.currentCategory.id}-${algorithms[index - 1].id}`
+ );
+
+ return `
+
+
${index + 1}
+
${algo.name}
+
${getDifficultyText(algo.difficulty)}
+ ${isCompleted ? '
✓ 已完成' : ''}
+ ${isLocked ? '
🔒 未解锁' : ''}
+
+ `;
+ }).join('');
+}
+
+function getDifficultyText(difficulty) {
+ const texts = {
+ 1: '⭐ 简单',
+ 2: '⭐⭐ 中等',
+ 3: '⭐⭐⭐ 困难'
+ };
+ return texts[difficulty] || '⭐ 简单';
+}
+
+function startGame(algorithmId) {
+ GameState.currentAlgorithm = GameState.currentCategory.algorithms.find(
+ a => a.id === algorithmId
+ );
+
+ if (!GameState.currentAlgorithm) return;
+
+ document.getElementById('game-title').textContent = GameState.currentAlgorithm.name;
+ document.getElementById('algorithm-description').textContent = GameState.currentAlgorithm.description;
+ document.getElementById('time-complexity').textContent = GameState.currentAlgorithm.timeComplexity;
+ document.getElementById('space-complexity').textContent = GameState.currentAlgorithm.spaceComplexity;
+
+ GameState.isRunning = false;
+ GameState.isPaused = false;
+ GameState.currentStep = 0;
+ GameState.animationSteps = [];
+
+ document.getElementById('start-btn').textContent = '开始';
+ document.getElementById('step-info').textContent = '点击"开始"按钮开始可视化';
+ document.getElementById('challenge-area').classList.add('hidden');
+
+ if (GameState.currentAlgorithm.init) {
+ GameState.currentAlgorithm.init();
+ }
+
+ showScreen('game-screen');
+}
+
+function startVisualization() {
+ if (GameState.isRunning) {
+ GameState.isPaused = !GameState.isPaused;
+ document.getElementById('start-btn').textContent = GameState.isPaused ? '继续' : '暂停';
+ if (!GameState.isPaused) {
+ runAnimation();
+ }
+ return;
+ }
+
+ GameState.isRunning = true;
+ GameState.isPaused = false;
+ GameState.startTime = Date.now();
+ document.getElementById('start-btn').textContent = '暂停';
+
+ if (GameState.currentAlgorithm.run) {
+ GameState.currentAlgorithm.run();
+ }
+}
+
+function resetVisualization() {
+ GameState.isRunning = false;
+ GameState.isPaused = false;
+ GameState.currentStep = 0;
+ document.getElementById('start-btn').textContent = '开始';
+ document.getElementById('step-info').textContent = '点击"开始"按钮开始可视化';
+ document.getElementById('challenge-area').classList.add('hidden');
+
+ if (GameState.currentAlgorithm.init) {
+ GameState.currentAlgorithm.init();
+ }
+}
+
+function toggleSpeed() {
+ currentSpeedIndex = (currentSpeedIndex + 1) % speedLevels.length;
+ GameState.speed = speedLevels[currentSpeedIndex];
+ document.getElementById('speed-btn').textContent = `速度: ${GameState.speed}x`;
+}
+
+async function runAnimation() {
+ while (GameState.isRunning && !GameState.isPaused && GameState.currentStep < GameState.animationSteps.length) {
+ const step = GameState.animationSteps[GameState.currentStep];
+ await executeStep(step);
+ GameState.currentStep++;
+ await sleep(getDelay());
+ }
+
+ if (GameState.currentStep >= GameState.animationSteps.length && GameState.isRunning) {
+ GameState.isRunning = false;
+ document.getElementById('start-btn').textContent = '完成';
+ showChallenge();
+ }
+}
+
+function getDelay() {
+ return Math.max(50, 500 / GameState.speed);
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function executeStep(step) {
+ if (step.type === 'compare') {
+ document.getElementById('step-info').textContent = `比较位置 ${step.indices[0]} 和 ${step.indices[1]}: ${step.values[0]} vs ${step.values[1]}`;
+ highlightBars(step.indices, 'comparing');
+ } else if (step.type === 'swap') {
+ document.getElementById('step-info').textContent = `交换位置 ${step.indices[0]} 和 ${step.indices[1]}`;
+ highlightBars(step.indices, 'swapping');
+ swapBars(step.indices, step.values);
+ } else if (step.type === 'sorted') {
+ document.getElementById('step-info').textContent = `位置 ${step.index} 已排序`;
+ markBarSorted(step.index);
+ } else if (step.type === 'info') {
+ document.getElementById('step-info').textContent = step.message;
+ } else if (step.type === 'highlight') {
+ highlightBars(step.indices, step.className || 'current');
+ } else if (step.type === 'custom') {
+ if (step.action) {
+ step.action();
+ }
+ }
+}
+
+function highlightBars(indices, className) {
+ const bars = document.querySelectorAll('.bar');
+ bars.forEach((bar, i) => {
+ bar.classList.remove('comparing', 'swapping', 'current');
+ if (indices.includes(i)) {
+ bar.classList.add(className);
+ }
+ });
+}
+
+function swapBars(indices, values) {
+ const bars = document.querySelectorAll('.bar');
+ const maxVal = GameState.maxBarValue;
+ indices.forEach((index, i) => {
+ bars[index].style.height = `${(values[i] / maxVal) * 250}px`;
+ bars[index].querySelector('.bar-value').textContent = values[i];
+ });
+}
+
+function markBarSorted(index) {
+ const bars = document.querySelectorAll('.bar');
+ bars[index].classList.add('sorted');
+}
+
+function showChallenge() {
+ if (!GameState.currentAlgorithm.challenge) {
+ showResult(true);
+ return;
+ }
+
+ const challenge = GameState.currentAlgorithm.challenge;
+ document.getElementById('challenge-question').textContent = challenge.question;
+
+ const optionsContainer = document.getElementById('challenge-options');
+ optionsContainer.innerHTML = challenge.options.map((option, index) => `
+
+ `).join('');
+
+ document.getElementById('challenge-area').classList.remove('hidden');
+}
+
+function checkAnswer(selected, correct) {
+ const buttons = document.querySelectorAll('.challenge-options button');
+ buttons.forEach((btn, i) => {
+ btn.disabled = true;
+ if (i === correct) {
+ btn.classList.add('correct');
+ } else if (i === selected && i !== correct) {
+ btn.classList.add('wrong');
+ }
+ });
+
+ const isCorrect = selected === correct;
+ setTimeout(() => showResult(isCorrect), 1000);
+}
+
+function showResult(success) {
+ const levelId = `${GameState.currentCategory.id}-${GameState.currentAlgorithm.id}`;
+ const isNewCompletion = success && !GameState.completedLevels.has(levelId);
+
+ if (isNewCompletion) {
+ GameState.completedLevels.add(levelId);
+ const baseScore = GameState.currentAlgorithm.difficulty * 100;
+ const timeBonus = Math.max(0, Math.floor((300000 - (Date.now() - GameState.startTime)) / 1000));
+ GameState.score += baseScore + timeBonus;
+ saveProgress();
+ updateStats();
+ }
+
+ document.getElementById('result-title').textContent = success ? '🎉 恭喜完成!' : '💪 继续加油!';
+ document.getElementById('result-message').textContent = success
+ ? '你成功掌握了这个算法!'
+ : '再试一次,你会做得更好!';
+
+ const completionTime = Math.floor((Date.now() - GameState.startTime) / 1000);
+ document.getElementById('completion-time').textContent = `${Math.floor(completionTime / 60)}分${completionTime % 60}秒`;
+ document.getElementById('earned-score').textContent = success ? `+${GameState.currentAlgorithm.difficulty * 100}` : '0';
+
+ const stars = success ? Math.min(3, Math.ceil(3 - (GameState.currentStep / GameState.animationSteps.length) * 2)) : 0;
+ const starsContainer = document.getElementById('stars');
+ starsContainer.innerHTML = Array(3).fill(0).map((_, i) =>
+ `★`
+ ).join('');
+
+ showScreen('result-screen');
+}
+
+function retryLevel() {
+ startGame(GameState.currentAlgorithm.id);
+}
+
+function nextLevel() {
+ const algorithms = GameState.currentCategory.algorithms;
+ const currentIndex = algorithms.findIndex(a => a.id === GameState.currentAlgorithm.id);
+
+ if (currentIndex < algorithms.length - 1) {
+ startGame(algorithms[currentIndex + 1].id);
+ } else {
+ showCategory(GameState.currentCategory.id);
+ }
+}
+
+function generateRandomArray(size = 20, max = 100) {
+ return Array.from({ length: size }, () => Math.floor(Math.random() * max) + 1);
+}
+
+function createBars(array) {
+ const container = document.getElementById('visualization-area');
+ const maxVal = Math.max(...array);
+ GameState.maxBarValue = maxVal;
+ GameState.currentArray = [...array];
+ const barWidth = Math.max(20, Math.floor((container.clientWidth - array.length * 4) / array.length));
+
+ container.innerHTML = array.map((val, i) => `
+
+ ${val}
+
+ `).join('');
+}
+
+function createArrayDisplay(array, highlightIndices = []) {
+ const container = document.getElementById('visualization-area');
+ container.innerHTML = `
+
+ ${array.map((val, i) => `
+
${val}
+ `).join('')}
+
+ `;
+}
+
+function createGrid(rows, cols, grid = null) {
+ const container = document.getElementById('visualization-area');
+ const displayGrid = grid || Array(rows).fill(null).map(() => Array(cols).fill(0));
+
+ container.innerHTML = `
+
+ ${displayGrid.flat().map((cell, i) => `
+
${cell || ''}
+ `).join('')}
+
+ `;
+
+ return displayGrid;
+}
+
+function updateGridCell(row, col, className, value = '') {
+ const cell = document.querySelector(`.grid-cell[data-row="${row}"][data-col="${col}"]`);
+ if (cell) {
+ cell.className = `grid-cell ${className}`;
+ cell.textContent = value;
+ }
+}
+
+document.addEventListener('DOMContentLoaded', init);
diff --git a/001game/index.html b/001game/index.html
new file mode 100644
index 0000000000..4dc8c74bba
--- /dev/null
+++ b/001game/index.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+ 算法可视化挑战
+
+
+
+
+
+
+
+
+
+
欢迎来到算法世界!
+
通过可视化学习各种经典算法,挑战你的算法思维!
+
+
+
+
+
+
+
+
+
+
+
+
+ 时间复杂度:
+ 空间复杂度:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/001game/styles.css b/001game/styles.css
new file mode 100644
index 0000000000..80059844a0
--- /dev/null
+++ b/001game/styles.css
@@ -0,0 +1,884 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ --primary-color: #6366f1;
+ --primary-dark: #4f46e5;
+ --secondary-color: #22d3ee;
+ --accent-color: #f472b6;
+ --success-color: #22c55e;
+ --warning-color: #f59e0b;
+ --danger-color: #ef4444;
+ --bg-dark: #0f172a;
+ --bg-card: #1e293b;
+ --bg-lighter: #334155;
+ --text-primary: #f8fafc;
+ --text-secondary: #94a3b8;
+ --border-color: #475569;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: linear-gradient(135deg, var(--bg-dark) 0%, #1a1a2e 100%);
+ color: var(--text-primary);
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+#app {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px 30px;
+ background: var(--bg-card);
+ border-radius: 16px;
+ margin-bottom: 30px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+}
+
+.header h1 {
+ font-size: 1.8rem;
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.stats {
+ display: flex;
+ gap: 30px;
+}
+
+.stats span {
+ font-size: 1.1rem;
+ color: var(--text-secondary);
+}
+
+.stats span span {
+ color: var(--secondary-color);
+ font-weight: bold;
+}
+
+.screen {
+ display: none;
+ animation: fadeIn 0.3s ease;
+}
+
+.screen.active {
+ display: block;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+.welcome {
+ text-align: center;
+ padding: 40px;
+ background: var(--bg-card);
+ border-radius: 16px;
+ margin-bottom: 30px;
+}
+
+.welcome h2 {
+ font-size: 2rem;
+ margin-bottom: 15px;
+ color: var(--text-primary);
+}
+
+.welcome p {
+ color: var(--text-secondary);
+ font-size: 1.1rem;
+}
+
+.category-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 20px;
+}
+
+.category-card {
+ background: var(--bg-card);
+ border-radius: 16px;
+ padding: 25px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
+ position: relative;
+ overflow: hidden;
+}
+
+.category-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 4px;
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
+ transform: scaleX(0);
+ transition: transform 0.3s ease;
+}
+
+.category-card:hover {
+ transform: translateY(-5px);
+ border-color: var(--primary-color);
+ box-shadow: 0 10px 30px rgba(99, 102, 241, 0.2);
+}
+
+.category-card:hover::before {
+ transform: scaleX(1);
+}
+
+.category-icon {
+ font-size: 3rem;
+ margin-bottom: 15px;
+}
+
+.category-card h3 {
+ font-size: 1.3rem;
+ margin-bottom: 10px;
+ color: var(--text-primary);
+}
+
+.category-card p {
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ margin-bottom: 15px;
+}
+
+.category-progress {
+ height: 6px;
+ background: var(--bg-lighter);
+ border-radius: 3px;
+ overflow: hidden;
+}
+
+.category-progress-bar {
+ height: 100%;
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
+ transition: width 0.5s ease;
+}
+
+.level-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 15px;
+ margin-top: 20px;
+}
+
+.level-card {
+ background: var(--bg-card);
+ border-radius: 12px;
+ padding: 20px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
+ text-align: center;
+}
+
+.level-card:hover {
+ border-color: var(--primary-color);
+ transform: scale(1.02);
+}
+
+.level-card.completed {
+ border-color: var(--success-color);
+}
+
+.level-card.locked {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.level-number {
+ font-size: 2rem;
+ font-weight: bold;
+ color: var(--primary-color);
+ margin-bottom: 10px;
+}
+
+.level-card.completed .level-number {
+ color: var(--success-color);
+}
+
+.level-name {
+ font-size: 1rem;
+ color: var(--text-primary);
+ margin-bottom: 5px;
+}
+
+.level-difficulty {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+.back-btn {
+ background: transparent;
+ border: 2px solid var(--border-color);
+ color: var(--text-primary);
+ padding: 10px 20px;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-size: 1rem;
+}
+
+.back-btn:hover {
+ border-color: var(--primary-color);
+ color: var(--primary-color);
+}
+
+.game-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ background: var(--bg-card);
+ border-radius: 12px;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.game-header h2 {
+ color: var(--text-primary);
+}
+
+.game-controls {
+ display: flex;
+ gap: 10px;
+}
+
+.game-controls button {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.3s ease;
+}
+
+#start-btn {
+ background: var(--success-color);
+ color: white;
+}
+
+#start-btn:hover {
+ background: #16a34a;
+}
+
+#reset-btn {
+ background: var(--warning-color);
+ color: white;
+}
+
+#reset-btn:hover {
+ background: #d97706;
+}
+
+#speed-btn {
+ background: var(--primary-color);
+ color: white;
+}
+
+#speed-btn:hover {
+ background: var(--primary-dark);
+}
+
+.game-info {
+ background: var(--bg-card);
+ padding: 20px;
+ border-radius: 12px;
+ margin-bottom: 20px;
+}
+
+.game-info p {
+ color: var(--text-secondary);
+ margin-bottom: 10px;
+ line-height: 1.6;
+}
+
+.complexity {
+ display: flex;
+ gap: 30px;
+ color: var(--text-secondary);
+}
+
+.complexity span {
+ background: var(--bg-lighter);
+ padding: 8px 15px;
+ border-radius: 6px;
+}
+
+.complexity span span {
+ color: var(--secondary-color);
+ font-weight: bold;
+}
+
+.visualization-area {
+ background: var(--bg-card);
+ border-radius: 12px;
+ padding: 30px;
+ min-height: 400px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ gap: 4px;
+ flex-wrap: wrap;
+ align-content: flex-end;
+}
+
+.bar {
+ background: linear-gradient(180deg, var(--primary-color), var(--primary-dark));
+ border-radius: 4px 4px 0 0;
+ transition: height 0.1s ease, background 0.1s ease;
+ position: relative;
+}
+
+.bar.comparing {
+ background: linear-gradient(180deg, var(--warning-color), #d97706);
+}
+
+.bar.swapping {
+ background: linear-gradient(180deg, var(--danger-color), #dc2626);
+}
+
+.bar.sorted {
+ background: linear-gradient(180deg, var(--success-color), #16a34a);
+}
+
+.bar.current {
+ background: linear-gradient(180deg, var(--secondary-color), #06b6d4);
+}
+
+.bar-value {
+ position: absolute;
+ top: -25px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+.game-footer {
+ margin-top: 20px;
+}
+
+.step-info {
+ background: var(--bg-card);
+ padding: 15px 20px;
+ border-radius: 12px;
+ color: var(--text-secondary);
+ text-align: center;
+ font-size: 1rem;
+}
+
+.challenge-area {
+ background: var(--bg-card);
+ padding: 25px;
+ border-radius: 12px;
+ margin-top: 20px;
+ text-align: center;
+}
+
+.challenge-area.hidden {
+ display: none;
+}
+
+.challenge-area p {
+ font-size: 1.1rem;
+ margin-bottom: 20px;
+ color: var(--text-primary);
+}
+
+.challenge-options {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ flex-wrap: wrap;
+}
+
+.challenge-options button {
+ padding: 12px 25px;
+ background: var(--bg-lighter);
+ border: 2px solid var(--border-color);
+ color: var(--text-primary);
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.3s ease;
+}
+
+.challenge-options button:hover {
+ border-color: var(--primary-color);
+ background: var(--primary-color);
+}
+
+.challenge-options button.correct {
+ background: var(--success-color);
+ border-color: var(--success-color);
+}
+
+.challenge-options button.wrong {
+ background: var(--danger-color);
+ border-color: var(--danger-color);
+}
+
+.result-content {
+ background: var(--bg-card);
+ padding: 40px;
+ border-radius: 16px;
+ text-align: center;
+ max-width: 500px;
+ margin: 50px auto;
+}
+
+.result-content h2 {
+ font-size: 2rem;
+ margin-bottom: 20px;
+ color: var(--text-primary);
+}
+
+.stars {
+ font-size: 3rem;
+ margin-bottom: 20px;
+}
+
+.star {
+ color: var(--bg-lighter);
+ margin: 0 5px;
+ transition: all 0.3s ease;
+}
+
+.star.filled {
+ color: var(--warning-color);
+ text-shadow: 0 0 20px rgba(245, 158, 11, 0.5);
+}
+
+.result-stats {
+ margin: 20px 0;
+ color: var(--text-secondary);
+}
+
+.result-stats p {
+ margin: 10px 0;
+}
+
+.result-buttons {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin-top: 25px;
+ flex-wrap: wrap;
+}
+
+.result-buttons button {
+ padding: 12px 25px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.3s ease;
+ background: var(--primary-color);
+ color: white;
+}
+
+.result-buttons button:hover {
+ background: var(--primary-dark);
+ transform: translateY(-2px);
+}
+
+.array-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.array-item {
+ width: 50px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-lighter);
+ border-radius: 8px;
+ font-size: 1.2rem;
+ font-weight: bold;
+ color: var(--text-primary);
+ transition: all 0.3s ease;
+}
+
+.array-item.highlight {
+ background: var(--primary-color);
+ transform: scale(1.1);
+}
+
+.array-item.found {
+ background: var(--success-color);
+}
+
+.array-item.searching {
+ background: var(--warning-color);
+}
+
+.grid-container {
+ display: grid;
+ gap: 2px;
+ justify-content: center;
+}
+
+.grid-cell {
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-lighter);
+ border-radius: 4px;
+ font-weight: bold;
+ color: var(--text-primary);
+ transition: all 0.2s ease;
+}
+
+.grid-cell.visited {
+ background: var(--primary-color);
+}
+
+.grid-cell.path {
+ background: var(--success-color);
+}
+
+.grid-cell.current {
+ background: var(--warning-color);
+}
+
+.grid-cell.wall {
+ background: var(--danger-color);
+}
+
+.grid-cell.start {
+ background: var(--secondary-color);
+}
+
+.grid-cell.end {
+ background: var(--accent-color);
+}
+
+.tree-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+}
+
+.tree-node {
+ width: 50px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--primary-color);
+ border-radius: 50%;
+ font-weight: bold;
+ color: white;
+ margin: 5px;
+ transition: all 0.3s ease;
+}
+
+.tree-node.highlight {
+ background: var(--warning-color);
+ transform: scale(1.2);
+}
+
+.tree-level {
+ display: flex;
+ justify-content: center;
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.fibonacci-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ justify-content: center;
+ padding: 20px;
+}
+
+.fib-box {
+ padding: 15px 20px;
+ background: var(--bg-lighter);
+ border-radius: 8px;
+ text-align: center;
+ min-width: 80px;
+ transition: all 0.3s ease;
+}
+
+.fib-box.active {
+ background: var(--primary-color);
+ transform: scale(1.1);
+}
+
+.fib-box .index {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+
+.fib-box .value {
+ font-size: 1.5rem;
+ font-weight: bold;
+ color: var(--text-primary);
+}
+
+.code-display {
+ background: #0d1117;
+ border-radius: 12px;
+ padding: 20px;
+ margin-top: 20px;
+ overflow-x: auto;
+}
+
+.code-display pre {
+ color: #c9d1d9;
+ font-family: 'Consolas', 'Monaco', monospace;
+ font-size: 0.9rem;
+ line-height: 1.6;
+}
+
+.code-line {
+ padding: 2px 10px;
+ border-left: 3px solid transparent;
+}
+
+.code-line.highlight {
+ background: rgba(99, 102, 241, 0.2);
+ border-left-color: var(--primary-color);
+}
+
+.graph-container {
+ position: relative;
+ width: 100%;
+ min-height: 400px;
+}
+
+.graph-node {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--primary-color);
+ border-radius: 50%;
+ font-weight: bold;
+ color: white;
+ transition: all 0.3s ease;
+ z-index: 2;
+}
+
+.graph-node.visited {
+ background: var(--success-color);
+}
+
+.graph-node.current {
+ background: var(--warning-color);
+}
+
+.sudoku-grid {
+ display: grid;
+ grid-template-columns: repeat(9, 1fr);
+ gap: 2px;
+ background: var(--border-color);
+ padding: 4px;
+ border-radius: 8px;
+ max-width: 450px;
+ margin: 0 auto;
+}
+
+.sudoku-cell {
+ width: 45px;
+ height: 45px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-lighter);
+ font-size: 1.2rem;
+ font-weight: bold;
+ color: var(--text-primary);
+ transition: all 0.2s ease;
+}
+
+.sudoku-cell:nth-child(3n) {
+ border-right: 2px solid var(--border-color);
+}
+
+.sudoku-cell:nth-child(n+19):nth-child(-n+27),
+.sudoku-cell:nth-child(n+46):nth-child(-n+54) {
+ border-bottom: 2px solid var(--border-color);
+}
+
+.sudoku-cell.given {
+ color: var(--text-secondary);
+}
+
+.sudoku-cell.solving {
+ background: var(--primary-color);
+}
+
+.sudoku-cell.solved {
+ background: var(--success-color);
+}
+
+.nqueens-board {
+ display: grid;
+ gap: 2px;
+ background: var(--border-color);
+ padding: 4px;
+ border-radius: 8px;
+ max-width: 400px;
+ margin: 0 auto;
+}
+
+.queen-cell {
+ width: 45px;
+ height: 45px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.8rem;
+ transition: all 0.2s ease;
+}
+
+.queen-cell.light {
+ background: var(--bg-lighter);
+}
+
+.queen-cell.dark {
+ background: var(--bg-card);
+}
+
+.queen-cell.has-queen {
+ background: var(--warning-color);
+}
+
+.queen-cell.conflict {
+ background: var(--danger-color);
+}
+
+.maze-grid {
+ display: grid;
+ gap: 1px;
+ background: var(--border-color);
+ padding: 2px;
+ border-radius: 8px;
+ max-width: 500px;
+ margin: 0 auto;
+}
+
+.maze-cell {
+ width: 25px;
+ height: 25px;
+ background: var(--bg-lighter);
+ transition: all 0.1s ease;
+}
+
+.maze-cell.wall {
+ background: var(--bg-dark);
+}
+
+.maze-cell.path {
+ background: var(--success-color);
+}
+
+.maze-cell.current {
+ background: var(--warning-color);
+}
+
+.maze-cell.start {
+ background: var(--secondary-color);
+}
+
+.maze-cell.end {
+ background: var(--accent-color);
+}
+
+@media (max-width: 768px) {
+ .header {
+ flex-direction: column;
+ gap: 15px;
+ text-align: center;
+ }
+
+ .stats {
+ gap: 20px;
+ }
+
+ .game-header {
+ flex-direction: column;
+ text-align: center;
+ }
+
+ .game-controls {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .category-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .level-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .bar {
+ min-width: 15px;
+ }
+
+ .sudoku-cell,
+ .queen-cell {
+ width: 35px;
+ height: 35px;
+ font-size: 1rem;
+ }
+
+ .maze-cell {
+ width: 20px;
+ height: 20px;
+ }
+}
+
+@media (max-width: 480px) {
+ .level-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .challenge-options {
+ flex-direction: column;
+ }
+
+ .challenge-options button {
+ width: 100%;
+ }
+
+ .result-buttons {
+ flex-direction: column;
+ }
+
+ .result-buttons button {
+ width: 100%;
+ }
+}
diff --git a/DIRECTORY.md b/DIRECTORY.md
index b7b8aca45d..8046a4303e 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -322,8 +322,8 @@
* [TernarySearch](Search/TernarySearch.js)
* [UnionFind](Search/UnionFind.js)
* **Sliding-Windows**
- * [MaxSumSubarrayFixed](Sliding-Windows/MaxSumSubarrayFixed.js)
* [LongestSubarrayWithSumAtMost](Sliding-Windows/LongestSubarrayWithSumAtMost.js)
+ * [MaxSumSubarrayFixed](Sliding-Windows/MaxSumSubarrayFixed.js)
* **Sorts**
* [AlphaNumericalSort](Sorts/AlphaNumericalSort.js)
* [BeadSort](Sorts/BeadSort.js)