第七步
.
判定五子连珠然后判定游戏结果
3.联动模块组合封装:
完整代码:
(1)game.c
#include "game.h"
/*说明:
1.定义全局变量,保证代码逻辑尽可能简化,传参不那么复杂
2.代表玩家输入位置的最近的位置下标
3.不是说一定要定义成全局变量,而是为了减少传参等,降低复杂度*/
int x = 0;
int y = 0;
//按照x,y作为起点,按照特定的方向,求连续相对的最大格式
int ChessCount(int board[][COL], int row, int col, enum Dir d)
{
int _x = x - 1; //棋盘行标从1开始
int _y = y - 1; //棋盘列标从1开始
//统计以当前棋子为起始位置8个方向的棋子数
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;
}
/*说明:IsOver的返回值有4种情况
1.NEXT:表明要继续
2.PLAYER1_WIN: 玩家1获胜
3.PLAYER2_WIN:玩家2获胜
4.DRAW: 平局*/
int IsOver(int board[][COL], int row, int col)
{
/*棋子统计逻辑
1.以落子位置作为起点,进行8个方向的相同玩家方棋子统计
落子位置下标与方向统计有很强的关联性
2.当玩家2落子,说明玩家1没有赢,因此需要先走一步就先判断一次,走到当前才有可能赢
3.+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) {
//有五子连珠,则一定有人赢
//x, y保存的是玩家最近一次落子
if (board[x - 1][y - 1] == PLAYER1) {
return PLAYER1_WIN;
}
else {
return PLAYER2_WIN;
}
//这个if-else语句可换成 return board[x-1][y-1];
}
int i = 0;
for (; i < row; i++) {
int j = 0;
for (; j < col; j++) {
if (board[i][j] == 0) {
return NEXT;
}
}
}
return DRAW;
}
void ShowBoard(int board[][COL], int row, int col)
{
//C语言清屏,减少每次棋盘打印,实现原地刷新效果
printf("\e[1;1H\e[2J"); //Linux环境下,C语言实现清屏
printf("\n");
//将数组内容,进行可视化
printf(" "); //优化棋盘使其美观
int i = 1;
for (; i <= col; i++) {
printf("%3d", i);
}
printf("\n");
for (i = 0; i < row; i++) {
int j = 0;
printf("%2d ", i + 1);
for (; j < col; j++) {
if (board[i][j] == 0) {
printf(" . ");
}
else if (board[i][j] == PLAYER1) {
printf("● ");
}
else {
printf("○ ");
}
}
printf("\n");
}
}
void PlayerMove(int board[][COL], int row, int col, int who)
{
//判断玩家的落子位置:合法性、去重
while (1) {
printf("玩家[%d] 请输入你的落子坐标: ", who);
scanf("%d %d", &x, &y);
//判断输入坐标的合法性,行列
if (x < 1 || x > row || y < 1 || y > col) {
printf("位置错误!\n");
continue;
}
//在数组当当中的下标值
else if (board[x - 1][y - 1] != 0) {
printf("此位置已被占用!\n");
continue;
}
else {
//合法性,去重
board[x - 1][y - 1] = who;
break;
}
}
}
void Game()
{
int board[ROW][COL]; //在函数中定义数组,其实是一个随机数,需要清空以确定坐标数
/*1.采用ROW,COL型的二维数组,进行游戏信息的保存
2.全部清空有许多种做法:采用双循环,采用Init函数等
3.注:memset按照字节为单位操作,menset内存设置
4.使用memset清空,默认棋盘数据都是0*/
memset(board, 0, sizeof(board));
int result = NEXT;
do {
ShowBoard(board, ROW, COL); //显示棋盘
PlayerMove(board, ROW, COL, PLAYER1);//玩家1先走,也可以随机选择先走——“先手”
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);
//Player1 win, Player2 win, Draw
switch (result) {
case PLAYER1_WIN:
printf("恭喜玩家1获胜!\n");
break;
case PLAYER2_WIN:
printf("恭喜玩家2获胜!\n");
break;
case DRAW:
printf("游戏平局,和气生财!\n");
break;
default:
//这种情况不会出现,do nothing!
break;
}
//ShowBoard(board, ROW, COL);
}
(2)main.c
#include "game.h"
//游戏菜单
void Menu()
{
printf("############################\n");
printf("## 1. 开始 0. 退出 ##\n");
printf("############################\n");
printf(" ————By 作者:新晓·故知\n");
printf("请选择:");
}
int main()
{
int quit = 0; //用来判断游戏是否退出
int select = 0;
//使得游戏可以多次进行
while (!quit) {
Menu(); //游戏菜单
scanf("%d", &select);
switch (select) { //根据用户选择,执行合适的游戏逻辑代码
case 1:
Game();
break;
case 0:
quit = 1;
printf("退出游戏,再见!\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
}
return 0;
}
(3)game.h
#pragma once
#include <stdio.h>
#include <string.h>
//增强代码可维护性
#define ROW 10
#define COL 10
#define PLAYER1 1 //玩家1编号,默认棋盘数据是0,玩家1落子,该位置被改成1
#define PLAYER2 2 //玩家2编号,默认棋盘数据是0,玩家2落子,该位置被改成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(); //游戏功能声明
(4)Makefile
game:game.c main.c ProcBar.c
gcc $^ -o $@
.PHONY:clean
clean:
rm -f game