C语言---三子棋小游戏

184 阅读6分钟

学习了C语言中的二维数组,本照着学以致用的原则,现在利用对二维数组的操作来实现一个简单版的三子棋游戏。

三子棋其实我们都玩过,在我们这边又叫"一条龙"。就是一个九空格,下棋的双方依次在九个空格里面下棋,直到有一方在九宫格的某一行或者某一列,或者对角线下了同样的棋,那么这一方就会取得胜利。

如下图:

创建文件

这里需要三个文件:

  • test.c 用于测试游戏的逻辑
  • game.c 用于游戏代码的实现
  • game.h 用于游戏代码的声明(函数声明、符号定义)

游戏逻辑分析

1、写出游戏主逻辑 2、创建并初始化棋盘 3、打印棋盘 4、玩家下棋 5、电脑下棋 6、判断输赢

第一步:写出游戏主逻辑(test.c)

首先需要创建个menu()菜单,菜单里面包含三子棋游戏。玩家选择1表示玩游戏,玩家选择0表示退出游戏,所以需要switch语句。如果玩家玩了一次,感觉不过瘾,继续选择1,然后接着玩游戏,所以这里需要do...while语句。

代码实现:

game.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

test.c

#include "game.h"

void menu()
{
	printf("**********************************\n");
	printf("********* 1. 玩游戏  *************\n");
	printf("********* 0.退出游戏 *************\n");
	printf("**********************************\n");
}

void game()
{
	printf("玩游戏\n");
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//这里是整个游戏的函数。
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while(input);
	return 0;
 }

测试: 在这里插入图片描述

第二步:创建并初始化棋盘

棋盘的样子:

在这里插入图片描述

创建棋盘,很简单,无非就是创建一个3行3列的二维数组。那如何初始化棋盘呢?如上图,其实也很简单就是在每一个格格里面用空格填充就可以了。

代码实现:

test.c

#include "game.h"

void menu()
{
	printf("**********************************\n");
	printf("********* 1. 玩游戏  *************\n");
	printf("********* 0.退出游戏 *************\n");
	printf("**********************************\n");
}

void game()
{
	char board[ROW][COL] = { 0 };
	InitBoard(board,ROW,COL);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 2:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while(input);
	return 0;
 }

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] = " ";
		}
	}
}

game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

#define ROW 3
#define COL 3
//声明初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

第三步:打印棋盘(版本一:代码有局限性,但是可以使用)

test.c

#include "game.h"

void menu()
{
	printf("**********************************\n");
	printf("********* 1. 玩游戏  *************\n");
	printf("********* 0.退出游戏 *************\n");
	printf("**********************************\n");
}

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board,ROW,COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while(input);
	return 0;
 }

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;
	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");
		}
	}
}

game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.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);

其实可以发现,这里的打印期盼是有局限形的。因为输出的格式我们是写死的,不灵活,所以这个是个思路,但是不完美。我们可以按照下面的第二版本的来写。

第三步:打印棋盘(版本二:具有灵活性,推荐使用)

game.c

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");
		}
	}
}

第四步:玩家下棋

这一步实现玩家下棋,并且玩家每下一步棋后需要在打印棋盘。我们想一想下棋是两个人游戏,玩家下过之后,就需要电脑下棋,然后玩家下,电脑下...一直循环往复,直到判断出一方输赢然后停止游戏。那这个就需要在一个while循环语句中。

代码如下:(重复代码已省略)

test.c

//玩家下棋
	while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
	}

game.h

//玩家下棋
void PlayerMove(char baord[ROW][COL], int row, int 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 (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");
		}
	}
}

第五步:电脑下棋

这里实现电脑下棋采用随机的方式进行下棋。所以说需要引入几个关于时间,生成随机数的库函数。除此之外其它思路和玩家下棋思路一样。

代码如下:(重复代码已省略)

test.c

...
int main()
{
	srand((unsigned int)time(NULL));
	...
	return 0;
 }

game.h

...
#include <stdlib.h>
#include <time.h>
...
//声明电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

game.c

//电脑下棋
...
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:>\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		//随机生成横坐标 ,范围是0~2
		x = rand() % row;
		//随机生成纵坐标 ,范围是0~2
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

第六步:判断输赢

判断输赢的动作应该在玩家或者电脑下过棋之后的动作。 判断输赢有以下几个内容:

  • 玩家赢
  • 电脑赢
  • 平局
  • 继续游戏

其中赢,又分为几部分:

任意一列全部一样为赢 任意一行全部一样为赢 对角线全部一样为赢

test.c

#include "game.h"

void menu()
{
	printf("**********************************\n");
	printf("********* 1. 玩游戏  *************\n");
	printf("********* 0.退出游戏 *************\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);
		//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
		DisplayBoard(board, ROW, COL);
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
		DisplayBoard(board, ROW, COL);
	}
	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();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while(input);
	return 0;
 }

game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#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 baord[ROW][COL], int row, int col);

//声明电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

//声明判断输赢
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;
	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;
	//判断坐标输入是否合法
	while (1)
	{
		printf("玩家下棋:>\n");
		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;
	int y = 0;
	while (1)
	{
		//随机生成横坐标 ,范围是0~2
		x = rand() % row;
		//随机生成纵坐标 ,范围是0~2
		y = rand() % col;
		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];
	}

	//到这个地方了说明没有人赢,那就需要判断平局
	//判断平局的方法就是:没有人/电脑赢,并且棋盘已经满了,那就是平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	//游戏继续
	return 'C';
}


全部代码展示

test.c

#include "game.h"

void menu()
{
	printf("**********************************\n");
	printf("********* 1. 玩游戏  *************\n");
	printf("********* 0.退出游戏 *************\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);
		//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
		DisplayBoard(board, ROW, COL);
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
		DisplayBoard(board, ROW, COL);
	}
	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();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while(input);
	return 0;
 }

game.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#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 baord[ROW][COL], int row, int col);

//声明电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

//声明判断输赢
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;
	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;
	//判断坐标输入是否合法
	while (1)
	{
		printf("玩家下棋:>\n");
		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;
	int y = 0;
	while (1)
	{
		//随机生成横坐标 ,范围是0~2
		x = rand() % row;
		//随机生成纵坐标 ,范围是0~2
		y = rand() % col;
		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];
	}

	//到这个地方了说明没有人赢,那就需要判断平局
	//判断平局的方法就是:没有人/电脑赢,并且棋盘已经满了,那就是平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	//游戏继续
	return 'C';
}


效果展示:

image.png

image.png

image.png

总结

三子棋游戏可以很好的锻炼二维数组的知识。此游戏的根本就是对二维数组的操作。此代码只是实现了最基本的游戏功能,部分代码还可以优化,还可以更加灵活,比如说:可以让电脑玩家更加智能。从而是游戏增加难度等等。