简单三子棋的实现(C语言)

108 阅读5分钟

小游戏流程概述

完成一个游戏,我们首先要考虑的就是游戏大体框架.那我们就从流程开始入手,我们就使用game();函数来实现基本流程.

  1. 首先要有的肯定是游戏菜单,俗称目录.我们就使用menu()函数来展示基本菜单功能.
  2. 第二呢我们就需要创建我们的棋盘并初始化.我们使用InitBoard();函数来初始化我们的棋盘.
  3. 棋盘初始化之后我们使用display_board();函数来打印棋盘.并在每次落子之后重新打印棋盘.
  4. 拥有基本条件后,我们就可以使玩家之间落子博弈了.当然,我们还需要在每次落子之后判定游戏是否结束.
  5. 当游戏结束后,我们就需要重新展示菜单.询问玩家下一步流程.

当我们有了基本流程,我们就可以依次着手进行模块设计了

首先我们需要创建两个源文件和一个头文件.

名称使用
test.c使用test.c来实现游戏的基本流程
game.h使用game.h来声明我们需要的函数和头文件
game.c使用game.c来书写游戏具体实现

接下来我们就在test.c中写入main()函数.使用input变量来控制流程.

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

srand((unsigned int)time(NULL)); 此条语句是生成随机数要使用的经典语句.看到它,我们就知道后面模块中要使用随机数.如果不理解这里暂且忘掉它,因为它的存在不会对代码产生影响.

代码向下走,我们就进入了一个简单的循环,首先menu(); 函数会打印我们的菜单,使得用户选择流程并进入switch语句.

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

简单的菜单使得用户的选择用三种返回,而我们用input来当做循环的结束条件,巧妙地完成了菜单的简易功能.

而当玩家选择了1,也就是进行游戏时,我们就进入到了game(); 模块开始了我们的游戏.

void game()
{
	char ret;
	char board[ROW][COL] = { 0 };
	init_board(board,ROW,COL);
	display_board(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		display_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		display_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == 'O')
	{
		printf("玩家赢\n");
	}
	else if (ret == 'X')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}

进入游戏,我们首先就是要初始化并打印出我们的棋盘.这里我们调用init_board(board,ROW,COL);display_board(board, ROW, COL); 两个函数来实现初始化.

参数属性
board使用二维数组表示棋盘
ROW表示棋盘的行数
COL表示棋盘的列数

初始化完成后我们就要进入游戏,我们使用player_move(board, ROW, COL);computer_move(board, ROW, COL); 来分别实现玩家落子和电脑落子.我们这里的流程设置为玩家和电脑对弈,如果需要改变玩法,可以更改这里的设置来实现.

落子之后我们使用is_win(board, ROW, COL); 来判断游戏是否结束.并返回一个用于流程运行的字符.我们使用ret来接收并进行判断.

这些就是我们需要在test.c中书写的代码了.下面展示test.c中的完整代码.(VS2019)

//三子棋   test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

void game()
{
	char ret;
	char board[ROW][COL] = { 0 };
	init_board(board,ROW,COL);
	display_board(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		system("cls");
		display_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		//system("cls");
		display_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == 'O')
	{
		printf("玩家赢\n");
	}
	else if (ret == 'X')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}

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.c中除了上述代码外还需要添加头文件game.h.因为game.h中声明了我们需要的所有函数和头文件.

接下来我们来看game.c中具体的实现.

由于game.c中是对模块的具体实现.那我们就根据test.c中的函数步骤来展示具体实现过程.

首先是初始化函数void init_board(char board[ROW][COL], int row, int col);.

void init_board(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{

		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

我们将棋盘中元素全都初始化为 ' ',这样有利于我们后续打印和更改.

接下来是打印棋盘函数void display_board(char board[ROW][COL], int row, int col);.

void display_board(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{

		for (int j = 0; j < col; j++)
		{
			printf(" %c ",board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		for (int j = 0; j < col; j++)
		{
			if (i < col - 1)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

我们使用'|'和'---'配合打印出一个简单的棋盘供我们使用.

下来就是我们玩家和电脑的落子过程.分别是void player_move(char board[ROW][COL], int row, int col);void computer_move(char board[ROW][COL], int row, int col) 两个函数.

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋->\n");

	while (1)
	{
		printf("请输入下棋坐标-> ");
		printf("提示:坐标(1,1 -- %d,%d)\n", row, col);
		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] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

由于我们写的是一个简单的三子棋游戏,所以我们这里只要求电脑可以下棋的程度.而最简单的电脑,那就是随机生成一组(x,y)来表示棋盘中的一个位置,并且判断该位置没有被占用的情况下落子即可.

void computer_move(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] = 'X';
			break;
		}
	}
}

由于我们需要使用rand() 生成随机数,所以在前面main函数里,书写了和它配套使用的语句. 而且由于我们生成的随机数范围是(0-65535),所以我们还需要给(x,y)%行和列;

落子之后,我们需要使用char is_win(char board[ROW][COL], int row, int col); 判断游戏是否结束.

char is_win(char board[ROW][COL], int row, int col)
{
	int j = 0;
	for (j = 0; j < row; j++)
	{
		if (board[j][0] == board[j][1] && board[j][1] == board[j][2] && board[j][2] !=' ')
		{
			return board[j][0];
		}
	}
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[2][j] != ' ')
		{
			return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][2] && board[2][0] != ' ')
	{
		return board[2][0];
	}
	if (is_full(board,row,col))
	{
		return 'Q';
	}
	return 'C';
}

我们这里书写的判断是三子棋的输赢判断,如果ROW和COL不等于3的话,可能判断就不适用了.判断还使用了int is_full(char board[ROW][COL], int row, int col); 函数用来判断棋盘是否还有空位置.

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

而写到这里,我们的game.c文件中具体的代码已经全都书写完了.下面展示game.c中的完整代码.(VS2019)

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void init_board(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{

		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void display_board(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{

		for (int j = 0; j < col; j++)
		{
			printf(" %c ",board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		for (int j = 0; j < col; j++)
		{
			if (i < col - 1)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋->\n");

	while (1)
	{
		printf("请输入下棋坐标-> ");
		printf("提示:坐标(1,1 -- %d,%d)\n", row, col);
		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] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

void computer_move(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] = 'X';
			break;
		}
	}
}

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

char is_win(char board[ROW][COL], int row, int col)
{
	int j = 0;
	for (j = 0; j < row; j++)
	{
		if (board[j][0] == board[j][1] && board[j][1] == board[j][2] && board[j][2] !=' ')
		{
			return board[j][0];
		}
	}
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[2][j] != ' ')
		{
			return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][2] && board[2][0] != ' ')
	{
		return board[2][0];
	}
	if (is_full(board,row,col))
	{
		return 'Q';
	}
	return 'C';
}

game.c的头和test.c相同,都要引入game.h头文件.

接下来我们来看game.h中具体的书写

#pragma once

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

#define ROW 3
#define COL 3

void init_board(char board[ROW][COL], int row, int col);
void display_board(char board[ROW][COL],int row,int col);
void player_move(char board[ROW][COL], int row, int col);
void computer_move(char board[ROW][COL], int row, int col);
char is_win(char board[ROW][COL], int row, int col);

game.h就是用来引入我们的头文件和声明函数

以上就是实现简单三子棋的全部代码了.

让我们以简单的游戏截图来结束这篇小游戏吧.

8SVODE0RP

看来我们简单的电脑也不弱呀.