- 效果
- 实现功能
- 流程图
- 开发工具
- 函数说明
- 链表操作函数
- 功能函数
- 控制函数
- 源代码
话不多说,先看成果图:
- 游戏界面加载
- 棋子自定义
- 鼠标操控落子位置
- 悔棋
- 复盘
- 棋子追踪(标记最新落下的棋子)
主体功能的流程图如下:
开发工具Visual Studio 2019、EasyX.
Visual Studio 2019的安装方法在此获取,EasyX图形库在官网获取。
也可以在 Q群 :178668046 - 群文件 - 开发工具 获取。
注:这里Visual Studio 2019并不是必需的,使用适配EasyX图形库的任一编译器即可。
为了实现对弈时的悔棋、复盘操作,本文建立了一个双向链表,来存储对局信息。由于悔棋操作需要每次取走棋盘上最新落下的棋子,所以利用栈来存储对局信息最为合适。而复盘操作需要按照棋手落子顺序,将对局过程再次展现出来,利用队列来存储对局信息最为合适,所以我将悔棋操作用到的栈升级为一个双向链表(为了方便描述与理解,以下依旧称此双向链表为栈)。
棋手落子时将落子位置push进栈,悔棋时pop出栈;复盘时将栈底元素看作队头元素,栈顶元素看作队尾元素,从队头元素开始依此出队。这样可以同时实现栈和队列的功能,同时满足悔棋和复盘的需求。
| 函数名 | 函数作用 |
|---|---|
| push() | 元素入栈 |
| pop() | 元素出栈 |
| lClear() | 清空栈 |
| fuPan() | 进行复盘 |
| 函数名 | 函数作用 |
|---|---|
| m_withdraw() | 悔棋 |
| m_emphasize() | 标记最新落下的棋子 |
| m_remove(int x, int y) | 为棋子取消标记 |
| panDuan() | 判断对局是否结束 |
| 函数名 | 函数作用 |
|---|---|
| init_system() | 系统初始化 |
| init_game() | 对局界面初始化 |
| init_qipan() | 棋盘数组初始化 |
| m_startgame() | 对局控制函数 |
| showWhoWin() | 展示哪一方获胜 |
| m_replay() | 再来一局 |
| close_system() | 关闭系统 |
- 代码需要保存为.cpp格式才可以正常运行。
- 代码运行需要棋子图片,可以进 群:178668046 直接获取,或者自己设置喜欢的棋子图片也可。设置方法:1. 在代码的同级目录新建名为 image 的文件夹,并将自己准备的图片放到 image文件夹中;2. 在函数init_system()中,将要导入图片的名称及格式替换为自己准备的图片的名称及格式。
#include#include #include #include #include #define N 15 #define LIGHTBROWN RGB(255,211,155) // 棋盘颜色 int m_bine = 34, m_wh = 40; // 定义棋子大小、每格像素数 int m_x0 = 100, m_y0 = 40; // 初始棋子位置 int m_x9 = 660, m_y9 = 600; // 最终棋子位置 int a = 740, b = 350, c = 810, d = 390;// 悔棋按钮的位置 int flag; struct Point { int a, b; struct Point* prior; struct Point* next; }; int qipan[N][N]; int ffupan[N][N]; int x, y, x0, y0; // (x0,y0)是棋盘上的坐标,(x,y)是数组下标 struct Point* top, * base; // 全局变量 分别指向栈顶和栈底 IMAGE dog, cat, dog1, cat1; HWND hwnd; void push(); // 进栈 //实际是一个队列加栈,双向链表 ,不是栈 void pop(); // 出栈 void lClear(); // 清空 void fuPan(); // 复盘,由栈底向栈顶搜索 int panDuan(); // 判断是否有连成五子 int showWhoWin(); // 输出谁赢了 void init_system(); // 初始化系统 void init_game(); // 初始化游戏界面 void init_qipan(); // 棋盘数组初始化 void m_replay(); // 重玩 void m_startgame(); // 游戏开始 void m_withdraw(); // 悔棋 void m_emphasize(); // 标记(强调)最新落子 void m_remove(int x, int y); // 取消标记 void close_system(); // 关闭系统 void push() { struct Point* p; p = (struct Point*)malloc(sizeof(struct Point)); if (!p) exit(0); p->a = x; p->b = y; p->prior = NULL; p->next = top; if (top == NULL) base = p; else top->prior = p; top = p; } void pop() { struct Point* p; p = top; qipan[top->a][top->b] = 0; top = top->next; if (top == NULL) { base = NULL; x = 0, y = 0; } else { top->prior = NULL; x = top->a; y = top->b; x0 = x + 1; y0 = y + 1; } free(p); } void lClear() { struct Point* p; p = top; while (top != NULL) { p = top; top = top->next; free(p); } base = NULL; } void fuPan() { struct Point* p, * q; p = base; int i, j; for (i = 0; i < N; i++) for (j = 0; j < N; j++) ffupan[i][j] = qipan[i][j]; init_qipan(); init_game(); fillrectangle(a, b - 100, c + 100, d - 100); // setcolor(BLACK); setbkmode(TRANSPARENT); settextstyle(30, 0, L"楷体"); outtextxy(a + 5, b - 95, L"按空格继续"); setcolor(WHITE); int color = 1; while (p != NULL) { qipan[p->a][p->b] = ffupan[p->a][p->b]; system("cls"); x = p->a; y = p->b; int xx = m_x0 + x * m_wh; int yy = m_y0 + y * m_wh; if (color == 1) { putimage(xx - m_bine / 2, yy - m_bine / 2, &dog); color = 2; } else { putimage(xx - m_bine / 2, yy - m_bine / 2, &cat); color = 1; } m_emphasize(); system("pause"); m_remove(x, y); p=p->prior; } } int main() { init_system(); m_startgame(); close_system(); } void init_system() // 初始化系统 { init_game(); init_qipan(); loadimage(&dog, L"image/dog0.jpg", m_bine, m_bine); loadimage(&cat, L"image/cat0.jpg", m_bine, m_bine); loadimage(&dog1, L"image/dog1.jpg", 64, 64); loadimage(&cat1, L"image/cat1.jpg", 64, 64); } void init_game() // 初始化游戏界面 { int i; initgraph(960, 640); hwnd = GetHWnd(); SetWindowText(hwnd, L"五子棋"); setlinecolor(LIGHTBROWN); setfillcolor(LIGHTBROWN); fillrectangle(m_x0 - m_wh, m_y0 - m_wh, m_x9 + m_wh, m_y9 + m_wh); fillrectangle(a, b, c, d); setcolor(BLACK); setbkmode(TRANSPARENT); settextstyle(30, 0, L"楷体"); outtextxy(a + 5, b + 5, L"悔棋"); setcolor(WHITE); setlinecolor(BLACK); setfillcolor(BLACK); for (i = 0; i < N; i++) line(m_x0, m_y0 + i * m_wh, m_x9, m_y0 + i * m_wh); for (i = 0; i < N; i++) line(m_x0 + i * m_wh, m_y0, m_x0 + i * m_wh, m_y9); fillcircle(m_x0 + 7 * m_wh, m_y0 + 7 * m_wh, 3); fillcircle(m_x0 + 3 * m_wh, m_y0 + 3 * m_wh, 3); fillcircle(m_x0 + 3 * m_wh, m_y0 + 11 * m_wh, 3); fillcircle(m_x0 + 11 * m_wh, m_y0 + 3 * m_wh, 3); fillcircle(m_x0 + 11 * m_wh, m_y0 + 11 * m_wh, 3); setcolor(WHITE); // setlinecolor(WHITE); // setfillcolor(BLACK); } void init_qipan() { int i, j; for (i = 0; i < N; i++) for (j = 0; j < N; j++) qipan[i][j] = 0; } void close_system() // 关闭系统 { settextstyle(20, 0, L"楷体"); outtextxy(810, 600, L"按任意键结束。。"); system("pause"); closegraph(); } void m_replay()// 重新游戏 { lClear(); memset(qipan, 0, sizeof(qipan));// 初始化数组 // color = 1; init_game(); } void m_emphasize() { int a1, b1; a1 = m_x0 + x * m_wh; b1 = m_y0 + y * m_wh; setlinecolor(RED); line(a1 - m_wh / 2, b1 - m_wh / 2, a1 - m_wh / 2 + 5, b1 - m_wh / 2); line(a1 - m_wh / 2, b1 - m_wh / 2, a1 - m_wh / 2, b1 - m_wh / 2 + 5); line(a1 + m_wh / 2 - 5, b1 - m_wh / 2, a1 + m_wh / 2, b1 - m_wh / 2); line(a1 + m_wh / 2, b1 - m_wh / 2, a1 + m_wh / 2, b1 - m_wh / 2 + 5); line(a1 - m_wh / 2, b1 + m_wh / 2, a1 - m_wh / 2 + 5, b1 + m_wh / 2); line(a1 - m_wh / 2, b1 + m_wh / 2 - 5, a1 - m_wh / 2, b1 + m_wh / 2); line(a1 + m_wh / 2 - 5, b1 + m_wh / 2, a1 + m_wh / 2, b1 + m_wh / 2); line(a1 + m_wh / 2, b1 + m_wh / 2 - 5, a1 + m_wh / 2, b1 + m_wh / 2); setcolor(WHITE); } void m_remove(int x, int y) { int a1, b1; a1 = m_x0 + x * m_wh; b1 = m_y0 + y * m_wh; setlinecolor(LIGHTBROWN); line(a1 - m_wh / 2, b1 - m_wh / 2, a1 - m_wh / 2 + 5, b1 - m_wh / 2); line(a1 - m_wh / 2, b1 - m_wh / 2, a1 - m_wh / 2, b1 - m_wh / 2 + 5); line(a1 + m_wh / 2 - 5, b1 - m_wh / 2, a1 + m_wh / 2, b1 - m_wh / 2); line(a1 + m_wh / 2, b1 - m_wh / 2, a1 + m_wh / 2, b1 - m_wh / 2 + 5); line(a1 - m_wh / 2, b1 + m_wh / 2, a1 - m_wh / 2 + 5, b1 + m_wh / 2); line(a1 - m_wh / 2, b1 + m_wh / 2 - 5, a1 - m_wh / 2, b1 + m_wh / 2); line(a1 + m_wh / 2 - 5, b1 + m_wh / 2, a1 + m_wh / 2, b1 + m_wh / 2); line(a1 + m_wh / 2, b1 + m_wh / 2 - 5, a1 + m_wh / 2, b1 + m_wh / 2); setcolor(WHITE); } int panDuan() { int who, count = 1; int i = 1, j = 1; who = qipan[x][y]; if (who == 0) return 0;// 针对上来就悔棋的情况 // 水平的左边 while (who == qipan[x][y - i] && y - i >= 0) { i++; count++; if (count == 5) return who; } // 水平的右边 i = 1; while (who == qipan[x][y + i] && y + i < N) { i++; count++; if (count == 5) return who; } // 垂直上方 i = 1; count = 1; while (who == qipan[x - i][y] && x - i >= 0) { i++; count++; if (count == 5) return who; } // 垂直下方 i = 1; while (who == qipan[x + i][y] && x + i < N) { i++; count++; if (count == 5) return who; } // 解决左上的斜线 上方 i = 1; j = 1; count = 1; while (who == qipan[x - i][y - j] && x - i >= 0 && y - j >= 0) { i++; j++; count++; if (count == 5) return who; } // 解决左上的斜线 下方 i = 1; j = 1; while (who == qipan[x + i][y + j] && x + i < N && y + j < N) { i++; j++; count++; if (count == 5) return who; } // 解决右上的斜线 上方 i = 1; j = 1; count = 1; while (who == qipan[x - i][y + j] && x - i >= 0 && y + j < N) { i++; j++; count++; if (count == 5) return who; } // 解决右上的斜线 下方 i = 1; j = 1; while (who == qipan[x + i][y - j] && x + i < N && y - j >= 0) { i++; j++; count++; if (count == 5) return who; } return 0;// 无人胜利 } int showWhoWin() { int whowin; // 谁赢了 1-dog 2-cat 0-NULL whowin = panDuan(); if (whowin == 0) return 0; else { int id; if (whowin == 1) { id = MessageBox(hwnd, TEXT("恭喜汪汪队立大功!n是否复盘?"), TEXT("游戏结束"), MB_YESNO); } else id = MessageBox(hwnd, TEXT("恭喜喵喵队立大功!n是否复盘?"), TEXT("游戏结束"), MB_YESNO); if (id == IDYES) return 1; else return 2; } } void m_startgame() { MOUSEMSG m_mouse; int color = 1; top = NULL; base = NULL; // init_game里面 while (1) { if (color == 1) putimage(a, 200, &dog1); else putimage(a, 200, &cat1); m_mouse = GetMouseMsg(); if (m_mouse.uMsg == WM_LBUTTONDOWN) { if (m_mouse.x > a && m_mouse.x < c && m_mouse.y > b && m_mouse.y < d) // 判断是否悔棋 { if (top == NULL) MessageBox(hwnd, TEXT("已无法继续悔棋!"), TEXT("提示"), MB_OK); else { m_withdraw(); if (color == 1) color = 2; else color = 1; } } else if (m_mouse.x > m_x0 - m_wh / 2 && m_mouse.x < m_x9 + m_wh / 2 && m_mouse.y > m_y0 - m_wh / 2 && m_mouse.y < m_y9 + m_wh / 2) // 判断是否在棋盘内 { int x1, y1; // 为m_remove()服务 x1 = x; y1 = y; for (x = 0; x < N; x++) { for (y = 0; y < N; y++) { if (m_mouse.x >= m_x0 - m_wh / 2 + x * m_wh && m_mouse.x <= m_x0 + m_wh / 2 + x * m_wh && m_mouse.y >= m_y0 - m_wh / 2 + y * m_wh && m_mouse.y <= m_y0 + m_wh / 2 + y * m_wh) // 纠正棋子位置,使其落在两线交叉点 { x0 = x + 1; y0 = y + 1; m_mouse.x = m_x0 + x * m_wh; m_mouse.y = m_y0 + y * m_wh; break; } } //break;可优化 } x = x0 - 1; y = y0 - 1; if (qipan[x][y] != 0) // 如果此处有棋子则不能下棋 continue; qipan[x][y] = color; push(); if (color == 1) { putimage(m_mouse.x - m_bine / 2, m_mouse.y - m_bine / 2, &dog); color = 2; } else { putimage(m_mouse.x - m_bine / 2, m_mouse.y - m_bine / 2, &cat); color = 1; } m_remove(x1, y1); m_emphasize(); } else continue; // 鼠标无效点击 flag = showWhoWin(); if (flag != 0) // 不为0即游戏结束 { if (flag == 1) fuPan(); int id; id = MessageBox(hwnd, TEXT("是否重新开始游戏?"), TEXT("游戏结束"), MB_YESNO); if (id == IDYES) m_replay(); else break; color = 1; } } } } void m_withdraw() { // 去除棋子的追踪特效 m_remove(x, y); int a, b; a = m_x0 + x * m_wh; b = m_y0 + y * m_wh; setcolor(LIGHTBROWN); setfillcolor(LIGHTBROWN); fillcircle(a, b, m_wh / 2); //floodfill(a, b, LIGHTBROWN); // 为悔棋棋子的位置加上黑线 setcolor(BLACK); setfillcolor(BLACK); if (x0 == 1 && y0 == 1) { line(m_x0, m_y0, m_x0 + m_wh / 2, m_y0); line(m_x0, m_y0, m_x0, m_y0 + m_wh / 2); } else if (x0 == 15 && y0 == 1) { line(m_x9 - m_wh / 2, m_y0, m_x9, m_y0); line(m_x9, m_y0, m_x9, m_y0 + m_wh / 2); } else if (x0 == 1 && y0 == 15) { line(m_x0, m_y9, m_x0 + m_wh / 2, m_y9); line(m_x0, m_y9 - m_wh / 2, m_x0, m_y9); } else if (x0 == 15 && y0 == 15) { line(m_x9, m_y9 - m_wh / 2, m_x9, m_y9); line(m_x9 - m_wh / 2, m_y9, m_x9, m_y9); } else if (x0 == 1) { line(a, b, a + m_wh / 2, b); line(a, b - m_wh / 2, a, b + m_wh / 2); } else if (x0 == 15) { line(a - m_wh / 2, b, a, b); line(a, b - m_wh / 2, a, b + m_wh / 2); } else if (y0 == 1) { line(a - m_wh / 2, b, a + m_wh / 2, b); line(a, b, a, b + m_wh / 2); } else if (y0 == 15) { line(a - m_wh / 2, b, a + m_wh / 2, b); line(a, b - m_wh / 2, a, b); } else { line(a - m_wh / 2, b, a + m_wh / 2, b); line(a, b - m_wh / 2, a, b + m_wh / 2); if (x0 == 4 && y0 == 4) fillcircle(a, b, 3); else if (x0 == 4 && y0 == 12) fillcircle(a, b, 3); else if (x0 == 12 && y0 == 4) fillcircle(a, b, 3); else if (x0 == 12 && y0 == 12) fillcircle(a, b, 3); else if (x0 == 8 && y0 == 8) fillcircle(a, b, 3); } pop(); if (top != NULL) // 为最新棋子加上特效 m_emphasize(); }



