简介:这份资源是一份Android五子棋小游戏的课程设计报告,面向高校移动应用开发课程的学生、毕业设计选题者以及需要Android项目实战参考的开发者。报告围绕一款支持人机对战与人人对战的五子棋应用展开,涵盖项目背景、开发技术与环境、MVC系统架构、SQLite数据库设计、详细功能实现、运行演示及心得体会,完整呈现了从需求分析到落地实现的开发思路。压缩包内为1个doc文档,约8.29MB,图文并茂、格式规范,全文约9362字,可直接作为课程设计或毕业设计论文的写作模板与素材。内容中详细讲解了Java技术、Android Studio开发环境、ComputerAI与GameView等实体类设计,以及登录注册、主页面逻辑等模块,并配有软件架构图与运行截图。目前已有320人学习,适合需要规范论文结构、借鉴Android项目开发流程与AI决策思路的读者参考。
1. 一份能直接复现的 Android 五子棋课程设计:从背景到演示的完整拆解
很多同学做 Android 课程设计时,第一反应是去搜一份现成的报告模板,结果拿到手发现只有目录和几张截图,代码跑不起来,设计章节全是空话。这份 Android 五子棋小游戏的课程设计报告,核心价值不在于“五子棋”本身有多难,而在于它把项目背景、开发环境、详细设计、运行演示和心得体会串成了一条可复现的链路。五子棋规则简单、界面直观,但要把棋盘绘制、落子判定、胜负检测、悔棋重开这些逻辑写清楚,再配上规范的论文格式和图文并茂的演示,恰好覆盖了课程设计从选题到答辩的全部环节。适合正在做 Android 课程设计、需要一份结构完整且代码能跑通的参考方案的同学,也适合想用一个小项目把自定义 View、事件处理、状态管理串起来的开发者。
2. 项目背景与开发环境:为什么选五子棋,用什么工具链
2.1 选题理由:规则简单但逻辑完整,适合作为课程设计载体
课程设计的选题通常要满足三个条件:需求明确、工作量适中、能体现课程核心知识点。五子棋恰好卡在这个交叉点上。它的规则用一句话就能说清——黑先白后,横竖斜任意方向连成五子即胜——但实现起来涉及棋盘数据结构、触摸事件坐标映射、胜负判定算法、界面刷新和状态回退。相比做一个计算器,五子棋多了图形绘制和实时交互;相比做一个聊天应用,它又不需要后端和网络权限,全部逻辑可以在一台设备上闭环。
从 Android 课程的知识覆盖来看,五子棋能练到自定义 View 的 onDraw 和 onTouchEvent、二维数组的状态管理、Handler 或直接 invalidate 的刷新机制、以及 Activity 生命周期下的状态保存。这些点都是 Android 基础教学里的重点,答辩时也容易讲清楚“我为什么这么设计”。另一个现实考虑是,五子棋的界面不依赖复杂素材,棋盘和棋子用 Canvas 画出来就行,省去了找图、切图、适配分辨率的麻烦,对课程设计这种周期短的任务很友好。
2.2 开发环境清单:版本选择与项目结构
开发环境这块,我一般建议用 Android Studio 配合 Gradle 构建,语言选 Java 或 Kotlin 都可以。如果课程教材是 Java 版,就统一用 Java,避免答辩时被问到语言特性答不上来。下面是一份常见的环境配置表,版本号只写大版本,具体小版本按你本机已安装的稳定版来。
| 项目 | 推荐配置 | 说明 |
|---|---|---|
| 开发工具 | Android Studio | 用自带 SDK Manager 管理组件 |
| 构建工具 | Gradle 7.x 及以上 | 与 AGP 版本匹配即可 |
| 编程语言 | Java 8 或 Kotlin | 跟课程教材保持一致 |
| 最低 SDK | API 21 | 覆盖绝大多数教学设备 |
| 目标 SDK | API 33 或 34 | 按当前稳定版填写 |
| 测试设备 | 模拟器或真机 | 真机触摸体验更准 |
项目结构上,建议只保留一个 Activity,把棋盘逻辑封装成一个自定义 View,这样代码层次清晰,报告里的“详细设计”章节也好展开。目录大致是:MainActivity.java负责界面容器和按钮控制,ChessBoardView.java负责绘制和触摸,GameLogic.java单独放胜负判定和状态管理。把逻辑从 View 里抽出来,是为了在报告里能画出模块图,也方便单元测试。
2.3 创建项目与基础配置的实操步骤
打开 Android Studio,新建一个 Empty Views Activity 项目,语言选 Java,包名按学校要求填写。创建完成后,先改build.gradle里的 minSdk 和 targetSdk,再在AndroidManifest.xml里确认没有多余权限——五子棋不需要网络、存储、定位,权限列表应该是干净的,这一点在报告里可以写成“最小权限原则”的体现。
// MainActivity.java 基础框架 public class MainActivity extends AppCompatActivity { private ChessBoardView boardView; private Button btnRestart, btnUndo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 绑定自定义棋盘视图 boardView = findViewById(R.id.board_view); btnRestart = findViewById(R.id.btn_restart); btnUndo = findViewById(R.id.btn_undo); // 重开按钮:清空棋盘状态 btnRestart.setOnClickListener(v -> boardView.restartGame()); // 悔棋按钮:回退一步 btnUndo.setOnClickListener(v -> boardView.undoMove()); } }这段代码的逻辑很直白:Activity 只做视图绑定和按钮事件转发,真正的游戏状态全部交给ChessBoardView。参数上要注意,findViewById的 id 必须和布局文件里一致,按钮的点击监听用 lambda 写起来短,但如果你用的 Java 版本低于 8,就得改回匿名内部类。布局文件里,棋盘 View 用match_parent撑满,按钮放在底部用 LinearLayout 横排,这样在不同屏幕上都不会挤在一起。
3. 详细设计:棋盘绘制、落子判定与胜负检测的实现
3.1 自定义 View 的 onDraw:把棋盘和棋子画出来
棋盘绘制是五子棋最直观的部分。常见做法是在onDraw里先画背景色,再画网格线,最后遍历二维数组把已有棋子画上去。网格线的间距要根据 View 的宽高动态计算,不能写死像素值,否则换一台分辨率不同的设备就错位。我一般会取宽高中较小值作为棋盘边长,再除以 14 得到格子间距,因为标准五子棋是 15 条线、14 个格子。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 计算棋盘边长和格子间距 int boardSize = Math.min(getWidth(), getHeight()); float cellSize = boardSize / 14f; float startX = (getWidth() - boardSize) / 2f + cellSize / 2f; float startY = (getHeight() - boardSize) / 2f + cellSize / 2f; // 画网格线 paint.setColor(Color.BLACK); paint.setStrokeWidth(2f); for (int i = 0; i < 15; i++) { float x = startX + i * cellSize; float y = startY + i * cellSize; canvas.drawLine(startX, y, startX + 14 * cellSize, y, paint); canvas.drawLine(x, startY, x, startY + 14 * cellSize, paint); } // 画棋子 for (int row = 0; row < 15; row++) { for (int col = 0; col < 15; col++) { if (board[row][col] != 0) { float cx = startX + col * cellSize; float cy = startY + row * cellSize; paint.setColor(board[row][col] == 1 ? Color.BLACK : Color.WHITE); canvas.drawCircle(cx, cy, cellSize * 0.4f, paint); } } } }这段代码里,board是一个 15×15 的整型二维数组,0 表示空,1 表示黑子,2 表示白子。startX和startY做了居中偏移,保证棋盘在任意宽高比下都居中。棋子半径取格子间距的 0.4 倍,留出间隙避免相邻棋子粘连。参数上唯一要调的是paint.setStrokeWidth,线太细在低密度屏上看不清,太粗又显得笨重,2f 到 3f 之间比较合适。
3.2 onTouchEvent 坐标映射:把手指点击转成行列索引
触摸事件处理的关键是把屏幕坐标转换成棋盘的行列索引。很多新手直接拿event.getX()去减startX,结果发现点偏了,原因是没考虑手指按下的位置和棋子中心的对齐。正确做法是算出最近的交叉点,再判断距离是否在可接受范围内,避免点到格子中间也落子。
@Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_DOWN) { return true; } int boardSize = Math.min(getWidth(), getHeight()); float cellSize = boardSize / 14f; float startX = (getWidth() - boardSize) / 2f + cellSize / 2f; float startY = (getHeight() - boardSize) / 2f + cellSize / 2f; // 计算最近的行列 int col = Math.round((event.getX() - startX) / cellSize); int row = Math.round((event.getY() - startY) / cellSize); // 边界检查 if (row < 0 || row >= 15 || col < 0 || col >= 15) { return true; } // 该位置已有棋子则忽略 if (board[row][col] != 0 || gameOver) { return true; } // 落子并切换玩家 board[row][col] = currentPlayer; if (checkWin(row, col, currentPlayer)) { gameOver = true; Toast.makeText(getContext(), (currentPlayer == 1 ? "黑棋" : "白棋") + "获胜", Toast.LENGTH_SHORT).show(); } else { currentPlayer = (currentPlayer == 1) ? 2 : 1; } invalidate(); // 触发重绘 return true; }这里用Math.round而不是强制类型转换,是因为浮点数直接转 int 会向下取整,导致点击交叉点右侧时算到左边一格。边界检查不能省,否则数组越界直接崩溃。gameOver标志位用来在分出胜负后锁住棋盘,防止继续落子。invalidate()是刷新界面的关键,不调用的话数组变了但屏幕不会更新,这是新手最容易翻车的地方之一。
3.3 胜负判定算法:四个方向扫描与边界处理
胜负判定是五子棋的核心逻辑。常见做法是以当前落子点为中心,分别向水平、垂直、主对角线、副对角线四个方向统计同色连续棋子数,任意方向达到或超过 5 就判胜。这里要注意边界处理,扫描时不能越界,同时要避免重复统计。
private 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; // 当前棋子本身算一个 // 正方向扫描 for (int step = 1; step < 5; step++) { int r = row + dir[0] * step; int c = col + dir[1] * step; if (r < 0 || r >= 15 || c < 0 || c >= 15 || board[r][c] != player) { break; } count++; } // 反方向扫描 for (int step = 1; step < 5; step++) { int r = row - dir[0] * step; int c = col - dir[1] * step; if (r < 0 || r >= 15 || c < 0 || c >= 15 || board[r][c] != player) { break; } count++; } if (count >= 5) { return true; } } return false; }这段代码用方向数组把四个方向统一处理,正反各扫最多 4 步,加上当前棋子最多 9 个,效率足够。参数上,step < 5是因为五子连珠最多只需要看前后各 4 个位置。边界判断写在循环条件里,一旦越界或遇到不同颜色就 break,避免无效扫描。有些实现会单独写四个函数,代码更长但可读性未必更好,用方向数组在报告里也更容易画流程图说明。
3.4 悔棋与重开:用栈保存落子历史
悔棋功能需要记录每一步的落子位置,常见做法是用一个栈或者 ArrayList 保存row和col。重开则是清空数组、重置玩家和游戏状态。这两个功能在答辩时经常被问到,因为涉及状态回退和数据结构的选择。
private final Stack<int[]> moveHistory = new Stack<>(); // 落子时压栈 moveHistory.push(new int[]{row, col}); // 悔棋:弹出最后一步并清空该位置 public void undoMove() { if (moveHistory.isEmpty() || gameOver) { return; } int[] last = moveHistory.pop(); board[last[0]][last[1]] = 0; currentPlayer = (currentPlayer == 1) ? 2 : 1; // 切换回上一步玩家 invalidate(); } // 重开:清空所有状态 public void restartGame() { for (int i = 0; i < 15; i++) { for (int j = 0; j < 15; j++) { board[i][j] = 0; } } moveHistory.clear(); currentPlayer = 1; gameOver = false; invalidate(); }用Stack是因为悔棋天然符合后进先出,push和pop语义清晰。注意悔棋后要切换玩家,否则会出现“悔棋后还是同一个人下”的 bug。重开时除了清空数组和栈,还要把gameOver置回 false,不然棋盘会一直锁着。这些细节在报告里可以写成“状态一致性维护”,是体现设计严谨性的地方。
4. 运行演示与报告撰写:截图、格式与答辩要点
4.1 运行演示的截图清单与录制建议
运行演示部分,报告里通常需要放几张关键截图:初始棋盘、对局中、获胜提示、悔棋前后对比。截图用模拟器自带的截图功能或者真机三指截屏都行,注意截之前把状态栏时间调成一致,避免看起来像拼凑的。如果学校要求录屏,用 Android Studio 的 Logcat 配合屏幕录制,或者直接真机录屏,时长控制在 30 秒到 1 分钟,展示落子、判定、悔棋、重开四个动作即可。
截图放进报告时,每张图下面要有一行图注,写清楚“图 X 黑棋落子后界面”这种,不要只放图不解释。图文并茂不是图多就行,而是图能辅助说明设计点。比如放一张棋盘绘制的截图,旁边标注“网格间距根据屏幕宽度动态计算”,这样答辩时老师一看就知道你动了脑子。
4.2 课程设计报告的章节结构与格式规范
一份规范的课程设计报告,章节顺序一般是:封面、摘要、目录、项目背景、开发环境、详细设计、运行演示、心得体会、参考文献。其中详细设计是重头,要拆成模块来写,比如棋盘绘制模块、落子判定模块、胜负检测模块、悔棋重开模块,每个模块配一段说明加关键代码。代码不要整段贴,挑核心的十几行,其余用文字描述逻辑。
格式上,字体、行距、页边距按学校模板来,标题层级用 Word 的样式功能,不要手动调字号。代码块用等宽字体,加浅灰底纹,行号可加可不加。参考文献列三到五条,可以是 Android 官方文档、Java 编程教材、数据结构教材,不要列不存在的链接。9362 字这个量级,详细设计占一半,背景和环境占两成,演示和心得占三成,结构就比较稳。
4.3 心得体会怎么写才不空
心得体会最容易写成“通过这次课程设计我学到了很多”这种空话。我一般建议从具体问题切入:比如“坐标映射一开始算错了半格,后来用 Math.round 解决”“悔棋忘了切换玩家,测试时发现连续下了两步黑棋”。把踩坑和解决过程写出来,比喊口号有说服力。也可以写选型时的纠结,比如为什么用自定义 View 而不是 SurfaceView,为什么用栈而不是队列。这些思考能体现你对技术的理解,答辩时老师也愿意听。
5. 避坑与排查:五子棋课程设计里最容易翻车的五个点
5.1 棋盘画出来了但棋子点不上去
现象:界面能显示棋盘,但点击没有任何反应。原因通常是onTouchEvent没有返回 true,或者自定义 View 没有设置clickable。解决:在onTouchEvent的 ACTION_DOWN 分支返回 true,并在构造函数里调用setClickable(true)。另外检查 View 的宽高是不是 0,如果布局里用了wrap_content而 onMeasure 没重写,View 可能根本不占空间。
5.2 落子位置偏移半格
现象:点击交叉点,棋子却画在格子中间。原因是用(int)强转坐标导致向下取整,或者startX计算时没加cellSize / 2。解决:用Math.round取最近整数,并确认startX和startY的偏移量包含半个格子。如果还是偏,打印event.getX()和计算出的col对比,看是哪一步差了。
5.3 胜负判定漏判斜向五连
现象:水平垂直能判胜,斜着连五个没反应。原因通常是方向数组写错,比如副对角线用了{1, 1}而不是{1, -1},或者扫描时行列增减搞反。解决:把四个方向单独测试,在棋盘上手动摆出斜向五连,看checkWin返回什么。方向数组{1, -1}表示行增列减,对应从左上到右下的副对角线,别和主对角线混了。
5.4 悔棋后界面不刷新
现象:点了悔棋,数组里的棋子清掉了,但屏幕上还在。原因是undoMove里忘了调invalidate()。解决:任何修改board数组的操作之后都要调invalidate(),包括落子、悔棋、重开。可以在报告里把这条写成“状态变更与视图刷新绑定”的设计原则。
5.5 旋转屏幕后棋局丢失
现象:手机横竖屏切换,棋盘清空回到初始状态。原因是 Activity 重建时没有保存游戏状态。解决:重写onSaveInstanceState把board数组和currentPlayer存进 Bundle,在onCreate里恢复。如果嫌数组序列化麻烦,也可以锁定屏幕方向为竖屏,在 Manifest 里加android:screenOrientation="portrait",课程设计里这么做完全可接受。
6. 进阶技巧:把五子棋做成能拿高分的样子
如果你想让这份课程设计在答辩时更出彩,可以在基础功能上加一两个进阶点,但不要贪多,加多了反而容易出 bug。第一个技巧是加一个简单的 AI 对手,不用做深度搜索,只做“评分选点”就行:遍历所有空位,对每个空位计算如果下黑子或白子能形成多少连子,选分数最高的位置。这样代码量不大,但演示时能体现“人机对战”,报告里也能多一个算法模块。
// 简易评分:统计某空位周围八个方向的同色棋子数 private int evaluatePosition(int row, int col, int player) { int score = 0; int[][] dirs = {{0,1},{1,0},{1,1},{1,-1}}; for (int[] d : dirs) { int count = 1; for (int s = 1; s < 5; s++) { int r = row + d[0] * s, c = col + d[1] * s; if (r < 0 || r >= 15 || c < 0 || c >= 15 || board[r][c] != player) break; count++; } for (int s = 1; s < 5; s++) { int r = row - d[0] * s, c = col - d[1] * s; if (r < 0 || r >= 15 || c < 0 || c >= 15 || board[r][c] != player) break; count++; } if (count >= 5) score += 100; // 能赢的位置给高分 else score += count * count; // 连子越多分越高 } return score; }这个评分函数只做启发式,不保证最优,但演示足够。参数上,count * count是让长连的权重更高,避免 AI 只堵不攻。第二个技巧是加落子动画,用ValueAnimator让棋子从透明到不透明,或者从大到小弹一下,视觉上更精致。第三个技巧是把棋盘颜色和棋子样式做成可切换的主题,用 SharedPreferences 存用户选择。这三个里选一个加到报告里,作为“扩展功能”章节,答辩时能多讲两分钟。
我自己做课程设计时最大的教训是:不要等到代码全写完再写报告,边写代码边截图、边记录问题和解决过程,最后整理成文会轻松很多。另外,答辩前一定要在真机上完整跑一遍,模拟器上触摸不准的问题真机上可能更明显。希望帮到你。
本文还有配套的精品资源,点击获取