VC++ MFC围棋程序实战:双人对战与核心逻辑实现

简介:这是一份基于VC++/MFC编写的围棋对弈程序源码,面向有一定C++基础、希望深入理解Windows图形界面与游戏逻辑的开发者,尤其适合想通过完整小项目巩固MFC框架的学习者。压缩包共24个文件,约36KB,包含7个h头文件与6个cpp源文件构成核心逻辑,另有3个bmp位图与2个ico图标负责界面素材,以及dsp、dsw工程文件和rc资源脚本,可直接用Visual Studio打开编译运行。程序支持双人对决,启动后可选择适配15、17寸液晶的窗口尺寸,落子响应迅速,交互流畅。目前已有171人学习关注。通过阅读源码,读者能掌握MFC文档视图结构、绘图刷新、鼠标消息处理与棋盘状态管理等关键实现,并借鉴作者在界面布局与资源组织上的思路,是入门Windows游戏编程与MFC实战的轻量参考案例。

1. 双人对决的VC++围棋程序:一个能直接跑起来的MFC实战项目

很多人学完VC++和MFC的语法之后,卡在“不知道拿什么练手”这一步。控件拖了、消息映射写了、对话框弹出来了,但真让自己从零搭一个能交互、有规则、能双人对战的完整程序,还是不知道从哪下手。这个双人对决的VC++围棋程序就是冲着这个缺口来的——它用MFC搭界面,用GDI画棋盘和棋子,把落子、提子、禁着点判断、胜负判定这些围棋核心逻辑全部用C++实现了一遍,没有依赖任何第三方棋类引擎。整个工程可以直接用Visual Studio打开编译,跑起来就是两个人轮流点鼠标下棋的完整程序。适合正在学MFC想找一个有实际逻辑的项目练手的人,也适合想拿一个现成的棋类框架改成五子棋、黑白棋的开发者。它不是一个玩具级的Demo,棋盘状态管理、气计算、打劫判断这些容易翻车的地方都做了处理,代码结构也留了扩展空间。

2. 工程结构与核心模块:从棋盘数据到界面渲染的完整链路

2.1 项目目录与文件职责划分

拿到源码包之后,先别急着编译,花两分钟把目录结构看清楚,后面改代码的时候能省很多时间。这个工程是标准的MFC对话框应用结构,主要文件如下:

文件职责
GoGameDlg.h / .cpp主对话框类,负责界面初始化、鼠标消息响应、按钮事件
Board.h / .cpp棋盘核心类,管理19x19棋盘状态、落子、提子、气计算
Stone.h棋子数据结构,记录颜色和位置
Resource.h / .rc资源定义,包括对话框布局、菜单、图标
stdafx.h / .cpp预编译头,MFC标准包含

核心逻辑全部集中在Board类里,界面部分在GoGameDlg里。这种分离方式的好处是,如果你想换一套UI,Board类可以原封不动地搬到别的框架里用。我一般拿到一个新项目也会先找这种“逻辑和界面分不分得开”的信号,分得越干净,二次开发越省事。

2.2 棋盘数据结构:二维数组还是位棋盘

打开Board.h,第一眼看到的是棋盘状态的存储方式。这个项目用的是最直观的二维数组:

// Board.h class Board { public: enum StoneColor { EMPTY = 0, BLACK = 1, WHITE = 2 }; Board(); void Reset(); // 重置棋盘 bool PlaceStone(int row, int col, StoneColor color); // 落子 StoneColor GetStone(int row, int col) const; int GetCapturedCount(StoneColor color) const; private: static const int BOARD_SIZE = 19; StoneColor m_board[BOARD_SIZE][BOARD_SIZE]; // 棋盘状态 int m_capturedBlack; // 黑方被提子数 int m_capturedWhite; // 白方被提子数 bool HasLiberty(int row, int col, bool visited[][BOARD_SIZE]); // 判断某位置是否有气 void RemoveGroup(int row, int col); // 提掉整块无气的棋 bool IsKo(int row, int col, StoneColor color); // 打劫判断 };

用二维数组的好处是直观,m_board[row][col]一眼就能看出是哪个位置。19x19一共361个点,每个点用一个枚举值表示,内存占用可以忽略不计。常见做法是用位棋盘来加速气计算,但这个项目规模小,二维数组加递归搜索完全够用,代码可读性反而更好。如果你后面想改成五子棋,把BOARD_SIZE改成15,再改一下胜负判定逻辑就行。

2.3 落子与提子逻辑:气计算的递归实现

围棋最核心的规则就是“气”——一块棋如果没有气,就要被提掉。这个项目用递归的方式计算气,代码在Board.cpp里:

// Board.cpp bool Board::HasLiberty(int row, int col, bool visited[][BOARD_SIZE]) { // 边界检查 if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) return false; if (visited[row][col]) return false; visited[row][col] = true; // 空位就是气 if (m_board[row][col] == EMPTY) return true; // 如果是同色棋子,继续向四个方向搜索 StoneColor color = m_board[row][col]; if (HasLiberty(row - 1, col, visited)) return true; if (HasLiberty(row + 1, col, visited)) return true; if (HasLiberty(row, col - 1, visited)) return true; if (HasLiberty(row, col + 1, visited)) return true; return false; }

这段代码的逻辑是:从某个棋子出发,向上下左右四个方向递归搜索,如果碰到空位就说明这块棋还有气,返回true;如果搜完所有相连的同色棋子都没找到空位,说明这块棋已经被包围,需要提掉。visited数组用来防止重复访问,避免无限递归。

参数说明:row和col是当前搜索的位置,visited是访问标记数组,每次调用PlaceStone之前需要先清零。注意这个递归的深度最大是棋盘上的棋子数,19x19最多361层,不会栈溢出。

落子的时候,先判断该位置是否为空,然后临时放上棋子,再检查周围对方的棋是否有气,没气的提掉,最后检查自己这块棋是否有气,没气的回滚。这个顺序不能反,否则会出现“自杀”的情况。

2.4 界面渲染:用GDI画棋盘和棋子

界面部分在GoGameDlg.cpp的OnPaint函数里,用GDI的LineTo和Ellipse画棋盘线和棋子:

// GoGameDlg.cpp void CGoGameDlg::OnPaint() { CPaintDC dc(this); // 画棋盘线 int margin = 30; int cellSize = 25; for (int i = 0; i < 19; i++) { // 横线 dc.MoveTo(margin, margin + i * cellSize); dc.LineTo(margin + 18 * cellSize, margin + i * cellSize); // 竖线 dc.MoveTo(margin + i * cellSize, margin); dc.LineTo(margin + i * cellSize, margin + 18 * cellSize); } // 画星位 int stars[][2] = {{3,3},{3,9},{3,15},{9,3},{9,9},{9,15},{15,3},{15,9},{15,15}}; for (int i = 0; i < 9; i++) { int x = margin + stars[i][0] * cellSize; int y = margin + stars[i][1] * cellSize; dc.Ellipse(x - 3, y - 3, x + 3, y + 3); } // 画棋子 for (int r = 0; r < 19; r++) { for (int c = 0; c < 19; c++) { StoneColor color = m_board.GetStone(r, c); if (color == EMPTY) continue; int x = margin + c * cellSize; int y = margin + r * cellSize; CBrush brush(color == BLACK ? RGB(0,0,0) : RGB(255,255,255)); CBrush* oldBrush = dc.SelectObject(&brush); dc.Ellipse(x - 10, y - 10, x + 10, y + 10); dc.SelectObject(oldBrush); } } }

这段代码先画19条横线和19条竖线,再画9个星位,最后遍历棋盘数组把棋子画上去。margin是棋盘边缘留白,cellSize是每个格子的大小,这两个值决定了棋盘的视觉比例。如果你想调整棋盘大小,改这两个参数就行,但要注意鼠标点击的坐标换算也要同步改。

鼠标点击的处理在OnLButtonDown里,把屏幕坐标转换成棋盘行列,然后调用Board的PlaceStone。坐标换算的公式是col = (point.x - margin + cellSize/2) / cellSize,加cellSize/2是为了让点击落在交叉点附近也能正确识别。

3. 编译运行与双人对战流程:从打开工程到第一手棋

3.1 环境准备与编译步骤

这个工程是标准的MFC项目,用Visual Studio打开就能编译。我实测用的是VS2019,VS2017和VS2022也可以,但要注意MFC组件需要单独安装。如果你装VS的时候没勾选“使用C++的桌面开发”里的MFC选项,打开工程会报一堆“找不到afxwin.h”的错误。

安装MFC组件的步骤:打开Visual Studio Installer,点“修改”,在“单个组件”里搜索“MFC”,勾选“适用于最新v143生成工具的C++ MFC”,然后点“修改”等安装完成。这一步不做的话,后面所有编译都是白费。

编译的时候,打开GoGame.sln,选择Debug x86或者Release x86,直接按F7。如果一切正常,会在Debug或Release目录下生成GoGame.exe。双击就能运行。

注意:如果编译报错“无法打开包括文件: afxwin.h”,说明MFC组件没装好,回到上面那一步重新检查。

3.2 双人对战的操作流程

程序跑起来之后,是一个对话框界面,中间是棋盘,两边显示黑白双方的提子数。操作流程很简单:

  1. 黑方先手,鼠标点击棋盘交叉点落子
  2. 如果落子位置已经有棋子,程序会忽略这次点击
  3. 如果落子后对方有棋子被提,提子数会更新
  4. 如果落子会导致自己无气(自杀),程序会拒绝这次落子
  5. 如果触发打劫,程序会提示“打劫,请重新落子”
  6. 双方轮流落子,直到一方认输或双方都选择停一手

界面上有两个按钮:“认输”和“停一手”。认输就是直接结束游戏,对方获胜。停一手是围棋里的“Pass”,如果双方连续停一手,游戏结束,开始数子判定胜负。

3.3 胜负判定与数子逻辑

这个项目用的是中国规则数子法。游戏结束后,程序会遍历棋盘,把每个空点归属到最近的颜色,然后计算黑方和白方的总子数。黑方需要贴3又3/4子(相当于7.5目),所以黑方需要185子以上才能赢。

数子的代码在Board.cpp的CalculateScore函数里:

// Board.cpp int Board::CalculateScore(StoneColor color) { int score = 0; bool visited[BOARD_SIZE][BOARD_SIZE] = {false}; for (int r = 0; r < BOARD_SIZE; r++) { for (int c = 0; c < BOARD_SIZE; c++) { if (m_board[r][c] == color) { score++; // 自己的棋子算一子 } else if (m_board[r][c] == EMPTY && !visited[r][c]) { // 空点需要判断归属 StoneColor owner = GetAreaOwner(r, c, visited); if (owner == color) score++; } } } return score; }

GetAreaOwner的逻辑是:从一个空点出发,向四周扩散,如果这个区域只接触到一种颜色的棋子,那么这个区域就归那种颜色。如果同时接触到黑白两色,就是“公气”,双方都不算。

参数说明:color是要计算分数的颜色,visited是访问标记数组。这个函数的时间复杂度是O(n²),19x19的棋盘跑起来瞬间完成,不需要优化。

4. 避坑与常见问题:编译报错、落子异常、界面闪烁的排查

4.1 编译报错“无法打开包括文件: afxwin.h”

现象:打开工程按F7,直接报错找不到afxwin.h,后面跟着一堆MFC相关的头文件都找不到。

原因:Visual Studio安装的时候没有勾选MFC组件。MFC不是默认安装的,需要手动在Installer里勾选。

解决:打开Visual Studio Installer,点“修改”,在“单个组件”里搜索“MFC”,勾选对应版本的MFC组件,点“修改”等安装完成。安装完之后重新打开工程,清理解决方案再重新编译。

4.2 落子之后棋子不显示

现象:鼠标点击棋盘,程序没崩溃,但棋盘上什么都没画出来。

原因:OnPaint函数没有被触发。MFC的对话框默认不会在每次数据变化后自动重绘,需要在落子之后手动调用Invalidate()。

解决:在PlaceStone调用成功之后,加一行Invalidate();,强制对话框重绘。如果加了Invalidate还是不行,检查OnPaint里画棋子的循环是不是写错了行列顺序,比如把m_board[r][c]写成了m_board[c][r]。

4.3 提子逻辑异常,棋子被误提

现象:明明还有气的棋,程序却把它提掉了,或者该提的棋没提。

原因:HasLiberty函数的visited数组没有在每次调用前清零,导致上一次搜索的标记影响了这一次。

解决:在PlaceStone里调用HasLiberty之前,先把visited数组全部置为false。我一般会写一个辅助函数ClearVisited(),每次搜索前调一次,避免忘记。

4.4 界面闪烁严重

现象:每次落子之后,整个棋盘闪一下,体验很差。

原因:OnPaint里直接画所有内容,没有使用双缓冲。GDI直接画到屏幕上,每次重绘都会先擦除背景再画,造成闪烁。

解决:在OnPaint里加双缓冲。先创建一个内存DC和兼容位图,所有绘制操作先画到内存DC上,最后一次性BitBlt到屏幕DC。代码大概是这样:

// GoGameDlg.cpp - 双缓冲绘制 void CGoGameDlg::OnPaint() { CPaintDC dc(this); CRect rect; GetClientRect(&rect); CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); memDC.SelectObject(&bitmap); // 所有绘制操作改成画到memDC // ... 画棋盘、画棋子 ... dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); }

4.5 打劫判断不生效

现象:对方提子之后,自己立刻提回来,程序没有拦截。

原因:IsKo函数的判断逻辑写反了,或者没有记录上一步的棋盘状态。

解决:打劫判断需要记录上一步的棋盘状态,然后模拟当前落子,如果落子后的棋盘状态和上一步之前的状态相同,就是打劫。这个项目里用了一个简单的哈希比较,把棋盘状态转成一个字符串来对比。如果你要改,注意每次落子后都要更新“上一步状态”的记录。

5. 二次开发与改造思路:从围棋到五子棋、AI对战、网络对战的扩展路径

5.1 改成五子棋:改棋盘大小和胜负判定

这个工程改成五子棋是最简单的改造路径。第一步,把Board.h里的BOARD_SIZE从19改成15。第二步,把CalculateScore函数删掉,换成五子棋的胜负判定:任意方向连续五子即获胜。

// Board.cpp - 五子棋胜负判定 bool Board::CheckWin(int row, int col, StoneColor color) { int directions[4][2] = {{0,1},{1,0},{1,1},{1,-1}}; for (int d = 0; d < 4; d++) { int count = 1; // 正方向 for (int i = 1; i < 5; i++) { int r = row + directions[d][0] * i; int c = col + directions[d][1] * i; if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; if (m_board[r][c] != color) break; count++; } // 反方向 for (int i = 1; i < 5; i++) { int r = row - directions[d][0] * i; int c = col - directions[d][1] * i; if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; if (m_board[r][c] != color) break; count++; } if (count >= 5) return true; } return false; }

这段代码从落子点出发,向四个方向分别统计连续同色棋子的数量,任意方向达到5个就判胜。参数说明:row和col是最后落子的位置,color是落子颜色。注意方向数组里{1,-1}表示右上到左下这个斜方向,正方向和反方向要分开统计。

5.2 加入简单AI:用贪心算法实现电脑落子

如果你想加一个电脑对手,最简单的做法是贪心算法:遍历所有空点,对每个点打分,选分数最高的位置落子。打分规则可以这样设计:

棋型分数
自己连五100000
对方连五90000
自己活四10000
对方活四9000
自己活三1000
对方活三900

这个打分表的意思是:优先自己赢,其次阻止对方赢,然后才是做自己的棋。实现的时候,对每个空点,分别计算如果自己下在这里能形成什么棋型,以及如果对方下在这里能形成什么棋型,取两者中较大的分数作为该点的得分。

// AI.cpp - 贪心算法选点 CPoint AI::ChooseBestMove(Board& board, StoneColor aiColor) { StoneColor oppColor = (aiColor == BLACK) ? WHITE : BLACK; int bestScore = -1; CPoint bestMove(-1, -1); for (int r = 0; r < BOARD_SIZE; r++) { for (int c = 0; c < BOARD_SIZE; c++) { if (board.GetStone(r, c) != EMPTY) continue; // 评估自己下这里的分数 int myScore = EvaluatePosition(board, r, c, aiColor); // 评估对方下这里的分数 int oppScore = EvaluatePosition(board, r, c, oppColor); int score = max(myScore, oppScore); if (score > bestScore) { bestScore = score; bestMove = CPoint(r, c); } } } return bestMove; }

这个AI很弱,但作为练手足够了。如果你想做强一点,可以把贪心算法换成极小极大搜索加Alpha-Beta剪枝,但那需要额外处理搜索深度和评估函数的平衡,代码量会大很多。

5.3 网络对战:用Winsock实现双机对战

把双人对战改成网络对战,核心是把落子操作从本地鼠标点击改成网络消息。MFC里可以用CAsyncSocket类,这是MFC对Winsock的封装,用起来比原生Winsock简单。

改造思路:一台机器做服务端,一台做客户端。服务端监听端口,客户端连接上来之后,双方轮流发送落子坐标。收到对方落子消息后,调用Board的PlaceStone,然后Invalidate重绘。

// Network.cpp - 发送落子消息 void CGoGameDlg::SendMove(int row, int col) { if (m_pSocket == nullptr) return; char buffer[32]; sprintf_s(buffer, "%d,%d", row, col); m_pSocket->Send(buffer, strlen(buffer)); } // 接收落子消息 void CGoGameDlg::OnReceive(int nErrorCode) { char buffer[1024]; int len = m_pSocket->Receive(buffer, 1024); if (len > 0) { buffer[len] = '\0'; int row, col; sscanf_s(buffer, "%d,%d", &row, &col); m_board.PlaceStone(row, col, m_opponentColor); Invalidate(); } CAsyncSocket::OnReceive(nErrorCode); }

网络对战要注意的是同步问题:双方都要维护同一个棋盘状态,任何一方落子后都要把坐标发给对方。如果网络延迟大,可能会出现双方同时落子导致状态不一致的情况,需要加一个简单的锁或者回合制控制。

5.4 改造验证:怎么确认你的修改没把原逻辑改坏

每次改完代码,不要只跑一遍看看能不能编译。我一般会做三个验证:第一,跑一局完整的双人对战,确认落子、提子、打劫、胜负判定都正常;第二,故意在已经有棋子的位置落子,确认程序会忽略;第三,故意制造一个自杀落子,确认程序会拒绝。这三个场景覆盖了核心逻辑的主要分支,跑通了基本就没大问题。

从那以后我每次改这种棋类项目的核心逻辑,都会先把这三个场景走一遍,再去看界面效果。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

VC++ MFC双人对决围棋程序实战:从落子提子到胜负计算

VC++ MFC双人对决围棋程序实战:从落子提子到胜负计算

简介&#xff1a;这是一份基于VC与MFC框架编写的双人对决围棋程序源码&#xff0c;面向正在学习Windows游戏编程、希望理解MFC文档视图架构与图形绘制流程的开发者。程序支持双人轮流落子&#xff0c;并可根据15、17寸液晶屏选择合适窗口尺寸&#xff0c;落子响应迅速&#xff…

2026/10/21 2:43:59 阅读更多 →
arping 命令详解:局域网 MAC 地址探测与 IP 冲突排查实战

arping 命令详解:局域网 MAC 地址探测与 IP 冲突排查实战

简介&#xff1a;这是一份面向网络管理员、运维工程师及网络编程学习者的 arping 工具源码资源包&#xff0c;聚焦以太网环境下基于 ARP 协议探测主机在线状态并获取其 MAC 地址这一典型场景。资源共 19 个文件&#xff0c;以 cpp 与 h 源文件为核心&#xff0c;涵盖 ARP 报文构…

2026/10/21 2:43:59 阅读更多 →
基于协同过滤的电影推荐系统:Django+MySQL毕业设计实战拆解

基于协同过滤的电影推荐系统:Django+MySQL毕业设计实战拆解

简介&#xff1a;这套基于协同过滤的电影推荐系统毕业设计源码&#xff0c;面向计算机相关专业学生与推荐系统初学者&#xff0c;完整实现了基于用户与基于物品两种推荐算法&#xff0c;涵盖相似度计算、评分预测与 Top-N 推荐等完整流程。项目基于 Django 2.2.1、Python 3.7 与…

2026/10/21 2:43:59 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/21 9:32:40 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/21 9:32:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/21 9:32:40 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/21 9:32:40 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/21 9:32:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/21 9:31:32 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/21 21:32:28 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:50:56 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:51:55 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 16:24:48 阅读更多 →