首页 > 软件开发 > 软件开发

【C语言基础学习---三子棋游戏】(包含详细讲解+优化策略及实现)

admin 软件开发 2021-04-26 15:58:12 c语言   数组   游戏   游戏开发   人工智能  
后台-系统设置-扩展变量-手机广告位-内容正文底部

三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。
我们学习c语言也有一段时间了,现在我们尝试自己用C语言写一个三子棋小游戏玩玩吧!


前期准备:

工程文件思路:
主函数放到ticktacktoe(三子棋)中
三子棋具体的实现放到game.c / game.h中
在这里插入图片描述
主函数游戏思路:
三子棋游戏思路:
1、至少玩一次,可以玩多次,do…while循环
2、进入游戏后先打印菜单提示
3、提示用户输入,根据输入值来确定后续的游戏进程(1代表玩游戏,0代表退出,其他需要重新选择

三子棋玩游戏的思路:
首先我们要知道三子棋的一些信息:
1、三子棋形状
在这里插入图片描述
在这里插入图片描述
2、游戏规则:同一形状连成直线(三格)即获胜
所以获胜的条件:1、横排同形状 2、竖排同形状 3、对角线同形状

三子棋游戏实现具体思路:
1.我们要记录下棋的结果,就需要对应的二维数组来存储
2.创建的二维数组要进行初始化,赋值成空格" "
3.打印棋盘,看一下展示的效果
在这里插入图片描述

  • 游戏状态判断 四种状态 玩家赢,电脑赢,平局,继续
    4.玩家下棋
    5.判断玩家是否游戏胜利 判断游戏状态是否继续
    6.电脑下棋(随机落子的方式)
    7.判断电脑是否游戏胜利 判断游戏状态是否继续

可优化点
1.电脑走可优化,将随机走变成有策略走,让电脑看起来更像“玩家”。(代码中实现了这个优化)
在这里插入图片描述

2.判断游戏进展部分可优化,比如说后续ROW,COL都改成4,也就是将三子棋变成四子棋,本程序中的游戏进展部分也需要调整了。为了让ROW,COL在变化调整后依然适用,我们可以对其进行相应的优化。(本次代码暂未实现该优化)


优化1实现:
思路:这个优化的实现注意考虑两个方面的内容:
1、如果电脑落子,有机会直接赢,则优先落在能赢的坐标上
2、如果玩家落子有机会赢,拦截玩家路子坐标(步骤1优先级高于步骤2)
3、如果步骤1、步骤2均不满足,电脑继续伪随机落子

源码下载及效果展示:

三子棋游戏github源码
效果展示:
在这里插入图片描述

完整代码:

源文件ticktacktoe.c内容

/*******************/
//以下是源文件ticktacktoe.c内容
/*******************/
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"


void game_menu(void)
{
	printf("*********************************\n");
	printf("********* 1.play game ***********\n");
	printf("********* 0.exit game ***********\n");
	printf("*********************************\n");


}
void play_game(void)
{
	char board[ROW][COL] = { 0 };//创建二维数组来存储棋盘下棋数据
	Init_board(board, ROW, COL);//初始化棋盘
	Display_board(board, ROW, COL);//打印棋盘
	char ret = 0;//接收游戏进行的进展
	//玩家赢:*  电脑赢:#  平局:Q   继续:C
	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 == '*')
	{
		printf("恭喜你,玩家赢得游戏!\n");
	}
	else if (ret == '#')
	{
		printf("很遗憾,电脑赢得游戏!\n");
	}
	else if (ret == 'Q')
	{
		printf("这把平局,还差一点点就赢了哦!\n");
	}
	Display_board(board, ROW, COL);//打印棋盘
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		game_menu();//打印游戏菜单
		printf("请选择:>>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			play_game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重选\n");
			break;
		}


	} while (input);

	return 0;
}


头文件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);
//判断电脑能否赢
int Check_computer(char board[ROW][COL], int row, int col);
//判断玩家能否赢
int Judge_player(char board[ROW][COL], int row, int col, int k);

源文件game.c内容

/*******************/
//以下是源文件game.c内容
/*******************/

#include"game.h"
//实现初始化棋盘
void Init_board(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 Display_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//打印row行,将棋盘一行 + ___看作一行打印内容 
	{
		for (j = 0; j < col; j++)//打印col列
		{
			printf(" %c ", board[i][j]);//将 %c 看作一个打印单位
			if (j < col - 1)//一行就前col-1个需要打印|,最后一个不打印
				printf("|");
		}
		printf("\n");//一行内容打印完后换行
		if (i < row - 1)//最后一行不打印
		{
			for (j = 0; j < col; j++)
			{
				printf("---");//将___视为一个整体
				if (j < col - 1)//一行就前col-1个需要打印|,最后一个不打印
					printf("|");
			}
		}
		//printf("___|___|___\n");//这种打印方式仅能打印三行三列的棋盘,不推荐
		printf("\n");//一行内容打印完后换行
	}
}

//实现玩家下棋
void Player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家落子:>>\n");
	while (1)
	{
		printf("请输入落子的坐标:>>");
		scanf("%d %d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//坐标合理性判断
		{
			//判断该坐标是否被占用
			if (board[x - 1][y - 1] == ' ')//玩家输入的1 1 对应的下标其实是 0 0
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("坐标已被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标输入错误,请重新输入\n");
		}
	}


}

//实现电脑下棋
//void Computer_move(char board[ROW][COL], int row, int col)
//{
//
//	while (1)
//	{
//		int x = rand() % row;
//		int y = rand() % col;
//		if (board[x][y] == ' ')
//		{
//			board[x][y] = '#';
//			break;
//		}
//	}
//}

int Judge_player(char board[ROW][COL], int row, int col, int k)
{
	//下面这个函数就是判断玩家是否有机会赢,如果能赢就堵住他
	int i = 0;
	int j = 0;
	while (0 == k)
	{
		//判断玩家在横行上是否会赢
		for (i = 0; i < row; i++)
		{
			if (board[i][0] == board[i][1] && board[i][1] == '*' && board[i][2] == ' ')
			{
				board[i][2] = '#';
				k = 1;
				break;
			}


			if (board[i][0] == board[i][2] && board[i][0] == '*' && board[i][1] == ' ')
			{
				board[i][1] = '#';
				k = 1;
				break;
			}


			if (board[i][1] == board[i][2] && board[i][1] == '*' && board[i][0] == ' ')
			{
				board[i][0] = '#';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//判断玩家在竖列上是否会赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && board[1][j] == '*' && board[2][j] == ' ')
			{
				board[2][j] = '#';
				k = 1;
				break;
			}


			if (board[0][j] == board[2][j] && board[2][j] == '*' && board[1][j] == ' ')
			{
				board[1][j] = '#';
				k = 1;
				break;
			}


			if (board[1][j] == board[2][j] && board[2][j] == '*' && board[0][j] == ' ')
			{
				board[0][j] = '#';
				k = 1;
				break;
			}
		}
		break;
	}

	//判断玩家在对角线上是否会赢,又加了一个while是为了让判断对角线的代码成块。
	while (0 == k)
	{
		if (board[0][0] == board[1][1] && board[1][1] == '*' && board[2][2] == ' ')
		{
			board[2][2] = '#';
			k = 1;
			break;
		}


		if (board[0][0] == board[2][2] && board[2][2] == '*' && board[1][1] == ' ')
		{
			board[1][1] = '#';
			k = 1;
			break;
		}


		if (board[1][1] == board[2][2] && board[1][1] == '*' && board[0][0] == ' ')
		{
			board[0][0] = '#';
			k = 1;
			break;
		}


		if (board[0][2] == board[1][1] && board[0][2] == '*' && board[2][0] == ' ')
		{
			board[2][0] = '#';
			k = 1;
			break;
		}


		if (board[0][2] == board[2][0] && board[2][0] == '*' && board[1][1] == ' ')
		{
			board[1][1] = '#';
			k = 1;
			break;
		}


		if (board[1][1] == board[2][0] && board[2][0] == '*' && board[0][2] == ' ')
		{
			board[0][2] = '#';
			k = 1;
			break;
		}
		break;
	}
	return k;//返回值如果是1那么已经对玩家进行了阻拦,如果是0则无需阻拦。
}

int Check_computer(char board[ROW][COL], int row, int col)//判断电脑下一步落子能否赢游戏
{
	int i = 0;
	int j = 0;
	int k = 0;
	while (k == 0)
	{
		//判断电脑行排能否赢
		for (i = 0; i < row; i++)
		{
			if (board[i][0] == board[i][1] && board[i][0] == '#' && board[i][2] == ' ')
			{
				board[i][2] = '#';
				k = 1;
				break;
			}
			if (board[i][0] == board[i][2] && board[i][0] == '#' && board[i][1] == ' ')
			{
				board[i][1] = '#';
				k = 1;
				break;
			}
			if (board[i][1] == board[i][2] && board[i][1] == '#' && board[i][0] == ' ')
			{
				board[i][0] = '#';
				k = 1;
				break;
			}
		}
		if (k != 0)//落子后跳出
			break;
		//判断电脑列排能否赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && board[0][j] == '#' && board[2][j] == ' ')
			{
				board[2][j] = '#';
				k = 1;
				break;
			}
			if (board[0][j] == board[2][j] && board[0][j] == '#' && board[1][j] == ' ')
			{
				board[1][j] = '#';
				k = 1;
				break;
			}
			if (board[2][j] == board[1][j] && board[0][j] == '#' && board[0][j] == ' ')
			{
				board[0][j] = '#';
				k = 1;
				break;
			}
		}
		break;
	}
	//判断电脑对角线能否赢
	while (0 == k)
	{
		//左上角到右下角对角线判断
		if (board[0][0] == board[1][1] && board[0][0] == '#' && board[2][2] == ' ')
		{
			board[2][2] = '#';
			k = 1;
			break;
		}
		if (board[0][0] == board[2][2] && board[0][0] == '#' && board[1][1] == ' ')
		{
			board[1][1] = '#';
			k = 1;
			break;
		}
		if (board[1][1] == board[2][2] && board[0][0] == '#' && board[0][0] == ' ')
		{
			board[0][0] = '#';
			k = 1;
			break;
		}
		//左下角到右上角对角线判断
		if (board[2][0] == board[1][1] && board[2][0] == '#' && board[0][2] == ' ')
		{
			board[0][2] = '#';
			k = 1;
			break;
		}
		if (board[2][0] == board[0][2] && board[2][0] == '#' && board[1][1] == ' ')
		{
			board[1][1] = '#';
			k = 1;
			break;
		}
		if (board[0][2] == board[1][1] && board[0][2] == '#' && board[2][0] == ' ')
		{
			board[2][0] = '#';
			k = 1;
			break;
		}
		break;
	}
	k = Judge_player(board, row, col, k);
	return k;
}

//实现电脑下棋优化
void Computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	int ret = 0;
	ret = Check_computer(board, row, col);
	while (0 == ret)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断棋盘是否满了
int Is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				count++;
		}
	}
	if (count == 0)
		return 1;
	else
		return 0;
}

//实现判断游戏进展
char Is_win(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][0] == board[i][2]) && (board[i][0] != ' '))
		{
			return board[i][0];
		}
	}
	//判断三列
	for (i = 0; i < row; i++)
	{
		if ((board[0][i] == board[1][i]) && (board[0][i] == board[2][i]) && (board[0][i] != ' '))
		{
			return board[0][i];
		}
	}
	//判断两条对角线
	if ((board[0][0] == board[1][1]) && (board[0][0] == board[2][2]) && (board[0][0] != ' '))
	{
		return board[0][0];
	}
	if ((board[0][2] == board[1][1]) && (board[0][2] == board[2][0]) && (board[0][2] != ' '))
	{
		return board[0][2];
	}
	//判断平局  棋盘满了返回1,不满返回0
	int ret = Is_full(board, row, col);
	if (ret == 1)
		return 'Q';
	else
		return 'C';
}

文章来源:https://blog.csdn.net/QIYICat/article/details/116126461

后台-系统设置-扩展变量-手机广告位-内容正文底部
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。
本文地址:https://jcdi.cn/ruanjiankaifa/ruanjiankaifa/739.html

留言与评论(共有 0 条评论)
   
验证码:
后台-系统设置-扩展变量-手机广告位-评论底部广告位

教程弟

https://www.jcdi.cn/

统计代码 | 京ICP1234567-2号

Powered By 教程弟 教程弟

使用手机软件扫描微信二维码