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

KevinRay_ 2024-09-04 15:05:02 阅读 97

目录

前言

一、游戏规则

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.项目文件创建

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

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

2.头文件说明

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

<code>#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函数

建立棋盘使用memset函数初始化棋盘;do...while执行游戏运行;IsOver函数判断游戏是否结束;PlayerMove函数玩家走一步棋;Showboard 打印出棋盘;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 DRAW3

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方向 之和,其中任意一个出现相同的连续五个棋子,即游戏结束

<code>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 DRAW3

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)



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。