一、项目背景
此游戏是笔者大二第一学期时受好友之托,重拾c语言编写的小程序,这篇文章主要介绍Game中的游主体逻辑部分。程序主体多用if-else,while,for等关键字,适合c语言初学者阅读,若有疑问或bug或建议可评论指出。
二、编程环境
笔者用的是 Visual Studio 2019,调用库有:
#include <iostream>
#include <windows.h>
#include <conio.h>
三、规则介绍
四、程序主体
1、chess结构体
struct chess {
int player; // 1 A电脑 2 B人类 0 棋盘
int ani; // 8方块 7象(E) 6虎(T) 5狮(L) 4狼(W) 3狗(D) 2猫(C) 1鼠(M) 0空
}Board[Vol * Vol], PlayB;
PlayB使用户控制可以移动的方块棋子,Board是组成棋盘的各个棋子。
player的值代表这个棋子属于哪一方,ani的值代表这个棋子是空还是动物还是方块(方块的player为0)。
2、主要函数:
其中关于UI设计的六个函数分别有着隐藏光标、定位、绘制棋盘、绘制方框、绘制棋子、展示提示的作用;Init部分的三个函数重绘棋盘,让数据初始化,便于重新开始;Game部分的三个函数分别是游戏主体逻辑、比较棋子大小、游戏结束。
3、游戏主体逻辑
(1)结构
可以观察到,当数据初始化完成后,就进入了循环输入反馈的游戏主体中。而根据输入,有三种反馈,分别是方块棋子的移动、空格选中或取消棋子、召唤己方动物。在反馈结束后,根据f(判断棋子互动有无的标志)判断是否进行棋子互动。若达到结束条件,则结束游戏。
(2)小键盘移动
// 上下左右移动
if (ch_1 == -32) {
ch_2 = _getch();
switch (ch_2) {
case 72:y == 1 ? y = Vol : y--; break;
case 75:x == 1 ? x = Vol : x--; break;
case 80:y == Vol ? y = 1 : y++; break;
case 77:x == Vol ? x = 1 : x++; break;
default:break;}
在用_getch()(在头文件conio.h)获得上下左右键的键值时候,他们是双键值,会返回高八位和低八位的int型数值。经测试,返回的第一个数值为-32,第二个数值上72左75下80右77。这里采用的是碰壁返回另一边的碰壁逻辑。
(3)空格选中棋子
else if (ch_1 == ' ') {
if (PlayB.player == 0) {//手上无棋子,需拿起棋子
if (Board[(x - 1) * Vol + y - 1].player == 2) {
PlayB.ani = Board[(x - 1) * Vol + y - 1].ani;
PlayB.player = Board[(x - 1) * Vol + y - 1].player;
Board[(x - 1) * Vol + y - 1].player = Board[(x - 1) * Vol + y - 1].ani = 0;
Chess(x, y, PlayB);
tips = 4;
}
}
else if (PlayB.player == 2) {
if (Board[(x - 1) * Vol + y - 1].player == 2)
tips = 5;
else if (Board[(x - 1) * Vol + y - 1].player == 0) {
Board[(x - 1) * Vol + y - 1].ani = PlayB.ani;
Board[(x - 1) * Vol + y - 1].player = PlayB.player;
PlayB.ani = 8;
PlayB.player = 0;
Chess(x, y, PlayB);
tips = 6;
}
else if (Board[(x - 1) * Vol + y - 1].player == 1) {
f = PlayB.ani;
PlayB.ani = 8;
PlayB.player = 0;
}
}
}
首先判断手上是否有棋子,根据方块棋子的player属性判断其为方块还是己方动物,否则不响应。若为方块,则可以拿起棋子,当落点所属玩家为人类时,落点棋盘上的棋子属性变为空(皆0),手上方块属性变为己方动物属性。若为己方动物,则按下空格视为落子,若落子处为空,则直接落子,与提子为反逻辑;若落子处为敌方动物,则成功落子并进行棋子互动;若落子处为己方动物,则会提示无法落子。
(4)召唤棋子
// 召唤己方棋子
switch (ch_1) {
case 'e':case 'E':Allchess[6] = Allchess[6] > 0 ? Allchess[6] - 1 : -1; f = 7; break;
case 't':case 'T':Allchess[5] = Allchess[5] > 0 ? Allchess[5] - 1 : -1; f = 6; break;
case 'l':case 'L':Allchess[4] = Allchess[4] > 0 ? Allchess[4] - 1 : -1; f = 5; break;
case 'w':case 'W':Allchess[3] = Allchess[3] > 0 ? Allchess[3] - 1 : -1; f = 4; break;
case 'd':case 'D':Allchess[2] = Allchess[2] > 0 ? Allchess[2] - 1 : -1; f = 3; break;
case 'c':case 'C':Allchess[1] = Allchess[1] > 0 ? Allchess[1] - 1 : -1; f = 2; break;
case 'm':case 'M':Allchess[0] = Allchess[0] > 0 ? Allchess[0] - 1 : -1; f = 1; break;
case 'g':case 'G':end = 1; break;
default:break;
}
召唤棋子即召唤手中动物到棋盘上,因手中动物每种各两只,所以需要进行计数处理,并赋值f,进行棋子互动。其中按下G键游戏提前结束。
(5)棋子互动
if (f)
{
if (Allchess[f - 1] > -1) {//手上还有棋子吗?
if (Board[(x - 1) * Vol + y - 1].player == 1) {//该位置有敌方棋子
int flag = Compare(f, Board[(x - 1) * Vol + y - 1].ani);
if (flag == 2) { //两子皆亡
Achess[Board[(x - 1) * Vol + y - 1].ani - 1]--;
Bchess[f - 1]--;
Board[(x - 1) * Vol + y - 1].ani = Board[(x - 1) * Vol + y - 1].player = 0;
tips = 2;
sumA--; sumB--;
}
else if (flag == 1) {//人类获胜!
Achess[Board[(x - 1) * Vol + y - 1].ani - 1]--;
Board[(x - 1) * Vol + y - 1].ani = f;
Board[(x - 1) * Vol + y - 1].player = 2;
tips = 3;
sumA--;
}
else {//电脑获胜
Bchess[f - 1]--;
tips = 1;
sumB--;
}
}
else if (Board[(x - 1) * Vol + y - 1].player == 0) { //该地无棋子
Board[(x - 1) * Vol + y - 1].ani = f;
Board[(x - 1) * Vol + y - 1].player = 2;
tips = 6;
}
else if (Board[(x - 1) * Vol + y - 1].player == 2) { //该地有己方棋子
Allchess[f - 1]++;
tips = 5;
}
}
else
tips = 9;
f = 0;
Chess(x, y, Board[(x - 1) * Vol + y - 1]);
}
棋子互动首先判定手中是否有棋子,若无则f=0,跳出互动;
若手中有棋子,则根据落点棋子player属性互动。若落点棋子为空,则直接落子;若落点棋子为己方棋子,则因该位置已满,无法放入,棋子回到手中,计数器+1;若落点棋子为敌方棋子,则进行比较,有以下三种结果:被吃,吃,同归于尽。同时相应计数器-1。
4、cheat处理及颜色
if (cheat)
switch (board.ani) {
case 7:printf(RED"E " NONE); break;
case 6:printf(RED"T " NONE); break;
case 5:printf(RED"L " NONE); break;
case 4:printf(RED"W " NONE); break;
case 3:printf(RED"D " NONE); break;
case 2:printf(RED"C " NONE); break;
case 1:printf(RED"M " NONE); break;
default:break;
}
else
printf(RED"●" NONE);
若cheat为1,则敌方棋子为已知,否则为未知。(方便测试时用)
加上如下宏定义,可以输出不同颜色的字体:
#define RED "\033[0;32;31m" //字体红色
#define NONE "\033[m" //字体无色
五、结语
难度中规中矩,适合c语言初学者新手上路,该题另有复盘要求:
可作为读者的小练习。代码放在评论区。