C语言实现三子棋以及五子棋
前言
三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。
一、三子棋游戏流程
- 菜单的选择和创建
- 创建棋盘并初始化
- 打印棋盘
- 玩家下棋(玩家以输入坐标的方式),用’*’表示玩家所下的棋子
- 判断输赢(玩家赢 - ‘*’ 电脑赢 - ‘#’ 平局 - ‘Q’)
- 电脑下棋
- 判断输赢
- 打印游戏棋盘并返回菜单
二、游戏实现
1.菜单界面
1表示游戏开始 2表示退出。
代码如下:
void menu()
{
printf("************************************\n");
printf("**********1.play 0.exit**********\n");
printf("************************************\n");
}
menu();//打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
printf("三子棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误!\n");
break;
}
} while (input);
这里我们把menu放在main函数里有助于函数的结构呈现。
2.创建棋盘并初始化
三子棋可看作为3x3的二维数组,所以我们可以定义一个二维数组来构建。在这里我们可以建立一个全局变量方面修改和后续介绍的拓展有关。
#define ROW 5
#define COL 5
数组的初始化用两个for循环嵌套,将数组元素设置为空格
代码如下(示例):
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.打印棋盘
创建好棋盘之后将其显示出来,用“|”分割每一列,用”___"设置出该下的棋子的位置。每个空格对应着定义好的二维数组的每个数据元素,使用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)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
4.玩家下棋
用*表示玩家下的棋子,在玩家下完棋子之后,我们对坐标进行范围合法的判断,会出现坐标位置被占用或者坐标非法(这里我们考虑到越界访问和非整型的输入)所以我们用if语句将其声明出来,提示玩家应该重新输入。接收玩家所输入的地址位置(判断的数组元素应该是玩家输入值-1)
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋;>");
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");
break;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
}
那么我们应该如何去判断棋盘是否为满或者是否位置被占用呢??我们创建一个IsFull的函数来判断是否为满。数组下标从0开始的,所以用循环判断board[i][j]是否为空。
//如果满了返回1 不满返回0
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;
}
5.电脑下棋
要实现电脑下棋,我们可能会有疑惑,怎么才能让电脑去落子,并且是随机的,这里我们引出随机数,srand函数 需要包含头文件 <stdlib.h>和<time.h>头文件,来设置随机数的生成起点,这个函数经常跟rand函数一起使用,是随机数发生器的初始化函数。将电脑下的棋子设置为#。
srand((unsigned int)time(NULL));
代码如下:
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋;>\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y] == ' ')
board[x][y] = '#';
break;
}
}
}
6.判断输赢
当玩家和电脑各自下完棋后都要进行输赢的判断,我们知道三子棋的规则,先构成三子在一条线上者为获胜者(包括斜线),那么我们就思路去设置函数分别从行、列、斜线去判断。
在这里,我们约定:
玩家赢 - ‘*’ 电脑赢 - ‘#’ 平局 - ‘Q’ 游戏继续 - ‘C’;
当一条线上都为*或者都为#游戏产生获胜者。
函数的声明:
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][1] != ' ')
{
return board[i][1];
}
}
我们要求每一行的相邻俩个数据元素的值都相等且不等于空,以及以下列和斜线的判断都如此。
列的判断:
//列
int j = 0;
for (j = 0; j < col; j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];
}
}
那么斜线我们应该怎么来用代码实现呢?可以从左斜线和右斜线分别来判断,斜线比较容易理解,只要将board[0][0], board[1][1], board[2][2]三个位置的值两两进行判断,
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];
}
这里有同学会问那这里都限制了坐标为三个,那么还如何能实现五子棋甚至更多的呢?我在这里用循环写了一个代码来实现当ROW和COL变化后该如何进行对角线的判断。
char ch = board[0][0];
for (i = 0; i < row; i++)
{
if (ch != board[i][i] || ch == ' ')
{
break;
}
if (i == row - 1)
{
return board[1][1];
}
}
ch = board[0][col - 1];
int k = col - 1;
for (i = 0; i < row; i++, j--)
{
if (board[i][k] != ch || ch == ' ')
{
break;
}
if (i == row - 1)
{
return ch;
}
}
例如:平局的判断比较简单,当棋盘下满是还没有分别出获胜者则为平局
//没有人赢判断平局
if (IsFull(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
最后我们 将游戏结束时,再次打印出棋盘,更加直观的展示游戏的过程!
DisplayBoard(char board[ROW][COL], int row, int col)
三、代码实现
1.game.h–游戏函数的声明
#pragma once
#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 ComputerMove(char board[ROW][COL], int row, int col);
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//游戏继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col);
2.game.c–游戏函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game1.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;
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)
{
int j = 0;
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;
printf("玩家下棋;>");
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");
break;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋;>\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//如果满了返回1 不满返回0
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][1] != ' ')
{
return board[i][1];
}
}
//列
int j = 0;
for (j = 0; j < col; j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];
}
}
//对角线
/*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];
}*/
char ch = board[0][0];
for (i = 0; i < row; i++)
{
if (ch != board[i][i] || ch == ' ')
{
break;
}
if (i == row - 1)
{
return board[1][1];
}
}
ch = board[0][col - 1];
int k = col - 1;
for (i = 0; i < row; i++, j--)
{
if (board[i][k] != ch || ch == ' ')
{
break;
}
if (i == row - 1)
{
return ch;
}
}
//没有人赢判断平局
if (IsFull(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
3.test.c–测试游戏
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game1.h"
void menu()
{
printf("************************************\n");
printf("**********1.play 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)
{
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
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));//设置随机数的生成起点
int input = 0;
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);
return 0;
}
四、总结
第一次写博客,希望各位批评指正,一起学习哦!
三子棋小游戏的实现,需要我们对数组和循环知识的基础,以及函数的思想,相信大家有了一定的了解并且能够创建自己的三子棋小游戏或者更高,那么有没有发现,电脑下棋似乎有点笨呼呼的,那么希望大家能够”各显神通“,赋予电脑能够”聪明“一点儿,我们可以一起交流,如果对大家有所帮助劳烦点点关注哦!