目录
问题描述
逻辑分析
具体实现
1.进入菜单界面
2.初始化棋盘
3.打印棋盘
4.玩家下棋
5.电脑下棋
6.判断输赢
运行结果
完整代码
game.h
game.c
test.c
问题描述
结合C语言所学知识,简单实现一个三子棋小游戏。
逻辑分析
- 进入菜单界面
- 初始化棋盘
- 打印棋盘
- 玩家下棋
- 判断输赢
- 电脑下棋
- 判断输赢
- 游戏结束
具体实现
1.进入菜单界面
玩家通过输入1或者0来选择进入对应的环节:输入“1”则进入游戏环节;输入“0”则退出游戏;如果输入其他数字,则会提示“选择错误”
void menu()
{
printf("****************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("****************************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
//printf("三子棋\n");
game();//游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
}
运行结果:
2.初始化棋盘
棋盘格式设置为3*3的二维数组大小,元素类型为char,初始化为空格,即什么数据也不存储。行列采用宏定义的方式,是为了方便后期对棋盘的大小进行统一修改,同时也为了提高代码的可读性
#define ROW 3
#define COL 3
//初始化棋盘为全空格
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
3.打印棋盘
将棋盘进行拆分,第一行为: 字符 | 字符 | 字符 (注意字符前后的空格),第二行我们为:---|---|---。在打印数据时,根据for循环,打印三行数据,也就是三行字符 | 字符 | 字符 ,同时三行数据中穿插着两行---|---|---作为分隔。
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//打印数据
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ",board[i][j]);//空格+字符+空格
//打印"|",只打印前两列
if(j<col-1)
printf("|");
}
printf("\n");//换行
//打印分割行,只打印前两行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col- 1)
printf("|");
}
printf("\n");//换行
}
}
}
运行结果:
4.玩家下棋
玩家根据提示输入行列坐标,比如1 1,同时使用’*’表示玩家落子。玩家在落子之前需要对坐标进行合法性的判断,即判断所输入的坐标是否在棋盘规定的范围内以及是否已被占用。玩家要选择在棋盘上空闲的位置进行落子,否则会提示出错。输入坐标合法之后要立即跳出循环,否则会陷入死循环。
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋\n");
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
//坐标合法性的判断
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;//不加break会进入死循环
}
else
{
printf("该坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
运行结果:
5.电脑下棋
在玩家落子之后,电脑会随机生成行列坐标并在棋盘上空闲的地方进行相应的落子,'#’表示电脑落子。生成随机数之前需要基于当前时间设定“种子”的值srand((unsigned int)time(0))。在调用rand()函数之前,要先在主函数中调用srand函数。如果没有上述准备工作,那么种子的值就会默认为1,会生成相同的随机数序列。
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;//0-2
y = rand() % col;//0-2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;//跳出循环
}
}
}
运行结果:
6.判断输赢
在判断输赢时,我们约定:‘*’ 表示玩家赢;‘#’ 表示电脑赢;’ Q’ 表示平局;‘C’ 表示继续
玩家赢:行,列以及两条对角线有三个连续的‘*’则表示玩家赢
电脑赢:行,列以及两条对角线有三个连续的‘#’则表示电脑赢
平局:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘已满,则表示平局
继续:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘未满,则游戏继续
//判断平局
static int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;//没满
}
}
}
return 1;//满了
}
//判断输赢
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
//判断行
for (i = 0; i < row; i++)
{
if (board[i][0]==board[i][1]&&board[i][1]==board[i][2] &&board[i][1]!= ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断对角线
if (board[0][0]==board[1][1] && board[1][1]==board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2]==board[1][1] && board[1][1]== board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}
运行结果
玩家赢:
电脑赢:
平局:
完整代码
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//初始化棋盘为全空格
void InitBoard(char board[ROW][COL],int row,int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘为全空格
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
//版本1.0
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// int j = 0;
// for (j = 0; j < col; j++)
// {
// printf("%c",board[i][j]);//打印的是棋盘初始化中的空格
// }
// printf("\n");
// }
//}
//版本2.0
//存在缺陷,行数可以改变,但是列数被固定死无法修改
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// //打印数据
// printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);//存在缺陷,行数可以改变,但是列数被固定死无法修改
//
// //打印分割行
// if (i < row - 1)//最后一行的分割行不打印
// {
// printf("---|---|---\n");
// }
// }
//}
//版本3.0
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//打印数据
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ",board[i][j]);//空格+字符+空格
//打印"|",只打印前两列
if(j<col-1)
printf("|");
}
printf("\n");//换行
//打印分割行,只打印前两行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col- 1)
printf("|");
}
printf("\n");//换行
}
}
}
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋\n");
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
//坐标合法性的判断
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;//不加break会进入死循环
}
else
{
printf("该坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;//0-2
y = rand() % col;//0-2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;//跳出循环
}
}
}
//判断平局
static int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;//没满
}
}
}
return 1;//满了
}
//判断输赢
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
//判断行
for (i = 0; i < row; i++)
{
if (board[i][0]==board[i][1]&&board[i][1]==board[i][2] &&board[i][1]!= ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断对角线
if (board[0][0]==board[1][1] && board[1][1]==board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2]==board[1][1] && board[1][1]== board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
//test.c:测试三子棋游戏的逻辑
//game.c:游戏的实现
//game.h:函数声明,符号的定义
void menu()
{
printf("****************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("****************************\n");
}
void game()
{
char ret = 0;
//存放下棋的数据
char board[ROW][COL] = { 0 };
//初始化棋盘为全空格
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家下棋
player_move(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret=is_win(board,ROW,COL);
if (ret != 'C')
{
break;
}
//电脑下棋
computer_move(board, ROW, COL);//随机下棋,哪里空着,往哪里下
DisplayBoard(board, ROW, COL);
//判断输赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢了\n");
}
else if (ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
//打印棋盘
DisplayBoard(board, ROW, COL);
}
//什么情况,游戏就结束了呢?
//玩家赢:'*'
//电脑赢:'#'
//平局:'Q'
//继续:'C'
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case 1:
//printf("三子棋\n");
game();//游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}