C语言一个小时实现简易三子棋,看看你能不能让愚蠢的电脑获胜,还不快上手试试吗(无AI算法)

今天利用空余时间,便写一写三子棋的实现,可能很多人第一次看到这些东西,心里就会有一点害怕,可能会说:天呐,这得多难啊,我真的能写吗?那么回答是肯定的:能写,并且还很简单。那就让我们来看一看吧!

我们首先要分析整个三子棋的需要,因为我们不是做人机对战(过段时间博主会发一篇五子棋人机对战的实现),主要实现人机对战需要考虑的情况比较多,比较复杂,在这里既然是新手向,大家有一个制作的思路就可以了。

三子棋下棋,首先我们需要做到能看到棋盘,之后是下棋,电脑会在我们之后。每一步我们都想屏幕能清空,同时保留一次棋盘。然后是判断输赢的问题。(如果书写对战AI,还需要考虑棋盘情况来书写对应的算法)

为了实现功能,我们将其拆解成三部分:一个test.c,一个game.c和一个game.h。

其中test.c用来书写实现逻辑,game.c用来书写函数定义,game.h则用来声明。

第一部分:

测试文件test.c,这个文件我们需要一个目录指引,也就是玩还是不玩,这个很容易实现,之后是定制棋盘,我们在头文件里会声明这些东西(ROW和COL一类的量)。首先肯定是初始化一个棋盘,这个很简单,直接给棋盘赋值空格就好(InitBoard),之后我们需要让它变成我们想要的样子(也就是DisplayBoard),这需要一点点的功夫,比如我们期望如下: 

那么我们就需要在书写定义的时候用点心了,不过我们现在看的是主函数,所以我们先关注主函数:

有了棋盘我们就还需要能下子,就需要玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin),这时候就基本上思路齐全了。

那么我们的主函数的逻辑已经比较清晰了,就直接上代码吧:

#include"game.h"
void menu()
{
	printf("===========================n");
	printf("=======   1.play    =======n");
	printf("=======   0.exit    =======n");
	printf("===========================n");
}

void game()
{
	//存储数据-二维数组
	char board[ROW][COL];
	//初始化棋盘-初始化空格
	InitBoard(board, ROW, COL);
	//打印棋盘-本质打印数组的内容
	DisplayBoard(board, ROW, COL);
	char ret = 0;//用来获取游戏状态
	while (1)
	{
		//玩家走
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IfWin(board, ROW, COL);
		if (ret != 'C')
			break;
		//电脑走
		ComputerMove(board, ROW, COL);
		system("cls");
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IfWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
	{
		printf("玩家胜利n");
	}
	else if (ret == '#')
	{
		printf("电脑胜利n");
	}
	else
	{
		printf("平局n");
	}
	DisplayBoard(board, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:n");
		scanf_s("%d", &input);
		system("cls");
		switch (input)
		{
		case 1:
			printf("三子棋游戏:n");
			game();
			break;
		case 0:
			printf("退出游戏n");
			break;
		default:
			printf("选择错误,请重新选择:n");
			break;
		}
	} while (input != 0);
}

在这里需要注意IfWin返回值的情况,这个是需要提前考虑的,由于玩家使用 “ * ”我们就认为返回了“ * ”就是玩家胜利,返回“ # ”就是电脑胜利,返回“ C ”(continue)就继续,返回 “ Q "就是平局

这样会使代码更加简单,至于为什么,我们马上就能知道。

第二步,书写头文件,主函数已经有了需求,接下来在头文件里书写一下声明就OK了:

#pragma once
//头文件的包含
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<Windows.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);

//判断输赢
//1.玩家赢 '*'  2.电脑赢  '#' 3.平局  'Q' 4.游戏继续  'C'
char IfWin(char board[ROW][COL], int row, int col);

第三步就是很关键的一步了,这一步决定了程序能否正常运行,因为它书写的是我们的函数定义,

我们已经知道,我们需要初始化一个棋盘(InitBoard),展示棋盘情况(DisplayBoard)玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin

那么就分开看看这些函数模块(不要忘记包含头文件#include"game.h"):

1、InitBoard

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

这个就是妥妥的二维数组赋值,全赋值空格,没什么好说的。

2、DisplayBoard

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("n");
		}
	}
}

因为我们有所期望,就是上图的那个棋盘,所以需要思考怎么制作棋盘,也很简单,打印每一行的数据,用 空格+数据+空格+竖杠 作为一个大元素 ,同时最后一个大元素没有竖杠。用代码解释的话,就是上面所示。

之后是每一行的区别,我们需要隔离棋盘的行,那么打印分隔线即可,道理如同打印“大元素”。

这个期望,我们就解决了。

3、PlayerMove

玩家落子,我们需要思考合法性,并给出反馈,所以需要需要if条件,首先判断输入的数据是否合理,之后是判断所输入的空间是否被占用。(由于我们习惯性的把横坐标在前,纵坐标灾后,同时注意数组的下标从0开始数,所以接收的时候为了符合判断,我们颠倒一下x和y,同时3-y,x-1

注意思考为什么是3-y(或者说是ROW - y),因为我们打印出来的列是从上向下数的,而我们看棋盘是从下向上看,所以只能倒过来接受数据才符合我们的思路

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	printf("玩家走:n");
	printf("请输入下棋的坐标:");
	//判断坐标合法性
	while (1)
	{
		scanf_s("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//下棋
			//判断坐标是否呗被占用
			if (board[3 - y][x - 1] == ' ')
			{
				board[3 - y][x - 1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用,请重新输入n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入n");
		}
	}
}

如果二者都没有,那么我们就给原来的空格赋值为 “ * ”

4、ComputerMove

电脑下子就简单的多了,只需要随机赋值就可以,同时判断一下占用情况。

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑走:n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		//判断占用
		if (board[x - 1][y - 1] == ' ')
		{
			board[x - 1][y - 1] = '#';
			break;
		}
	}
}

5、IfWin

整个代码,唯一有一点难度的应该就是这里了,这里笔者写的比较简陋,我们首先判断每一行是否相等,再每一列,再对角线,如果都没有,就进入棋盘是否满了的判断,如果棋盘满了,那么就是平局,没满,就继续下棋,这里我们需要返回值。同时注意,我们的返回值就是棋盘存储的数据,这一点简化了我们的代码

//判断游戏是否有输赢
char IfWin(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];
		}
	}
	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}
	//判断对角线
	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];
	}
	//判断平局
	//满了返回1,没满返回0
	if (IsFull(board, ROW, COL) == 1)
	{
		return 'Q';
	}
	//都没有,继续游戏
	return 'C';
}

那么我们自然需要一个判断棋盘是否已满的函数,由于笔者的电脑出了一点小问题,只能书写这种非常原始的判断方式,有能力的伙伴可以自行改善代码哦

int IsFull(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		if (board[i][0] == ' ')
			return 0;
		else if (board[i][1] == ' ')
			return 0;
		else if (board[i][2] == ' ')
			return 0;
		else if (board[0][i] == ' ')
			return 0;
		else if (board[1][i] == ' ')
			return 0;
		else if (board[2][i] == ' ')
			return 0;
		else if (board[i][i] == ' ')
			return 0;
	}
	return 1;
}

到这里,我们的简化版三子棋就实现了,想要书写AI算法的也可以根据棋盘判断的情况进行思考,思考电脑应该如何围堵,落子,然后代码实现。

这里就不多说了,那么感谢看到这里啦!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇

)">
下一篇>>