三子棋
一、三子棋代码实现思路
今天我们来使用C语言实现一个简单的小游戏三子棋。
这里在实现三子棋代码时,我将会把三子棋代码的主函数,函数以及头文件声明,其他函数主体分别放在三个不同的文件中:
test.c
//主函数,以及基本函数
game.h
//功能函数的实现
game.c
//头文件引用以及功能函数的声明
1.在界面上打印菜单
首先,我们需要在界面上打印菜单,以提供选项给玩家来选择是否进行三子棋游戏,这里可以直接使用printf()函数在界面上直接输出菜单:
#include <stdio.h>
void menu()
{
printf("************************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("************************\n");
}
int main()
{
menu();//菜单
}
打印出来的菜单就是这样:
在这里我们根据菜单设计,当玩家输入‘1’时,进入游戏,当玩家输入‘0’时,退出游戏,当玩家不慎输入其他值时,提醒一下玩家输入有误,并使玩家重新输入选项。根据这样的需求这里就需要使用到switch选择语句和do while循环语句进行实现。
main函数内部代码修改:
int main()
{
int input = 0;
do
{
menu();//菜单
printf("请选择:>");
scanf("%d", &input);//玩家输入
switch (input)//输入判断
{
case 1:
printf("进入游戏\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入异常,请重新输入\n");
break;
}
} while (input);
return 0;
}
这样玩家就可以进行是否进行游戏的选择了
2.创建3*3的棋盘
第二步,就是要创建一个3*3的棋盘来提供给玩家来下棋,在这里我们可以使用,上一节刚讲过的二维数组来创建棋盘,但是创建数组是需要初始化的,但我们需要的棋盘又需要拿落子不能存在其他内容,其实我们可以把棋盘创建成一个二维字符数组,并把内容都初始化为空格(因为空格在屏幕上打印是看不见的),这样玩家在下棋时在棋盘上就不会看见其他无关内容了
代码实现:,这里先在main函数里的case1:语句内定义一个game()函数,
case 1:
game();
然后在game()函数中创建棋盘并使用一个Initboard()函数进行初始化
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
}
这里二维数组在定义时,数组的大小使用了宏定义,这样方便以后对数组大小的修改,同时宏定义是放在game.h文件中的:
#define ROW 3//行
#define COL 3//列
初始化棋盘的代码放在game.c的文件里实现:
void Initboard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
3.在界面上打印棋盘
当我们创建好棋盘后就需要在屏幕上给玩家打印一下棋盘
这里假设我们要打印的棋盘是这样的:
对这个棋盘的构成进行分析:
这里我们就清楚了,我们在打印棋盘是时都有哪些元素需要被打印
代码实现:在game()函数中使用一个Displayboard()函数来打印
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
}
打印棋盘的代码放在game.c的文件里实现:
void Displayboard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
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)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
这里我们就成功打印出了我们需要的棋盘
4.下棋
当我们的棋盘打印好后,就需要玩家和电脑相继进行下棋了
这里我们设定玩家的棋子为’*‘,电脑的棋子为’#’
玩家落子
我们在game()函数内部使用PlayerMove()函数来实现玩家下棋
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
//玩家下棋
PlayerMove(board, ROW, COL);
}
这里我们假设,当玩家输入要下棋的坐标后,棋盘上对应位置将会出现玩家的棋子,同时下棋是多次要进行的重复步骤,当我们每一次下棋时,如果输入的坐标上有棋子或者该坐标超出棋盘范围时,这时我们就需要提醒一下玩家输入的坐标异常,并让玩家重新输入
玩家下棋的代码放在game.c的文件里实现:
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家走:>\n");
printf("请输入下棋的坐标:>");
scanf("%d %d",&x,&y);
//判断坐标合法性
if (1 <= x && x <= row && 1 <= y && y <= col)
{
//下棋
//坐标是否被占用
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
电脑落子
这里对电脑下棋的算法代码不进行复杂的设计,我们现阶段只通过让电脑生成随机的坐标来进行下棋就行。
我们在game()函数内部使用ComputerMove()函数来实现玩家下棋
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
//玩家下棋
PlayerMove(board, ROW, COL);
//电脑下棋
ComputerMove(board, ROW, COL);
}
在电脑生成随机坐标时,我们需要调用rand()函数,使用rand()函数时,还需要srand()函数配合使用,srand()函数只需要调用一次即可,所以可以放在main函数的内部,上述两个函数使用需要的头文件:
#include <stdlib.h>
#include <time.h>
电脑下棋的代码放在game.c的文件里实现:
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
//判断占用
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
5.判断输赢
三子棋的规则是:
两名选手在棋盘上轮流下棋。当有一名选手率先在行、列或者斜线上连续组成了 3 个自己的棋子时,即为获胜。如果棋盘下满也没有胜者,则为平局。
根据上述规则,我们在game()函数内部使用Iswin()函数来实现输赢的判断,并且我们将Iswin()函数的返回值作为判断的结果,这里我们假设:
玩家赢返回 ’*‘ ;
电脑赢返回’ #‘ ;
平局返回 ’Q‘ ;
棋局未结束则返回 ’C’ ,以便下一轮的下棋。
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
//玩家下棋
PlayerMove(board, ROW, COL);
//判断输赢结果
char ret = 0;
ret = Iswin(board, ROW, COL);
}
输赢判断的代码放在game.c的文件里实现:
//判断是否棋盘是否满
int IsFUll(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
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 = 0;
//判断三横行
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][1];
}
}
//判断三竖行
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][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];
}
//判断平局
//棋盘满了返回1,不满返回0
int ret = IsFUll(board, ROW, COL);
if (ret == 1)
{
return 'Q';
}
//继续
return 'C';
}
当每一次玩家下棋后和电脑下棋后,都需要对棋盘进行一次判定,看是否有胜者产生,并且玩家和电脑下棋的动作是多次的,所以这里我们就需要使用while循环来实现玩家和电脑轮流下棋的操作,将game()函数内的代码进行优化:
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
//判断输赢结果
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
ComputerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("你赢了\n");
}
else if(ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
Displayboard(board, ROW, COL);
}
二、最终完整三子棋代码
test.c
#include "game.h"
void menu()
{
printf("************************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("************************\n");
}
void game()
{
//1.创建棋盘
char board[ROW][COL];
//初始化
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
//判断输赢结果
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
ComputerMove(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("你赢了\n");
}
else if(ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
Displayboard(board, ROW, COL);
}
game.h
#pragma once
#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢 - *
//电脑赢 - #
//平局 - Q
//继续 - C
char Iswin(char board[ROW][COL], int row, int col);
game.c
#include "game.h"
void Initboard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
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 = 0;
int j = 0;
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)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家走:>\n");
printf("请输入下棋的坐标:>");
scanf("%d %d",&x,&y);
//判断坐标合法性
if (1 <= x && x <= row && 1 <= y && 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");
while (1)
{
int x = rand() % row;
int y = rand() % col;
//判断占用
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断是否满
int IsFUll(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
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 = 0;
//判断三横行
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][1];
}
}
//判断三竖行
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][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];
}
//判断平局
//棋盘满了返回1,不满返回0
int ret = IsFUll(board, ROW, COL);
if (ret == 1)
{
return 'Q';
}
//继续
return 'C';
}
思考:
当我们完整写出三子棋的代码后,对该代码是否有优化的空间呢?
这里提出几点可优化的地方,仅供参考:
- 电脑下棋的算法是否可以更加智能
- 输赢的判断规则,是否可以跟随棋盘的大小变化而不断变化(如:五子棋等)
对于这几点问题,你可以自己思考思考,并能否通过代码进行优化一下。
博主后期可能会出相关优化的博客进行讲解,希望各位读者点个关注,以便后期能及时看到优化代码。
这里再次感谢各位的阅读,有什么想法可以在评论区留言以及私聊我哦,那我们下期再见吧!谢谢!