【三子棋】玩家对战及人机对战

        功能实现即标题所见,相信这是大多数C语言学习者第一个入手的小游戏,通过对功能稍复杂的程序的编写能提高我们的逻辑能力及代码动手能力,废话不多说,我们进入正题。

 


以下是文章目录,有需要的同学可以自行跳转。

 

目录

function1菜单界面

逻辑框架1  (do while)      

逻辑框架2 (switch case)

逻辑框架 3 (人机对战)

function 2 展示棋盘

function 3 电脑落子

function 4 玩家1落子

function 5 判断输赢

逻辑框架 4 (玩家对战)

 function 6 玩家2落子

总览

导图


 


        棋盘的最终格式决定采用以'0'(字符零)来初始化,然后以 ‘ * ’  ‘ # ’ 来代表棋子供玩家进行下棋,首先,既然是游戏,我们便需要一个菜单界面,用 ‘ * ’ 来作为边框是较为常见的选择。

function1菜单界面

void Menu(void)//菜单界面
{
	printf("*************************\n");
	printf("***** 1.玩家对战 ********\n");
	printf("***** 2.人机对战 ********\n");
	printf("***** 3.退出游戏 ********\n");
	printf("*************************\n");
}

       

0694f519f65243feacd4f7c55a95d052.png


 

逻辑框架1  (do while)      

         整体逻辑上第一步,我们需要玩家自行选择游戏模式或者退出游戏,然后我们要在一局游戏进行完后再次让玩家进行选择,所以很显然这是一个循环的过程。于是我们需要一个do while语句,并且while的判断永远为真,只有在用户选择退出时再跳出循环,如此以来我们的基本框架有了。

do
{
    //选项1
    //选项2
    //选项3

} while(1);

然后第一步我们需要创建一个棋盘并(利用二维数组表示)对其进行初始化,如果玩家重复进入游戏则需要重复创建我们便将这个功能放到do while 开头中。过程实现如下 

	char Board[Row][Col] = { 0 };//因为我们还要存放字符,所以它类型为char
		//初始化
		for (int i = 0; i < 3; i++)//三行
		{
			for (int j = 0; j < 3; j++)//三列
			{
				Board[i][j] = '0';//初始化为字符零
			}
		}

注: 我们之所以全部初始化为字符 ‘ 0 ’ 是因为这样整个列表中将会统一格式,便于输出和修改,如果在最初将数组设置为int类型,则以后想放置“ * ” “ # ”等字符将非常不方便。


 

逻辑框架2 (switch case)

        进入游戏后玩家需要做出选择,此时我们需要创建一个整形变量(例如本文中使用的 ‘Slect’ )用于存储玩家的选择。然后经过Switch case语句的判断决定游戏的走向

int Slect = 0;//Slect用于存储用户选项
do
{
    //Menu();菜单功能放置的位置
    char Board[Row][Col] = { 0 };//因为我们还要存放字符,所以它类型为char
	//初始化
	for (int i = 0; i < 3; i++)//三行
	{
		for (int j = 0; j < 3; j++)//三列
		{
			Board[i][j] = '0';//初始化为字符零
		}
	}
    scanf("%d",&n);

    switch (Slect)
    {

        case 1:{
               //选项一
                }
            break;

        case 2:{
               //选项2
                }
            break;

        case 3:{
                //选项3
                }
            break;
        default:
            break;

    }
} while(1);

注意:其中Row 和 Col 是已经利用宏定义定义好的常量,定义过程如下

在文件开头或者头文件中输入

#define Row 3
#define Col 3


逻辑框架 3 (人机对战)

如果玩家选择2,程序将从case2语句进入,然后我们便开始实行玩家对战的功能。

玩家对战的基本过程也是回合制的,你来我往,所以这里仍旧需要一个do while结构

首先呢我们先分析一下回合过程

首先是电脑放置棋子,然后是玩家放置棋子,在玩家放置之前我们还需要展示棋盘,但是既然放置了棋子就会可能有胜负的产生,所以我们要在每一个操作部分后加一个输赢判断的过程,为此我们可以专门设置一个函数用于输赢的判断。所以我们要实现以下功能。

function 2 展示棋盘


void ShowBoard(char board[][Col])//展示棋盘
{
	printf("\n");
	printf("   1    2    3\n");   //列标号
	int i = 0, j = 0;
	for (i = 0; i < Row; i++)   
	{
		printf("%d", i + 1);    //行标号
		for (j = 0; j < Col; j++)
		{
			printf("|");    //打印纵向格子
			if (board[i][j] == '0')
			{
				printf("   ");//是0的话就空出来
			}
			else
			{
				printf(" %c ", board[i][j]);
			}
			printf("|");    //打印纵向格子
		}
		if(i != 2) //除了左后一行
		printf("\n---------------");   //打印横向格子
		printf("\n");
	}
	printf("\n\n");
}

function 3 电脑落子

//电脑落子
void Computer(char board[][Col])
{
	printf("Computer:");
	do
	{
		//生成随机数选择坐标
		int row = (rand() % 3);//随机数生成横坐标
		int col = (rand() % 3);//随机数生成纵坐标
		// 坐标行和列都为零到二,对应玩家认为的一到三
		//判断这个位置能不能防置
		if (board[row][col] != '0')// 若果不能则重新生成
		{
			continue;//不能放置,跳过后边的语句重新生成
		}
		else//如果为空则可以放
		{
			board[row][col] = '*';
			printf(" 电脑放置的坐标为(%d ,%d)\n", row + 1, col + 1);
			break;
		}

	} while(1);
	ShowBoard(board);   //完成后就展示放的位置
}

function 4 玩家1落子

void PlayerOne(char board[][Col])//玩家一操作函数
{
	printf("PlayerOne:\n");//玩家一用‘#’
	do
	{
		int row = 0, col = 0;
		
		printf("请输入你要放置棋子的坐标[][]:\n");  //这里的行和列不用输入下标,只输入
		printf("第几行___?\n");                    //对应的逻辑标号,这里将他们分开处理了
		scanf("%d",&row);
		printf("第几列___?\n");
		scanf("%d", &col);
		//判断这个位置能不能放置
		if (board[row - 1][col - 1] != '0')
		{
			printf("这个位置无法放置,非法坐标请重新选择:\n");
			continue;
		}
		else       //能放置
		{
			board[row - 1][col - 1] = '#';
			printf("放置成功!!");
			ShowBoard(board);      //放置结束后需要展示放置的位置
			break;
		}
	} while (1);
}

function 5 判断输赢

获胜的情况很好统计,一共只有三大类情况,并不涉及太多的分类。

第一类:一行三个元素全是相同的,我们用一个循环判断,行不断改变,然后列坐标三个依次从0加到2,判断之间是否相同,这里有个十分重要的条件,三者相等且不为 0 ’,也就是我们任意选择三个被判断元素中的一个让它不等于零就可以了,注意是字符零表达方式为‘0’,不是数字零。

第二类:同第一类原理相同,之时这次变成列变化,行交表从0到2递增。

第三类:对角线,这个就需要我们把两种小情况逐一书写了,这个版本暂时没有好的优化方法。

最后:谁赢了就返回谁的符号就行了,见代码如何实现,这就是我们将函数返回值设置成char类型的原因。便于我们后续的应用。

char IsWin(char board[][Col])
{
	//行3
	for (int i = 0; i < 3; i++)
	{
		if (board[i][0] == board[i][1]
			&& board[i][1] == board[i][2]
			&& board[i][0] == board[i][2]
			&& board[i][0] != '0')
		{
			return board[i][0];
		}
	}
	//列3
	for (int i = 0; i < 3; i++)
	{
		if (board[0][i] == board[1][i]
			&& board[1][i] == board[2][i]
			&& board[2][i] == board[0][i]
			&& board[0][i] != '0')
		{
			return board[1][i];
		}
	}
	//斜线2
	if (board[0][0] == board[1][1]
		&& board[1][1] == board[2][2]
		&& board[0][0] == board[2][2]
		&& board[0][0] != '0'
		|| board[0][2] == board[1][1]
		&& board[1][1] == board[2][0]
		&& board[0][2] == board[2][0]
		&& board[1][1] != '0')
	{
		return board[0][0];
	}
	return '0';
}

然后我们需要将这些功能连贯起来>>

ShowBoard(Board);   //展示棋盘
			printf("您已经选择玩家对战模式\n玩家1使用‘#'\n################\n玩家2使用‘*’\n****************\n");
			int num = 0;//记录回合数
			do
			{
				PlayerOne(Board); num++;
				if (IsWin(Board) == '#')
				{
					printf("游戏结束\n玩家1胜利!!\n");
					break;
				}
				if (num == 9)   //如果棋盘摆满还未分出胜负则判断为平局
				{
					printf("游戏结束\n!平局!\n");
					break;
				}
				PlayerTwo(Board); num++;
				if (IsWin(Board) == '*')
				{
					printf("游戏结束\n玩家2胜利!!\n");
					break;
				}
			} while (1);

 8e4598b0d2e744679c05bd41bb2aa67a.png

05214ea40ee042f381987d1ab964bbd2.png


 

 

逻辑框架 4 (玩家对战)

大致流程和人机对战相似,实现如下

 

printf("您已经选择人机对战模式!\n");
			printf("‘*’为电脑,‘#’为玩家\n");
			int num = 0;//num用于记录回合数
			do
			{
				Computer(Board);//电脑落子
				num++;
				if (IsWin(Board) == '*')//判断输赢
				{
					printf("电脑胜利!!\n");
					break;
				}
				// 平局结束游戏
				if (num == 9)
				{
					printf("平局了\n");
					break;
				}
				PlayerOne(Board);//玩家落子
				num++;
				if (IsWin(Board) == '#')
				{
					printf("玩家1胜利!!\n");
					break;
				}
				//任意一方胜利结束游戏
				//没有胜利则继续

 function 6 玩家2落子


void PlayerTwo(char board[][Col])
{
	printf("PlayerTwo:\n");//玩家二用‘*’
	do
	{
		int row = 0, col = 0;

		printf("请输入你要放置棋子的坐标[][]:\n");
		printf("第几行___?\n");
		scanf("%d", &row);
		printf("第几列___?\n");
		scanf("%d", &col);
		//判断这个位置能不能防止
		if (board[row - 1][col - 1] != '0')
		{
			printf("这个位置无法放置,非法坐标请重新选择:\n");
			continue;
		}
		else
		{
			board[row - 1][col - 1] = '*';
			printf("放置成功!!");
			ShowBoard(board);
			break;
		}
	} while (1);
}

 4db90e1832264d68b92a9961f52a644c.pngd95b67d331514fd697413c27e65288b6.png

 

 

       不管是人机对战还是玩家对战我们都在循环中每次落子行为后给临时变量num加一,这样可以来记录我们的操作个数,当num为9时,仍没有分出胜负,则判断为平局,这里有个小细节,num == 9 这条判断语句应该放在先手玩家的判断输赢的语句之后,如果放在其判断输赢语句之前若最后一次落子先手玩家才胜利则会出现误判的情况,若将该判断语句放在游戏过程最后,则会使得在棋盘放满后后手玩家无法放置棋子程序无法进行的bug,这点需要注意。

总览

 大致过程就是这样,老规矩,以下是完整版本:

//头文件部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
#include<time.h>
#define Row 3
#define Col 3
//函数部分
#include"head.h"
void Menu(void)//菜单界面
{
	printf("*************************\n");
	printf("***** 1.玩家对战 ********\n");
	printf("***** 2.人机对战 ********\n");
	printf("***** 3.退出游戏 ********\n");
	printf("*************************\n");
}


void ShowBoard(char board[][Col])//展示棋盘
{
	printf("\n\n");
	printf("   1    2    3\n");
	int i = 0, j = 0;
	for (i = 0; i < Row; i++)
	{
		printf("%d", i + 1);
		for (j = 0; j < Col; j++)
		{
			printf("|");
			if (board[i][j] == '0')
			{
				printf("   ");
			}
			else
			{
				printf(" %c ", board[i][j]);
			}
			printf("|");
		}
		if(i != 2)
		printf("\n---------------");
		printf("\n");
	}
	printf("\n\n");
}


//电脑落子
void Computer(char board[][Col])
{
	printf("Computer:");
	do
	{
		//生成随机数选择坐标
		int row = (rand() % 3);//随机数生成横坐标
		int col = (rand() % 3);//随机数生成纵坐标
		// 坐标行和列都为零到二,对应玩家认为的一到三
		//判断这个位置能不能防置
		if (board[row][col] != '0')// 若果不能则重新生成
		{
			continue;//不能放置,跳过后边的语句重新生成
		}
		else//如果为空则可以放
		{
			board[row][col] = '*';
			printf(" 电脑放置的坐标为(%d ,%d)\n", row + 1, col + 1);
			break;
		}

	} while(1);
	ShowBoard(board);
}


void PlayerOne(char board[][Col])//玩家一操作函数
{
	printf("PlayerOne:\n");//玩家一用‘#’
	do
	{
		int row = 0, col = 0;
		
		printf("请输入你要放置棋子的坐标[][]:\n");
		printf("第几行___?\n");
		scanf("%d",&row);
		printf("第几列___?\n");
		scanf("%d", &col);
		//判断这个位置能不能防止
		if (board[row - 1][col - 1] != '0')
		{
			printf("这个位置无法放置,非法坐标请重新选择:\n");
			continue;
		}
		else
		{
			board[row - 1][col - 1] = '#';
			printf("放置成功!!");
			ShowBoard(board);
			break;
		}
	} while (1);
}

void PlayerTwo(char board[][Col])
{
	printf("PlayerTwo:\n");//玩家一用‘#’
	do
	{
		int row = 0, col = 0;

		printf("请输入你要放置棋子的坐标[][]:\n");
		printf("第几行___?\n");
		scanf("%d", &row);
		printf("第几列___?\n");
		scanf("%d", &col);
		//判断这个位置能不能防止
		if (board[row - 1][col - 1] != '0')
		{
			printf("这个位置无法放置,非法坐标请重新选择:\n");
			continue;
		}
		else
		{
			board[row - 1][col - 1] = '*';
			printf("放置成功!!");
			ShowBoard(board);
			break;
		}
	} while (1);
}

//判断输赢以及平局
char IsWin(char board[][Col])
{
	//行3
	for (int i = 0; i < 3; i++)
	{
		if (board[i][0] == board[i][1]
			&& board[i][1] == board[i][2]
			&& board[i][0] == board[i][2]
			&& board[i][0] != '0')
		{
			return board[i][0];
		}
	}
	//列3
	for (int i = 0; i < 3; i++)
	{
		if (board[0][i] == board[1][i]
			&& board[1][i] == board[2][i]
			&& board[2][i] == board[0][i]
			&& board[0][i] != '0')
		{
			return board[1][i];
		}
	}
	//斜线2
	if (board[0][0] == board[1][1]
		&& board[1][1] == board[2][2]
		&& board[0][0] == board[2][2]
		&& board[0][0] != '0'
		|| board[0][2] == board[1][1]
		&& board[1][1] == board[2][0]
		&& board[0][2] == board[2][0]
		&& board[1][1] != '0')
	{
		return board[0][0];
	}
	return '0';
}
//主函数部分
#include"head.h"

int main(void)
{
	srand((unsigned int)time(NULL));
	do
	{
		Menu();
		int Slect = 0;
		scanf("%d", &Slect);
		char Board[Row][Col] = { 0 };
		//初始化
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				Board[i][j] = '0';
			}
		}
		switch (Slect)
		{
		case 1:{
			ShowBoard(Board);
			printf("您已经选择玩家对战模式\n玩家1使用‘#'\n################\n玩家2使用‘*’\n****************\n");
			int num = 0;//记录回合数
			do
			{
				PlayerOne(Board); num++;
				if (IsWin(Board) == '#')
				{
					printf("游戏结束\n玩家1胜利!!\n");
					break;
				}
				if (num == 9)
				{
					printf("游戏结束\n!平局!\n");
					break;
				}
				PlayerTwo(Board); num++;
				if (IsWin(Board) == '*')
				{
					printf("游戏结束\n玩家2胜利!!\n");
					break;
				}
			} while (1);
		}
			break;
		case 2:{
			printf("您已经选择人机对战模式!\n");
			printf("‘*’为电脑,‘#’为玩家\n");
			int num = 0;//num用于记录回合数
			do
			{
				Computer(Board);//电脑落子
				num++;
				if (IsWin(Board) == '*')//判断输赢
				{
					printf("电脑胜利!!\n");
					break;
				}
				// 平局结束游戏
				if (num == 9)
				{
					printf("平局了\n");
					break;
				}
				PlayerOne(Board);//玩家落子
				num++;
				if (IsWin(Board) == '#')
				{
					printf("玩家1胜利!!\n");
					break;
				}
				//任意一方胜利结束游戏
				//没有胜利则继续
			} while (1);
		}
			break;
		case 3:{
			printf("游戏已结束\n");
			return 0;
		}
			break;
		default:{
			printf("输入的为无效值,请重新选择!!\n");
		}
			break;
		}
	} while (1);
	return 0;
}

导图

4157303dfdfe4722a2f3300924657b7a.png

 12c719a2c38e4e9da237243a5cd2cde8.png

 ok,本次分享就这些啦,谢谢大家的支持。下次的小游戏是扫雷,期待大家光临。

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒雒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值