目录
构思
1、主程序
2、初始化
3、游戏菜单
4、打印棋盘
6、玩家下棋
7、判断输赢
8、功能整合
人机下棋
完整版:
game.h
game.c
text.c
测试功能代码
构思
五子棋不必多介绍了,大家小时候都玩过哈。
我们要通过程序实现这个小游戏,大体上的构思得有:
游戏流程:运行游戏>>打印棋盘>>下棋>>判断输赢
由此,我们声明如下函数、棋盘数组和回合数 (用于双人下棋时下不同棋子的判断)。
将这些都放在我创建的头文件game.h中方便其他文件使用:
#include <stdio.h>
#include <Windows.h>
int map[19][19];//棋盘
int flag;//回合数
//初始化棋盘
void init();
//游戏菜单
void menuView();
//打印棋盘
void gameView_ShowMap();
//玩家下棋
int playerMove(int x, int y);
//判断输赢
int isWin(int x, int y);
//判断输赢
void winView();
1、主程序
先进行游戏下棋,后进行判断输赢,比较符合do—while的特点。
int main()
{
int input = 0;
do
{
menuView();
printf("请选择:");
scanf("%d", &input);
switch (input) {
case 1:
gameView();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新选择.\n");
break;
}
} while (input);
return 0;
}
- 首先打印菜单,提示玩家进行选择开始游戏。
- 通过输入变量input的值,判断是否进行游戏,
- 值为1则调用gameView函数进行游戏,0则结束游戏。
- gameView函数对各种功能函数进行整合。
在此之前先将各种功能函数进行实现,最后将它们放入gameView整合。
2、初始化
void init() {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
map[i][j] = 0;
}
}
flag = 0;
}
- 将棋盘每个格子初始化为0。
- 回合数初始化为0。
3、游戏菜单
void menuView() {
printf("*************************\n");
printf("***** 1. play ******\n");
printf("***** 0. exit ******\n");
printf("*************************\n");
}
4、打印棋盘
这里我选择设计了棋盘格和横纵坐标,我们可以根据需要自行设计。
void gameView_ShowMap() {
int i, j;
printf(" ");
for (i = 0; i < 19; i++) {//打印横坐标
printf("%3d ",i);
}
printf("\n ");
for (i = 0; i < 19; i++) {
printf("+---");
}
printf("+\n");
for (i = 0; i < 19; i++) {
printf("%2d |", i);//每行输出前都先打印纵坐标
for (j = 0; j < 19; j++) {
printf(" %d ", map[i][j]);//打印棋子
if (j < 18)
printf("|");
}
printf("|\n");
if(i<18)
printf(" |");
if(i==18)
printf(" +");
for (j = 0; j < 19; j++) {
printf("---");
if (j < 18)
printf("+");
}
printf("+\n");
}
}
效果如下:
6、玩家下棋
int playerMove(int x, int y) {
if (x >= 0 && x < 19 && y >= 0 && y < 19) {
if (map[x][y] == 0) {
if (flag % 2 == 0)
map[x][y] = 1;//下黑子
else
map[x][y] = 2;//下白子
//落子成功
return 1;
}
else {
//该位置已有棋子
return 0;
}
}
else {
//坐标不合法
return -1;
}
}
- 首先判断下棋位置是否在棋盘0到18的横纵坐标内,如果不在函数返回值-1,
- 如果在棋盘内,则进行下棋。
- 棋盘每个格子初始值为0,判断当前格子的值为0才可以下棋,否则返回值为0。
- 回合数flag初始值为0,当flag为偶数下黑子>>1,奇数下白子>>2,成功下棋返回值为1。
7、判断输赢
int isWin(int x, int y) {
int i, j;
for (i = 0; i < 19; i++) {
for (j = 0; j < 19; j++) {
if (map[i][j] == 0) {
continue;
}
//横着连成五子
if (j < 15)
if (map[i][j] == map[i][j + 1] && map[i][j] == map[i][j + 2]
&& map[i][j] == map[i][j + 3] && map[i][j] == map[i][j + 4])
return map[i][j];
//竖着连成五子
if (i < 15)
if (map[i][j] == map[i + 1][j] && map[i][j] == map[i + 2][j]
&& map[i][j] == map[i + 3][j] && map[i][j] == map[i + 4][j])
return map[i][j];
//左斜着连成五子-> " \ "
if (i < 15 && j < 15)
if (map[i][j] == map[i + 1][j + 1] && map[i][j] == map[i + 2][j + 2]
&& map[i][j] == map[i + 3][j + 3] && map[i][j] == map[i + 4][j + 4])
return map[i][j];
//右斜着连成五子-> " / "
if (i < 15 && j > 4)
if (map[i][j] == map[i + 1][j - 1] && map[i][j] == map[i + 2][j - 2]
&& map[i][j] == map[i + 3][j - 3] && map[i][j] == map[i + 4][j - 4])
return map[i][j];
}
}
return 0;
}
-
i < 15
用于确保在检查垂直和左斜方向的连续五子时,起始位置(i, j)
之后至少还有4个位置。因为五子连成一线需要五个连续的位置,所以确保从当前位置开始往下检查的时候不会超出数组的边界。 -
j < 15
用于确保在检查水平和左斜方向的连续五子时,起始位置(i, j)
之后至少还有4个位置。同样,这是为了确保从当前位置开始往右检查的时候不会超出数组的边界。 -
j > 4
用于确保在检查右斜方向的连续五子时,起始位置(i, j)
之前至少还有4个位置。这是为了确保从当前位置开始往左检查的时候不会超出数组的边界。
8、功能整合
void gameView()
{
init();
int x = 0, y = 0;
int ret = 0;//辅助判断坐标是否合法
while (1) {
gameView_ShowMap();
printf("请输入要下棋的坐标:");
scanf("%d %d", &x, &y);
if (playerMove(x, y) == 0) {
printf("\n!!!该坐标已被占用!!!\n");
Sleep(2000);
continue;
}
else if (playerMove(x, y) == -1) {
printf("\n!!!请输入合法坐标!!!\n");
Sleep(2000);
continue;
}
else {
flag++;//切换回合
}
if (isWin(x, y) == 0) {
continue;
}
else if (isWin(x, y) == 1) {
printf("黑子获胜\n");
break;
}
else if (isWin(x, y) == 2) {
printf("白子获胜\n");
break;
}
}
}
- 这里的Sleep函数需要头文件#include <Windows.h>,使用该函数暂停两秒,防止continue后下次循环打印的棋盘将提示信息挡住。
- 下棋坐标不合法打印提示信息后,进入下次循环重新输入。
- 成功下棋则flag自增,切换回合。
- 每次下棋后都要判断输赢,有人赢了则停止循环,否则继续下棋。
- (其实应该从下棋次数第五次开始判断输赢更合理,读者可以自行添加判断)
人机下棋
想要实现人机下棋可以看看我这篇文章《三子棋》 ,里面实现了人机下棋,读者可以自行模仿改进,实现其功能的重要函数可以在这篇文章中学习 rand&srand函数 。
完整版:
game.h
#include <stdio.h>
#include <Windows.h>
int map[19][19];//棋盘
int flag;//回合数
//初始化
void init();
//游戏菜单
void menuView();
//打印棋盘
void gameView_ShowMap();
//玩家下棋
int playerMove(int x, int y);
//判断输赢
int isWin(int x, int y);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init() {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
map[i][j] = 0;
}
}
flag = 1;
}
int isWin(int x, int y) {
int i, j;
for (i = 0; i < 19; i++) {
for (j = 0; j < 19; j++) {
if (map[i][j] == 0) {
continue;
}
//横着连成五子
if (j < 15)
if (map[i][j] == map[i][j + 1] && map[i][j] == map[i][j + 2]
&& map[i][j] == map[i][j + 3] && map[i][j] == map[i][j + 4])
return map[i][j];
//竖着连成五子
if (i < 15)
if (map[i][j] == map[i + 1][j] && map[i][j] == map[i + 2][j]
&& map[i][j] == map[i + 3][j] && map[i][j] == map[i + 4][j])
return map[i][j];
//左斜着连成五子-> " \ "
if (i < 15 && j < 15)
if (map[i][j] == map[i + 1][j + 1] && map[i][j] == map[i + 2][j + 2]
&& map[i][j] == map[i + 3][j + 3] && map[i][j] == map[i + 4][j + 4])
return map[i][j];
//右斜着连成五子-> " / "
if (i < 15 && j > 4)
if (map[i][j] == map[i + 1][j - 1] && map[i][j] == map[i + 2][j - 2]
&& map[i][j] == map[i + 3][j - 3] && map[i][j] == map[i + 4][j - 4])
return map[i][j];
}
}
return 0;
}
int playerMove(int x, int y) {
if (x >= 0 && x < 19 && y >= 0 && y < 19) {
if (map[x][y] == 0) {
if (flag % 2 == 0)
map[x][y] = 1;//下黑子
else
map[x][y] = 2;//下白子
//落子成功
return 1;
}
else {
//该位置已有棋子
return 0;
}
}
else {
//坐标不合法
return -1;
}
}
void menuView() {
printf("*************************\n");
printf("***** 1. play ******\n");
printf("***** 0. exit ******\n");
printf("*************************\n");
}
void gameView_ShowMap() {
int i, j;
printf(" ");
for (i = 0; i < 19; i++) {//打印横坐标
printf("%3d ",i);
}
printf("\n ");
for (i = 0; i < 19; i++) {
printf("+---");
}
printf("+\n");
for (i = 0; i < 19; i++) {
printf("%2d |", i);//每行输出前先都打印纵坐标
for (j = 0; j < 19; j++) {
printf(" %d ", map[i][j]);
if (j < 18)
printf("|");
}
printf("|\n");
if(i<18)
printf(" |");
if(i==18)
printf(" +");
for (j = 0; j < 19; j++) {
printf("---");
if (j < 18)
printf("+");
}
printf("+\n");
}
}
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void gameView()
{
init();
int x = 0, y = 0;
int ret = 0;//辅助判断坐标是否合法
while (1) {
gameView_ShowMap();
printf("请输入要下棋的坐标:");
scanf("%d %d", &x, &y);
if (playerMove(x, y) == 0) {
printf("\n!!!该坐标已被占用!!!\n");
Sleep(2000);
continue;
}
else if (playerMove(x, y) == -1) {
printf("\n!!!请输入合法坐标!!!\n");
Sleep(2000);
continue;
}
else {
flag++;//切换回合
}
if (isWin(x, y) == 0) {
continue;
}
else if (isWin(x, y) == 1) {
printf("黑子获胜\n");
break;
}
else if (isWin(x, y) == 2) {
printf("白子获胜\n");
break;
}
}
}
int main()
{
int input = 0;
do
{
menuView();
printf("请选择:");
scanf("%d", &input);
switch (input) {
case 1:
gameView();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新选择.\n");
break;
}
} while (input);
return 0;
}
测试功能代码
此代码可以替换int main主函数,用于测试函数功能是否正确,可以减少自行下棋测试时间
成功运行输出如下:
int main()
{
int testflag = 0;
//init测试代码
init();
if (flag != 0) {
printf("init()错误");
exit(0);
}
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
if (map[i][j]) {
printf("init()错误");
exit(0);
}
}
}
printf("init()测试成功\n");
testflag++;
//playerMove测试代码
int result = 1;
result &= playerMove(2, 2);
result &= playerMove(2, 3);
result &= playerMove(2, 4);
result &= playerMove(2, 5);
if (result != 1 || (map[2][2] && map[2][3] && map[2][4] && map[2][5]) != 1) {
printf("playerMove()错误");
exit(0);
}
flag = 1;
result &= playerMove(2, 5);
if (result != 0 || map[2][5] != 1) {
printf("playerMove()错误");
exit(0);
}
printf("playerMove()测试成功\n");
testflag++;
//isWin测试代码
playerMove(2, 1);
if (isWin(2, 1)) {
printf("isWin()错误");
exit(0);
}
playerMove(1, 0);
playerMove(3, 2);
playerMove(4, 3);
playerMove(5, 4);
if (isWin(1, 0) != 2) {
printf("isWin()错误");
exit(0);
}
printf("isWin()测试成功\n");
testflag++;
if (testflag == 3) {
printf("service代码测试成功\n");
}
return 0;
}