C语言小游戏--三子棋

news/2024/7/5 4:14:26

目录

问题描述

逻辑分析

具体实现

1.进入菜单界面

2.初始化棋盘

3.打印棋盘

4.玩家下棋

5.电脑下棋

6.判断输赢

运行结果

完整代码

game.h

game.c

test.c


问题描述

结合C语言所学知识,简单实现一个三子棋小游戏。

逻辑分析

  1. 进入菜单界面
  2. 初始化棋盘
  3. 打印棋盘
  4. 玩家下棋
  5. 判断输赢
  6. 电脑下棋
  7. 判断输赢
  8. 游戏结束

具体实现

1.进入菜单界面

玩家通过输入1或者0来选择进入对应的环节:输入“1”则进入游戏环节;输入“0”则退出游戏;如果输入其他数字,则会提示“选择错误”

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

void test()
{
	int input = 0;

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);

		switch (input)
		{
		case 1:
			//printf("三子棋\n");
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

}

运行结果:

2.初始化棋盘

棋盘格式设置为3*3的二维数组大小,元素类型为char,初始化为空格,即什么数据也不存储。行列采用宏定义的方式,是为了方便后期对棋盘的大小进行统一修改,同时也为了提高代码的可读性

#define ROW 3
#define COL 3

//初始化棋盘为全空格
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

3.打印棋盘

将棋盘进行拆分,第一行为: 字符 | 字符 | 字符 (注意字符前后的空格),第二行我们为:---|---|---。在打印数据时,根据for循环,打印三行数据,也就是三行字符 | 字符 | 字符 ,同时三行数据中穿插着两行---|---|---作为分隔。

//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//打印数据
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ",board[i][j]);//空格+字符+空格

			//打印"|",只打印前两列
			if(j<col-1)
				printf("|");
		}
		printf("\n");//换行

		//打印分割行,只打印前两行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");

				if (j < col- 1)
					printf("|");
			}
			printf("\n");//换行
		}
	}
}

运行结果:

4.玩家下棋

玩家根据提示输入行列坐标,比如1 1,同时使用’*’表示玩家落子。玩家在落子之前需要对坐标进行合法性的判断,即判断所输入的坐标是否在棋盘规定的范围内以及是否已被占用。玩家要选择在棋盘上空闲的位置进行落子,否则会提示出错。输入坐标合法之后要立即跳出循环,否则会陷入死循环。

//玩家下棋
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] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

运行结果:

5.电脑下棋

在玩家落子之后,电脑会随机生成行列坐标并在棋盘上空闲的地方进行相应的落子,'#’表示电脑落子。生成随机数之前需要基于当前时间设定“种子”的值srand((unsigned int)time(0))。在调用rand()函数之前,要先在主函数中调用srand函数。如果没有上述准备工作,那么种子的值就会默认为1,会生成相同的随机数序列。

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");

	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
}

运行结果:

6.判断输赢

在判断输赢时,我们约定:‘*’ 表示玩家赢;‘#’ 表示电脑赢;’ Q’ 表示平局;‘C’ 表示继续

玩家赢:行,列以及两条对角线有三个连续的‘*’则表示玩家赢

电脑赢:行,列以及两条对角线有三个连续的‘#’则表示电脑赢

平局:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘已满,则表示平局

继续:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘未满,则游戏继续

//判断平局
static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;//没满
			}
		}
	}

	return 1;//满了
}


//判断输赢
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][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];
	}

	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

运行结果

玩家赢:

 电脑赢:

平局:

完整代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<time.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 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.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"


//初始化棋盘为全空格
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}


//打印棋盘
//版本1.0
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		int j = 0;
//		for (j = 0; j < col; j++)
//		{
//			printf("%c",board[i][j]);//打印的是棋盘初始化中的空格
//		}
//		printf("\n");
//	}
//}

//版本2.0
//存在缺陷,行数可以改变,但是列数被固定死无法修改
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		//打印数据
//		printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);//存在缺陷,行数可以改变,但是列数被固定死无法修改
//
//		//打印分割行
//		if (i < row - 1)//最后一行的分割行不打印
//		{
//			printf("---|---|---\n");
//		}
//	}
//}

//版本3.0
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//打印数据
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ",board[i][j]);//空格+字符+空格

			//打印"|",只打印前两列
			if(j<col-1)
				printf("|");
		}
		printf("\n");//换行

		//打印分割行,只打印前两行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				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("请输入坐标:>");
		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] = '*';
				break;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}


//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");

	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
}


//判断平局
static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;//没满
			}
		}
	}

	return 1;//满了
}


//判断输赢
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][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];
	}

	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include"game.h"


//test.c:测试三子棋游戏的逻辑
//game.c:游戏的实现
//game.h:函数声明,符号的定义


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


void game()
{
	char ret = 0;

	//存放下棋的数据
	char board[ROW][COL] = { 0 };

	//初始化棋盘为全空格
	InitBoard(board, ROW, COL);

	//打印棋盘
	DisplayBoard(board, ROW, COL);

	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret=is_win(board,ROW,COL);
		if (ret != 'C')
		{
			break;
		}

		//电脑下棋
		computer_move(board, ROW, COL);//随机下棋,哪里空着,往哪里下
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}

	if (ret == '*')
	{
		printf("玩家赢了\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢了\n");
	}
	else
	{
		printf("平局\n");
	}

	//打印棋盘
	DisplayBoard(board, ROW, COL);
}


//什么情况,游戏就结束了呢?
//玩家赢:'*'
//电脑赢:'#'
//平局:'Q'
//继续:'C'
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);

		switch (input)
		{
		case 1:
			//printf("三子棋\n");
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

}


int main()
{
	test();
	return 0;
}


http://lihuaxi.xjx100.cn/news/1162717.html

相关文章

连接器信号完整性仿真教程 三

本文将讲解CST边界&#xff08;Boundary&#xff09;、背景&#xff08;Background&#xff09;的概念及背景材料与边件设置。边界与背景是CST仿真电磁的基础&#xff0c;有必要了解它&#xff0c;并掌握其设置方法与步骤。并以两个实例演示了具体设置的操作。 一 背景、边界与…

Go语言基础——内置函数

Go语言基础 目录 Go语言基础内置类型值类型&#xff1a;引用类型&#xff1a;(指针类型) 内置函数内置接口error 内置类型 值类型&#xff1a; boolint(32 or 64), int8, int16, int32, int64uint(32 or 64), uint8(byte), uint16, uint32, uint64float32, float64stringcompl…

Scala学习(六)---apply方法和单例对象

文章目录 1.Apply方法2.单例模式 1.Apply方法 Scala中实例化一个类&#xff0c;可以不用new &#xff0c; 其原因 其实就是 apply 方法 apply方法的调用约定&#xff1a; 用括号传递给类实例或者单例对象名一个或者多个对象的时候 scala会在对应的类或者对象中查找apply方法 且…

数值计算 - 常用函数值计算方法

秦九韶算法 什么是秦九韶算法&#xff1f; 可以把多项式改写为便于递推的形式&#xff1a; 泰勒展开式 什么是泰勒展开式&#xff1f; 泰勒公式&#xff0c;也称泰勒展开式。是用一个函数在某点的信息&#xff0c;描述其附近取值的公式。如果函数足够平滑&#xff0c;在已知函…

如何避免孩子独自在家偷偷使用电脑?

电脑为我们的生活带来了极大的便利&#xff0c;但是对于孩子来说&#xff0c;过早的接触网络很容易影响其健康的成长。家长在家的话&#xff0c;还可以监督孩子&#xff0c;但如果家长出门了&#xff0c;该如何避免孩子偷偷使用电脑呢&#xff1f;其实方法很简单&#xff0c;只…

Halcon中从两组点的对应关系生成仿射矩阵,及思考原理和代码实现

有几个算子有点类似&#xff0c;看了下区别 1.vector_to_rigid( : : Px, Py, Qx, Qy : HomMat2D) 2.vector_to_similarity( : : Px, Py, Qx, Qy : HomMat2D) 3.vector_to_aniso( : : Px, Py, Qx, Qy : HomMat2D) vector_to_rigid 这个是刚性变换&#xff0c; 只有旋转和平移…

你知道黑客是什么样?

1 前言 现代人的生活离不开互联网&#xff0c;提到互联网人们总会想到一个词&#xff0c;那就是黑客。对于黑客大家对他们的了解却只局限于电影、电视中他们“无处不在”的可怕和他们“翻手为云覆手为雨”的技术中。但真正的黑客到底是什么&#xff1f; 2 什么是黑客 笔挺的…

Ubuntu 18 设置时钟同步

Ubuntu 18自带了systemd-timesyncd服务来同步时钟&#xff0c;可以使用以下步骤来进行配置&#xff1a; 打开终端窗口&#xff0c;进入root用户权限&#xff1a;sudo su。 检查系统当前的时钟同步状态&#xff1a; timedatectl 如果输出类似下面的信息&#xff0c;表示NTP同步功…