C语言经典小游戏之三子棋(超详解释+源码)

news2024/11/17 17:46:12

“纵有疾风来,人生不言弃,风乍起,合当奋意向此生。” 今天我们一起来学习一下三子棋小游戏用C语言怎么写出来?

三子棋小游戏

  • 1.游戏规则介绍
  • 2.游戏准备
  • 3.游戏的实现
    • 3.1生成菜单
    • 3.2游戏的具体实现
      • 3.2.1初始化棋盘
      • 3.2.2打印棋盘
      • 3.2.3玩家下棋(玩家下棋为O)
      • 3.2.4电脑下棋(电脑下棋为X)
      • 3.2.5判断输赢
      • 3.2.6棋盘的优化
  • 4.三子棋游戏完整的代码实现(详细源码)

1.游戏规则介绍

《三子棋》是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。
在这里插入图片描述

2.游戏准备

这里呢,我们需要准备三个源文件,分别是test.c game.c game.h(名称随意,尽量通俗易通一些比较好)可能有疑问了,这里为什么需要三个源文件呢?会不会显得很麻烦?在前面的学习中,我们也提到了未来在工程中代码比较多,函数一般放在.h文件中声明,在.c文件中实现。
分文件书写形式的优点:1.方便多人协作 2.保护代码

这里我们给大家介绍一下在三子棋这个游戏里面,每个文件是做什么的呢?
(1)头文件game.h,头文件里是用来存放函数的声明,#define常量的定义,库函数的引用的。
(2)源文件test.c,这个文件里面放的是游戏的测试逻辑。
(3)源文件game.c,这个文件里面放的是游戏的实现逻辑(函数实现)。

3.游戏的实现

3.1生成菜单

这里呢,我们通过switch语句给用户选择,当用户输入不同的数字,我们的程序就会给出不同的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void menu()//生成菜单的函数
{
	printf("******************************\n");
	printf("**********  1.play  **********\n");
	printf("**********  0.exit  **********\n");
	printf("******************************\n");
}
void game()//实现玩游戏的函数
{

}
int main()
{
	int input = 0;//用户选择
	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;
}

通过上面的代码,我们已经构建出了基本的框架:
在这里插入图片描述

3.2游戏的具体实现

我们发现,在上面的框架代码中缺少了游戏实现部分的代码,这里,我们在慢慢的一点点细化游戏过程。
在这里插入图片描述

首先,我们可以看到,我们需要先构造出一个3*3的棋盘。使得每个九个空位都处于空的状态。这里我们就想到了二维数组的概念,那我们一起来试试吧!

3.2.1初始化棋盘

//game.h
#pragma once
# define ROW 3
# define COL 3
//初始化棋盘的函数的声明
void InitBoard(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;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

//test.c
void game()
{
	//最开始的时候数组的内容全是空格
	char board[ROW][COL]; 
	InitBoard(board, ROW, COL);
}

在这里插入图片描述

3.2.2打印棋盘

初始化棋盘之后,我们需要将棋盘打印出来才能被用户看到,那么我们同样的可以利用for循环来完成这步操作。

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
void DisplayBoard(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++)
		{
			printf("%c", board[i][j]);
		}
		printf("\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

在这里插入图片描述
在这里插入图片描述
但是我们发现,这里留下了一大片空白,根本看不到棋盘,这是为什么呢?因为我们已经将棋盘初始化为空格了,显然我们是看不到的,而我们想要的是九宫格的样子,这里的话我们就可以用-和|来组成我们的棋盘了。

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
void DisplayBoard(char board[ROW][COL], int row, int col)//打印棋盘函数的实现
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | %c \n", board[i][0], board[i][1],board[i][2]);
		printf("---|---|---\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

但是,我们发现,这个棋盘还是存在不足,它的下面多了一条线,那么我们就需要调整一下for循环的循环语句了。
在这里插入图片描述
这里,我们给出修改后的代码及运行结果:

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");//再打印分隔行
				if (j < col - 1)
				{
					printf("|");
				}
			}	
		}
		printf("\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

在这里插入图片描述
这样的话,我们通过修改# define ROW 3 # define COL 3的值来改变棋盘的大小:
在这里插入图片描述

3.2.3玩家下棋(玩家下棋为O)

//game.h
//玩家下棋函数声明
void PlayerMove(char board[ROW][COL], int row, int col);

//game.c
//玩家下棋函数的实现
void PlayerMove(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] == ' ')//玩家并不会考虑到数组下标从0开始,这里我们需要考虑进去
			{
				board[x - 1][y - 1] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新下棋!\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

//test.c
while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
	}

在这里插入图片描述

3.2.4电脑下棋(电脑下棋为X)

//game.h
//电脑下棋函数声明
void ComputerMove(char board[ROW][COL], int row, int col);

//game.c
//电脑下棋函数的实现
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % row;//电脑随机产生坐标
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}

//test.c
while (1)
	{
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
	}

在这里插入图片描述
但是,我们这里目前只考虑让这个游戏玩起来,电脑下棋只是随机产生一些值,并没有任何的逻辑在里面。

3.2.5判断输赢

输赢情况有以下几种:
1.玩家赢-返回O
2.电脑赢-返回X
3.平局-返回Q
4.游戏继续-返回C

//game.h
//判断输赢函数声明
char Is_win(char board[ROW][COL], int row, int col);

//game.c
//判断棋盘是不是满了的函数
static int Is_full(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++)
		{
			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][0] != ' ')
		{
			return board[i][0];
		}
	}
	for (i = 0; i < col; i++)//列相等
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][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))
	{
		return 'Q';
	}
	return 'C';
}

//test.c
if (ret == 'O')
	{
		printf("玩家赢!\n");
	}
		
	else if (ret == 'X')
	{
		printf("电脑赢!\n");
	}
	else
	{
		printf("平局!\n");
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.6棋盘的优化

我们发现程序基本已经可以运行了,但是如果添加清屏的操作会不会更好呢?这里,我们可以完善一下。
这里我们就需要引入system("cls"),它就可以实现清屏的操作啦!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.三子棋游戏完整的代码实现(详细源码)

game.h文件

#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.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);
//判断输赢函数声明
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;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");//再打印分隔行
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}
//玩家下棋函数的实现
void PlayerMove(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] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新下棋!\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}
//电脑下棋函数的实现
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % row;//电脑随机产生坐标
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}
//判断棋盘是不是满了的函数
static int Is_full(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++)
		{
			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][0] != ' ')
		{
			return board[i][0];
		}
	}
	for (i = 0; i < col; i++)//列相等
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][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))
	{
		return 'Q';
	}
	return 'C';
}

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "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]; 
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋函数调用
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
		//判断输赢
		ret = Is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		system("cls");//清屏
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);//电脑下棋函数调用
		DisplayBoard(board, ROW, COL);//将电脑下棋结果打印出来
		ret = Is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}	
		system("cls");//清屏
		DisplayBoard(board, ROW, COL);
	}
	system("cls");//清屏
	DisplayBoard(board, ROW, COL);
	if (ret == 'O')
	{
		printf("玩家赢!\n");
	}
		
	else if (ret == 'X')
	{
		printf("电脑赢!\n");
	}
	else
	{
		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;
 }

以上就是关于三子棋游戏的全部代码啦!当然这个程序还是存在可优化的空间(比如电脑下棋的时候能不能更加智能一点,还有待思考),欢迎大家在评论区交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/837321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ChatGLM_02】LangChain知识库+Lora微调chatglm2-6b模型+提示词Prompt的使用原则

经验沉淀 1 知识库1.1 Langchain知识库的主要功能(1) 配置知识库(2) 文档数据测试(3) 知识库测试模式(4) 模型配置 2 微调2.1 微调模型的概念2.2 微调模型的方法和步骤(1) 基于ptuning v2 的微调(2) 基于lora的微调 3 提示词3.1 Prompts的定义及原则(1) Prompts是什么&#xf…

使用 RediSearch 在 Redis 中进行全文检索

原文链接&#xff1a; 使用 RediSearch 在 Redis 中进行全文检索 Redis 大家肯定都不陌生了&#xff0c;作为一种快速、高性能的键值存储数据库&#xff0c;广泛应用于缓存、队列、会话存储等方面。 然而&#xff0c;Redis 在原生状态下并不支持全文检索功能&#xff0c;这使…

服务注册中心consul的服务健康监控及告警

一、背景 consul既可以作为服务注册中心&#xff0c;也可以作为分布式配置中心。当它作为服务注册中心的时候&#xff0c;java微服务之间的调用&#xff0c;会定期查询服务的实例列表&#xff0c;并且实例的状态是健康可用。 如果发现被调用的服务&#xff0c;注册到consul的…

使用即时消息缩短 B2B 销售周期

在B2B销售中&#xff0c;时间就是一切。所以企业需要做的就是尽可能快地回复客户的咨询&#xff0c;否则会造成严重的客户流失。什么是 B2B 销售&#xff1f;它就是企业对企业销售&#xff0c;企业主要或仅向其他企业销售其产品或服务&#xff0c;这些产品或服务可以是从软件到…

符号执行之angr_ctf

前言 angr_ctf 之前一直弄环境没弄好&#xff0c;现在都解决了&#xff0c;终于可以全部过一遍了&#xff0c;仓库提供了三个部分的文件&#xff0c;一个是源码编译部分&#xff0c;一个是编译好的文件的集合最后是每题的题解部分分别在 值得注意的是编译好的文件都是elf类型…

Python批量查字典和爬取双语例句

最近&#xff0c;有网友反映&#xff0c;我的批量查字典工具换到其它的网站就不好用了。对此&#xff0c;我想说的是&#xff0c;互联网包罗万象&#xff0c;网站的各种设置也有所不同&#xff0c;并不是所有的在线字典都可以用Python爬取的。事实上&#xff0c;很多网站为了防…

不用学PS,这个在线UI设计工具也让你秒变设计大神!

在线 UI 设计工具的独特之处在于其卓越的协作和实时性能&#xff0c;能够支持原型图形界面和用户体验设计&#xff0c;并提供团队在线协作设计功能。与竞争对手如 Adobe XD 和 Sketch 相比&#xff0c;它具备让不同地点和专业人员实时查看和处理设计项目的能力&#xff0c;从而…

秋招算法备战第37天 | 738.单调递增的数字、968.监控二叉树、贪心算法总结

738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; 这个问题是关于找到一个小于或等于给定数字n的最大单调递增数字。 我们可以将数字n转换为字符数组&#xff0c;然后从左到右扫描&#xff0c;寻找第一个违反单调递增条件的位置。一旦找到这样的位置&#xff0c;…

俄罗斯方块

俄罗斯方块简单实现 使用 pygame 模块实现俄罗斯方块的简单实现&#xff0c;这里没有使用pygame 自带的碰撞检测&#xff0c;而是自定义的方法实现边界碰撞和方块间碰撞检测。 代码实现 import random import pygame import time # 初始化游戏 pygame.init()# 设置游戏窗口大…

libcurl开源的、跨平台的网络传输库,用于在程序中实现数据传输功能的编译

文章目录 前言1、libcurl关键特点和功能2、没有使用openssl以及libssh2编译libcurl的文件和使用openssl和libssh2编译3、libcurl网络库的下载4、libcurl网络库的编译4.1、直接使用cmake编译&#xff0c;不使用 OpenSSL 和 libssh2库编译的出来的libcurl库4.2、使用 OpenSSL 和 …

windows之关闭占用端口的进程

1. 查询端口占用进程&#xff0c;这里以8001为例 netstat -ano | findstr 8001 2.结束进程 taskkill -pid 37072 -f

解释器模式-自定义语言的实现

有时&#xff0c;我们希望输入一串字符串&#xff0c;然后计算机能够按照预先定义的文法规则来对这个字符串进行解释&#xff0c;从而实现相应的功能。 例如&#xff0c;我们想实现简单的加减法接收器&#xff0c;只需输入一个表达式&#xff0c;它就能计算出表达式结果。比如…

SpringBoot统一功能处理(拦截器)

1.用户登录权限校验 1.1自定义拦截器 写一个类去实现HandlerInterceptor接口表示当前类是一个拦截器,再重写HandlerInterceptor接口中的方法,preHandle为在方法执行前拦截,postHandle为方法执行中拦截,afterCompletion为方法执行中拦截.需要在什么时候拦截就重写什么方法 Co…

微信公众号自动登录方案

基于微信公众号登录 借助微信公众号来试实现社区登录。登录的时候展示的是一个二维码&#xff0c;但实际上的操作是借助这个展示的过程&#xff0c;和前端构建一个半长连接&#xff0c;当用户向公众号发送验证码之后&#xff0c;微信公众平台会将用户发送的消息转发给服务器&a…

Unity进阶--通过PhotonServer实现联网登录注册功能(服务器端)--PhotonServer(二)

文章目录 Unity进阶--通过PhotonServer实现联网登录注册功能(服务器端)--PhotonServer(二)服务器端大体结构图BLL层&#xff08;控制层&#xff09;DAL层&#xff08;数据控制层&#xff09;模型层DLC 服务器配置类 发送消息类 以及消息类 Unity进阶–通过PhotonServer实现联网…

Gartner发布《2023年全球RPA魔力象限》:90%RPA厂商,将提供生成式AI自动化

8月3日&#xff0c;全球著名咨询调查机构Gartner发布了《2023年全球RPA魔力象限》&#xff0c;通过产品能力、技术创新、市场影响力等维度&#xff0c;对全球16家卓越RPA厂商进行了深度评估。 弘玑Cyclone&#xff08;Cyclone Robotics&#xff09;、来也&#xff08;Laiye&am…

【蓝图】p47下车减速功能

p47下车减速功能 p47下车减速功能加速功能下车减速功能 p47下车减速功能 加速功能 上图是ue自带的加速功能&#xff0c;检测到按w时输入轴会传1给设置油门输入&#xff0c;就会加速 所以&#xff0c;减速也可以通过蓝图反方向制作 下车减速功能 打开Sedan蓝图类的上下车图表…

day51-Mybatis-Plus/代码生成器

1.Mybatis-Plus 定义&#xff1a;是一个Mybatis的增强工具&#xff0c;只在Mybatis基础上增强不做改变&#xff0c;简化开发&#xff0c;提升效率 2.MP实战 2.1 创建springboot工程&#xff0c;勾选web&#xff0c;引入依赖 <dependency> <groupId>mysql<…

人工智能可解释性分析导论(初稿)

目录 思维导图 1.黑箱所带来的问题 2.从应用面论述为什么要进行可解释性分析 2.1可解释性分析指什么 2.2可解释性分析结合人工智能应用实例 2.3 可解释性分析的脑回路&#xff08;以可视化为例如何&#xff09; 3.如何研究可解释性分析 3.1使用好解释的模型 3.2传统机器学…

antDv table组件滚动截图方法的实现

在开发中经常遇到table内容过多产生滚动的场景&#xff0c;正常情况下不产生滚动进行截图就很好实现&#xff0c;一旦产生滚动就会变得有点棘手。 下面分两种场景阐述解决的方法过程 场景一&#xff1a;右侧不固定列的情况 场景二&#xff1a;右侧固定列的情况 场景一 打开…