闲来无事,用HTML+JS+CSS制作了一个数独游戏消遣。其实主要是自己做题的时候用笔画删除数字太容易出错,所以想搞一个程序稍微辅助一下。通过制作这个程序,反而提高了手工做题的水平,至少学会了记录步数以便于回退。
20250710功能更新:用户确定的单元格同时显示单元格值以及该单元格是第几步确定的,便于测试错误时回退;增加唯一可用数字高亮显示功能,帮助找到确定数据的单元格位置;增加撤销到指定步数功能,便于快速回撤到本次试探的开始位置。

0711:补上了手工输入题目的功能。

1、游戏的界面(旧界面):

2、游戏的玩法(新界面):

3、游戏结束时弹出提示框(旧界面):

下面是游戏的全部代码。其中HTML负责UI构造,CSS负责UI的显示,JS包含了游戏的全部逻辑。

1、sudokuUI.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>数独游戏</title><link rel="stylesheet" href="sudoku_style.css" />
</head>
<body><h1>数独游戏</h1><div class="sudoku-container"><table id="sudoku-board"></table><div class="buttons"><button id="new-game" class="single-line-button">新题</button><button id="reset-btn" class="single-line-button">重置</button><button id="answer-btn" class="single-line-button">答案</button><br/><button id="highlight-unique-btn"  class="double-line-button">高亮唯一<br/>可用数字</button><button id="go-back-btn" class="double-line-button">回退至<br/>指定步数</button><button id="input-new-game" class="double-line-button">手工输入<br/>新数独题</button></div></div><dialog id="gridDialog"><h3>在每个单元格中输入1-9的数字</h3><table id="gridTable"></table><button id="submitGrid">提交</button></dialog><script src="sudoku_script.js"></script>
</body>
</html>

2、sudoku_style.css

body {font-family: Arial, sans-serif;text-align: center;background-color: #f4f4f4;}h1 {margin-top: 30px;}.sudoku-container {display: inline-block;margin-top: 20px;padding: 20px;background: beige;border-radius: 10px;box-shadow: 0 0 15px rgba(0,0,0,0.1);}#sudoku-board {border-collapse: collapse;margin: 0 auto;}#sudoku-board td {width: 40px;height: 40px;text-align: center;vertical-align: middle;border: 1px solid #999;font-size: 18px;cursor: pointer;transition: background 0.2s ease;position: relative;padding: 0;}.cell-step {font-size: 12px;color: orange;margin-top: 2px;line-height: 1;text-align: center;}.sudoku-mini-grid {display: grid;grid-template-columns: repeat(3, 1fr);grid-template-rows: repeat(3, 1fr);width: 100%;height: 100%;}.mini-cell {font-size: 11px;color: #222;display: flex;align-items: center;justify-content: center;width: 100%;height: 100%;user-select: none;cursor: pointer;transition: background 0.2s, color 0.2s;}.mini-cell.gray {color: #bbb;
}.mini-cell.highlight {background: #b3e0ff !important;color: #222;border-radius: 5px;
}.mini-cell.black {color: #222;}.mini-cell.yellow {color: #ff0;}.mini-cell:hover:not(.gray) {background: #e0e0e0;}.sudoku-cell-fixed {font-size: 24px;font-weight: bold;color: #1976d2;display: flex;align-items: center;justify-content: center;height: 100%;}.sudoku-cell-user{font-size: 24px;font-weight: bold;color: #388e3c;display: flex;align-items: center;justify-content: center;height: 100%;}.sudoku-mini-grid {width: 100%;height: 100%;pointer-events: auto;}#sudoku-board tr:nth-child(3n) td {border-bottom: 2px solid #000;}#sudoku-board td:nth-child(3n) {border-right: 2px solid #000;}#sudoku-board tr:first-child td {border-top: 2px solid #000;}#sudoku-board td:first-child {border-left: 2px solid #000;}#sudoku-board .preset {background-color: #e0e0e0;font-weight: bold;}#sudoku-board .user-input {background-color: #fff;}#sudoku-board .error {color: red;}.buttons {margin-top: 20px;}.buttons button {margin: 10px;font-size: 16px;cursor: pointer;background-color: #4CAF50;color: white;border: none;border-radius: 5px;transition: background 0.2s ease;vertical-align: middle;}.single-line-button {padding: 10px 20px;}.double-line-button {padding: 5px;}.buttons button:hover {background-color: #45a049;}.buttons button:active {background-color: #3e8e41;}.sudoku-cell-fixed.red, .sudoku-cell-user.red { color: red; }.cell {width:28px;height:28px;
}

3、sudoku_script.js

// =====================
// 数独游戏核心脚本
// =====================// 获取页面上的主要DOM元素
const boardElement = document.getElementById("sudoku-board"); // 数独主棋盘容器
const newGameBtn = document.getElementById("new-game");      // 新游戏按钮
const resetBtn = document.getElementById("reset-btn");      // 重置按钮
const answerBtn = document.getElementById("answer-btn");    // 查看答案按钮
const highlightUniqueBtn = document.getElementById("highlight-unique-btn"); // 高亮唯一解按钮
const goBackBtn = document.getElementById("go-back-btn");   // 撤销步数按钮
const inputDialog = document.getElementById('gridDialog');         // 自定义题目输入对话框
const openBtn = document.getElementById('input-new-game'); // 打开自定义题目按钮
const inputTtable = document.getElementById('gridTable');           // 自定义题目表格
const submitBtn = document.getElementById('submitGrid');   // 提交自定义题目按钮// 撤销到指定步数
// 用户点击“撤销”按钮时触发
// 会弹窗让用户输入目标步数,撤销到该步
// 步数小于1或大于当前步数无效
// 会清除多余步数填入的格子
// 完成后刷新棋盘
goBackBtn.addEventListener("click", () => {if (stepCounter <= 1) {alert("没有可撤销的步数");return;}let input = prompt("输入要撤销到的目标步数,应为1~" + (stepCounter - 1) + "之间的数字");if (input === null) return; // 用户取消let targetStep = parseInt(input.trim(), 10);if (isNaN(targetStep) || targetStep < 1 || targetStep >= stepCounter) {alert("输入的步数不存在");return;}for (let r = 0; r < 9; r++) {for (let c = 0; c < 9; c++) {if (userSteps[r][c] !== null && userSteps[r][c] > targetStep) {currentBoard[r][c] = null;  // 清除已填入的数据userSteps[r][c] = null;  // 清除该单元格对应的步数}}}stepCounter = targetStep + 1;drawBoard();
});// 重置按钮事件
// 恢复到初始题面,清空用户所有填写和计时,刷新棋盘
resetBtn.addEventListener("click", () => {// 初始化相关变量currentBoard = JSON.parse(JSON.stringify(originalBoard));userSteps = Array.from({ length: 9 }, () => Array(9).fill(null));stepCounter = 1;redCells = new Set();timerStart = null;timerUsed = 0;// 重绘盘面drawBoard();
});// 默认初始题面,此题号称超五星难度。可被新题或自定义题替换
let originalBoard = [[null, null, null, 3, null, null, 1, null, null],[5, null, null, 4, null, null, null, 9, null],[null, null, null, null, 2, 8, 6, null, null],[null, 9, null, null, null, null, null, null, 1],[null, null, 8, null, null, 7, null, null, 2],[null, 1, null, null, 4, null, 8, null, null],[null, null, 4, null, 8, 5, null, null, null],[3, null, null, 1, null, null, null, 4, null],[null, null, 2, 6, null, null, null, null, null]
];let currentBoard = JSON.parse(JSON.stringify(originalBoard)); // 当前棋盘状态
// 步数记录:userSteps[row][col] = step(第几步),未填为 null
let userSteps = Array.from({length: 9}, () => Array(9).fill(null)); // 用户每步填写记录
let stepCounter = 1; // 当前步数,从1开始
// 保存被标红的格子,格式如:'row,col'
let redCells = new Set(); // 标记红色警示格
// 计时相关
let timerStart = null; // 计时起点
let timerUsed = 0;     // 用时(秒)// 渲染棋盘主函数
// 根据currentBoard和用户操作,动态生成HTML并绑定事件
function drawBoard() {boardElement.innerHTML = "";for (let row = 0; row < 9; row++) {const tr = document.createElement("tr");for (let col = 0; col < 9; col++) {const td = document.createElement("td");td.dataset.row = row;td.dataset.col = col;// 已确定数字if (currentBoard[row][col]) {const isPreset = originalBoard[row][col];let cellClass = isPreset ? 'sudoku-cell-fixed' : 'sudoku-cell-user';if (redCells.has(row + ',' + col)) {cellClass += ' red';}let stepHtml = '';if (!isPreset && userSteps[row][col] !== null) {stepHtml = `<div class="cell-step">${userSteps[row][col]}</div>`;}td.innerHTML = `<div class="${cellClass}">${currentBoard[row][col]}<br>${stepHtml}</div>`;// 右键取消td.oncontextmenu = function (e) {e.preventDefault();if (!isPreset) {// 记录当前格子的步数const removedStep = userSteps[row][col];currentBoard[row][col] = null;userSteps[row][col] = null;// 所有步数大于 removedStep 的格子步数-1for (let r = 0; r < 9; r++) {for (let c = 0; c < 9; c++) {if (userSteps[r][c] !== null && userSteps[r][c] > removedStep) {userSteps[r][c]--;}}}stepCounter--;redCells.delete(row + ',' + col);drawBoard();}};// 左键在红色与非红色之间切换td.onclick = function (e) {if (e.button === 0) {if (!redCells.has(row + ',' + col)) {redCells.add(row + ',' + col);drawBoard();} else {redCells.delete(row + ',' + col);drawBoard();}}};} else {// 未确定,渲染9小格const miniGrid = document.createElement('div');miniGrid.className = 'sudoku-mini-grid';for (let k = 1; k <= 9; k++) {const miniCell = document.createElement('div');miniCell.className = 'mini-cell';miniCell.textContent = k;// 判断是否可选if (isValidCell(row, col, k)) {miniCell.classList.add('black');// 左键点击将该数字作为单元格值miniCell.onmousedown = function (e) {e.preventDefault();// 仅在首次确定未确定格时启动计时if (currentBoard[row][col] === null && timerStart === null) {timerStart = Date.now();}currentBoard[row][col] = k;userSteps[row][col] = stepCounter++;drawBoard();};} else {miniCell.classList.add('gray');  // 同行、同列或同宫已出现的数字不可用}miniGrid.appendChild(miniCell);}td.appendChild(miniGrid);}tr.appendChild(td);}boardElement.appendChild(tr);}// 检查是否全部填满let allFilled = true;for (let row = 0; row < 9; row++) {for (let col = 0; col < 9; col++) {if (!currentBoard[row][col]) allFilled = false;}}if (allFilled) {if (timerStart !== null) {timerUsed = Math.round((Date.now() - timerStart) / 1000);setTimeout(() => { alert(`恭喜您解决了本题,共计耗时${timerUsed}秒!`); timerStart = null; }, 100);}}
}// 生成唯一解数独新题
// 新游戏按钮事件
// 自动生成一个唯一解的新题目,重置所有状态
newGameBtn.addEventListener("click", async () => {// 生成唯一解数独let puzzle;userSteps = Array.from({ length: 9 }, () => Array(9).fill(null));stepCounter = 1;do {puzzle = generateSudokuPuzzle();} while (!puzzle || countSolutions(puzzle) !== 1);originalBoard = puzzle;currentBoard = JSON.parse(JSON.stringify(originalBoard));redCells.clear();timerStart = null;drawBoard();
});// 高亮显示唯一可用数字
// 高亮显示所有唯一可填数字的格子
highlightUniqueBtn.addEventListener("click", () => {// 遍历所有格子for (let row = 0; row < 9; row++) {for (let col = 0; col < 9; col++) {const cell = getUniqNumberCell(row, col);if (cell) {cell.classList.add('highlight');}}}
});// 生成完整解
// 随机生成一个完整的数独解(9x9的填满且合法的盘面)
// 随机生成一个完整的、合法的数独解(递归回溯)
function generateFullSolution() {// 创建一个9x9的空棋盘,所有格子初始为nulllet board = Array.from({ length: 9 }, () => Array(9).fill(null));// 递归回溯填充函数,从左上角(0,0)开始function fill(row, col) {// 如果行号越界,说明已填满整盘,返回trueif (row === 9) return true;// 计算下一个要填的格子的行列号let nextRow = col === 8 ? row + 1 : row;let nextCol = col === 8 ? 0 : col + 1;// 1~9随机顺序尝试,增加解的多样性let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9].sort(() => Math.random() - 0.5);for (let num of nums) {// 判断num是否可以填入当前格(不违反数独规则)if (isValidForBoard(board, row, col, num)) {board[row][col] = num; // 填入数字// 递归填下一个格子,若成功则整盘可解if (fill(nextRow, nextCol)) return true;board[row][col] = null; // 回溯,撤销填入}}// 1~9都不行,说明此路不通,返回falsereturn false;}// 从(0,0)开始填盘fill(0, 0);return board; // 返回填好的完整解
}// 随机挖空,生成题目
// 由完整解随机挖空,生成唯一解题目
function generateSudokuPuzzle() {let solution = generateFullSolution();let puzzle = JSON.parse(JSON.stringify(solution));// 随机顺序挖空let cells = [];for (let r = 0; r < 9; r++) for (let c = 0; c < 9; c++) cells.push([r, c]);cells = cells.sort(() => Math.random() - 0.5);for (let i = 0; i < 60; i++) { // 最多挖60个空let [r, c] = cells[i];let backup = puzzle[r][c];puzzle[r][c] = null;// 挖空后如果解不唯一,撤回if (countSolutions(puzzle) !== 1) puzzle[r][c] = backup;}return puzzle;
}// 判断唯一解
// 判断给定棋盘的解的个数(递归回溯,计数)
function countSolutions(board) {let count = 0;let b = JSON.parse(JSON.stringify(board));function dfs() {for (let r = 0; r < 9; r++) {for (let c = 0; c < 9; c++) {if (b[r][c] === null) {for (let num = 1; num <= 9; num++) {if (isValidForBoard(b, r, c, num)) {b[r][c] = num;dfs();b[r][c] = null;if (count > 1) return;}}return;}}}count++;}dfs();return count;
}// 检查num能否填入board[row][col](不违反规则)
function isValidForBoard(board, 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 r = 0; r < 3; r++) {for (let c = 0; c < 3; c++) {if (board[boxRow + r][boxCol + c] === num) return false;}}return true;
}// 检查num能否填入当前棋盘currentBoard[row][col]
function isValidCell(row, col, num) {for (let i = 0; i < 9; i++) {// 同行或同列已存在num,则该格中的数不可用if ((i !== col && currentBoard[row][i] === num) ||(i !== row && currentBoard[i][col] === num)) {return false;}}// 同宫中存在num,则该格中的数不可用const boxRow = Math.floor(row / 3) * 3;const boxCol = Math.floor(col / 3) * 3;for (let r = 0; r < 3; r++) {for (let c = 0; c < 3; c++) {const x = boxRow + r;const y = boxCol + c;if (x !== row && y !== col && currentBoard[x][y] === num) {return false;}}}return true;
}// “查看答案”按钮事件
// 自动求解当前题目并展示
answerBtn.addEventListener("click", () => {userSteps = Array.from({ length: 9 }, () => Array(9).fill(null));stepCounter = 1;let solution = solveSudoku(JSON.parse(JSON.stringify(originalBoard)));if (!solution) {return;}currentBoard = solution;redCells.clear();timerStart = null;drawBoard();
});// 求解器部分(回溯算法)
// 回溯算法求解数独,返回解或false
function solveSudoku(board) {function 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 r = 0; r < 3; r++) {for (let c = 0; c < 3; c++) {if (board[boxRow + r][boxCol + c] === num) return false;}}return true;}function backtrack() {for (let row = 0; row < 9; row++) {for (let col = 0; col < 9; col++) {if (board[row][col] === null) {for (let num = 1; num <= 9; num++) {if (isValid(row, col, num)) {board[row][col] = num;if (backtrack()) return true;board[row][col] = null;}}return false;}}}return true;}if (backtrack()) {return board;}return false;
}// 判断整个棋盘是否合法(无重复数字)
function isValidSudoku(board) {const rows = Array.from({ length: 9 }, () => new Set());const cols = Array.from({ length: 9 }, () => new Set());const boxes = Array.from({ length: 9 }, () => new Set());for (let r = 0; r < 9; r++) {for (let c = 0; c < 9; c++) {const val = board[r][c];if (val === null) continue;const boxIndex = Math.floor(r / 3) * 3 + Math.floor(c / 3);if (rows[r].has(val) || cols[c].has(val) || boxes[boxIndex].has(val)) {return false;}rows[r].add(val);cols[c].add(val);boxes[boxIndex].add(val);}}return true;
}/*** 指定(row, col)中如果只有唯一可用的数字,将其高亮* @param {number} row 行号(0-8)* @param {number} col 列号(0-8)* @returns {HTMLElement|null} 唯一的可用数字所在 mini-cell元素,否则null*/
// 获取(row, col)格唯一可填数字的mini-cell元素(若唯一)
function getUniqNumberCell(row, col) {// 获取对应tdconst tr = boardElement.querySelectorAll('tr')[row];if (!tr) return null;const td = tr.querySelectorAll('td')[col];if (!td) return null;// 找到mini-gridconst miniGrid = td.querySelector('.sudoku-mini-grid');if (!miniGrid) return null;// 找到所有可用数字所在mini-cell。参见drawBoard(),可用数字所在mini-cell类名为blackconst blackCells = miniGrid.querySelectorAll('.mini-cell.black');if (blackCells.length === 1) {return blackCells[0];}return null;
}// 初始化9x9表格
// 初始化自定义题目输入表格(9x9输入框)
function initGridTable() {// 清空表格(避免重复添加)inputTtable.innerHTML = '';for (let i = 0; i < 9; i++) {const tr = document.createElement('tr');for (let j = 0; j < 9; j++) {const td = document.createElement('td');const input = document.createElement('input');input.type = 'text';input.classList.add('cell');input.maxLength = 1;// 输入验证:当输入框失去焦点时验证input.addEventListener('blur', function () {const value = this.value.trim();if (value !== '' && (value < '1' || value > '9')) {alert('请输入1-9之间的数字!');this.value = '';}});td.appendChild(input);tr.appendChild(td);}inputTtable.appendChild(tr);}
}// 打开对话框
// 打开自定义题目输入对话框
openBtn.addEventListener('click', () => {// 初始化表格(如果还没有初始化的话)initGridTable();inputDialog.showModal();
});// 提交按钮点击事件,将输入的数据保存到originalBoard数组中
// 提交自定义题目,校验唯一解并作为新题载入
submitBtn.addEventListener('click', () => {let boardconst rows = inputTtable.querySelectorAll('tr');for (let i = 0; i < rows.length; i++) {const cells = rows[i].querySelectorAll('input');for (let j = 0; j < cells.length; j++) {const value = cells[j].value.trim();currentBoard[i][j] = (value === '' ? null : parseInt(value, 10));}}// 关闭输入题目对话框inputDialog.close();if (isValidSudoku(currentBoard)) {if (countSolutions(currentBoard) === 1) {// 所出的题有且只有唯一解,初始化相关变量,开始新游戏originalBoard = JSON.parse(JSON.stringify(currentBoard));userSteps = Array.from({ length: 9 }, () => Array(9).fill(null));stepCounter = 1;redCells.clear();timerStart = null;timerUsed = 0;// 重绘盘面drawBoard();} else {  // 无解或有多个解,返回原盘面alert('此题不是有且只有唯一解');return;}} else {  // 出的题中同行、同列或同宫有相同数字,返回原盘面alert('此题不合法');return;}
});drawBoard();

本文代码在CSDN的C知道生成的单道数独题做题界面的代码框架基础上改进和增加功能而成。

使用技巧:

1、先把所有只有单个数字可用的单元格全部确定;

2、利用所掌握的技巧将所有可确定数字的单元格全部确定,技巧掌握得少不要紧,不用技巧都可以,不过花的时间稍多;

3、重复1,2,直至再没有可确定的单元格,然后选择只有两个数字的单元格,随便选一个数,然后再点击让它变红,然后又重复1、2步;

4、出现某个单元格无数字可填的时候,撤销到上一步标记红色的单元格中的步数,然后换该单元格中的另一个数,这次不用标记红色了,然后重复1,2,3。如果所有单元格都至少有两个数字可填,按步骤3的做法在只有两个候选数的单元格中随便选一个,标记红色,然后重复1、2步。出现第4步第一种情形,撤销到后一个红色标记单元格中的步数,再重复1,2,3,4步,直至完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88365.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88365.shtml
英文地址,请注明出处:http://en.pswp.cn/web/88365.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式硬件中电容的基本原理与实现详解02

我们今天重点讨论点知识点如下: 1.各种种类的电容优缺点对比讲解 2.电容的标称值介绍 3.电容的单位介绍 4.常见的电压信号有哪些? 5. 电容的耐压值讲解 6.电容的容值有哪些? 7.12pF、15pF 电容常用在什么场合? 8. 振荡电路中使用的电容常常需要使用什么材质的电容? 9.100n…

Python训练打卡DAY46

DAY46&#xff1a;通道注意力&#xff08;SE注意力&#xff09; 恩师浙大疏锦行 知识点&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通…

fastadmin_php专项

1.时间的判断,还有就是在php这边如何去拿前端html元素上面的值input($row.borrowtime);// 创建两个 DateTime 对象$row_expecttime new \DateTime(input($row.borrowtime));$par_expecttime new \DateTime( $params[expecttime]); // // 计算两个日期之间的差异 // …

如何在MySQL中选择使用InnoDB还是MyISAM引擎?

在 MySQL 中选择 InnoDB 还是 MyISAM 存储引擎时&#xff0c;需根据应用场景的需求权衡功能、性能和数据完整性。以下是具体的选择指南&#xff1a; 1. 优先考虑事务和外键需求必须使用 InnoDB&#xff1a; 若应用需要 事务支持&#xff08;如金融转账、订单处理&#xff09;或…

邀请函 | 知从科技邀您共赴2025 RISC-V 中国峰会

第五届RISC-V中国峰会将于2025年7月16至19日在上海张江科学会堂隆重举办&#xff0c;本届峰会由上海开放处理器产业创新中心&#xff08;SOPIC&#xff09;主办&#xff0c;RISC-V国际开源实验室&#xff08;RIOS实验室&#xff09;和上海张江高科技园区开发股份有限公司联合主…

企业数字化转型规划和建设方案(管理架构、应用架构、技术架构)PPT

一、战略定位与核心目标以 “技术赋能业务&#xff0c;数据驱动创新” 为核心思路&#xff0c;构建 “三步走” 战略演进路径&#xff0c;实现 IT 从 “基础支撑” 到 “战略引擎” 的升级&#xff1a;IT1.0&#xff08;1-2 年&#xff09;&#xff1a;夯实基础能力定位 “稳健…

基于Uniapp+MySQL+PHP的景区多商户小程序源码系统 带完整的搭建指南

温馨提示&#xff1a;文末有资源获取方式该系统采用 PHP MySQL 的经典开发组合。PHP 作为一种广泛使用的开源脚本语言&#xff0c;具有简单易学、运行速度快、跨平台性强等优点&#xff0c;能够快速开发出功能强大的 Web 应用程序。MySQL 则是一款稳定可靠的关系型数据库管理系…

阿里云和腾讯云RocketMQ 发消息和消费消息客户端JAVA接口

一、RocketMQ 概述RocketMQ 是阿里巴巴开源的一款分布式消息中间件&#xff0c;后捐赠给 Apache 基金会成为顶级项目。它具有低延迟、高并发、高可用、高可靠等特点&#xff0c;广泛应用于订单交易、消息推送、流计算、日志收集等场景。核心特点分布式架构&#xff1a;支持集群…

Vue响应式原理六:Vue3响应式原理

1. 多个对象响应式当前存在的问题&#xff1a;当前实现仅针对某个固定对象&#xff08;obj&#xff09;进行依赖收集&#xff0c;实际开发中需要处理多个不同对象将对象响应式处理逻辑抽取为通用函数&#xff0c;支持任意对象代码如下&#xff1a; // 方案一&#xff1a;Obje…

【算法笔记 day three】滑动窗口(其他类型)

hello大家好&#xff01;这份笔记包含的题目类型主要包括求子数组已经一些比较‘小众’的题目。和之前一样&#xff0c;笔记中的代码和思路要么是我手搓要么是我借鉴一些大佬的想法转化成自己的话复现。所以方法不一定是最好的&#xff0c;但一定是经过我理解的产物&#xff0c…

docker-镜像管理指南

在本节中&#xff0c;我们将详细介绍 Docker 镜像的常用命令&#xff0c;帮助您更好地管理和操作镜像。以下是核心命令及其功能说明&#xff1a;1.使用"ls"查看镜像列表#查看现有的镜像列表[rootdocker01 ~]# docker images [rootdocker01 ~]# docker image ls#仅查看…

Mac 电脑无法读取硬盘的解决方案

引言近年来&#xff0c;选择使用 Mac 电脑的用户越来越多&#xff0c;尤其是在设计、开发、剪辑、文档处理等领域&#xff0c;macOS 凭借其优秀的系统生态与硬件体验吸引了大量拥趸。与此同时&#xff0c;对于摄影师、剪辑师、程序员、学生等用户来说&#xff0c;一块移动硬盘往…

25春期末考

web 疯狂星期四 先来看一下源码 分析代码的黑名单后得知 我们可以用的字符就只剩下 字母a-z(大小写均可) 数字2 空格 这里的限制太多了 这里比较常用的getallheaders被ban掉了 这里就是用session来做 session_start()开启session session_id()获取session 这里我们要构造一…

时间显示 蓝桥云课Java

目录 题目链接 题目 解题思路 代码 题目链接 竞赛中心 - 蓝桥云课 题目 解题思路 通过%天数,得到一天内的时间,然后/小时单位(换算成毫秒的)得到小时,然后总数减去该小时,得到分钟数,秒数同理 代码 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不…

STM32F1控制步进电机

一、基础知识1. 步进电机控制方式脉冲方向控制&#xff08;最常见&#xff09;控制信号&#xff1a;DIR方向&#xff1a;高低电平决定正转或反转&#xff1b;STEP脉冲&#xff1a;每个脉冲电机前进一步&#xff08;可通过端口拉高拉低来模拟脉冲&#xff0c;或使用pwm来生成脉冲…

Docker 容器部署脚本

#!/bin/bash# # Author: ldj # Date: 2025-07-08 15:37:11 # Description: 首先删除旧的容器和镜像&#xff0c;然后登录到 Harbor 并拉取最新的镜像进行部署 # # 显示每条命令执行情况&#xff0c;便于调试 set -x harbor_addr$1 harbor_repo$2 project_name$3 version$4 po…

OpenCV 4.10.0 移植 - Android

前文: Ubuntu 编译 OpenCV SDK for Android Linux OpenCV 4.10.0 移植 概述 在移动应用开发领域&#xff0c;Android平台与OpenCV库的结合为开发者提供了强大的图像处理和计算机视觉能力。OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件…

go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南

go go go 出发咯 - go web开发入门系列&#xff08;二&#xff09; Gin 框架实战指南 往期回顾 go go go 出发咯 - go web开发入门系列&#xff08;一&#xff09; helloworld 前言 前一节我们使用了go语言简单的通过net/http搭建了go web服务&#xff0c;但是仅使用 Go 的标…

编译OpenHarmony-4.0-Release RK3566 报错

编译OpenHarmony-4.0-Release RK3566 报错1. 报错问题2.问题解决3.解决方案4.​调试技巧​subsystem name config incorrect in ‘/home/openharmony/OpenHarmony/vendor/kaihong/khdvk_356b/bundle.json’, build file subsystem name is kaihong_products,configured subsy1.…

【PTA数据结构 | C语言版】线性表循环右移

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 给定顺序表 A(a1​,a2​,⋯,an​)&#xff0c;请设计一个时间和空间上尽可能高效的算法将该线性表循环右移指定的 m 位。例如&#xff0c;(1,2,5,7,3,4,6,8) 循环右移 3 位&#xff08;m3) 后的结果…