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

news2024/12/26 0:31:44

目录

前言

一、游戏规则

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.coloradmin.cn/o/2048586.html

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

相关文章

Java语言程序设计——篇十三(4)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

下载中心: 使用异步方法生成文件任务+键集分页查询

文章目录 引言I 下载中心功能进度表设计异步处理文件生成案例II 键集分页查询提高查询效率解决分页查询出现数据重复或丢失案例III 工具线程池基于EasyExcel 生成 excel文件存储系统see also引言 需求: 根据查询条件导出数据,比如交易流水、设备安装资料。 流程设计:点击导…

牛客面经学习笔记(二)

锂离子电池的充电过程可以分为四个阶段&#xff1a;涓流充电&#xff08;低压预充&#xff09;、恒流充电、恒压充电以及充电终止。 阶段1&#xff1a;涓流充电——涓流充电用来先对完全放电的电池单元进行预充(恢复性充电)。在电池电压低于3V左右时采用涓流充电&#xff0c;涓…

Vue 生命周期详解含demo、面试常问问题案例

Vue 生命周期详解、面试常问问题案例 含 demo 文章目录 Vue 生命周期详解、面试常问问题案例 含 demo一、Vue 生命周期是什么二、Vue 中如何使用生命周期钩子1. **beforeCreate**2. **created**3. **beforeMount**4. **mounted**5. **beforeUpdate**6. **updated**7. **beforeD…

8.15笔记

一、mycat读写分离实现 1. 添加一个新的虚拟主机&#xff0c;设置ip为10.1.1.60,主机名为mycat.yuanyu.zhangmin.关闭防火墙 SELinux NetworkManager 2. 上传jdk和mycat安装包 3. 解压并且添加到指定的位置 [rootmycat ~]# ls anaconda-ks.cfg frp initserver.sh jdk1.8…

018集——递归函数和for循环对比(从1加到100实例) ——C#学习笔记

本例分别用递归函数和for循环&#xff0c;求出1到100的和&#xff1a; using System;namespace Mytest {class Mytest{static void Main(string[] args){Calculate Myc new Calculate();int YourNumber 100;int Mysum Myc.Xto1(YourNumber);Console.WriteLine("从1加到…

Element-03.组件-Pagination分页

一.常见组件-分页-属性 参数&#xff1a;background 说明&#xff1a;是否为分页按钮添加背景色 类型&#xff1a;boolean 有background即添加&#xff0c;没有则不添加 参数&#xff1a;layout 说明&#xff1a;组件布局&#xff0c;子组件名用逗号分隔 类型&#x…

86.小米相机修改拍照(尺寸,画幅,比例)的方法

目录 1.打开相机&#xff0c;拍照模式&#xff0c;上面有个箭头或三个点&#xff0c;点击 2.点击画幅 3.点击你想要的画幅即可。 想要修改手机照片的&#xff08;尺寸&#xff0c;画幅&#xff0c;比例&#xff09;时&#xff0c;总会去找分辨率&#xff0c;其实并不是&…

用后端实现一个简单的登录模块2 前端页面

该模块能做到的功能&#xff1a; 1阶&#xff1a;输入账号和密码&#xff0c;输入正确即可返回登录成功的信息&#xff0c;反之则登录失败 2阶&#xff1a;有简单的前端页面&#xff0c;有登录成功和失败的弹窗&#xff0c;还有登录成功的主页面 3阶&#xff1a;前端页面的注…

探索Python的中文转换魔法:zhconv库的神秘力量

文章目录 探索Python的中文转换魔法&#xff1a;zhconv库的神秘力量第一部分&#xff1a;背景介绍第二部分&#xff1a;库的概述第三部分&#xff1a;安装指南第四部分&#xff1a;函数使用示例第五部分&#xff1a;实际应用场景第六部分&#xff1a;常见问题与解决方案第七部分…

【Vue3】路由Query传参

【Vue3】路由Query传参 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子…

24/8/17算法笔记 策略梯度reinforce算法

import gym from matplotlib import pyplot as plt %matplotlib inline#创建环境 env gym.make(CartPole-v0) env.reset()#打印游戏 def show():plt.imshow(env.render(mode rgb_array))plt.show() show()定义网络模型 import torch #定义模型 model torch.nn.Sequential(t…

8月17日星期六今日早报简报微语报早读

8月17日星期六&#xff0c;农历七月十四&#xff0c;早报微语早读。 1、海关总署&#xff1a;接触过猴痘病例的人员入境时应主动申报&#xff1b; 2、284名运动员出征&#xff01;巴黎残奥会中国体育代表团成立&#xff1b; 3、四部门&#xff1a;继续执行对彩票一次中奖不超…

C语言 之 字符相关函数

文章目录 字符分类函数字符转换函数 本章内容主要讲的是c语言中的字符相关的一些函数的作用用法和使用 为了方便我们对字符的各种操作&#xff0c;C语⾔标准库中提供了⼀系列库函数&#xff0c;我们大概可以根据其功能分成两类函数 字符分类函数 C语言中有一系列函数是专门用…

【区块链+金融服务】基于 VoneBaaS 的区块链金融服务管理平台 | FISCO BCOS应用案例

在顺应数字经济发展、强化科技赋能金融的背景下&#xff0c;中央及地方政策层面鼓励“区块链 金融”应用示范项目&#xff0c; 在中小企业征信及风险管理、供应链金融等重点领域开展应用&#xff0c;探索建立区块链金融风险监控体系。同时&#xff0c;随着 科技金融服务逐步升…

学生阅读行为与图书预定平台的设计与实现(全网独一无二,24年最新定做)

目录&#xff1a; 前言&#xff1a; 具体功能如下&#xff1a; 1. 学生角色 2. 图书管理员 3. 图书采购人员 4. 系统管理员 技术栈介绍&#xff1a; 1. Springboot 2. MySQL 3. Vue 4. Java 系统详细界面&#xff1a; 为什么选择我&#xff1a; 前言&#xff1a; 博…

71、docker网络

一、docker网络&#xff1a; 1.1、默认模式&#xff1a; 桥接模式 部署好docker服务&#xff0c;启动之后&#xff0c;就会创建一个虚拟网桥&#xff0c;docker0&#xff0c;这是一个虚拟网络设备&#xff0c;类似于交换机。 [rootdocker1 ~]# ifconfig docker0: flags4163…

Linux:Linux环境基础开发工具使用

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一. Linux 软件包管理器 yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 二 Linux编辑器-vim使用 ​编辑 2.1 vim的基本概念 2.2 vim正常模式命令集…

安装postgresql和PGVector

1. 概述 研发有需要&#xff0c;要使用PGVector做向量。简单记录安装postgresql和PGVector过程。 2. 参考 postgresql官方下载连接 postgresql官方linux yum安装 PostgreSQL的安装、配置与使用指南 PostgreSQL向量数据插件–pgvector安装 3. 安装 3.1 只安装postgresql&am…

进程地址空间(一)

目录 1. 语言层面上的地址2. 引入新概念 ---- 地址空间的概念3. 进一步理解地址空间4. 为什么要有地址空间 在正式介绍进程地址空间之前&#xff0c;我们需要做一些铺垫&#xff0c;在父子进程同时运行时&#xff0c;从代码层面上的变量的地址&#xff0c;引入进程地址空间的概…