发布时间:2026/8/29 23:43:33
Android五子棋开发实战:从UI绘制到AI算法实现 简介在Android应用开发中自定义View是实现复杂UI绘制的核心技术它通过Canvas API直接控制像素绘制相比组合标准控件能获得更高性能和更灵活的视觉效果。其原理是在View的onDraw方法中进行图形指令的编排与执行这对于游戏、图表等场景具有重要价值。结合事件处理机制可以实现精准的用户交互。本文以五子棋游戏为具体应用场景详细解析了如何通过自定义View绘制棋盘与棋子并实现高效的双人对战逻辑与胜负判定算法。同时深入探讨了如何构建游戏AI涉及局面评估函数与搜索算法等关键热词为开发者提供了一个完整的Android游戏开发实践案例。1. 项目概述从零构建一个Android五子棋应用最近在整理自己的项目库翻到了一个几年前用Android Studio写的五子棋应用。这个项目虽然不大但麻雀虽小五脏俱全它几乎涵盖了Android应用开发中UI绘制、事件交互、游戏逻辑和简单AI算法等核心知识点。对于想从“Hello World”迈向第一个完整小项目的Android新手或者想重温基础的老手来说都是一个非常不错的练手选择。五子棋规则简单但实现起来从棋盘绘制、落子判断到胜负判定每一步都需要仔细思考。今天我就把这个项目的完整实现思路、关键代码以及我踩过的那些坑系统地梳理一遍希望能给你带来一个清晰的实现路径。这个项目最终会呈现一个完整的双人对战五子棋应用并且会探讨如何实现一个具备基本攻防能力的“地狱难度”AI对手。我们将使用Java语言在Android Studio这个官方IDE中完成。无论你是刚刚配置好Android开发环境还是已经有一定基础想找个项目练练手跟着这篇内容走下来你都能获得一个可以运行、可以扩展的完整应用更重要的是理解每个环节背后的“为什么”。2. 项目整体设计与核心思路拆解在动手写代码之前理清思路至关重要。一个五子棋应用我们可以把它拆解成几个相对独立的模块这样开发起来会更有条理调试也更容易。2.1 核心模块划分我的设计主要分为四个核心层数据模型层这是应用的大脑负责存储游戏状态。核心就是一个二维数组比如int[][] board用来表示15x15的棋盘。每个位置的值代表状态0为空1为黑子2为白子。此外还需要记录当前该谁落子、游戏是否结束、获胜方是谁等状态。视图绘制层这是应用的脸面负责把数据模型“画”出来。我们需要自定义一个View通常是继承View或SurfaceView在它的onDraw方法里绘制棋盘背景、网格线、以及根据数据模型数组绘制出的黑白棋子。用户交互层这是应用的双手负责接收用户的触摸操作。在自定义View的onTouchEvent方法中我们需要将用户触摸的屏幕坐标(x, y)转换换算成棋盘的逻辑坐标(row, column)然后通知数据模型层“用户在(row, column)位置落子了”。逻辑控制层这是应用的神经中枢负责协调以上三层。它监听用户交互层的事件更新数据模型然后通知视图重绘。同时它包含了最核心的胜负判定算法和AI算法。2.2 技术选型与考量为什么选择这样的结构这是基于Android应用开发的基本范式MVC或类似变体和五子棋这个具体场景决定的。自定义View绘制棋盘相比于使用大量ImageView来拼凑棋盘和棋子自定义View的性能更高内存占用更小并且能更灵活地控制绘制细节比如棋子的光泽感、棋盘线的粗细。对于棋盘这种规则性强的图形自己绘制是更专业的选择。二维数组存储状态这是最直观、最高效的数据结构。通过行列索引可以瞬间访问任何位置的棋子状态这对于频繁的胜负判断和AI计算至关重要。虽然也可以用一维数组或List但二维数组在逻辑表达上最清晰。Java实现逻辑本项目核心是算法和逻辑Java作为Android官方长期支持的语言其成熟的生态和清晰的面向对象特性非常适合实现游戏规则和AI。当然你也可以用Kotlin逻辑是相通的。注意在项目初期切忌追求大而全的架构如立刻引入复杂的MVP/MVVM。对于这种小型项目清晰的模块化思维比严格的架构模式更重要。先让应用跑起来再考虑重构。3. 核心细节解析与实操要点接下来我们深入每个模块看看具体实现时有哪些需要注意的细节和技巧。3.1 棋盘绘制像素与逻辑的转换绘制棋盘的第一步是确定棋盘的大小和位置。我们通常让棋盘居中显示并四周留有一些边距。public class ChessBoardView extends View { private int mBoardSize 15; // 15x15的棋盘 private float mCellSize; // 每个格子的宽度像素 private float mOffsetX, mOffsetY; // 棋盘左上角在View中的起始坐标像素 Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 计算格子大小和起始偏移让棋盘居中 int minSize Math.min(w, h) - 40; // 留20像素的边距 mCellSize minSize * 1.0f / mBoardSize; mOffsetX (w - mCellSize * mBoardSize) / 2; mOffsetY (h - mCellSize * mBoardSize) / 2; } Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBoard(canvas); drawPieces(canvas); } private void drawBoard(Canvas canvas) { Paint paint new Paint(); paint.setColor(Color.BLACK); paint.setStrokeWidth(2); // 画横线 for (int i 0; i mBoardSize; i) { float startX mOffsetX; float startY mOffsetY i * mCellSize; float stopX mOffsetX (mBoardSize - 1) * mCellSize; canvas.drawLine(startX, startY, stopX, startY, paint); } // 画竖线...类似逻辑 // 画五个小黑点天元和星... } }关键点解析onSizeChanged这是计算绘制参数的最佳位置。它会在View尺寸确定后调用我们在这里根据View的实际宽高动态计算出每个格子的大小(mCellSize)和棋盘的起始位置(mOffsetX, mOffsetY)。这保证了棋盘在不同尺寸屏幕上的自适应。坐标转换这是交互的基础。我们需要一个方法将触摸事件的屏幕像素坐标(x, y)转换成棋盘的逻辑坐标(row, col)。private int[] getBoardPosition(float x, float y) { int col (int) ((x - mOffsetX mCellSize / 2) / mCellSize); int row (int) ((y - mOffsetY mCellSize / 2) / mCellSize); // 确保坐标在棋盘范围内 if (col 0 || col mBoardSize || row 0 || row mBoardSize) { return null; } return new int[]{row, col}; }这里加上mCellSize / 2是为了实现“点击格子附近即落子在该格中心”的效果提升用户体验。3.2 胜负判定算法效率与清晰的平衡五子棋的胜负判定即检查在落子点的横、竖、左斜、右斜四个方向上是否存在连续五个同色棋子。这是一个典型的搜索问题。最直观的方法是“暴力搜索”从落子点向一个方向数格子直到遇到不同色棋子或边界然后反方向再数看看总数是否5。四个方向各做一次。public class GameLogic { private int[][] board; private int boardSize; public boolean checkWin(int row, int col, int player) { // 定义四个方向的向量横、竖、左上-右下、右上-左下 int[][] directions {{0, 1}, {1, 0}, {1, 1}, {1, -1}}; for (int[] dir : directions) { int count 1; // 落子点本身算一个 // 正向搜索 count countDirection(row, col, dir[0], dir[1], player); // 反向搜索 count countDirection(row, col, -dir[0], -dir[1], player); if (count 5) { return true; } } return false; } private int countDirection(int row, int col, int dRow, int dCol, int player) { int count 0; int r row dRow; int c col dCol; while (r 0 r boardSize c 0 c boardSize board[r][c] player) { count; r dRow; c dCol; } return count; } }实操心得判定时机胜负判定不需要在每次绘制时进行只需在每次有效落子后针对该落子点进行判定即可这是最高效的。平局判断当棋盘被下满即没有位置为0且未分出胜负时即为平局。可以在每次落子后增加一个检查。性能对于15x15的棋盘这个算法的复杂度是O(1)因为最多检查有限个方向完全足够。即使未来扩展到大棋盘也毫无压力。4. 实操过程与核心环节实现现在我们把各个模块组装起来形成一个可运行的单机双人对战版本。4.1 搭建基础对战框架创建项目与自定义View在Android Studio中新建一个Empty Activity项目。创建一个ChessBoardView类继承View实现上述的onSizeChanged、onDraw和getBoardPosition方法。在activity_main.xml布局中使用这个自定义View。实现游戏逻辑类创建GameLogic类包含board数组、当前玩家currentPlayer、游戏状态gameOver等属性。实现makeMove(int row, int col)方法检查位置是否为空、更新数组、调用checkWin判定胜负、切换玩家。将胜负结果通过接口回调或LiveData通知给Activity。连接View与Logic在ChessBoardView中持有GameLogic的引用。在onTouchEvent中调用getBoardPosition转换坐标然后调用logic.makeMove(row, col)。在makeMove成功后调用invalidate()触发onDraw重绘棋盘。onDraw中的drawPieces方法需要遍历logic.board数组来绘制所有棋子。至此一个基础的双人对战五子棋就完成了。你可以和身边的朋友在同一台设备上轮流点击屏幕对战了。4.2 实现“地狱难度”AI让机器和人下棋核心是评估函数和搜索算法。对于五子棋一个简单但有效的AI可以通过以下步骤构建局面评估函数给棋盘上的每个空位打分。分数越高表示在这个位置落子对我方越有利。如何打分我们可以扫描整个棋盘识别出各种棋型。定义棋型例如连五100000分、活四10000分、冲四1000分、活三1000分、眠三100分等等。“活”指两头无阻挡“冲”或“眠”指一头被堵。遍历评分对于每个空位假设分别放置黑子和白子然后检查以这个点为中心在四个方向上能形成什么棋型将对应的分数累加。最后AI的得分是“我方在此落子形成的棋型分”减去“对方在此落子形成的棋型分”。这就是一个简单的攻防一体评估。有限深度搜索纯粹的贪心算法只选当前评分最高的点很容易被欺骗。我们需要一定的前瞻性。采用极大极小搜索算法假设双方都会选择对自己最优的走法。搜索过程AI模拟自己走一步最大化自己的分数然后模拟对手走一步最小化AI的分数即最大化对手的分数如此交替进行形成一个搜索树。深度限制由于五子棋分支因子很大我们不可能搜索到底。通常设置一个搜索深度比如3层到达深度后就用上面的评估函数给当前局面打分。Alpha-Beta剪枝这是极大极小搜索的优化可以剪掉大量不必要的分支极大提升搜索速度。简单说就是记录当前搜索路径的最好结果如果发现某条分支不可能比已知结果更好就停止搜索这条分支。实现步骤在GameLogic中新增一个AIPlayer类。实现evaluatePosition(int player)评估函数遍历所有空位计算分数。实现minimax(int depth, int alpha, int beta, boolean isMaximizingPlayer)搜索函数。添加一个getAIMove()方法调用搜索算法返回评分最高的落子位置。在游戏逻辑中当轮到AI时调用getAIMove()然后像普通落子一样处理。踩坑提醒AI算法的调试非常耗时。建议先从简单的评估函数和浅深度如2层开始确保AI能下出合乎规则的棋。然后逐步增加棋型判断的复杂度和搜索深度。同时一定要注意搜索的性能过深的搜索会导致UI卡顿可以考虑将AI计算放在子线程中。5. 常见问题与排查技巧实录在开发过程中我遇到了不少典型问题这里总结一下希望能帮你快速排雷。5.1 触摸不灵敏或落子位置不准现象点击棋盘边缘或格子线时没有反应或者落子点总感觉偏了一点。排查首先检查getBoardPosition函数中的坐标转换逻辑。打印出触摸点的(x, y)和转换后的(row, col)看是否符合预期。确认mOffsetX, mOffsetY, mCellSize的计算是否正确。确保棋盘绘制和坐标转换使用的是同一套参数。检查onTouchEvent中是否正确处理了ACTION_DOWN事件。有时为了更好的体验我们只在ACTION_UP时处理落子。解决在getBoardPosition中调整“吸附”到格子中心的阈值。例如之前是(x - mOffsetX mCellSize / 2) / mCellSize你可以尝试不加mCellSize / 2或者使用更复杂的判断逻辑。5.2 棋盘绘制闪烁或刷新异常现象落子后新棋子没有立刻显示或者整个棋盘闪一下。排查避免在onDraw中创建对象这是最常见的性能杀手。Paint、Path等对象应该在View初始化时创建并复用。检查invalidate()的调用确保只在数据真正改变后如成功落子调用invalidate()。不要在每一帧都调用。考虑使用SurfaceView如果绘制内容非常复杂比如带动画自定义View的onDraw在主线程执行可能引起卡顿。SurfaceView在独立线程绘制更适合游戏。解决将Paint对象定义为成员变量在构造方法或init块中初始化。5.3 AI思考时间过长导致ANR现象轮到AI时界面卡住长时间无响应最终可能弹出“应用无响应”的对话框。排查这明显是耗时操作阻塞了主线程UI线程。AI的搜索和评估计算是CPU密集型任务。解决使用异步任务将getAIMove()的计算过程放入AsyncTask、Thread或CoroutineKotlin中执行。显示等待提示在AI思考时显示一个ProgressDialog或一个“AI思考中...”的提示改善用户体验。设置搜索超时在搜索算法中增加一个计时器到达指定时间如3秒后无论是否搜索完成都返回当前找到的最佳走法。这可以防止极端情况下AI“思考”太久。5.4 游戏状态管理混乱现象游戏结束后还能落子或者重新开始游戏后棋盘状态没有重置。排查检查GameLogic中的gameOver标志位在makeMove方法开始时是否进行了判断。检查“重新开始”功能是否正确地重置了board数组、currentPlayer和gameOver标志并调用了View的invalidate()。解决在GameLogic中提供一个resetGame()方法集中重置所有状态。在Activity中点击重新开始按钮时调用此方法并刷新UI。5.5 项目导入或构建失败现象从别处下载的项目源码在Android Studio中打开后Gradle构建失败提示各种依赖错误。排查这是Android开发中的常见问题通常与Gradle版本、插件版本、SDK版本不匹配有关。解决检查项目结构打开File - Project Structure查看Modules下的Compile Sdk Version、Build Tools Version是否与你本地SDK匹配。检查Gradle文件比较项目中的gradle/wrapper/gradle-wrapper.properties文件指定Gradle版本和根目录build.gradle中的classpath指定Android Gradle插件版本尝试将其修改为你本地环境已知可用的版本组合。你可以在新建一个空白项目看看它使用的版本是什么。离线模式与缓存有时网络问题会导致依赖下载失败。可以尝试打开File - Settings - Build, Execution, Deployment - Build Tools - Gradle勾选Offline work然后点击File - Invalidate Caches and Restart清除缓存并重启。手动下载依赖对于特定的.jar或.aar包下载失败如gradle-6.5-bin.zip可以根据错误提示的URL用浏览器手动下载然后放到Android Studio提示的缓存目录中通常位于用户目录/.gradle/wrapper/dists/下对应的文件夹内。开发就是一个不断遇到问题并解决问题的过程。对于这个五子棋项目从绘制交互到AI算法每一步的细节都值得推敲。我的建议是先确保基础功能绘制、落子、双人对战流畅运行然后再挑战AI部分。当你看到自己编写的AI能够和你打得有来有回时那种成就感是非常棒的。这个项目还有很多可以扩展的地方比如联网对战、更炫的棋子动画、棋谱保存与回放等等你可以根据自己的兴趣继续深入。本文还有配套的精品资源点击获取

相关新闻

2026/8/29 23:43:33

基于MATLAB实现层次分析法(AHP):从多准则决策到编程实践

1. 项目概述:当决策遇上量化工具在工程、管理、经济乃至日常生活的很多场景里,我们常常面临多准则的复杂决策。比如,公司要选一个供应商,得看价格、质量、交货期、售后服务好几个方面;个人想买辆车,也得权衡…

2026/8/29 23:38:33

58同城校招前端笔试全解析:题型拆解与备考策略复盘

58同城2023校招笔试-前端:从题型拆解到备考策略的完整复盘到现在还有不少准备校招的同学在问,58同城这类大型平台的校招前端笔试到底在考什么。这篇文章我结合自己备考和带人的经验,把这类校招笔试题的逻辑完整拆一遍,从出题人视角…

2026/8/29 23:38:33

58同城前端校招笔试题复盘:JS基础、事件循环与异步调度器

去年秋招,我投了58同城的前端岗位,笔试通知来得比预想中快。从收到邮件到正式开考,中间只有不到48小时,加上当时还在同时准备其他几家,可以说是挤出时间仓促上阵。整套卷子90分钟,题目覆盖面很广&#xff0…

2026/8/29 23:58:35

数字标牌项目实战:酒店互动宣传册如何从纸质升级为触摸屏方案

数字标牌这个行业,外面人看着就是“放个屏幕播视频”,真做起来才知道门道有多深。这次要聊的项目,是给CTM Media做的酒店场景数字宣传册升级。核心思路很简单:把大堂里那堆没人翻的纸质旅游手册,换成一块能触摸、能互动…

2026/8/29 23:58:35

校园交友网站-springboot

本项目为前几天收费帮学妹做的一个项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 基于springboot的校园交友网站通过Mysql数据库连接数据库 http://localhost:8080/s…

2026/8/29 23:58:35

算力金融化时代的GPU选型、驱动部署与私有化平台搭建指南

过去两年,算力从一个偏底层的硬件指标,逐渐变成了行业讨论中的高频词。尤其是当“5000亿美元”“算力金融化”这类表述出现时,很多开发者第一反应是:这和我写代码、调模型、部署服务有什么关系?实际上关系很大。算力被…

2026/8/29 23:58:35

基于SpringBoot的“艺展集”文创作品线上展览与交易平台的设计与实现

1. 项目背景与意义随着文化创意产业的快速发展,越来越多的文创作品需要通过线上渠道进行展示与交易。然而,传统电商平台往往缺乏对文创作品的艺术性展示支持,难以满足创作者与收藏者之间的深度互动需求。本项目“艺展集”旨在构建一个集线上展…

2026/8/29 23:53:35

FT232R驱动深度解析:USB转UART通信的底层原理与实战排错

简介:USB转UART是嵌入式系统中最基础的通信桥梁,其核心在于协议转换与硬件抽象。FT232R作为成熟可靠的桥接芯片,通过集成USB协议栈与UART控制器,实现端点映射、波特率精准分频及EEPROM硬件指纹识别。驱动的本质并非简单安装&#…

2026/8/29 21:30:11

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/28 16:16:21

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/29 23:41:12

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/29 0:01:10

etc目录下的profile.d文件目录设置环境变量和全局脚本shell

一、设置环境变量etc目录下的profile.d文件目录 /etc/profile.d1、编写 vi test.sh文件内容# jdk变量 export ZHK_HOME/root export PATH$PATH:$ZHK_HOME/test # 可以取出来ZHK_HOME变量给ZZZ_HOME赋值 export ZZZ_HOME${ZHK_HOME}/test2、刷新 执行source /etc/profile 命令使…

2026/8/29 0:01:10

【JavaScript】内存管理-垃圾回收机制-内存泄露

内存管理 C 语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()。 而 JavaScript 是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。释放的过程称为垃圾回收。 整…

2026/8/29 0:01:10

Labgrid-MCP:为嵌入式硬件实验室接入AI Agent操控能力

Labgrid-MCP 的目标是把 MCP(Model Context Protocol)能力延伸到真实嵌入式硬件实验室:AI Agent 通过一个标准化的 MCP Server,就能查看目标板状态、控制上电断电、复位开发板、读取串口日志,甚至执行镜像刷写。对于经…

2026/8/28 16:16:48

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/28 16:16:50

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/28 11:06:45

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…