当前位置: 首页 > news >正文

【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)

文章目录

  • DIY 实战:从扫雷小游戏开发再探问题分解能力
    • 3 问题分解实战(自顶向下)
      • 3.2 页面渲染逻辑
      • 3.3 事件绑定逻辑
    • 4 代码实现(自底向上)
      • 4.1 页面渲染部分
      • 4.2 事件绑定部分

写在前面
本篇将利用《Learn AI-assisted Python Programming》第七章介绍的问题分解方法,完成简版扫雷游戏的后续逻辑分解。由于篇幅过长,与 AI 相关的具体交互过程和小结复盘留到下篇介绍,敬请关注!

DIY 实战:从扫雷小游戏开发再探问题分解能力

3 问题分解实战(自顶向下)

3.2 页面渲染逻辑

(接 上篇)…… init() 的拆分就暂告一个段落了,如下图所示:

start
init
bindEvents
renderGameBoard
renderStatsInfo
generateMineCells

图 4 初步确定的 init() 函数拆分方案

3.3 事件绑定逻辑

虽然页面上划分了三个区域:难度选择区、地雷统计区、扫雷面板区,但实际需要绑定事件的只有两个,统计区的数据更新是和游戏面板同步的,因此只拆成两个子函数即可:

bindEvents
bindLevelButtonActions
bindCellAction

先看难度选择区的事件绑定逻辑 bindLevelButtonActions(),这个比较容易,通过切换一个标识类 active 控制按钮本身的样式,然后再触发页面初始化函数 start(currentLv) 即可。

重点是每个单元格的事件绑定,这是整个扫雷游戏最核心的部分,需要仔细讨论每一种可能出现的状态。注册事件首选 mousedown,这样可以很方便地利用 event.which 属性知晓鼠标点击的具体按键:

  • event.which1 表示按下了鼠标左键;
  • event.which2 表示按下了鼠标滚轮(一般很少用到);
  • event.which3 表示按下了鼠标右键;

由于状态较多,这里建议使用排除法,先把旁枝末节的情况排除掉,剩下的就是核心逻辑了:

  1. 首先是禁用鼠标右键菜单;
  2. 接着禁用鼠标滚轮操作;
  3. 如果该单元格已经点开了(即不是地雷,且已经用左键点过的安全单元格),就直接中止后续操作;
  4. 对于未考察的单元格,分两种情况:
    1. 如果按下的是鼠标右键,则通过标注地雷加上小旗图标,同时更新地雷统计数据;
    2. 如果按下的是鼠标左键,则又分三种情况:
      1. 如果已经标记为地雷,则中止操作;
      2. 如果是地雷,则公布所有地雷,禁用所有单元格点击,并提示游戏失败;
      3. 如果不是地雷,再分两种情况:
        1. 周围八个单元格存在地雷,则根据具体数量添加不同的样式类,标记出具体数字;
        2. 周围不存在地雷,则依次遍历每一个周边单元格,再次按当前按下的是左键(即 3.2 步)进行递归检索;

绘制流程图如下:

触发 mousedown 事件
禁用右键菜单
禁用滚轮点击
单元格已点开
中止操作
未考察单元格
按下的是右键
按下的是左键
1标注地雷/小旗图标
2更新地雷统计数据
已标记为地雷
是否为地雷
中止操作
公布所有地雷
禁用所有点击
提示游戏失败
周围有地雷
添加数字样式类
显示周围地雷数量
遍历周边单元格
递归执行

因此,事件绑定函数可以拆分成这几个部分:

为右键
为左键
有雷
无雷
bindEvents
bindLevelButtonActions
bindCellMousedownActions
handlePopupAndScrollWheel
判定左右键
handleRightClick
判定是否为地雷
showMinesAndCleanup
周围是否有雷
renderMineCount
searchAround递归调用

这样一来,事件绑定的问题分解就全部完成了。

4 代码实现(自底向上)

终于来到激动人心的代码实现环节了!根据刚才的分解情况,按照自底向上依次实现各个叶子级功能点:

4.1 页面渲染部分

先是页面渲染的三个子函数:

init
renderGameBoard
renderStatsInfo
generateMineCells

对应代码:

let mineFound = 0;
function init(lv) {// 1. create table elementsconst doms = renderGameBoard(lv);// 2. render stats info$('#mineCount').innerHTML = lv.mine;$('#mineFound').innerHTML = mineFound;// 3. create mine arrayconst mines = generateMineCells(lv, doms);return mines;
}

先实现页面单元格的动态渲染函数 renderGameBoard(lv)

function renderGameBoard({ row, col }) {const table = $('.gameBoard');table.innerHTML = ''; // reset table contentconst fragment = document.createDocumentFragment();for (let i = 1; i <= row; i++) {const tr = document.createElement('tr');for (let j = 1; j <= col; j++) {const td = document.createElement('td');td.dataset.id = `${i},${j}`;td.classList.add('cell');tr.appendChild(td);}fragment.appendChild(tr);}table.appendChild(fragment);return $$('.cell');
}

注意:

  1. td.dataset.id 设置的是单元格 ID 坐标,它和状态矩阵总编号之间转换关系可以写入 utils.js 工具模块:

    /*** Converts a 2D array index to a 1D array index.* @param {string} ij  The string representation of the 2D index, e.g., "1,2".* @param {number} col The number of columns in the 2D array.* @returns {number} The 1D index corresponding to the 2D index.*/
    export function getId(ij, col) {const [i, j] = ij.split(',').map(n => parseInt(n, 10));return (i - 1) * col + j;
    }/*** Converts a 1D array index to a 2D array index.* @param {number} id The 1D index.* @param {number} col The number of columns in the 2D array.* @returns {Array<number>} An array containing the row and column indices.*/
    export function getIJ(id, col) {const j = id % col === 0 ? col : id % col;const i = (id - j) / col + 1;return [i, j];
    }
    
  2. $$ 是一个简化后的工具函数,从 utils.js 模块导入:

    /*** Selects all elements matching a CSS selector.* @param {string} selector The CSS selector to match elements.* @returns {NodeList} A NodeList of elements matching the selector.*/
    export const $$ = document.querySelectorAll.bind(document);
    

接着实现地雷统计指标的初始化 renderStatsInfo。由于只有两句话,因此不单独创建新的子函数:

// 2. render stats info
$('#mineCount').innerHTML = lv.mine;
$('#mineFound').innerHTML = mineFound;

然后是渲染部分的最后一项 generateMineCells(lv, doms)

function generateMineCells(lv, doms) {// 1. create mine cellsconst mines = initMineCells(lv);// 2. populate neighboring idspopulateNeighboringIds(mines, lv, doms);return mines;
}

这里之所以又分出两个子函数,是因为实现过程中发现可以将部分点击事件的逻辑(例如计算周边区域的地雷数)分摊到状态矩阵的初始化来处理,没必要在每次按下鼠标时再算。因此,对于每个状态矩阵的元素而言,还应该有个新增属性 neighbors,用于存放周边元素 ID 的子数组。于是 initMineCells 负责生成状态矩阵,populateNeighboringIds 负责填充每个状态元素的初始状态值(当前位置的周边地雷数、紧邻单元格的 ID 数组):

function initMineCells({row, col, mine}) {const size = row * col; // total number of cellsconst mines = range(size).sort(() => Math.random() - 0.5).slice(-mine);// console.log(mines);const mineCells = range(size).map(id => {const isMine = mines.includes(id),mineCount = isMine ? 9 : 0,neighbors = isMine ? null : []; // neighbors of the cellreturn {id,isMine,mineCount,neighbors, // neighbors of the cellchecked: false, // whether the cell is checked or notflagged: false // whether the cell is flagged or not};});return mineCells;
}

上述代码有两个地方需要注意:

  1. 地雷的乱序算法:使用随机值实现:() => Math.random() - 0.5

  2. 快速生成 [1, n] 的正整数数组:

    /*** Generates an array of numbers from 1 to size (inclusive).* @param {number} size The size of the range.* @returns  {Array<number>} An array of numbers from 0 to size.*/
    export function range(size) {return [...Array(size).keys()].map(n => n + 1);
    }
    

紧接着填充状态值 mineCountneighbors

function populateNeighboringIds(mineCells, {col, row}, doms) {const safeCells = mineCells.filter(({ isMine }) => !isMine);// 1. Get neighbor ids for each cellsafeCells.forEach(cell => {const [i, j] = getIJ(cell.id, col);for (let r = Math.max(1, i - 1), rows = Math.min(row, i + 1); r <= rows; r++) {for (let c = Math.max(1, j - 1), cols = Math.min(col, j + 1); c <= cols; c++) {if (r === i && c === j) continue;const neighborId = getId(`${r},${c}`, col);cell.neighbors.push(neighborId);}}});// 2. Calculate total number of neighboring minessafeCells.forEach(cell => {// get neighbor ids for each cellconst mineCount = cell.neighbors.reduce((acc, neighborId) => {const {isMine} = mineCells[neighborId - 1];return acc + (isMine ? 1 : 0);}, 0);cell.mineCount = mineCount;});
}

注意,这里出现了第一个比较繁琐的逻辑(L6–L7):判定周边单元格的上、下、左、右边界。如果当前单元格坐标为 (i, j),不考虑雷区边框的情况下,其周边单元格的行号范围是 [i-1, i+1]、列数范围是 [j-1, j+1]。现在考虑边框,则需要用 Math.maxMath.min 限制一下。这个写法其实是 Copilot 根据我的注释自动生成的。可见 Copilot 在小范围内对这样非常确定的需求理解得很到位,我们只需要略微检查一下边界条件的取值就行了。

4.2 事件绑定部分

再来回顾一下事件绑定逻辑的总结构:

为右键
为左键
有雷
无雷
bindEvents
bindLevelButtonActions
bindCellMousedownActions
handlePopupAndScrollWheel
判定左右键
which = 3
handleRightClick
判定是否为地雷
cell.isMine = true
showMinesAndCleanup
周围是否有雷
mineCount = 0
renderMineCount
searchAround
递归调用

由于层次过深,盲目按照自底向上的思路实现子函数可行性不大,因为还没有对每个函数的参数及返回值做进一步确认。因此这里还是自顶向下实现。

先来看最外层:

function bindEvents(lv, mines) {// when selecting a levelbindLevelButtonActions();// when clicking on the game board$('.gameBoard').onmousedown = (ev) => {ev.preventDefault();if(ev.target.classList.contains('gameBoard')) {// 禁用 table 元素上的右键菜单ev.target.oncontextmenu = (e) => {e.preventDefault();};}};// when clicking on cellsbindCellMousedownActions(lv, mines);
}

之所以中间多了一部分,是因为实测时发现单元格之间还存在少量间隔,如果不小心在这些地方点击右键,仍然会出现上下文菜单,因此特地做了补救。

接着先来实现难度选择区的事件绑定:

function bindLevelButtonActions() {const btns = Array.from($$('.level [data-level]'));btns.forEach((btn, _, arr) => {btn.onclick = ({ target }) => {// toggle active classarr.forEach(bt => (target !== bt) ?bt.classList.remove('active') :bt.classList.add('active'));// reset mines foundmineFound = 0;currentLv = getCurrentLevel(target.dataset.level);// reload gamestart(currentLv);};});// when clicking on the restart button$('.restart').onclick = (ev) => {ev.preventDefault();mineFound = 0; // reset mine found countstart(currentLv);ev.target.classList.add('hidden');};
}

这里又增补了一个按钮:.restart。这是每局结束时才会出现的按钮,专门用于重新开始游戏。同理,也是实测时发现的细节。

最后是扫雷区的鼠标事件 bindCellMousedownActions

function bindCellMousedownActions(lv, mines) {// when clicking on cells$$('.cell').forEach(cell => {cell.onmousedown = ({ target, which }) => {// 禁用右键菜单target.oncontextmenu = e => e.preventDefault();// 禁用鼠标滚轮if(which === 2) return;const cellObj = findMineCellById(target.dataset.id, lv, mines);if(cellObj.checked) {// already checked or flaggedconsole.log('Already checked, abort');return; }if (which === 3) {// 右击:添加/删除地雷标记handleRightClick(target, lv, mines);return;}if (which === 1) {// 左击// 1. 如果已插旗,则不处理if (cellObj.flagged) {console.log('Already flagged, abort');return;}// 2. 踩雷,游戏结束:if (cellObj.isMine) {showMinesAndCleanup(target, mines, lv);// 提示重启游戏setTimeout(() => {$('.restart').classList.remove('hidden');alert('游戏结束!你踩到地雷了!');}, 0);return;}// 3. 若为安全区域,标记为已检查searchAround(cellObj, target, lv.col, mines);// 4. 查看是否胜利const allChecked = mines.filter(e => !e.isMine && !e.checked).length === 0;if (allChecked) {congratulateVictory(mines, lv);}}};});
}

根据问题拆分情况,这里又分出了 5 个具体的子函数,除了 findMineCellById 是临时新增外,其余都是游戏运行必不可少的核心逻辑:

// 1. 根据单元格坐标 id 获取对应的状态矩阵元素
function findMineCellById(id, {col}, mines) {const index = getId(id, col) - 1;return mines[index];
}// 2. 右键标记为地雷以及取消地雷标记的处理逻辑
function handleRightClick(target, lv, mines) {target.classList.toggle('mine');target.classList.toggle('ms-flag');const cellObj = findMineCellById(target.dataset.id, lv, mines);cellObj.flagged = !cellObj.flagged; // toggle flagged status// 更新地雷标记数if (cellObj.flagged) {$('#mineFound').innerHTML = (++mineFound);} else {$('#mineFound').innerHTML = (--mineFound);}
}// 3. 踩到地雷时的处理逻辑
function showMinesAndCleanup(target, mines, lv) {// 1. 标记当前踩雷的单元格target.classList.add('fail');// 2. 公布所有地雷showFinalResult(mines, lv);
}// 4. 游戏胜利的处理逻辑
function congratulateVictory(mines, lv) {showFinalResult(mines, lv);setTimeout(() => {alert('恭喜你,成功扫除所有地雷!');$('.restart').classList.remove('hidden');}, 0);
}
function showFinalResult(mines, lv) {// 1. 渲染出所有地雷renderAllMines(mines, lv.col);// 2. 标记所有单元格为已检查(防止误操作)mines.forEach(mine => mine.checked = true);// 3. 所有标记正确的单元格背景色变为绿色renderAllCorrectFlagged(mines, lv);
}// 5. 当前单元格及周边都没有地雷时的处理逻辑
function searchAround(curCell, curDom, colSize, mines) {curCell.checked = true;// Render the current cellcurDom.classList.add('number', `mc-${curCell.mineCount}`);curDom.innerHTML = curCell.mineCount;// 如果是空白单元格,则递归显示周围的格子,直到遇到非空白单元格if (curCell.mineCount === 0) {curDom.innerHTML = '';curCell.neighbors.forEach(nbId => {const nbCell = mines[nbId - 1];const nbDom = $(`[data-id="${getIJ(nbId, colSize)}"]`);if(!nbCell.checked && !nbCell.flagged && !nbCell.isMine) {searchAround(nbCell, nbDom, colSize, mines);}});}
}

这样就实现了所有的处理逻辑,完整代码及最终页面已经放到了 InsCode 上,感兴趣的朋友可以 Fork 到本地试试。

本来计划把后续和 Copilot 的交互过程也梳理一下,结果又写了这么多内容,只有放到下一篇继续了。

(未完待续)

相关文章:

【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)

文章目录 DIY 实战&#xff1a;从扫雷小游戏开发再探问题分解能力3 问题分解实战&#xff08;自顶向下&#xff09;3.2 页面渲染逻辑3.3 事件绑定逻辑 4 代码实现&#xff08;自底向上&#xff09;4.1 页面渲染部分4.2 事件绑定部分 写在前面 本篇将利用《Learn AI-assisted Py…...

【数据可视化-27】全球网络安全威胁数据可视化分析(2015-2024)

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

Cephalon端脑云:神经形态计算+边缘AI·重定义云端算力

前引&#xff1a;当算力不再是“奢侈品” &#xff0c;在人工智能、3D渲染、科学计算等领域&#xff0c;算力一直是横亘在个人与企业面前的“高墙”。高性能服务器价格动辄数十万元&#xff0c;专业设备维护成本高&#xff0c;普通人大多是望而却步。然而&#xff0c;Cephalon算…...

CSS简单实用的加载动画、骨架屏有效果图

效果图 .wxml <!-- 骨架屏 --> <view wx:for"{{skeleton}}" wx:key"index" class"container center" style"--w:{{item.w}}rpx;--h:{{item.h}}rpx" /> <!-- 加载 --> <view class"arco-loading center&quo…...

图论算法体系:并查集、生成树、排序与路径搜索全解析

从图论的基础理论入门&#xff0c;到深搜广搜搭建起图论的骨架。 从并查集到最小生成树&#xff0c;从拓扑排序到最短路径。 .... 群星璀璨&#x1f609; 并查集最小生成树 Prim算法Kruskal算法 拓扑排序&#xff08;kahn算法&#xff09;最短路径 Dijkstra算法 Dijkstra朴素Di…...

OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑

目录 引言&#xff1a;一场蓄谋已久的"蛇吞象"计划 一、Chrome&#xff1a;数字世界的"黄金入口" 1.1 用户规模对比&#xff1a;ChatGPT与Chrome的悬殊差距 1.2 Chrome的生态价值远超浏览器本身 二、OpenAI的"入口焦虑"与战略布局 2.1 AI时…...

DrissionPage 请求一次换一个代理(不重启chrome)

实现原理&#xff1a;通过插件实现 # !/usr/bin/python3 # -*- coding:utf-8 -*- """ author: JHC000abcgmail.com file: switch_ip.py time: 2025/4/23 22:05 desc:"""R""" 1. chrome s商店下载Proxy SwitchyOmega 3 (ZeroOme…...

JBoltAI 赋能金融文档:基于 RAG 的基金招募说明书视觉增强方案

在金融领域&#xff0c;基金招募说明书是投资者了解基金产品关键信息的重要文件。然而&#xff0c;这类文件通常以 PDF 格式呈现&#xff0c;内容繁杂、文本枯燥&#xff0c;对于普通投资者而言&#xff0c;理解起来存在一定难度。而如何利用 AI 技术对这类枯燥文本进行视觉增强…...

【玩转全栈】—— Django+vue3+讯飞星火API 实现前端页面实时AI答复

技术栈&#xff1a;vue3 element-plus axios pinia router Django5 websocket 讯飞星火API 本文将实现一个 AI 聊天对话功能&#xff0c;将前端用户输入问题以及之前对话发送给后端&#xff0c;通过 api 访问大模型&#xff0c;返回前端实时对话数据。 调用 讯飞星火API…...

1.1 java开发的准备工作(入门)

准备工作 一.JDK 开始写java程序之前需要安装jdk jdk是java开发工具&#xff0c;包含着JRE和里面的JVM(虚拟机&#xff0c;可以使得不同环境下都能运行Java程序)&#xff0c;和开发工具。 二.了解写程序的三大步骤步骤 java成功运行主要需要经过代码编写&#xff0c;编译&a…...

socket编程基础

上一篇 --- 网络基础概念&#xff08;下&#xff09;https://blog.csdn.net/Small_entreprene/article/details/147320155?fromshareblogdetail&sharetypeblogdetail&sharerId147320155&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link 理…...

根据定义给出json_schema:

根据您提供的智能体定义&#xff0c;以下是符合JSON Schema Draft-07规范的完整架构描述&#xff08;包含中文注释说明&#xff09;&#xff1a; {"$schema": ""title": "智能体架构规范","type": "object","req…...

深入微服务核心:从架构设计到规模化

作者&#xff1a;腾讯云开发者 原文&#xff1a;深入微服务核心&#xff1a;从架构设计到规模化 01 微服务 什么是微服务&#xff1f; 微服务就是一些协同工作的小而自治的服务。我们在一个单体系统中&#xff0c;通常会采用一些抽象层或者模块来保证代码的内聚性&#xff0c…...

linux与c语言基础知识(未全部完成)

文章很多处理论&#xff0c;没办法写出来&#xff0c;&#xff08;linux的一些理论问题&#xff0c;我有时间后&#xff0c;会逐个解决&#xff09; 文章大多数的理论来字这个链接&#xff0c; C语言快速入门-C语言基础知识-CSDN博客 一. linux&#xff08;Ubuntu&#xff09; …...

【专题刷题】滑动窗口(四):

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…...

小白自学python第一天

学习python的第一天 一、常用的值类型&#xff08;先来粗略认识一下~&#xff09; 类型说明数字&#xff08;number&#xff09;包含整型&#xff08;int&#xff09;、浮点型&#xff08;float&#xff09;、复数&#xff08;complex&#xff09;、布尔&#xff08;boolean&…...

Redis 服务自动开启、设置密码和闪退问题

一、Redis 服务自动开启 1、以管理员身份运行命令提示符 右键点击“命令提示符”图标&#xff0c;选择“以管理员身份运行”。 2、注册为 Windows 服务 redis-server --service-install 3、启动服务 redis-server --service-start 4、测试 Redis 连接 redis-cli ping …...

2025年渗透测试面试题总结-拷打题库14(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年渗透测试面试题总结-拷打题库14 1. WAF存在的意义 2. 威胁感知能力衡量指标 3. 感知规则有效性…...

java后端开发day35--集合进阶(四)--双列集合:MapHashMapTreeMap

&#xff08;以下内容全部来自上述课程&#xff09; 1.双列集合 1.1 特点 双列集合一次需要存一对数据&#xff0c;分别为键和值键不能重复&#xff0c;值可以重复键和值是一一对应的&#xff0c;每一个键只能找到自己对应的值键值这个整体&#xff0c;我们称之为“键值对”…...

进行网页开发时,怎样把function()中变量值在控制台输出,查看?

在网页开发过程中&#xff0c;为了及时了解JavaScript中的function函数中的变量值&#xff0c;可以用控制台命令console.log()把变量的值在控制台输出&#xff0c;方便调试时对函数变量值进行了解。 看下面的一段示例&#xff1a; <!DOCTYPE html> <html> &l…...

【计算机网络】现代网络技术核心架构与实战解析

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现案例1&#xff1a;TCP服务端/客户端通信案例2&#xff1a;Wireshark抓包分析 三、性能对比测试方法…...

Python内置函数---bool()

用于将任意对象转换为布尔值&#xff08;True或False&#xff09; 1. 基本语法与参数 bool(x) - 参数&#xff1a;x为可选参数&#xff0c;可以是任意Python对象&#xff08;如数值、字符串、列表、自定义对象等&#xff09;。 - 返回值&#xff1a;根据x的真值性返回True或Fa…...

Vue 3中如何封装API请求:提升开发效率的最佳实践

在现代前端开发中&#xff0c;API请求是不可避免的一部分&#xff0c;尤其是与后端交互时。随着Vue 3的广泛应用&#xff0c;如何高效地封装API请求&#xff0c;既能提升代码的可维护性&#xff0c;又能确保代码的高复用性&#xff0c;成为了很多开发者关注的话题。 在本文中&…...

【Redis】redis主从哨兵

Redis 主从复制 在访问量极高的场景下&#xff0c;单台 Redis 已难以承载所有请求&#xff0c;且单点故障风险高。通过主从复制&#xff0c;可以实现读写分离、数据备份与高可用。 概念 主节点&#xff08;Master&#xff09;&#xff1a;负责写操作&#xff0c;将数据变更同…...

16.第二阶段x64游戏实战-分析二叉树结构

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;15.第二阶段x64游戏实战-分析怪物血量&#xff08;遍历周围&#xff09; 首先通…...

vue | 不同 vue 版本对复杂泛型的支持情况 · vue3.2 VS vue3.5

省流总结&#xff1a;defineProps 的泛型能力&#xff0c;来直接推导第三方组件的 props 类型 引入第三方库的类型&#xff0c;并直接在 <script setup> 中作为 props 使用。这种类型一般是复杂泛型&#xff08;包含联合类型、可选属性、交叉类型、条件类型等&#xff0…...

OpenGL学习笔记(Blinn-Phong、伽马矫正、阴影)

目录 Blinn-PhongGamma矫正GammaGamma矫正实现方法sRGB纹理衰减 阴影shadow mapping渲染阴影改进阴影贴图PCF GitHub主页&#xff1a;https://github.com/sdpyy1 OpenGL学习仓库:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdp…...

GPLT-2025年第十届团体程序设计天梯赛总决赛题解(2025天梯赛题解,266分)

今天偶然发现天梯赛的代码还保存着&#xff0c;于是决定写下这篇题解&#xff0c;也算是复盘一下了 L1本来是打算写的稳妥点&#xff0c;最后在L1-6又想省时间&#xff0c;又忘记了insert&#xff0c;replace这些方法怎么用&#xff0c;也不想花时间写一个文件测试&#xff0c…...

day4 pandas学习

%pip install openxyxl 找一个自己觉得有意思的文件。我找的是成绩单来玩。 这节学的比较耗时了&#xff0c;大概用了60分钟。 import pandas as pd data2 pd.read_csv(rD:\python代码区\代码随想录挑战-调试区\python训练营\1_计算类专业分流学生成绩排名.csv) #print(data)…...

【Java学习笔记】循环结构

循环结构 一、for循环 for循环结构 for(循环变量初始化;循环条件;循环变量迭代){循环操作&#xff08;可以多条语句&#xff09; }for循环写死循环 for(;;){语句 }注意点&#xff1a;循环变量的初始化在for语句内&#xff0c;属于是局部变量&#xff0c;在全局中会出现未定义…...

URP-UGUI交互功能实现

一、非代码层面实现交互&#xff08;SetActive&#xff09; Button &#xff1a;在OnClick&#xff08;&#xff09;中添加SetActive方法&#xff08;但是此时只首次有效&#xff09; Toggle &#xff1a;在OnClick&#xff08;&#xff09;中添加动态的SetActive方法 &#…...

08-IDEA企业开发工具-集成AI插件通义灵码

需要登陆才可使用&#xff01;&#xff01;&#xff01; 1. 安装AI编程插件 找到插件: 在IDEA的设置中&#xff0c;找到插件&#xff08;Plugins&#xff09;部分。安装插件: 搜索“通义灵码”&#xff0c;找到后点击安装&#xff08;Install&#xff09;&#xff0c;接受条款…...

解决报错:this[kHandle] = new _Hash(algorithm, xofLen);

前端项目编译报错&#xff1a; node:internal/crypto/hash:68this[kHandle] new _Hash(algorithm, xofLen);^Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:68:19)at Object.createHash (node:crypto:138:10)at modu…...

使用 Streamlit 打造一个简单的照片墙应用

在现代 web 开发中&#xff0c;快速构建交互式应用是一项重要的技能。Streamlit 是一个强大的 Python 库&#xff0c;允许开发者以最小的代码量创建美观且功能丰富的 web 应用。今天&#xff0c;我们将通过分析一段简单的 Streamlit 代码&#xff0c;展示如何构建一个照片墙应用…...

深度学习优化器和调度器的选择和推荐

一、常用优化器对比 1. 随机梯度下降&#xff08;SGD&#xff09; 原理&#xff1a;每次迭代使用小批量数据计算梯度并更新参数。优点&#xff1a;实现简单&#xff0c;适合大规模数据集。缺点&#xff1a;收敛速度慢&#xff0c;容易陷入局部最优或鞍点。适用场景&#xff1…...

“时间”,在数据处理中的真身——弼马温一般『无所不能』(DeepSeek)

电子表格时间处理真理&#xff1a;数值存储最瘦身&#xff0c;真身闯关通四海。 笔记模板由python脚本于2025-04-23 22:25:59创建&#xff0c;本篇笔记适合喜欢在电子表格中探求时间格式的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验…...

为什么Spring中@Bean注解默认创建单例Bean

在Spring框架中&#xff0c;使用Bean注解定义的对象默认确实是单例的&#xff0c;这是由Spring容器的设计哲学和实际需求决定的。下面我从多个角度解释这一设计选择的原因和机制。 1. Spring Bean作用域基础 Spring定义了多种Bean作用域&#xff0c;其中默认是单例(Singleton…...

GPLT-2025年第十届团体程序设计天梯赛总决赛题解(2025天梯赛题解,共计266分)

今天偶然发现天梯赛的代码还保存着&#xff0c;于是决定写下这篇题解&#xff0c;也算是复盘一下了 L1本来是打算写的稳妥点&#xff0c;最后在L1-6又想省时间&#xff0c;又忘记了insert&#xff0c;replace这些方法怎么用&#xff0c;也不想花时间写一个文件测试&#xff0c…...

JDK(Ubuntu 18.04.6 LTS)安装笔记

一、前言 本文与【MySQL 8&#xff08;Ubuntu 18.04.6 LTS&#xff09;安装笔记】同批次&#xff1a;先搭建数据库&#xff0c;再安装JDK&#xff0c;后面肯定就是部署Web应用&#xff1a;典型的单机部署。“麻雀虽小五脏俱全”&#xff0c;善始善终&#xff0c;还是记下来吧。…...

Java 拦截器完全指南:原理、实战与最佳实践

一、引言 拦截器的基本概念 在现代 Java Web 开发中&#xff0c;拦截器&#xff08;Interceptor&#xff09;是一种用于在请求处理前后插入自定义逻辑的机制。简单来说&#xff0c;它是一种“横切逻辑处理器”&#xff0c;可以用来对请求进行预处理、后处理&#xff0c;甚至终…...

2025.04.23华为机考第二题-200分

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 02. 魔法彩灯森林 问题描述 在卢小姐的魔法花园中,有一棵神奇的彩灯树。这棵树的每个节点都装有一盏魔法灯,灯有三种颜色状态:红色(用数字1表示)、绿色(用数字2表示)和蓝色(…...

【Leetcode 每日一题】1399. 统计最大组的数目

问题背景 给你一个整数 n n n。请你先求出从 1 1 1 到 n n n 的每个整数 10 10 10 进制表示下的数位和&#xff08;每一位上的数字相加&#xff09;&#xff0c;然后把数位和相等的数字放到同一个组中。 请你统计每个组中的数字数目&#xff0c;并返回数字数目并列最多的组…...

系统重装——联想sharkbay主板电脑

上周给一台老电脑重装系统系统&#xff0c;型号是lenovo sharkbay主板的电脑&#xff0c;趁着最近固态便宜&#xff0c;入手了两块长城的固态&#xff0c;装上以后插上启动U盘&#xff0c;死活进不去boot系统。提示 bootmgr 缺失&#xff0c;上网查了许久&#xff0c;终于解决了…...

Python数据清洗笔记(上)

一、数据清洗概述 数据清洗是数据分析过程中至关重要的一步&#xff0c;约占整个数据分析过程的60%-80%的时间。主要包括处理缺失值、异常值、重复值、格式不一致等问题。 二、常用工具 主要使用Python的Pandas库进行数据清洗&#xff1a; import pandas as pd import nump…...

三、Python编程基础03

目录 一、debug 调试的使用1. 打断点2. 右键 Debug 运行代码3. 单步执行代码,查看过程 二、字符串1、定义与下标引用2、切片3、查找4、去除空白字符5、转换大小写与拆分6、其他方法-替换、连接、是否为纯数字7、登录案例优化 三、列表 list1、列表基础操作2、案例&#xff1a; …...

西门子S7-200SMART 控制Profinet闭环步进MD-4250-PN (1)电机及专栏介绍

一、前言 本系列是我继 《西门子S7-1200PLC 控制步进电机 MD-4240-PN》系列专栏后&#xff0c;新开的一篇专栏。 系列的主题围绕 S7-200SMART Profinet闭环步进(MD-4250-PN) 触摸屏的硬件&#xff0c;预计作四篇文章&#xff0c;分别为&#xff1a;专栏介绍、硬件介绍、PLC…...

NoSQL 简单讲解

目录 1. NoSQL 的背景与意义 1.1 数据库的演变 1.2 NoSQL 的兴起 2. NoSQL 数据库的分类 2.1 键值存储&#xff08;Key-Value Stores&#xff09; 2.2 文档数据库&#xff08;Document Stores&#xff09; 2.3 列族存储&#xff08;Column-Family Stores&#xff09; 2.…...

TCP 协议:原理、机制与应用

一、引言 在当今数字化的时代&#xff0c;网络通信无处不在&#xff0c;而 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;作为互联网协议栈中的核心协议之一&#xff0c;扮演着至关重要的角色。无论是浏览网页、发送电子邮件还是进行文件…...

C++23 新特性:令声明顺序决定非静态类数据成员的布局 (P1847R4)

文章目录 引言背景知识非静态类数据成员类的内存布局 P1847R4提案内容示例代码 影响和优势提高代码的可预测性与其他语言和库的交互更加方便简化代码调试和优化 编译器支持情况实际应用场景嵌入式系统开发跨语言编程内存优化 总结 引言 在C的发展历程中&#xff0c;每一个新版…...

Java 环境配置详解(Windows、macOS、Linux)

Java 环境配置是学习和开发 Java 程序的第一步&#xff0c;也是至关重要的一步。一个正确配置的 Java 环境能够保证你的 Java 程序能够顺利编译、运行和调试。本文将详细介绍在 Windows、macOS 和 Linux 三种主流操作系统上配置 Java 环境的步骤&#xff0c;力求详尽、易懂&…...