C语言实现三子棋小游戏

前言与概述

本文章讲述如何通过C语言开发一款三子棋的小游戏。笔者才识浅薄,如有错误,欢迎各位编程大佬在评论区批评指正,笔者不胜感激。

游戏介绍

三子棋是一款益智的趣味小游戏。多名玩家在3*3的棋盘下棋,棋盘共九个方格,每个方格最多只能放置一枚棋子。只要有一名玩家下的三个棋子可以连成一条直线,则该玩家取得游戏的胜利,游戏结束。如果棋盘已满,还是没有玩家下的棋子可以连成直线,那么游戏平局。

游戏实现路径

①:创建并初始化棋盘 ②:打印棋盘 ③:玩家和电脑下棋 ④:判断游戏状态

游戏运行全过程

当用户输入1进入游戏,程序会打印未下棋子的棋盘,用户通过输入要下棋子的坐标,将棋子下在棋盘的指定位置。电脑会在棋盘的随机位置下棋,打印下棋后棋盘的状况。如果玩家或电脑下的三个棋子可以连成直线,则玩家或电脑取胜。如果棋盘已满,但玩家和电脑下的棋子都不能连成直线,则游戏平局。否则,游戏继续。当游戏结束,用户输入0可以退出程序。

游戏开发前准备

为了更好的实现游戏的相关功能,笔者在该工程目录下创建了四个文件,分别是main.cpp、test.cpp、game.cpp、game.h。main.cpp用于实现用户与玩家的交互界面。test.cpp用于测试游戏的实现逻辑。game.cpp用于游戏实现的相关函数。game.h用于存放源文件所需要的头文件、符号常量、函数声明,当其它源文件需要调用这些内容,直接引用即可。

交互界面

实现要求

游戏至少运行一次,如果玩家玩完一把,感觉不过瘾,还可以继续玩。直到玩家想主动退出游戏。这里就可以使用do-while循环语句。用户输入1,进入游戏;用户输入0,退出游戏;用户输入其它数字,提示:“输入错误,请重新输入”,直到用户输入正确数字。这里就可以使用switch-case语句,对用户输入的情况进行判断,并作出相应的反馈。当然,我们需要制作并打印一个简易的菜单,让用户可以轻松的与程序进行交互。

示例代码

//用于实现玩家与游戏的交互和打印主界面
#include "game.h"
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;
}

menu()函数代码

void menu()
{
	printf("***************************\n");
	printf("********   1.play   *******\n");
	printf("********   0.exit   *******\n");
	printf("***************************\n");
}

模拟运行 

 

补充说明

①:do-while循环语句,循环条件是变量input的值。变量input保存着用户输入的值,当用户输入的值非0,循环条件一直成立,程序持续运行,如果用户输入的值为0,循环条件不成立,程序退出。

②:main.cpp文件引用了自定义的头文件,引用方式为”头文件名.h”引用该头文件,相当于将头文件里所有的内容复制到main.cpp文件中。

创建和初始化棋盘

创建棋盘

三子棋是3*3的棋盘,在C语言中可以抽象的理解为3*3的二维数组。二维数组的行和列可以是3与3的常量,也可以是#define定义常量3。笔者更倾向于#define定义常量3。因为数组需要存放“棋子”,所以数组类型是char类型,数组可以初始化为0。

创建二维数组示例代码

	//创建二维数组
	char board[3][3] = { 0 };//常量3作为二维数组行和列
	#define ROW 3
	#define COLUMN 3
	char board[ROW][COLUMN] = { 0 };//使用#define 定义常量3作为二维数组行和列

初始化棋盘

初始化棋盘就是把数组里所有的元素都改为空格。for循环可以用作遍历数组里的元素,“=”赋值符可以修改数组里任意元素的内容。

示例代码

void format_board(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < column; j++)
		{
			board[i][j] = ' ';
		}
	}
}

代码分析

对棋盘初始化不需要任何返回值,所以返回类型使用void类型(空类型)即:没有返回值,也不需要返回值。在实参方面,因为需要修改数组,所以需要将数组名传过去(本质上传的是数组首元素的地址),同时for循环遍历二维数组需要知道数组的行和列,需要将常量ROW和常量COLUMN传过去。在形参方面,因为数组是char类型的二维数组,所以需要交代数组行和列的大小(行可以省去)。同时传来的还有常量ROW和常量COLUMN的值,所以需要用int类型的变量接收(注意:如果使用变量名是row和column,必须小写,防止与常量ROW和常量COLUMN发生冲突,导致编译器报错)。循环变量i、j的初始值是0,因为数组下标从0开始,for循环的循环条件是变量i或j的值小于变量row或变量column,而不是小于等于,是为了避免出现数组元素溢出。“=”将空格赋予数组,不可以只敲一个 ,因为空格是字符,所以需要以字符的形式(‘ ‘)传给数组。

打印棋盘

打印棋盘的实质就是访问数组中的每个元素,为了使棋盘更加美观,我们可以适当的使用空格、-(中划线-英文)、|(竖线)。

示例代码1

void print_board(char board[ROW][COLUMN], int row, int column)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]);
		printf("\n");
		if (i < row - 1)
		{
			printf("---|---|---");
			printf("\n");
		}
	}
}

 示例代码2

void print_board(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row;i++)
	{
		for (j = 0; j < column;j++)
		{
			printf(" %c ", board[i][j]);
			if (j < column - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		int z = 0;
		if (i < row - 1)
		{
			for (z = 0; z < row; z++)
			{
				printf("---");
				if (z < row - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}

模拟运行 

 

补充说明

示例代码1相对简单,易于理解。不过仅仅只适用于3*3的数组中,示例代码2代码量更大,使用多个if语句和for循环,将单行打印拆分成多个部分,使得该函数可以打印任意行的数组。

玩家与电脑下棋

玩家下棋

玩家通过输入坐标,将棋子放置在棋盘的指定位置。对坐标有一定的限制:首先,坐标不可以越界,即横坐标和纵坐标必须是大于0小于4的整数。其次,一个方格只能放一个棋子,如果坐标上已经有棋子,就不可以再放了。玩家棋子用“*”表示。

示例代码

void player_move(char board[ROW][COLUMN], int row, int column)
{
	while (1)
	{
		int x, y;
		printf("玩家下棋>");
		scanf("%d %d", &x, &y);
		if (x > 0 && x < 4 && y > 0 && y < 4)
		{
			if (board[x-1][y-1] == ' ')
			{
				board[x-1][y-1] = '*';
				break;
			}
			else
			{
				printf("该处已布置棋子,请重新下棋>\n");
			}
		}
		else
		{
			printf("坐标不合规,请重新输入>\n");
		}
	}
}

运行结果 

 

代码分析

函数内使用while循环,因为玩家在输入坐标时,可能会输错。当玩家下错棋,程序会提示错因,并等待重新输入坐标,直到坐标正确,就会将棋子放在棋盘指定的位置,并通过break语句跳出循环。判断坐标是否被占用、棋子放置棋盘都将变量x和变量y的值减一,是因为玩家输入的坐标始终比实际在数组中的位置横纵坐标各多一。例如玩家输入的坐标是1 1,实际在数组中的位置是下标0 0。

电脑下棋

电脑下棋同样也是通过坐标,将棋子放置在棋盘的指定位置。不过,对于计算机而言,我们可以通过控制计算机生成随机数的范围,来避免出现坐标不合规的问题。由于,计算机生成的坐标是随机的,所以可能存在坐标被占用的情况,当生成的坐标被占用,就让计算机重新生成坐标,直到生成的坐标没有被占用,就将棋子放置在生成的坐标位置处。

示例代码

void computer_move(char board[ROW][COLUMN], int row, int column)
{
	int x = 0;
	int y = 0;
	printf("电脑下>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % column;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

运行结果 

 

代码分析

rand()函数用于生成随机数,需要先调用srand()函数,同时srand()函数需要整型数字作为种子,但如果种子不变,那生成的随机数就不变,所以需要一个时刻变化的数作为种子。时间戳是无时无刻不在变化。time(NULL)用于获取当前时间的时间戳,但时间戳并不是整型数字,就需要强制转换为int类型((unsigned int))。在程序运行中,srand((unsigned int)time(NULL))只需要调用一次即可,多次快速调用,会导致生成的随机数相近甚至相同。rand()函数生成的随机数较大,通过% row,就可以将生成的随机数控制在0-2之间。

判断游戏状态

玩家赢或电脑赢

算法

三子棋获胜的规则:任何一方下的三个棋子,可以连成直线。连成直线有三种可能:同一行、同一列、对角线。

 

在同一行中,横坐标的值相等;在同一列中,纵坐标的值相等。由于同一行、同一列都各有三种可能性,所以可以使用for循环,遍历三次。

三个棋子可以连成直线,就是数组中三个元素的值相等。判断三个元素相等的方式是第一个元素等于第二个元素,第二个元素等于第三个元素。为了避免数组中三个元素等于空格,导致程序误判,所以需要判断其中一个元素不等于空格。

返回三个相同元素中的一个,根据返回的元素判断谁赢。如果返回的是*(玩家下的棋),表明玩家赢;如果返回的是#(电脑下的棋),表示电脑赢。

示例代码

char win(char board[ROW][COLUMN], int row, int column)
{
	int i = 0;
	int j = 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];
		}
	}
	for (j = 0; j < column; 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];
	}
	int result = 0;
	result = is_full(board, row, column);
	if (result == 0)
	{
		return 'c';
	}
	else
	{
		return 'q';
	}
}

平局或游戏继续

算法

判断平局的实现方式:在任何一方都没有取胜的情况下,棋盘已经没有空余的方格。实际上就是遍历完数组所有元素,没有一个元素是空格。游戏继续的实现方式:在任何一方都没有取胜的情况下,棋盘还有空余的方格。实际上就是遍历数组,还有数组的元素是空格。笔者定义变量result,用于接收返回值。如果返回值是0,说明数组中还有元素是空格,棋盘未满,返回字符c,表示游戏继续;如果返回值是1,说明数组中所有元素都不是空格,棋盘已满,返回字符q,表示平局。

示例代码

int  is_full(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < column; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

游戏总测试 

 

源代码 

main.cpp源代码

#define _CRT_SECURE_NO_WARNINGS 1
//用于实现玩家与游戏的交互和打印主界面
#include "game.h"
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	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;
}

test.cpp源代码 

#define _CRT_SECURE_NO_WARNINGS 1
//用于测试游戏的实现逻辑
#include "game.h"
void game()
{
	//创建二维数组
	char board[ROW][COLUMN] = { 0 };
	//格式化棋盘
	format_board(board, ROW, COLUMN);
	//打印棋盘
	print_board(board, ROW, COLUMN);
	//玩家下棋
	while (1)
	{
		char amount = 0;
		player_move(board, ROW, COLUMN);
		amount = win(board, ROW, COLUMN);
		if (amount == '*')
		{
			printf("玩家赢!\n");
			print_board(board, ROW, COLUMN);
			break;
		}
		else if (amount == '#')
		{
			printf("电脑赢!\n");
			print_board(board, ROW, COLUMN);
		}
		else if (amount == 'q')
		{
			printf("平局!\n");
			print_board(board, ROW, COLUMN);
			break;
		}
		computer_move(board, ROW, COLUMN);
		amount = win(board, ROW, COLUMN);
		if (amount == '*')
		{
			printf("玩家赢!\n");
			print_board(board, ROW, COLUMN);
			break;
		}
		else if (amount == '#')
		{
			printf("电脑赢!\n");
			print_board(board, ROW, COLUMN);
		}
		else if (amount == 'q')
		{
			printf("平局!\n");
			print_board(board, ROW, COLUMN);
			break;
		}
		print_board(board, ROW, COLUMN);
	}
}

game.h源代码 

//用于存放源文件所需要的头文件、符号常量、函数声明
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu();
void game();
#define ROW 3
#define COLUMN 3
void format_board(char board[ROW][COLUMN], int row, int column);
void print_board(char board[ROW][COLUMN], int row, int column);
void player_move(char board[ROW][COLUMN], int row, int column);
void computer_move(char board[ROW][COLUMN], int row, int column);
char win(char board[ROW][COLUMN], int row, int column);
int  is_full(char board[ROW][COLUMN], int row, int column);

 game.cpp源代码

#define _CRT_SECURE_NO_WARNINGS 1
//用于游戏实现的相关函数
#include "game.h"
void menu()
{
	printf("***************************\n");
	printf("********   1.play   *******\n");
	printf("********   0.exit   *******\n");
	printf("***************************\n");
}
void format_board(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < column; j++)
		{
			board[i][j] = ' ';
		}
	}
}
void print_board(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row;i++)
	{
		for (j = 0; j < column;j++)
		{
			printf(" %c ", board[i][j]);
			if (j < column - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		int z = 0;
		if (i < row - 1)
		{
			for (z = 0; z < row; z++)
			{
				printf("---");
				if (z < row - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}
void player_move(char board[ROW][COLUMN], int row, int column)
{
	while (1)
	{
		int x, y;
		printf("玩家下棋>");
		scanf("%d %d", &x, &y);
		if (x > 0 && x < 4 && y > 0 && y < 4)
		{
			if (board[x-1][y-1] == ' ')
			{
				board[x-1][y-1] = '*';
				break;
			}
			else
			{
				printf("该处已布置棋子,请重新下棋>\n");
			}
		}
		else
		{
			printf("坐标不合规,请重新输入>\n");
		}
	}
}
void computer_move(char board[ROW][COLUMN], int row, int column)
{
	int x = 0;
	int y = 0;
	printf("电脑下>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % column;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

char win(char board[ROW][COLUMN], int row, int column)
{
	int i = 0;
	int j = 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];
		}
	}
	for (j = 0; j < column; 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];
	}
	int result = 0;
	result = is_full(board, row, column);
	if (result == 0)
	{
		return 'c';
	}
	else
	{
		return 'q';
	}
}
int  is_full(char board[ROW][COLUMN], int row, int column)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < column; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以回答这个问题。以下是一个简单的C语言实现单人三子棋游戏的代码: ```c #include <stdio.h> #include <stdlib.h> #define BOARD_SIZE 3 void init_board(char board[][BOARD_SIZE]); void print_board(char board[][BOARD_SIZE]); int make_move(char board[][BOARD_SIZE], int row, int col, char player_char); int check_win(char board[][BOARD_SIZE], char player_char); int main() { char board[BOARD_SIZE][BOARD_SIZE]; int row, col, win; char player_char = 'X'; init_board(board); print_board(board); while (1) { printf("\nPlayer %c's turn.\n", player_char); printf("Enter row number: "); scanf("%d", &row); printf("Enter column number: "); scanf("%d", &col); if (make_move(board, row, col, player_char)) { print_board(board); win = check_win(board, player_char); if (win) { printf("\nPlayer %c wins!\n", player_char); break; } player_char = (player_char == 'X') ? 'O' : 'X'; } else { printf("Invalid move. Try again.\n"); } } return 0; } void init_board(char board[][BOARD_SIZE]) { int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { board[i][j] = '-'; } } } void print_board(char board[][BOARD_SIZE]) { int i, j; printf("\n"); for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { printf("%c ", board[i][j]); } printf("\n"); } } int make_move(char board[][BOARD_SIZE], int row, int col, char player_char) { if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE || board[row][col] != '-') { return 0; } board[row][col] = player_char; return 1; } int check_win(char board[][BOARD_SIZE], char player_char) { int i, j; int row_win, col_win, diag_win1, diag_win2; for (i = 0; i < BOARD_SIZE; i++) { row_win = 1; col_win = 1; for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] != player_char) { row_win = 0; } if (board[j][i] != player_char) { col_win = 0; } } if (row_win || col_win) { return 1; } } diag_win1 = 1; diag_win2 = 1; for (i = 0; i < BOARD_SIZE; i++) { if (board[i][i] != player_char) { diag_win1 = 0; } if (board[i][BOARD_SIZE-1-i] != player_char) { diag_win2 = 0; } } if (diag_win1 || diag_win2) { return 1; } return 0; } ``` 这个程序实现了基本的单人三子棋游戏,并且包括了判断胜负的函数。您可以根据需要修改和扩展此代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值