C语言实现三子棋

305 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、基本思路

1.用一个简易的菜单来选择开始或退出游戏 2.打印初始棋盘(没有落子的棋盘) 3.玩家下棋 4.电脑下棋 5.上述两步循环进行,直到有一方胜利或双方达成平局 6.说明哪方获胜或者说明平局 (注:以上代码较多,所以我将代码分为三块:test.c用来测试,game.c用来放需要的函数,game.h引头文件和放函数声明)

二、代码实现

1.用一个简易的菜单来选择开始或退出游戏

首先可以设想一个简易的菜单(这里的菜单不必太过复杂,只要能实现开始和退出即可) 由于不论玩家是否想继续游戏,菜单都必须出现,所以使用do……while循环 代码如下(示例):

void menu()
{
	printf("*************************\n");
	printf("******   1、开始   ******\n");
	printf("******   0、退出   ******\n");
	printf("*************************\n");
}

int main()
{
	int input;
	do
	{
		menu();
		printf("请选择\n");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
			{
				game();//开始游戏
			}
			case 0:
			{
				break;//退出游戏
			}
			default:
			{
				printf("输入错误,请重新输入\n");
				break;
			}
		}
	} while (input);
	//如果input为0,结束循环,如果为其他,仍需进入循环
	//所以可将while(input!=0)简化为while(input)
    return 0;
}

2.打印初始棋盘(没有落子的棋盘)

(1)一个简易的棋盘

在这里插入图片描述

把下面的图形看做一个单元,每次循环打印这样呀一个单元。但要注意在最后一次打印时不需要打印下面的一行‘-’。

在这里插入图片描述

代码如下(示例):

#define ROW 3
#define COL 3
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)
		{
			for (j = 0; j < col; j++)
			{
				printf("---", board[i][j]);
				if (j < col - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
	printf("\n");
}

(2)将数组的内容初始化为' '

初始化较为简单,只要遍历数组,设置为' '即可 代码如下(示例):

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*3,所以在该代码中可直接设置行和列都是3,但方便起见,通过宏定义ROW和COL来调整行和列,这样如果想实现五子棋等游戏时会更方便,在该代码实现过程中也更利于修改)。

3.玩家下棋

我们希望玩家通过输入一对数来表示下棋的坐标(默认先输入横坐标,在输入纵坐标)。 但玩家输入的坐标一定在合法范围内吗?这就需要我们对输入的坐标进行检查:正常输入、输入的位置已经有棋子、输入的坐标不在范围内。这里需要一个循环,只有当玩家的输入合法时才能停止。

由于大多数人考虑的坐标范围是[1,ROW]、[1,COL],处理输入的坐标时应注意到这一点。 代码如下(示例):

void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("您下棋,请输入坐标\n");
	while (1)
	{
		int x, y;
		scanf("%d%d", &x, &y);//输入坐标
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{//对输入的坐标做检查
			if (board[x - 1][y - 1] != ' ')
			{
				printf("该位置已被占用,请重新输入\n");
			}
			else
			{
				board[x - 1][y - 1] = '*';
				//玩家下的棋用字符'*'表示
				break;//坐标合法时跳出循环
			}
		}
		else
		{
			printf("输入非法,请重新输入\n");
		}
	}
}

4.电脑下棋

在简易的三子棋中,我们只需要让电脑生成的坐标合法即可(这里用随机数来产生,不考虑电脑对对玩家进行堵截或者在能赢的情况下优先选择赢) 随机数的生成可用下面的代码实现 代码如下(示例):

srand((unsigned int)time(NULL));//建议放在main函数刚开始的位置
x = rand();//这样得到的x就是随机数

注意time()要引<time.h>头文件,而srand()要引<stdlib.h>头文件

下面是电脑下棋的代码 代码如下(示例):

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x, y;
	while (1)
	{
		x = rand() % ROW;//通过取模运算恰好得到[0,ROW)的随机数
		y = rand() % COL;
		if (board[x][y] == ' ')
		//棋盘未落子坐标才合法,否则重新生成坐标
		{
			board[x][y] = '#';
			//电脑下的棋用字符'#'表示
			break;
		}
	}
}

5.上述两步循环进行,直到有一方胜利或双方达成平局

这两步的循环很容易实现,且方便起见,在每次下棋后打印出新的棋盘 代码如下(示例):

while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
	}

那么如何判断获胜或平局呢? 获胜的判断较为简单,只需要某行或某列或对角线三个棋相同即可 代码如下(示例):

char CheckWinner(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][0] != ' ')
		//注意架上该位置不为' '的判断
		{
			return board[i][0];//某一行三个棋相同
			//这里的return直接返回该位置的字符
			//用字符代表玩家更加方便,不需要判断是电脑还是玩家
			//以下同理
		}
	}
	for (j = 0; j < col; j++)
	{
		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[3][3] && 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];//斜向左下三个棋相同
	}
}

平局需要棋子布满棋盘,而此时又没有三个棋子连成一条线 首先实现判断棋盘上是否有空位的函数 代码如下(示例):

int IsFull(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][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

如果棋盘已满,有不满足某一方获胜的条件,则为平局,这一部分可放如上面判断输赢的函数中 代码如下(示例):

if (IsFull(board, ROW, COL) == 1)
	{
		return 'Q';
	}
return 'C';

将该代码补在CheckWinner函数最后即可

6.说明哪方获胜或者说明平局

这一部分在game()函数中实现,将第五步刚开始的while循环中更改为

while (1)
	{
		PlayerMove(board, ROW, COL);
		ret = CheckWinner(board, ROW, COL);
		//用ret接受chechwinner的返回值来判断游戏是否继续
		if (ret != 'C')
		//如果return 'C'则说明没有任何一方获胜且此时棋盘未满
		//所以继续游戏
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);
		ret = CheckWinner(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");
	}
	DisplayBoard(board, ROW, COL);

三、全部代码

game.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 3
#define COL 3

//打印菜单
void menu();
//初始化棋盘
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);
//判断胜负
char CheckWinner(char board[ROW][COL], int row, int col);
//判断棋盘是否满了(即全部不是' ')
int IsFull(char board[ROW][COL], int row, int col);

test.c

#include "game.h"

void game()
{
	char ret;
	char board[ROW][COL];
	InitBoard(board, ROW, COL);//将棋盘每个元素初始化为' '
	DisplayBoard(board, ROW, COL);//打印初始棋盘
	while (1)
	{
		PlayerMove(board, ROW, COL);
		ret = CheckWinner(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);
		ret = CheckWinner(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");
	}
	DisplayBoard(board, ROW, COL);
}
int main()
{
	srand((unsigned int)time(NULL));
	int input;
	do
	{
		menu();
		printf("请选择\n");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
			{
				game();
			}
			case 0:
			{
				break;
			}
			default:
			{
				printf("输入错误,请重新输入\n");
				break;
			}
		}
	} while (input);
    return 0;
}

game.c

#include "game.h"

//打印菜单
void menu()
{
	printf("*************************\n");
	printf("******   1、开始   ******\n");
	printf("******   0、退出   ******\n");
	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] = ' ';
		}
	}
}
//打印初始棋盘
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)
		{
			for (j = 0; j < col; j++)
			{
				printf("---", board[i][j]);
				if (j < col - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
	printf("\n");
}

//玩家下棋,用'*'表示玩家下的棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("您下棋,请输入坐标\n");
	while (1)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] != ' ')
			{
				printf("该位置已被占用,请重新输入\n");
			}
			else
			{
				board[x - 1][y - 1] = '*';
				break;
			}
		}
		else
		{
			printf("输入非法,请重新输入\n");
		}
	}
}

//电脑下棋,用'#'表示电脑下的棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x, y;
	while (1)
	{
		x = rand() % ROW;
		y = rand() % COL;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断棋盘是否满了(如果全部不是' ',则棋盘还没满)
int IsFull(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][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

//判断胜负
char CheckWinner(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][0] != ' ')
		{
			return board[i][0];//某一行三个棋相同
		}
	}
	for (j = 0; j < col; j++)
	{
		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[3][3] && 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 (IsFull(board, ROW, COL) == 1)
	{
		return 'Q';
	}
	return 'C';
}

感谢阅读,如有错误请批评指正