需要补充的一点是,每次人/电脑下完棋,我们都肯定是想要看到效果的,所以每次下完棋,我们都需要打印一次棋盘,更新一次棋盘。
并且开始打印的棋盘都是视觉上的空棋盘,但实际上是我们用空格填充的。
棋盘,我们可以用一个二维数组来表示。
下边我们来具体看看怎么实现吧~
1.【打印菜单】
菜单里边肯定有我们选择到底是玩还是不玩吧,所以一定有两个选项.
下边提供一种较为简单的方法:
void menu()
{
printf("******************************\n");
printf("**** 1. play 0.exit ****\n");
printf("******************************\n");
}
2.【打印棋盘】
打印棋盘分为两个阶段,一个是还没有开始下棋的时候,另一个就是已经下的时候。
大体思路分析:
1.我们整体可以分为两个部分,一个是分割信息部分,另一个是非分隔信息部分。
2.很显然我们可以把它看成几个重复的小单元,通过for循环实现
实现的时候注意几个点:①换行②打印的条件——边界都是没有竖行/横线的,所以需要有一个打印分割信息的时候都需要有一个判断条件
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
//打印数据及与其较近的'|'
printf(" %c ",board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//打印'---'及与其较近的'|'
if (i < row - 1)
{
int k = 0;
for (k = 0;k < col;k++)
{
printf("---");
if(k<col-1)
printf("|");
}
printf("\n");
}
}
}
其实未下棋与下棋是只有一个区别,是棋盘数据元素是空格还是代表电脑或者人的棋子的特殊符号。
所以在一开始打印棋盘的时候,我们还需要另外定义一个初始化棋盘的函数
void InitBoard(char board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
board[i][j] = ' ';
}
}
}
在人/电脑下棋的时候将下棋位置的坐标修改成其他的元素,然后在此调用打印棋盘的函数,就能够得到想要的打印的结果了。
3.【人下棋】
人下棋是通过选择位置位置坐标来实现的。所以这个函数的实现我们只需要输入坐标,然后修改对应的元素值即可。
但是这个位置可不是随便修改的,有以下三种情况不能够选择,第一,是人原来已经占的位置,第二,是电脑原来占得位置,第三,是超过棋盘所能表示范围的位置。
只有当我们输入的坐标位置的元素是空格的时候,我们才能完成这次操作。
因此,这里我们可以通过一个while的死循环循环来实现,当发现输入的位置表示的元素是空格时我们修改并且跳出循环,否则一直选择位置。
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0, 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;
}
else
{
printf("坐标被占用,不能下棋,请重新选择其他位置\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
4.【电脑下棋】
电脑下棋与人下棋类似,只不过电脑下棋的位置是我们用随机数产生的特定范围的坐标。
说明:随机数的种子,程序已经一开始种下
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:>\n");
int x = 0, y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
5.【判断游戏状态】
我们通过列举将可能回赢的情况一一列举出来,然后进行判断即可。
不过在进行此函数代码书写之前,我们还需要进行判断棋盘是否满函数的定义,为判断是否平局的情况做准备。
char IsWin(char board[ROW][COL], int row, int col)
{
//先判断行
//判断列
//判断对角线
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
//其实这里判断的就有些局限了,只能够判断三子棋了
//判断行
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
//判断列
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
{
return board[0][j];
}
//判断对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' '
|| board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
{
return board[1][1];
}
}
//没人赢,就要平局——棋盘满了
if (IsFull(board, row, col))
{
return 'Q';
}
else
{
return 'C';//继续
}
}
}
int IsFull(char board[ROW][COL], int row, int col)
{
int i, j, tag = 1;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
//game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//方便以后更改棋盘大小
#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢——其实还是在弄数组元素值得判断
//玩家赢-‘*’
//电脑赢-‘#’
//平局-'Q'【以上三种都不能够再玩了】
//继续-'C'
char IsWin(char board[ROW][COL], int row, int col);
//test.c
//菜单
//打印棋盘
//下棋
#include "game.h"
void menu()
{
printf("******************************\n");
printf("**** 1. play 0.exit ****\n");
printf("******************************\n");
}
void game()
{
int ret;
//3*3的数组存放棋盘的信息
// 打印棋盘——下棋了打印空格,不下棋打印符号
//玩家下棋
// 电脑下棋
//判断输赢的过程
char board[ROW][COL] = { 0 };
//初始化棋盘的功能
InitBoard(board,ROW,COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//下棋
while (1)
{
//玩家部分
PlayerMove(board, ROW, COL);//其实就是对数组的操作
//判断输赢
ret= IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
DisplayBoard(board, ROW, COL);
//电脑部分
ComputerMove(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
DisplayBoard(board, ROW, COL);
}
if (ret == '#')
printf("电脑赢了\n");
else if (ret == '*')
printf("玩家赢了\n");
else if (ret == 'Q')
printf("平局了\n");
}
int main()
{
srand((unsigned int)time(NULL));//设置随机数的生成起点
int input;
do
{
menu();//打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
printf("开始游戏\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
//game.c
#include "game.h"
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
//打印数据
printf(" %c ",board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//打印分割信息
if (i < row - 1)
{
int k = 0;
for (k = 0;k < col;k++)
{
printf("---");
if(k<col-1)
printf("|");
}
printf("\n");
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0, 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;
}
else
{
printf("坐标被占用,不能下棋,请重新选择其他位置\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:>\n");
int x = 0, y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断输赢
//判断行
//判断列
//判断对角线
int IsFull(char board[ROW][COL], int row, int col)
{
int i, j, tag = 1;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
char IsWin(char board[ROW][COL], int row, int col)
{
//先判断行
//判断列
//判断对角线
int i, j;
for (i = 0;i < row;i++)
{
for (j = 0;j < col;j++)
{
//其实这里判断的就有些局限了,只能够判断三子棋了
//判断行
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
//判断列
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
{
return board[0][j];
}
//判断对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' '
|| board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
{
return board[1][1];
}
}
//没人赢,就要平局——棋盘满了
if (IsFull(board, row, col))
{
return 'Q';
}
else
{
return 'C';
}
}
}
1.利用宏定义定义棋盘的大小,方便后续的更改
2.分文件编写,.h文件里边放头文件的引用和功能函数的声明,test.c文件里边是游戏整体逻辑的实现,game.c里边放的是游戏函数的定义。
3.棋盘如何打印——分成小块循环控制,还要特别注意边界的控制
4.判断输赢的操作如何实现,同时如何减少代码冗余——分类
5.如何实现人下棋和电脑下棋的操作