【C语言小项目】五子棋游戏

目录

前言

一、游戏规则

1.功能分析

2.玩法分析

3.胜负判定条件

二、游戏实现思路

三、代码实现与函数封装

1.项目文件创建

2.头文件说明

3.函数封装 

1)菜单实现

 2)进度条实现

 3)main函数实现

 4)Game函数

 5)ShowBoard函数实现

6)PlayerMove函数实现

7)ChessCount函数实现

 8)IsOver函数实现

四、源码分享

1.main.c

2.ProBar.h

 3.probar.c

4.game.h

5.game.c

总结


前言

五子棋,又称连珠棋,是一种双人对弈的棋类游戏。游戏目标是在一个棋盘上,通过在横、竖、斜线上依次放置棋子,使自己的五个棋子连成一线,即横线、竖线或斜线,且无被对手堵住的空位,从而获胜。

实现简单的五子棋游戏,需要有二维数组函数调用等知识。本项目代码量大致为两三百行。

一、游戏规则

1.功能分析

五子棋的功能比较简单,只有黑棋落子和白棋落子,有些五子棋还有悔棋功能、显示胜率、记录走的步数等功能,但是初学时可以只实现简单的落子功能即可。

2.玩法分析

由黑棋先走,鼠标左击即可落子,然后轮到白棋回合,如此循环直到游戏结束。

3.胜负判定条件

五子棋的棋盘通常为15×15的格子,双方轮流在空位上放置自己的棋子,先连成五子的一方获胜。如果棋盘填满但没有五子连成一线,游戏结束为平局。

二、游戏实现思路

  1. 使用坐标输入代替鼠标点击,坐标应该符合人们使用习惯从1开始;(【鼠标左击】功能)
  2. 若有人胜利提示胜利方为谁,并结束游戏;
  3. 若无人胜利,且棋盘未满,提示继续;
  4. 若棋盘已满,提示平局;
  5. 实现清屏功能,每次落完子之后刷新屏幕;
  6. 可以加入进度条,在游戏开始时展示。

三、代码实现与函数封装

1.项目文件创建

将源代码分为五个文件,两个头文件(.h),用于声明函数,分别声明进度条和游戏函数;

三个源文件(.c),两个用于写函数体,实现进度条和游戏主体,一个为主函数,调用函数。

2.头文件说明

1.为了防止在项目中多次申明同一个头文件,需要加入防重复判断语句

#pragma once

2.头文件中可以直接引用需要的标准库,然后在源文件中只需要引用自己写的头文件即可;

#include "game.h"
#include "ProBar.h"

3.函数封装 

棋盘设置为15×15,且可更改方便之后的更改。

1)菜单实现

void Menu()
{
	printf("#########################\n");
	printf("### 1.Play     0.Exit ###\n");
	printf("#########################\n");
	printf("Please Select:> ");
}

 2)进度条实现

'\r'使光标每次回到开头的位置,使用fflush函数刷新输出界面,并使用usleep函数进行延迟,达到很好的效果。

#define NUM 100

void  process_bar()
{
	char bar[NUM+1];
	memset(bar, '\0', sizeof(bar));
	
	const char* lable = "|/-\\";
	int i = 0;
	while(i <= NUM)
	{
		printf("Load...[%-100s][%-3d%%][%c]\r", bar, i, lable[i%4]);
		fflush(stdout);
		bar[i++] = '#';
		usleep(10000);
	}
	printf("\n");
}

 3)main函数实现

使用quit变量来控制循环的退出;

Game函数实现游戏的主体。

int main()
{
	int quit = 0;

	int select = 0;
	while(!quit)
	{
						
		Menu();
		scanf("%d", &select);
		switch(select)
		{
			case 1:
				process_bar();
				Game();
				break;
			case 0:
				quit = 1;
				printf("Exit Success!\n");
				break;
			defualt :
				printf("Enter Error, Try Again!\n");
				break;
		}
	}
	

	return 0;
}

 4)Game函数

  1. 建立棋盘
  2. 使用memset函数初始化棋盘;
  3. do...while执行游戏运行;
  4. IsOver函数判断游戏是否结束;
  5. PlayerMove函数玩家走一步棋;
  6. Showboard 打印出棋盘;
  7. switch...case 显示出游戏结果。
#define ROW 20
#define COL 20

#define PLAYER1 1
#define PLAYER2 2

#define NEXT 0
#define PLAYER1_WIN 1
#define PLAYER2_WIN 2
#define DRAW		3


void Game()
{
	int board[ROW][COL];
	memset(board, '\0', sizeof(board));
	int result = NEXT;
	do
	{
		ShowBoard(board, ROW, COL);
		PlayerMove(board, ROW, COL, PLAYER1);
		result = IsOver(board, ROW, COL);
		if(NEXT != result)
		{
			break;
		}
		ShowBoard(board, ROW, COL);
		PlayerMove(board, ROW, COL, PLAYER2);

		result = IsOver(board, ROW, COL);
		if(NEXT != result)
		{
			break;
		}
	}while(1);
	//p1 win , p2 win, draw
	ShowBoard(board, ROW, COL);
	switch(result)
	{
		case PLAYER1_WIN:
			printf("congratulate Player1, you win!\n");
			break;
		case PLAYER2_WIN:
			printf("congratulate player2, you win!\n");
			break;
		case DRAW:
			printf("draw!\n");
			break;
		default:

			break;
	}
}

 5)ShowBoard函数实现

刷新屏幕

printf("\033c");

void ShowBoard(int board[ROW][COL], int row, int col)
{
	//clear screen
	//printf("\e[1;1H\e[2J")"]]");

	//printf("  ");

	printf("\033c");
	printf("\n\n  ");
	for(int i=0; i<col; i++)
	{
		printf("%3d", i+1);
	}
	printf("\n");

	for(int i=0; i<row; i++)
	{
		printf("%2d ", i+1);
		for(int j=0; j<col; j++)
		{
			if(board[i][j] == 0)
			{
				printf(" . ");
			}
			else if(board[i][j] == PLAYER1)
			{
				printf(" x ");
			}
			else
			{
				printf(" o ");
			}
		}
		printf("\n");
	}
}

6)PlayerMove函数实现

int x = 0;

int y = 0;

设置为全局变量,由用户输入,从1开始

int x = 0;
int y = 0;

void PlayerMove(int board[ROW][COL], int row, int col, int player)
{
	while(1)
	{
		printf("\nPlayer[%d] Please Enter Your Pos:>", player);
		scanf("%d %d", &x, &y);
        //判断合法坐标
		if(x<1 || x> row || y<1 || y>col)
		{
			printf("Pos is not right!\n");
			continue;
		}
		else if(board[x-1][y-1] != 0)
		{
			printf("Pos is occpuied!\n");
			continue;
		}
		else{
			board[x-1][y-1] = player;//谁落子,就放置谁的数据

			break;
		}

	}
}

7)ChessCount函数实现

要判断是否继续下棋需要判断是否已经五子连珠,就需要数下的这个子周围有没有出现五子连珠,由于每次下棋都会判断一次,所以不会出现漏判的情况。

enum Dir
{
	LEFT,
	RIGHT,
	UP,
	DOWN,
	LEFT_UP,
	LEFT_DOWN,
	RIGHT_UP,
	RIGHT_DOWN
};

int ChessCount(int board[ROW][COL], int row, int col, enum Dir d)
{
	int _x = x-1;
	int _y = y-1;
	
	int count = 0;

	while(1)
	{
		switch(d)
		{
			case LEFT:
				_y--;
				break;
			case RIGHT:
				_y++;
				break;
			case UP:
				_x--;
				break;
			case DOWN:
				_x++;
				break;
			case LEFT_UP:
				_x--;
				_y--;
				break;
			case LEFT_DOWN:
				_x++;
				_y--;
				break;
			case RIGHT_UP:
				_x--, _y++;
				break;
			case RIGHT_DOWN:
				_x++, _y++;
				break;
			default:
				//Do nothing
				break;

		}
		if(_x<0 || _x>row-1 || _y<0 || _y > col-1)
		{
			break;
		}

		if(board[x-1][y-1] == board[_x][_y])
		{
			count++;
		}
		else
		{
			break;
		}
	}
	return count;
}

 8)IsOver函数实现

任何落子位置都有八个方向,所以判定五子连珠,本质是判定1,5方向之和,2,6方向之和,3,7方向之和,4,8方向 之和,其中任意一个出现相同的连续五个棋子,即游戏结束

int IsOver(int board[ROW][COL], int row, int col)
{
    //注意,每次统计的时候,都没有统计当前节点,需要单独+1
	int count1 = ChessCount(board, row, col, LEFT) +
		ChessCount(board, row, col, RIGHT) + 1;
	int count2 = ChessCount(board, row, col, UP) +
		ChessCount(board, row, col, DOWN) + 1;
	int count3 = ChessCount(board, row, col, LEFT_UP) +
		ChessCount(board, row, col, RIGHT_DOWN) + 1;
	int count4 = ChessCount(board, row, col, LEFT_DOWN) +
		ChessCount(board, row, col, RIGHT_UP) + 1;
	
	if(count1 >= 5 || count2>=5 || count3>=5 || count4>=5)
	{
        //谁赢返回谁
		//return board[x-1][y-1];
		if(board[x-1][y-1] == PLAYER1)
		{
			return PLAYER1_WIN;
		}
		else
		{
			return PLAYER2_WIN;
		}
	}

	for(int i=0; i<row; i++)
	{
		for(int j=0; j<col; j++)
		{
			if(board[i][j] == 0)
			{
                //棋盘未满,返回继续
				return NEXT;
			}
		}
	}
    //棋盘已满且没人赢,返回平局
	return DRAW;
}

四、源码分享

1.main.c

#include "game.h"
#include "ProBar.h"

int main()
{
	int quit = 0;

	int select = 0;
	while(!quit)
	{
						
		Menu();
		scanf("%d", &select);
		switch(select)
		{
			case 1:
				process_bar();
				Game();
				break;
			case 0:
				quit = 1;
				printf("Exit Success!\n");
				break;
			defualt :
				printf("Enter Error, Try Again!\n");
				break;
		}
	}
	

	return 0;
}

2.ProBar.h

#pragma once 

#include <stdio.h>
#include <unistd.h>
#include <string.h>


#define NUM 100

void process_bar();

 3.probar.c

#include "ProBar.h"

void  process_bar()
{
	char bar[NUM+1];
	memset(bar, '\0', sizeof(bar));
	
	const char* lable = "|/-\\";
	int i = 0;
	while(i <= NUM)
	{
		printf("Load...[%-100s][%-3d%%][%c]\r", bar, i, lable[i%4]);
		fflush(stdout);
		bar[i++] = '#';
		usleep(10000);
	}
	printf("\n");
}

4.game.h

#include <string.h>
#include <stdlib.h>

#define ROW 20
#define COL 20

#define PLAYER1 1
#define PLAYER2 2

#define NEXT 0
#define PLAYER1_WIN 1
#define PLAYER2_WIN 2
#define DRAW		3

enum Dir
{
	LEFT,
	RIGHT,
	UP,
	DOWN,
	LEFT_UP,
	LEFT_DOWN,
	RIGHT_UP,
	RIGHT_DOWN
};

void Menu();
void Game();

int IsOver(int board[ROW][COL], int row, int col);
void ShowBoard(int board[ROW][COL], int row, int col);
int ChessCount(int board[ROW][COL], int row, int col, enum Dir d);
void Playermove(int board[ROW][COL], int row, int col, int player);

5.game.c

#include "game.h"
#include "ProBar.h"
int x = 0;
int y = 0;

void Menu()
{
	printf("#########################\n");
	printf("### 1.Play     0.Exit ###\n");
	printf("#########################\n");
	printf("Please Select:> ");
}

//four possbilities:
//NEXT: continue
//1: 1 win
//2: 2 win
//3: draw
int IsOver(int board[ROW][COL], int row, int col)
{
	//import && hard
	//wu zi lian zhu
	int count1 = ChessCount(board, row, col, LEFT) +
		ChessCount(board, row, col, RIGHT) + 1;
	int count2 = ChessCount(board, row, col, UP) +
		ChessCount(board, row, col, DOWN) + 1;
	int count3 = ChessCount(board, row, col, LEFT_UP) +
		ChessCount(board, row, col, RIGHT_DOWN) + 1;
	int count4 = ChessCount(board, row, col, LEFT_DOWN) +
		ChessCount(board, row, col, RIGHT_UP) + 1;
	
	if(count1 >= 5 || count2>=5 || count3>=5 || count4>=5)
	{
		//return board[x-1][y-1];
		if(board[x-1][y-1] == PLAYER1)
		{
			return PLAYER1_WIN;
		}
		else
		{
			return PLAYER2_WIN;
		}
	}

	for(int i=0; i<row; i++)
	{
		for(int j=0; j<col; j++)
		{
			if(board[i][j] == 0)
			{
				return NEXT;
			}
		}
	}
	return DRAW;
}

int ChessCount(int board[ROW][COL], int row, int col, enum Dir d)
{
	int _x = x-1;
	int _y = y-1;
	
	int count = 0;

	while(1)
	{
		switch(d)
		{
			case LEFT:
				_y--;
				break;
			case RIGHT:
				_y++;
				break;
			case UP:
				_x--;
				break;
			case DOWN:
				_x++;
				break;
			case LEFT_UP:
				_x--;
				_y--;
				break;
			case LEFT_DOWN:
				_x++;
				_y--;
				break;
			case RIGHT_UP:
				_x--, _y++;
				break;
			case RIGHT_DOWN:
				_x++, _y++;
				break;
			default:
				//Do nothing
				break;

		}
		if(_x<0 || _x>row-1 || _y<0 || _y > col-1)
		{
			break;
		}

		if(board[x-1][y-1] == board[_x][_y])
		{
			count++;
		}
		else
		{
			break;
		}
	}
	return count;
}

void ShowBoard(int board[ROW][COL], int row, int col)
{
	//clear screen
	//printf("\e[1;1H\e[2J")"]]");

	//printf("  ");

	printf("\033c");
	printf("\n\n  ");
	for(int i=0; i<col; i++)
	{
		printf("%3d", i+1);
	}
	printf("\n");

	for(int i=0; i<row; i++)
	{
		printf("%2d ", i+1);
		for(int j=0; j<col; j++)
		{
			if(board[i][j] == 0)
			{
				printf(" . ");
			}
			else if(board[i][j] == PLAYER1)
			{
				printf(" x ");
			}
			else
			{
				printf(" o ");
			}
		}
		printf("\n");
	}
}

void PlayerMove(int board[ROW][COL], int row, int col, int player)
{
	while(1)
	{
		printf("\nPlayer[%d] Please Enter Your Pos:>", player);
		scanf("%d %d", &x, &y);
		if(x<1 || x> row || y<1 || y>col)
		{
			printf("Pos is not right!\n");
			continue;
		}
		else if(board[x-1][y-1] != 0)
		{
			printf("Pos is occpuied!\n");
			continue;
		}
		else{
			board[x-1][y-1] = player;
			break;
		}

	}
}

void Game()
{
	int board[ROW][COL];
	memset(board, '\0', sizeof(board));
	int result = NEXT;
	do
	{
		ShowBoard(board, ROW, COL);
		PlayerMove(board, ROW, COL, PLAYER1);
		result = IsOver(board, ROW, COL);
		if(NEXT != result)
		{
			break;
		}
		ShowBoard(board, ROW, COL);
		PlayerMove(board, ROW, COL, PLAYER2);

		result = IsOver(board, ROW, COL);
		if(NEXT != result)
		{
			break;
		}
	}while(1);
	//p1 win , p2 win, draw
	ShowBoard(board, ROW, COL);
	switch(result)
	{
		case PLAYER1_WIN:
			printf("congratulate Player1, you win!\n");
			break;
		case PLAYER2_WIN:
			printf("congratulate player2, you win!\n");
			break;
		case DRAW:
			printf("draw!\n");
			break;
		default:

			break;
	}
}

总结

实际上这个版本还是一个非常简易的版本,在之后学习到别的模块之后可以对这个项目再进行改进,比如可以尝试以下功能:

  • 人机对战
  • 功能扩展:颜色提示,步数记录,先手随机交换等
  • 网络版本

码云Gitee项目链接:GoBangGame · Kevin Ray/LinuxPractice - 码云 - 开源中国 (gitee.com)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【系统架构设计】软件架构设计(2)

【系统架构设计】软件架构设计&#xff08;1&#xff09; 软件架构概述架构需求与软件质量属性软件架构风格层次系统架构风格面向服务的架构SOA概述微服务微服务和SOA差异 软件架构概述 架构需求与软件质量属性 软件架构风格 层次系统架构风格 面向服务的架构 SOA概述 面…

C语言手撕实战代码_循环单链表和循环双链表

C语言手撕实战代码_循环单链表和循环双链表 循环单链表习题1.建立带头结点的循环链表2.设计一个算法&#xff0c;将一个带有头结点的循环单链表中所有结点的链接方向逆转3.设计一个算法,将一个循环单链表左移k个结点4.设计一个算法将循环单链表中的结点p的直接前驱删除5.设计算…

游泳耳机品牌前十名哪个牌子好?如何选高配游泳耳机不花冤枉钱?

在快节奏的现代生活中&#xff0c;音乐已成为许多人放松和充电的重要方式之一。无论是晨跑、通勤还是健身&#xff0c;音乐总能陪伴我们度过每一个瞬间。而对于游泳爱好者来说&#xff0c;能够在水中享受音乐&#xff0c;更是将这一运动提升到了一个新的层次。然而&#xff0c;…

uniapp/uniapp x总结

uni-app组成和跨端原理 上图所诉 App的渲染引擎&#xff1a;同时提供了2套渲染引擎&#xff0c;.vue页面文件由webview渲染&#xff0c;原理与小程序相同&#xff1b;.nvue页面文件由原生渲染&#xff0c;原理与react native相同。开发者可以根据需要自主选择渲染引擎。 uniapp…

【unity小技巧】获取免费开源的人物模型,并为obj fbx人物模型绑定骨骼、动画——mixamo的使用介绍

文章目录 前言地址上传自己的3D角色下载单动画下载动作包角色模型导入Unity动画导入unity设置动画骨骼动画骨骼不配的问题参考完结 前言 其实前面我已经推荐了几种获取人物模型的方法&#xff1a; 1、【unity小技巧】下载原神模型&#xff0c;在Blender中PMX模型转FBX模型&…

多商户商品下单限购问题修复

问题: 当商品设置限购为 1 时,够买数量未超过限购,但是还是提示超出限购数量 修复方法: 修改代码路径: app\common\repositories\store\order\StoreOrderCreateRepository.php 修改代码一: i s p a y s a r r a y u n i q u e ( a r r a y c o l u m n ( is_pays array_un…

Python 设置Excel工作表页边距、纸张大小/方向、打印区域、缩放比例

在使用Excel进行数据分析或报告制作时&#xff0c;页面设置是确保最终输出效果专业、美观的关键步骤。合理的页面设置不仅能够优化打印效果&#xff0c;还能提升数据的可读性。本文将详细介绍如何使用Python操作Excel中的各项页面设置功能。 目录 Python 设置Excel工作表页边…

AutosarMCAL开发——基于EB FEE驱动

这目录 1. FEE原理2.EB配置以及接口应用3.总结 1. FEE原理 在Fls解析文章中介绍了Flash与EEPROM储存器的区别&#xff0c;本文将介绍FEE具体实现原理。 FEE模块&#xff0c;全称Flash EEPROM Emulation Module&#xff0c;旨在使用Flash模拟EEPROM以增加使用寿命。 术语解释 p…

CTFHUB | web进阶 | JSON Web Token | 无签名

一些JWT库也支持none算法&#xff0c;即不使用签名算法。当alg字段为空时&#xff0c;后端将不执行签名验证 开启题目 账号密码随便输&#xff0c;登录之后显示只有 admin 可以获得 flag 在此页面抓包发到 repeater&#xff0c;这里我们需要用到一个 Burp 插件&#xff0c;按图…

瑞吉外卖-登录时报错:接口404异常

一、错误描述 出现“系统接口404异常”的弹窗&#xff0c;同时一直显示登录中&#xff0c;而无法跳转到后台页面。 二、解决方法 1. 检查浏览器的网址 确保为localhost:8080/backend/page/login/login.html&#xff0c;而不是idea自动生成的&#xff0c;修改过来即可。 2.确…

Unity XR Interaction Toolkit 通过两个手柄控制物体放大缩小

1&#xff1a;给物体添加 XR General Grab Transformer 脚本 2&#xff1a;XR Grab Interactable 的 select mode 选择 Multiple

SpringIoCDI

前言&#x1f440;~ 上一章我们介绍了Spring MVC&#xff0c;今天介绍Spring核心功能之一IoC Spring到底是什么&#xff1f; Spring IoC 什么是 IoC 容器&#xff1f; IoC 介绍 DI 介绍 IoC详解 获取Bean对象的其他方式 Bean的存储 方法注解 Bean 扫描路径 DI详解 …

拼图游戏02

文章目录 概要整体架构流程代码过程小结 概要 现在需要将图片添加界面中 关键点在于它如何动态地根据游戏状态更新用户界面。它使用了Swing的布局管理器来定位组件&#xff0c;并且通过ImageIcon和JLabel来显示图像。注意&#xff0c;路径字符串中的反斜杠在Java中是转义字符…

选择排序(直接选择排序和堆排序)

一、直接选择排序 1.基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完。 2.动图展示 3.思路讲解 ①在元素集合array[i]—array[n-1]中选择关键码最大&…

以简单的例子从头开始建spring boot web多模块项目(一)

目的&#xff1a;从头梳理&#xff0c;如何手工从头建立多模块项目。 步骤&#xff1a; 1、建立maven项目,类型&#xff1a;maven Archetype&#xff0c;Name:ParentDemo 选择JDK版本&#xff0c;Archetype&#xff1a;org.apache.maven.archetypes:maven-archetype-quickstart…

网络UDP报文详细解析

目录 一、简介二、详细介绍三、其他相关链接1、TCP报文段的详细图总结2、TCP三次握手和四次挥手详解3、socket通信原理及相关函数详细总结4、网络包IP首部详细解析 一、简介 本文主要介绍UDP报文格式。 二、详细介绍 UDP是一种无连接、不可靠的用户数据报协议&#xff0c;其…

《图解设计模式》笔记(四)分开考虑

九、Bridge模式&#xff1a;将类的功能层次结构与实现层次结构分离 类的两个层次结构和作用 类的功能层次结构&#xff1a;希望增加新功能时 父类有基本功能&#xff0c;在子类中增加新功能 Something父类 …├─SomethingGood子类 想要再增加新功能 Something父类 …├─So…

Windows All download

前言 微软家族产品下载HEU_KMS_Activator download Windows PC desktop download 微软官网all 地址&#xff0c;地址1国内镜像地址&#xff0c;地址1 Windows 常规使用&#xff0c;运维&#xff0c;部署csdn 专栏 &#xff0c;付费专栏 参考 版本微软官网Windows 7,8,10,…

hyperf 协程作用和相关的方法

什么是协程 协程是一种轻量级的线程&#xff0c;由用户代码来调度和管理&#xff0c;而不是由操作系统内核来进行调度&#xff0c;也就是在用户态进行 判断当前是否处于协程环境内 在一些情况下我们希望判断一些当前是否运行于协程环境内&#xff0c; 对于一些兼容协程环境与…

使用html-docx-js + fileSaver实现前端导出word

因为html-docx-js是16年的老库了&#xff0c;它代码里面用到的with语法现在严格模式不允许&#xff0c;用npm直接引入会报错&#xff0c;所以我们需要用其它方式引入 首先要将html-docx-js的代码放到项目中 html-docx-js/dist/html-docx.js at master evidenceprime/html-do…