更新内容
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#ifdef _WIN32
#include <conio.h>
#include <windows.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#endif
#define ITEM_MAG 0
#define ITEM_KNIFE 1
#define ITEM_CUFF 2
#define ITEM_BEER 3
#define ITEM_CIG 4
#define ITEM_ADREN 5
#define ITEM_INVERT 6
#define ITEM_PHONE 7
#define ITEM_PILL 8
#define NITEM 9
#define MAXSHELL 16
#define MAXLOG 2000
#define MAXSLOT 9
#define MODE_CLASSIC 1
#define MODE_HEADSTART 2
#define MODE_NOITEM 3
#define MODE_INFINITE 4
#define DIFF_EASY 0
#define DIFF_NORMAL 1
#define DIFF_HARD 2
int g_auto = 0;
int g_noPause = 0;
int g_showShell = 1;
int g_showRemain = 1;
int g_autoCertain = 1;
int g_resume = 0;
int g_pillLuck = 0;
static char g_lastMsg[96];
#define ACT_PLAYER 0
#define ACT_DEALER 1
#define ACT_SYS 2
struct LogEntry {
int round;
int actor;
char act[16];
char detail[96];
int isLive;
int dmg;
};
struct Game {
char magic[8];
int version;
char playerName[24];
char matchTime[24];
int mode;
int difficulty;
int roundNum;
int hp[2];
int maxHp[2];
int shells[MAXSHELL];
int nShells;
int pos;
int itemCnt[2][NITEM];
int knifeReady;
int cuffed[2];
int playerTurn;
int revealNext;
int phoneN;
int phonePosArr[MAXSHELL];
int phoneValArr[MAXSHELL];
int godMode;
int blanksSelfHit;
int over;
int winner;
int score;
int stage;
int stageWins[2];
LogEntry log[MAXLOG];
int logLen;
int liveCnt;
int blankCnt;
};
static int my_getch();
static void setColor(int c) {
#ifdef _WIN32
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(h, (WORD)c);
#else
(void)c;
#endif
}
static void cls() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
static void pause1() {
if (g_noPause) return;
printf("\n 按任意键继续...");
fflush(stdout);
#ifdef _WIN32
_getch();
#else
my_getch();
#endif
printf("\n");
}
static int readNum() {
int val = 0, got = 0;
while (1) {
int ch = my_getch();
if (ch == '\r' || ch == '\n') break;
if (ch >= '0' && ch <= '9') {
val = val * 10 + (ch - '0');
got = 1;
printf("%c", ch);
fflush(stdout);
} else break;
}
printf("\n");
return got ? val : -1;
}
static void printLastMsg(const char* who) {
if (g_auto) return;
if (g_lastMsg[0]) {
setColor(14);
printf(" >> %s: %s\n", who, g_lastMsg);
setColor(7);
pause1();
}
}
static int my_getch() {
#ifdef _WIN32
return _getch();
#else
struct termios oldt, newt;
int ch;
tcgetattr(0, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(0, TCSANOW, &newt);
ch = getchar();
tcsetattr(0, TCSANOW, &oldt);
if (ch == EOF) exit(0);
return ch;
#endif
}
static void shuffleArr(int* a, int n) {
int i;
for (i = n - 1; i > 0; i--) {
int j = rand() % (i + 1);
int t = a[i]; a[i] = a[j]; a[j] = t;
}
}
static void nowStr(char* buf, int cap) {
time_t t = time(NULL);
struct tm* tm = localtime(&t);
if (tm == NULL) { strcpy(buf, "unknown"); return; }
strftime(buf, cap, "%Y-%m-%d %H:%M:%S", tm);
}
static void ensureDirs() {
#ifdef _WIN32
_mkdir("matches");
#else
mkdir("matches", 0755);
#endif
}
static const char* modeName(int m) {
switch (m) {
case MODE_CLASSIC: return "经典";
case MODE_HEADSTART: return "开局供给";
case MODE_NOITEM: return "无道具";
case MODE_INFINITE: return "无限道具";
}
return "未知";
}
static const char* diffName(int d) {
switch (d) {
case DIFF_EASY: return "简单";
case DIFF_NORMAL: return "中等";
case DIFF_HARD: return "困难";
}
return "未知";
}
static const char* itemName(int it) {
switch (it) {
case ITEM_MAG: return "放大镜";
case ITEM_KNIFE: return "小刀";
case ITEM_CUFF: return "手铐";
case ITEM_BEER: return "啤酒";
case ITEM_CIG: return "香烟";
case ITEM_ADREN: return "肾上腺素";
case ITEM_INVERT: return "逆转器";
case ITEM_PHONE: return "电话";
case ITEM_PILL: return "过期药品";
}
return "?";
}
static const char* actorName(int a) {
if (a == ACT_PLAYER) return "玩家";
if (a == ACT_DEALER) return "恶魔";
return "系统";
}
void resetGame(Game& g, int mode, int diff, const char* name);
void roundConfig(int rn, int& live, int& blank);
void newRound(Game& g);
void giveItems(Game& g, int who);
void addLog(Game& g, int actor, const char* act, const char* detail, int isLive, int dmg);
int calcPLive(Game& g);
int fire(Game& g, int target);
int applyItem(Game& g, int who, int it);
void printStatus(const Game& g);
void printShellGod(const Game& g);
void dealerAction(Game& g);
int playerAction(Game& g);
void runMatch(Game& g);
void computeScore(Game& g);
void printResult(const Game& g);
void godReview(const Game& g);
int saveSlot(Game& g, int slot);
int loadSlot(Game& g, int slot);
void saveMatch(Game& g);
void addScoreRecord(const Game& g);
void showScores();
void replayMatch(const char* file);
void help();
int modeSelect();
int diffSelect();
int mainMenu();
void runAutoTest();
void resetGame(Game& g, int mode, int diff, const char* name) {
memset(&g, 0, sizeof(Game));
strcpy(g.magic, "BRSAVE5");
g.version = 1;
strncpy(g.playerName, name, 23); g.playerName[23] = 0;
nowStr(g.matchTime, sizeof(g.matchTime));
g.mode = mode;
g.difficulty = diff;
g.stage = 1;
g.stageWins[0] = 0;
g.stageWins[1] = 0;
g.maxHp[0] = 4;
g.maxHp[1] = 4;
g.hp[0] = g.maxHp[0];
g.hp[1] = g.maxHp[1];
g.playerTurn = 1;
g.revealNext = -1;
g.phoneN = 0;
g.winner = -1;
g.over = 0;
}
void roundConfig(int rn, int& live, int& blank) {
int total;
if (rn <= 1) total = 2 + rand() % 3;
else if (rn == 2) total = 3 + rand() % 3;
else total = 4 + rand() % 3;
if (total > MAXSHELL - 2) total = MAXSHELL - 2;
if (total < 2) total = 2;
live = 1 + rand() % (total - 1);
blank = total - live;
}
void addLog(Game& g, int actor, const char* act, const char* detail, int isLive, int dmg) {
if (g.logLen >= MAXLOG) return;
LogEntry& L = g.log[g.logLen++];
L.round = g.roundNum;
L.actor = actor;
L.isLive = isLive;
L.dmg = dmg;
strncpy(L.act, act, 15); L.act[15] = 0;
strncpy(L.detail, detail, 95); L.detail[95] = 0;
}
void giveItems(Game& g, int who) {
if (g.mode == MODE_NOITEM) return;
int give = 2;
if (g.mode == MODE_HEADSTART && g.roundNum == 1) give = 4;
if (g.mode == MODE_INFINITE) give = 8;
int i, total = 0, k;
for (k = 0; k < NITEM; k++) total += g.itemCnt[who][k];
for (i = 0; i < give; i++) {
if (total >= 8) break;
int it = rand() % NITEM;
g.itemCnt[who][it]++;
total++;
}
}
void newRound(Game& g) {
g.roundNum++;
int live, blank, i;
roundConfig(g.stage, live, blank);
g.nShells = live + blank;
g.liveCnt = live;
g.blankCnt = blank;
for (i = 0; i < live; i++) g.shells[i] = 1;
for (; i < g.nShells; i++) g.shells[i] = 0;
shuffleArr(g.shells, g.nShells);
g.pos = 0;
g.revealNext = -1;
g.phoneN = 0;
g.knifeReady = 0;
giveItems(g, 0);
giveItems(g, 1);
char det[96];
sprintf(det, "装填完成: %d实弹 %d空弹 (共%d发)", live, blank, g.nShells);
addLog(g, ACT_SYS, "开始新轮", det, -1, 0);
}
int calcPLive(Game& g) {
int rem = g.nShells - g.pos, live = 0, i;
if (rem <= 0) return 0;
for (i = g.pos; i < g.nShells; i++) if (g.shells[i]) live++;
return live * 100 / rem;
}
static void phoneRemove(Game& g, int idx) {
int i;
for (i = 0; i < g.phoneN; i++) {
if (g.phonePosArr[i] == idx) {
int j;
for (j = i; j < g.phoneN - 1; j++) {
g.phonePosArr[j] = g.phonePosArr[j + 1];
g.phoneValArr[j] = g.phoneValArr[j + 1];
}
g.phoneN--;
i--;
}
}
}
int fire(Game& g, int target) {
int actor = g.playerTurn ? ACT_PLAYER : ACT_DEALER;
int vic = (target == 1) ? (1 - actor) : actor;
int firedPos = g.pos;
int isLive = g.shells[g.pos];
g.pos++;
int dmg;
if (g.knifeReady) { g.knifeReady = 0; dmg = isLive ? 2 : 0; }
else dmg = isLive ? 1 : 0;
if (g.hp[vic] > 0) g.hp[vic] -= dmg;
char det[96];
if (dmg == 0) sprintf(det, "空弹! 没有造成伤害");
else if (isLive) sprintf(det, "实弹命中! 造成%d点伤害", dmg);
else sprintf(det, "命中! 造成%d点伤害", dmg);
addLog(g, actor, (target == 1) ? "射击对方" : "射击自己", det, isLive, dmg);
{
const char* shooter = (actor == ACT_PLAYER) ? "你" : "恶魔";
const char* aim = (target == 1) ? ((actor == ACT_PLAYER) ? "恶魔" : "你") : "自己";
if (dmg == 0) {
if (target == 1) sprintf(g_lastMsg, "%s射击%s: 空弹! 白送回合", shooter, aim);
else sprintf(g_lastMsg, "%s射击自己: 空弹! 无伤保留回合", shooter);
} else {
sprintf(g_lastMsg, "%s射击%s: 实弹命中! 造成%d点伤害", shooter, aim, dmg);
}
}
if (target == 1) {
g.playerTurn = !g.playerTurn;
} else {
if (dmg > 0) {
g.playerTurn = !g.playerTurn;
} else {
g.blanksSelfHit++;
}
}
g.revealNext = -1;
if (g.phoneN > 0) phoneRemove(g, firedPos);
return dmg;
}
int applyItem(Game& g, int who, int it) {
int op = 1 - who;
int* cnt = g.itemCnt[who];
if (cnt[it] <= 0) return 2;
cnt[it]--;
char det[96];
switch (it) {
case ITEM_MAG:
if (g.pos < g.nShells) g.revealNext = g.shells[g.pos];
else g.revealNext = -1;
if (g.revealNext == 1) sprintf(det, "[放大镜] 看到下一发: 实弹!");
else if (g.revealNext == 0) sprintf(det, "[放大镜] 看到下一发: 空弹!");
else sprintf(det, "枪膛已空");
strcpy(g_lastMsg, det);
addLog(g, who, "放大镜", det, g.revealNext, 0);
return 0;
case ITEM_KNIFE:
g.knifeReady = 1;
strcpy(g_lastMsg, "[刀] 利刃出鞘: 下一发伤害翻倍(2点)");
addLog(g, who, "刀", "利刃出鞘: 下一发伤害翻倍(2点)", -1, 0);
return 0;
case ITEM_CUFF:
g.cuffed[op] = 1;
sprintf(det, "[手铐] 铐住%s: 跳过其下一个回合", (op == ACT_PLAYER) ? "玩家" : "恶魔");
strcpy(g_lastMsg, det);
addLog(g, who, "手铐", det, -1, 0);
return 0;
case ITEM_BEER:
if (g.pos < g.nShells) {
int was = g.shells[g.pos];
int idx = g.pos;
g.pos++;
if (g.phoneN > 0) phoneRemove(g, idx);
sprintf(det, "[啤酒] 弹出了第%d发: %s! (剩余%d发)", idx + 1, was ? "实弹" : "空弹", g.nShells - g.pos);
} else {
sprintf(det, "[啤酒] 枪膛已空");
}
g.revealNext = -1;
strcpy(g_lastMsg, det);
addLog(g, who, "啤酒", det, -1, 0);
return 0;
case ITEM_CIG:
if (g.hp[who] < g.maxHp[who]) g.hp[who]++;
sprintf(det, "[香烟] 恢复 +1 生命 (当前%d/%d)", g.hp[who], g.maxHp[who]);
strcpy(g_lastMsg, det);
addLog(g, who, "香烟", det, -1, 0);
return 0;
case ITEM_ADREN: {
int cand[NITEM], cnt2 = 0, k;
for (k = 0; k < NITEM; k++)
if (k != ITEM_ADREN && g.itemCnt[op][k] > 0) cand[cnt2++] = k;
if (cnt2 == 0) {
cnt[it]++;
sprintf(det, "对方没有可偷取的道具, 无法使用");
strcpy(g_lastMsg, det);
addLog(g, who, "肾上腺素", det, -1, 0);
return 2;
}
int pick = cand[rand() % cnt2];
g.itemCnt[op][pick]--;
g.itemCnt[who][pick]++;
sprintf(det, "偷到%s的%s, 立即使用", (op == ACT_PLAYER) ? "玩家" : "恶魔", itemName(pick));
strcpy(g_lastMsg, det);
addLog(g, who, "肾上腺素", det, -1, 0);
applyItem(g, who, pick);
return 1;
}
case ITEM_INVERT: {
if (g.pos < g.nShells) {
int i, c = 0;
for (i = g.pos; i < g.nShells; i++) { g.shells[i] = 1 - g.shells[i]; c++; }
sprintf(det, "翻转了剩余%d发子弹(实弹与空弹互换)", c);
} else {
sprintf(det, "枪膛已空");
}
g.revealNext = -1;
g.phoneN = 0;
strcpy(g_lastMsg, det);
addLog(g, who, "逆转器", det, -1, 0);
return 0;
}
case ITEM_PHONE: {
if (g.pos < g.nShells) {
int idx = g.pos + rand() % (g.nShells - g.pos);
if (g.phoneN < MAXSHELL) {
g.phonePosArr[g.phoneN] = idx;
g.phoneValArr[g.phoneN] = g.shells[idx];
g.phoneN++;
}
sprintf(det, "[电话] 第%d发是%s (提示已保留)", idx + 1, g.shells[idx] ? "实弹" : "空弹");
} else {
sprintf(det, "枪膛已空");
}
strcpy(g_lastMsg, det);
addLog(g, who, "电话", det, -1, 0);
return 0;
}
case ITEM_PILL: {
int heal = g_pillLuck ? (rand() % 10 < 7) : (rand() % 2 == 0);
if (heal) {
g.hp[who] += 2;
if (g.hp[who] > g.maxHp[who]) g.hp[who] = g.maxHp[who];
sprintf(det, "[过期药品] 恢复 +2 生命 (当前%d/%d)", g.hp[who], g.maxHp[who]);
} else {
if (g.hp[who] > 0) g.hp[who]--;
sprintf(det, "[过期药品] 扣除 -1 生命 (当前%d/%d)", g.hp[who], g.maxHp[who]);
}
strcpy(g_lastMsg, det);
addLog(g, who, "过期药品", det, -1, 0);
return 0;
}
}
return 2;
}
static void printBar(int cur, int max, int len) {
int i, full = len * cur / (max > 0 ? max : 1);
if (full > len) full = len;
setColor(12);
printf("[");
for (i = 0; i < full; i++) printf("#");
setColor(8);
for (; i < len; i++) printf(".");
setColor(12);
printf("]");
setColor(7);
printf(" %d/%d", cur, max);
}
void printShellGod(const Game& g) {
int i;
setColor(13);
printf(" [上帝模式] 枪膛透视:");
setColor(7);
for (i = 0; i < g.nShells; i++) {
if (i < g.pos) {
printf(" %sx", g.shells[i] ? "实" : "空");
} else if (i == g.pos) {
setColor(12);
printf(" %s<", g.shells[i] ? "实" : "空");
setColor(7);
} else {
setColor(10);
printf(" %s", g.shells[i] ? "实" : "空");
setColor(7);
}
}
printf(" (x=已发射, <=当前待发)\n");
}
void printStatus(const Game& g) {
int k;
cls();
setColor(15);
printf("================ 恶魔轮盘 ================\n");
setColor(14);
printf(" 玩家:%s 模式:%s 难度:%s 大轮%d/3 第%d次装填\n",
g.playerName, modeName(g.mode), diffName(g.difficulty), g.stage, g.roundNum);
setColor(7);
printf(" 你 HP "); printBar(g.hp[0], g.maxHp[0], 12); printf("\n");
printf(" 恶魔HP "); printBar(g.hp[1], g.maxHp[1], 12); printf("\n");
setColor(11);
printf(" 你的道具: ");
setColor(7);
{
int any = 0;
for (k = 0; k < NITEM; k++) if (g.itemCnt[0][k] > 0) {
printf("%s x%d ", itemName(k), g.itemCnt[0][k]);
any = 1;
}
if (!any) printf("(无)");
}
printf("\n");
setColor(11);
printf(" 恶魔道具: ");
setColor(7);
{
int any = 0;
for (k = 0; k < NITEM; k++) if (g.itemCnt[1][k] > 0) {
printf("%s x%d ", itemName(k), g.itemCnt[1][k]);
any = 1;
}
if (!any) printf("(无)");
}
printf("\n");
setColor(11);
printf(" 枪膛: ");
setColor(7);
if (g_showShell)
printf("本轮 %d实弹 + %d空弹 (共%d发), ", g.liveCnt, g.blankCnt, g.nShells);
printf("剩余%d发", g.nShells - g.pos);
if (g_showRemain) {
int rl = 0, i;
for (i = g.pos; i < g.nShells; i++) if (g.shells[i]) rl++;
printf(" (实弹%d 空弹%d)", rl, (g.nShells - g.pos) - rl);
}
printf(" (下一发是第%d/%d发)\n", g.pos + 1, g.nShells);
if (g.revealNext == 1) { setColor(12); printf(" [放大镜] 下一发是: 实弹!\n"); setColor(7); }
else if (g.revealNext == 0) { setColor(10); printf(" [放大镜] 下一发是: 空弹!\n"); setColor(7); }
{
int i;
for (i = 0; i < g.phoneN; i++)
if (g.phonePosArr[i] >= g.pos && g.phonePosArr[i] < g.nShells) {
setColor(11);
printf(" [电话] 第%d发是: %s\n", g.phonePosArr[i] + 1,
g.phoneValArr[i] ? "实弹" : "空弹");
setColor(7);
}
}
if (g.knifeReady) { setColor(14); printf(" [刀] 已出鞘: 下一发伤害为2点\n"); setColor(7); }
if (g.cuffed[1]) { setColor(14); printf(" [手铐] 恶魔被铐住, 下个回合将被跳过\n"); setColor(7); }
if (g.cuffed[0]) { setColor(14); printf(" [手铐] 你被铐住, 下个回合将被跳过\n"); setColor(7); }
if (g.godMode) printShellGod(g);
setColor(15);
printf(" 当前行动: %s\n", g.playerTurn ? "* 你的回合" : "> 恶魔回合");
setColor(7);
printf("==========================================\n");
}
void dealerAction(Game& g) {
int who = ACT_DEALER;
if (g.cuffed[1]) {
g.cuffed[1] = 0;
strcpy(g_lastMsg, "恶魔被手铐铐住, 本回合被跳过!");
addLog(g, ACT_SYS, "手铐", "恶魔被手铐铐住, 本回合被跳过!", -1, 0);
g.playerTurn = 1;
return;
}
if (g.difficulty == DIFF_EASY) {
int r = rand() % 10;
if (r < 4) fire(g, 1);
else if (r < 6) fire(g, 0);
else {
int items[NITEM], cnt = 0, k;
for (k = 0; k < NITEM; k++) if (g.itemCnt[who][k] > 0) items[cnt++] = k;
if (cnt > 0) {
int it = -1;
if (g.itemCnt[who][ITEM_MAG] > 0) it = ITEM_MAG;
else if (g.hp[who] < g.maxHp[who] && g.itemCnt[who][ITEM_CIG] > 0) it = ITEM_CIG;
else it = items[rand() % cnt];
int rc = applyItem(g, who, it);
if (rc == 1) {
if (g.hp[1] <= 0 || g.pos >= g.nShells) return;
dealerAction(g);
return;
}
if (rc == 2) fire(g, 1);
else if (g.pos >= g.nShells || g.hp[1] <= 0) return;
} else fire(g, 1);
}
return;
}
if (g.difficulty == DIFF_NORMAL) {
int guard = 0;
while (guard++ < 8 && g.hp[0] > 0 && g.hp[1] > 0 && g.pos < g.nShells) {
int known = g.revealNext;
if (known < 0 && g.itemCnt[who][ITEM_MAG] > 0) {
applyItem(g, who, ITEM_MAG);
known = g.revealNext;
continue;
}
if (g.itemCnt[who][ITEM_PILL] > 0) {
int usePill = 0;
if (g.hp[who] <= 2) usePill = 1;
else if (g.hp[who] <= 3 && g.itemCnt[who][ITEM_CIG] > 0) usePill = 1;
if (usePill) {
int hpBefore = g.hp[who];
applyItem(g, who, ITEM_PILL);
if (g.hp[who] < hpBefore && g.itemCnt[who][ITEM_CIG] > 0)
applyItem(g, who, ITEM_CIG);
if (g.hp[1] <= 0) return;
continue;
}
}
if (known == 1) {
if (g.itemCnt[who][ITEM_KNIFE] > 0) {
applyItem(g, who, ITEM_KNIFE);
fire(g, 1);
} else fire(g, 1);
return;
}
if (known == 0) {
if (g.itemCnt[who][ITEM_INVERT] > 0) {
applyItem(g, who, ITEM_INVERT);
if (g.itemCnt[who][ITEM_KNIFE] > 0) { applyItem(g, who, ITEM_KNIFE); fire(g, 1); }
else fire(g, 1);
return;
}
if (g.phoneN == 0 && g.itemCnt[who][ITEM_PHONE] > 0) {
applyItem(g, who, ITEM_PHONE);
continue;
}
if (g.itemCnt[who][ITEM_CUFF] > 0 && !g.cuffed[0]) {
applyItem(g, who, ITEM_CUFF);
continue;
}
fire(g, 0);
continue;
}
int pLive = calcPLive(g);
if (g.itemCnt[who][ITEM_CUFF] > 0 && !g.cuffed[0]) {
applyItem(g, who, ITEM_CUFF);
continue;
}
if (g.phoneN == 0 && g.itemCnt[who][ITEM_PHONE] > 0) {
applyItem(g, who, ITEM_PHONE);
continue;
}
if (pLive >= 60 && g.itemCnt[who][ITEM_BEER] > 0) {
applyItem(g, who, ITEM_BEER);
if (g.pos >= g.nShells) return;
continue;
}
if (pLive >= 55) { fire(g, 1); return; }
if (pLive <= 40) { fire(g, 0); continue; }
if (rand() % 2) fire(g, 0); else fire(g, 1);
return;
}
if (g.pos < g.nShells && g.hp[0] > 0) fire(g, 1);
return;
}
int isLive = (g.pos < g.nShells) ? g.shells[g.pos] : -1;
if (g.itemCnt[who][ITEM_PILL] > 0 && g.hp[who] <= 2) {
g_pillLuck = 1;
applyItem(g, who, ITEM_PILL);
g_pillLuck = 0;
if (g.hp[who] < 2 && g.itemCnt[who][ITEM_CIG] > 0) {
applyItem(g, who, ITEM_CIG);
if (g.itemCnt[who][ITEM_PILL] > 0 && g.hp[who] <= 2) {
g_pillLuck = 1;
applyItem(g, who, ITEM_PILL);
g_pillLuck = 0;
}
}
if (g.hp[1] <= 0) return;
}
if (isLive == 0) {
if (g.itemCnt[who][ITEM_INVERT] > 0) {
applyItem(g, who, ITEM_INVERT);
if (g.itemCnt[who][ITEM_KNIFE] > 0) { applyItem(g, who, ITEM_KNIFE); fire(g, 1); }
else fire(g, 1);
return;
}
if (g.hp[who] < g.maxHp[who] && g.itemCnt[who][ITEM_CIG] > 0)
applyItem(g, who, ITEM_CIG);
if (g.itemCnt[who][ITEM_CUFF] > 0 && !g.cuffed[0])
applyItem(g, who, ITEM_CUFF);
fire(g, 0);
} else {
if (g.itemCnt[who][ITEM_BEER] > 0) {
applyItem(g, who, ITEM_BEER);
if (g.pos >= g.nShells) return;
} else if (g.itemCnt[who][ITEM_KNIFE] > 0) {
applyItem(g, who, ITEM_KNIFE);
fire(g, 1);
} else {
fire(g, 1);
}
}
}
int playerAction(Game& g) {
if (g.cuffed[0]) {
g.cuffed[0] = 0;
addLog(g, ACT_SYS, "手铐", "玩家被手铐铐住, 本回合被跳过!", -1, 0);
printStatus(g);
setColor(12);
printf("\n 你被手铐铐住, 本回合被跳过!\n");
setColor(7);
pause1();
g.playerTurn = 0;
return 0;
}
if (g_auto) { fire(g, 1); return 0; }
if (g_autoCertain && g.pos < g.nShells) {
int rl = 0, i;
for (i = g.pos; i < g.nShells; i++) if (g.shells[i]) rl++;
if (rl == 0) {
addLog(g, ACT_SYS, "自动", "剩余全是空弹, 自动射击自己(安全保回合)", -1, 0);
fire(g, 0);
strcpy(g_lastMsg, "自动执行: 剩余全是空弹, 已射击自己(空弹无伤, 保留回合)");
printStatus(g);
setColor(10);
printf(" [自动] 剩余全是空弹, 已自动射击自己(空弹无伤, 保留回合)\n");
setColor(7);
pause1();
return 0;
}
if (rl == g.nShells - g.pos) {
addLog(g, ACT_SYS, "自动", "剩余全是实弹, 自动射击对方", -1, 0);
fire(g, 1);
strcpy(g_lastMsg, "自动执行: 剩余全是实弹, 已射击对方(实弹命中)");
printStatus(g);
setColor(10);
printf(" [自动] 剩余全是实弹, 已自动射击对方\n");
setColor(7);
pause1();
return 0;
}
}
while (1) {
printStatus(g);
printf("\n 请选择行动:\n");
setColor(14);
printf(" [1] 射击自己 [2] 射击对方\n");
printf(" [3] 使用道具 [4] 保存游戏\n");
printf(" [5] 放弃本局");
if (g.godMode) {
setColor(13);
printf(" [0] 上帝视角(已开启)");
}
setColor(7);
printf("\n 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') { fire(g, 0); printLastMsg("你"); return 0; }
if (c == '2') { fire(g, 1); printLastMsg("你"); return 0; }
if (c == '3') {
int cnt = 0, k;
for (k = 0; k < NITEM; k++) if (g.itemCnt[0][k] > 0) cnt++;
if (cnt == 0) {
setColor(12); printf(" 你没有道具!\n"); setColor(7);
pause1();
continue;
}
printStatus(g);
setColor(11);
printf("\n 选择要使用的道具:\n");
for (k = 0; k < NITEM; k++)
if (g.itemCnt[0][k] > 0) printf(" [%d] %s x%d\n", k + 1, itemName(k), g.itemCnt[0][k]);
printf(" [0] 返回\n");
setColor(7);
printf(" 输入: ");
fflush(stdout);
int d = my_getch();
printf("\n");
if (d == '0') continue;
int idx = d - '1';
if (idx >= 0 && idx < NITEM && g.itemCnt[0][idx] > 0) {
if (idx == ITEM_ADREN) {
int cand[NITEM], cnt2 = 0, k;
for (k = 0; k < NITEM; k++)
if (k != ITEM_ADREN && g.itemCnt[1][k] > 0) cand[cnt2++] = k;
if (cnt2 == 0) {
setColor(12);
printf(" 恶魔没有可偷取的道具, 无法使用肾上腺素!\n");
setColor(7);
pause1();
continue;
}
printStatus(g);
setColor(11);
printf("\n 选择要窃取的道具(偷到后立即使用):\n");
for (k = 0; k < cnt2; k++)
printf(" [%d] %s x%d\n", k + 1, itemName(cand[k]), g.itemCnt[1][cand[k]]);
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int dd = my_getch();
printf("\n");
if (dd == '0') continue;
int si = dd - '1';
if (si >= 0 && si < cnt2) {
int pick = cand[si];
g.itemCnt[0][ITEM_ADREN]--;
g.itemCnt[1][pick]--;
g.itemCnt[0][pick]++;
setColor(14);
printf(" 偷到恶魔的%s, 立即使用!\n", itemName(pick));
setColor(7);
int rc = applyItem(g, ACT_PLAYER, pick);
if (rc == 2) printf(" (偷来的道具无法使用)\n");
else if (g_lastMsg[0]) printf(" %s\n", g_lastMsg);
pause1();
if (g.hp[0] <= 0) return 0;
if (g.pos >= g.nShells) return 0;
continue;
}
continue;
}
int rc = applyItem(g, ACT_PLAYER, idx);
if (rc == 2) { setColor(12); printf(" 道具无法使用!\n"); setColor(7); pause1(); }
else if (g_lastMsg[0]) { setColor(14); printf(" %s\n", g_lastMsg); setColor(7); pause1(); }
if (g.hp[0] <= 0) return 0;
if (rc == 1) { g.playerTurn = 0; return 0; }
if (g.pos >= g.nShells) return 0;
}
continue;
}
if (c == '4') {
setColor(11);
printf(" 保存到哪个存档槽(1-%d)? [0取消]: ", MAXSLOT);
setColor(7);
fflush(stdout);
int s = my_getch();
printf("\n");
if (s >= '1' && s <= '0' + MAXSLOT) {
int slot = s - '0';
if (saveSlot(g, slot) == 0) {
setColor(10); printf(" [OK] 已保存到存档槽 %d\n", slot); setColor(7);
} else {
setColor(12); printf(" 保存失败!\n"); setColor(7);
}
}
pause1();
continue;
}
if (c == '5') {
setColor(12);
printf(" 确认放弃本局? (按 Y 确认, 其他键继续): ");
setColor(7);
fflush(stdout);
int y = my_getch();
printf("\n");
if (y == 'y' || y == 'Y') return -1;
continue;
}
if (c == '0' && g.godMode) { printStatus(g); pause1(); continue; }
}
}
void computeScore(Game& g) {
int s = 0, k;
if (g.over == 2) { g.score = 0; return; }
if (g.winner == 0) s += 200 + g.stageWins[0] * 40;
else s += 15 + g.stageWins[1] * 10;
if (g.winner >= 0 && g.hp[g.winner] > 0) s += g.hp[g.winner] * 10;
for (k = 0; k < NITEM; k++) s += (g.itemCnt[0][k] + g.itemCnt[1][k]) * 2;
s += g.blanksSelfHit * 5;
s += g.roundNum * 6;
g.score = s;
}
void printResult(const Game& g) {
cls();
setColor(15);
printf("================ 对局结束 ================\n");
setColor(7);
if (g.over == 2) {
setColor(12);
printf(" 你放弃了本局!\n");
} else if (g.winner == 0) {
setColor(10);
printf(" ***** 你赢了! *****\n");
printf(" 恶魔被击败, 你活了下来!\n");
} else {
setColor(12);
printf(" X 你输了... 恶魔取得了胜利\n");
}
setColor(7);
printf("\n 对局统计:\n");
printf(" 玩家: %s (HP %d/%d) 恶魔: (HP %d/%d)\n",
g.playerName, g.hp[0], g.maxHp[0], g.hp[1], g.maxHp[1]);
printf(" 模式: %s 难度: %s 大轮比分: 玩家%d : %d恶魔\n",
modeName(g.mode), diffName(g.difficulty), g.stageWins[0], g.stageWins[1]);
printf(" 总装填次数: %d 空弹打自己(赚回合): %d 次\n", g.roundNum, g.blanksSelfHit);
printf(" 总行动次数: %d\n", g.logLen);
setColor(14);
printf(" 本局得分: %d\n", g.score);
setColor(7);
printf("==========================================\n");
}
void godReview(const Game& g) {
int i, k;
cls();
setColor(13);
printf("============= 上帝模式 - 复盘 =============\n");
setColor(7);
printf(" 对局时间: %s\n 玩家: %s 模式:%s 难度:%s 结果:%s 得分:%d\n",
g.matchTime, g.playerName, modeName(g.mode), diffName(g.difficulty),
g.winner == 0 ? "玩家胜" : (g.winner == 1 ? "恶魔胜" : "放弃"), g.score);
printf(" 大轮比分: 玩家 %d : %d 恶魔 总装填 %d 次\n",
g.stageWins[0], g.stageWins[1], g.roundNum);
printf("\n [枪膛内子弹真实排列] (x=已发射, <=下一发)\n ");
for (i = 0; i < g.nShells; i++) {
if (i < g.pos) printf(" %sx", g.shells[i] ? "实" : "空");
else if (i == g.pos) printf(" %s<", g.shells[i] ? "实" : "空");
else printf(" %s", g.shells[i] ? "实" : "空");
}
printf("\n 已发射 %d/%d 发, 剩余 %d 发\n", g.pos, g.nShells, g.nShells - g.pos);
printf("\n [双方最终道具]\n");
printf(" 你 : ");
{
int any = 0;
for (k = 0; k < NITEM; k++) if (g.itemCnt[0][k] > 0) { printf("%s x%d ", itemName(k), g.itemCnt[0][k]); any = 1; }
if (!any) printf("(无)");
}
printf("\n 恶魔: ");
{
int any = 0;
for (k = 0; k < NITEM; k++) if (g.itemCnt[1][k] > 0) { printf("%s x%d ", itemName(k), g.itemCnt[1][k]); any = 1; }
if (!any) printf("(无)");
}
printf("\n");
printf("\n [每轮装填历史]\n");
for (i = 0; i < g.logLen; i++)
if (g.log[i].actor == ACT_SYS && strncmp(g.log[i].act, "开始新轮", 8) == 0)
printf(" 第%d轮: %s\n", g.log[i].round, g.log[i].detail);
printf("\n [完整行动日志]\n");
for (i = 0; i < g.logLen; i++) {
const LogEntry& L = g.log[i];
printf(" R%-2d %-6s %-12s %s\n", L.round, actorName(L.actor), L.act, L.detail);
}
setColor(13);
printf("==========================================\n");
setColor(7);
pause1();
}
void printStageEnd(const Game& g, int roundWinner) {
if (g_auto) return;
cls();
setColor(15);
printf("============== 第%d/3大轮结束 ==============\n", g.stage);
setColor(7);
if (roundWinner == 0) { setColor(10); printf(" ***** 你赢了本轮! *****\n"); }
else { setColor(12); printf(" X 你输了本轮...\n"); }
setColor(7);
printf(" 当前比分: 玩家 %d : %d 恶魔\n", g.stageWins[0], g.stageWins[1]);
if (g.stage >= 3) {
setColor(14);
printf(" 三场已全部打完, 按总比分判定本局胜负!\n");
} else {
setColor(11);
printf(" 下一轮双方生命将重置, 重新装填子弹...\n");
}
setColor(15);
printf("==========================================\n");
setColor(7);
pause1();
}
void runMatch(Game& g) {
int freshStage = !g_resume;
g_resume = 0;
while (g.stage <= 3 && g.over != 2) {
if (freshStage) {
g.hp[0] = g.maxHp[0];
g.hp[1] = g.maxHp[1];
g.blanksSelfHit = 0;
}
newRound(g);
int roundWinner = -1;
while (1) {
if (g.hp[0] <= 0) { roundWinner = 1; break; }
if (g.hp[1] <= 0) { roundWinner = 0; break; }
if (g.pos >= g.nShells) { newRound(g); continue; }
if (g.playerTurn) {
int r = playerAction(g);
if (r == -1) { g.over = 2; break; }
} else {
printStatus(g);
setColor(15);
printf("\n 恶魔正在思考...\n");
setColor(7);
pause1();
dealerAction(g);
printLastMsg("恶魔");
}
}
if (g.over == 2) break;
g.stageWins[roundWinner]++;
printStageEnd(g, roundWinner);
g.stage++;
freshStage = 1;
}
if (g.over == 2) g.winner = -1;
else {
g.winner = (g.stageWins[0] > g.stageWins[1]) ? 0 : 1;
g.over = 1;
}
computeScore(g);
addScoreRecord(g);
saveMatch(g);
printResult(g);
while (1) {
if (g_auto) break;
setColor(14);
printf("\n [1] 上帝模式复盘(查看枪膛内子弹等) [2] 返回主菜单\n");
setColor(7);
printf(" 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') { godReview(g); break; }
if (c == '2') break;
}
}
int saveSlot(Game& g, int slot) {
if (slot < 1 || slot > MAXSLOT) return -1;
char fn[64];
sprintf(fn, "slot%d.brs", slot);
FILE* f = fopen(fn, "wb");
if (!f) return -1;
fwrite(&g, 1, sizeof(Game), f);
fclose(f);
return 0;
}
int loadSlot(Game& g, int slot) {
if (slot < 1 || slot > MAXSLOT) return -1;
char fn[64];
sprintf(fn, "slot%d.brs", slot);
FILE* f = fopen(fn, "rb");
if (!f) return -1;
fread(&g, 1, sizeof(Game), f);
fclose(f);
if (strncmp(g.magic, "BRSAVE5", 7) != 0) return -1;
return 0;
}
void saveMatch(Game& g) {
ensureDirs();
char fname[48], file[80], line[256];
strcpy(fname, g.matchTime);
{
int i;
for (i = 0; fname[i]; i++)
if (fname[i] == ' ' || fname[i] == ':' || fname[i] == '-')
fname[i] = '_';
}
sprintf(file, "matches/match_%s.brs", fname);
FILE* f = fopen(file, "wb");
if (!f) return;
fwrite(&g, 1, sizeof(Game), f);
fclose(f);
FILE* idx = fopen("matches/index.txt", "a");
if (idx) {
sprintf(line, "%s|%s|%s|%s|%s|%s|%d\n",
file, g.matchTime, g.playerName, modeName(g.mode),
diffName(g.difficulty),
g.over == 2 ? "放弃" : (g.winner == 0 ? "胜" : "负"), g.score);
fputs(line, idx);
fclose(idx);
}
}
void addScoreRecord(const Game& g) {
FILE* f = fopen("scores.txt", "a");
if (!f) return;
fprintf(f, "%s|%s|%s|%s|%s|%d\n", g.matchTime, g.playerName,
modeName(g.mode), diffName(g.difficulty),
g.over == 2 ? "放弃" : (g.winner == 0 ? "胜" : "负"), g.score);
fclose(f);
}
void showScores() {
struct Rec { char time[24]; char name[24]; char mode[16]; char diff[16]; char res[8]; int score; };
Rec rec[256];
int n = 0;
FILE* f = fopen("scores.txt", "r");
if (f) {
char line[256];
while (n < 256 && fgets(line, sizeof(line), f)) {
char t[24], nm[24], md[16], df[16], rs[8];
int sc;
if (sscanf(line, "%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%d", t, nm, md, df, rs, &sc) == 6) {
strcpy(rec[n].time, t); strcpy(rec[n].name, nm);
strcpy(rec[n].mode, md); strcpy(rec[n].diff, df);
strcpy(rec[n].res, rs); rec[n].score = sc;
n++;
}
}
fclose(f);
}
if (n == 0) {
cls();
setColor(14);
printf("==== 分数排行榜 ====\n");
setColor(7);
printf(" 暂无对局记录, 快去玩一局吧!\n");
pause1();
return;
}
int i, j;
for (i = 0; i < n - 1; i++)
for (j = 0; j < n - 1 - i; j++)
if (rec[j].score < rec[j + 1].score) {
Rec t = rec[j]; rec[j] = rec[j + 1]; rec[j + 1] = t;
}
cls();
setColor(14);
printf("===== 分数排行榜 (Top %d) =====\n", n > 20 ? 20 : n);
setColor(7);
printf(" 名次 得分 玩家 时间 模式 难度 结果\n");
printf(" ----------------------------------------------------------------\n");
int top = n > 20 ? 20 : n;
for (i = 0; i < top; i++)
printf(" %-4d %-5d %-15s %-20s %-4s %-4s %s\n",
i + 1, rec[i].score, rec[i].name, rec[i].time,
rec[i].mode, rec[i].diff, rec[i].res);
printf(" ----------------------------------------------------------------\n");
pause1();
}
void replayMatch(const char* file) {
Game g;
FILE* f = fopen(file, "rb");
if (!f) { setColor(12); printf(" 读取回放失败!\n"); setColor(7); pause1(); return; }
fread(&g, 1, sizeof(Game), f);
fclose(f);
if (strncmp(g.magic, "BRSAVE5", 7) != 0) {
setColor(12); printf(" 回放文件无效!\n"); setColor(7); pause1(); return;
}
int i = 0, god = 0;
while (1) {
cls();
setColor(14);
printf("===== 对局回放 =====\n");
setColor(7);
printf(" 玩家:%s 模式:%s 难度:%s 时间:%s 得分:%d\n",
g.playerName, modeName(g.mode), diffName(g.difficulty), g.matchTime, g.score);
printf(" 大轮比分: 玩家 %d : %d 恶魔 结果: %s\n",
g.stageWins[0], g.stageWins[1],
g.over == 2 ? "放弃" : (g.winner == 0 ? "玩家胜" : "恶魔胜"));
printf(" --------------------------------------------------\n");
int showFrom = (i - 12 > 0) ? i - 12 : 0;
int j;
for (j = showFrom; j <= i && j < g.logLen; j++) {
const LogEntry& L = g.log[j];
if (j == i) setColor(10);
printf(" R%-2d %-6s %-12s %s\n", L.round, actorName(L.actor), L.act, L.detail);
if (j == i) setColor(7);
}
if (god) {
setColor(13);
printf(" [上帝模式] 枪膛: ");
setColor(7);
int kk;
for (kk = 0; kk < g.nShells; kk++)
printf("%s ", kk < g.pos ? (g.shells[kk] ? "实x" : "空x") : (g.shells[kk] ? "实" : "空"));
printf("\n");
}
printf(" --------------------------------------------------\n");
setColor(11);
printf(" 动作 %d/%d 空格/回车:下一步 G:上帝模式 Q:退出\n", i + 1, g.logLen);
setColor(7);
int c = my_getch();
if (c == ' ' || c == '\r' || c == '\n') { if (i < g.logLen - 1) i++; }
else if (c == 'g' || c == 'G') god = !god;
else if (c == 'q' || c == 'Q') break;
}
}
void selectReplay() {
FILE* f = fopen("matches/index.txt", "r");
if (!f) {
cls();
setColor(14); printf("==== 回放对局 ====\n");
setColor(7);
printf(" 暂无对局记录!\n");
pause1();
return;
}
char files[256][96];
int n = 0;
char line[256];
while (n < 256 && fgets(line, sizeof(line), f)) {
char f1[96], t[24], nm[24], md[16], df[16], rs[8]; int sc;
if (sscanf(line, "%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%d", f1, t, nm, md, df, rs, &sc) == 7) {
strcpy(files[n], f1);
n++;
}
}
fclose(f);
if (n == 0) {
cls();
setColor(14); printf("==== 回放对局 ====\n");
setColor(7);
printf(" 暂无对局记录!\n");
pause1();
return;
}
cls();
setColor(14);
printf("==== 选择要回放的对局 ====\n");
setColor(7);
FILE* f2 = fopen("matches/index.txt", "r");
if (f2) {
char lines[256][256];
int cnt = 0;
while (cnt < 256 && fgets(lines[cnt], sizeof(lines[cnt]), f2)) cnt++;
fclose(f2);
int i;
for (i = cnt - 1; i >= 0; i--) {
char f1[96], t[24], nm[24], md[16], df[16], rs[8]; int sc;
if (sscanf(lines[i], "%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%d", f1, t, nm, md, df, rs, &sc) == 7)
printf(" [%d] %s %s %s-%s %s %d分\n", cnt - i, t, nm, md, df, rs, sc);
}
}
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int sel = readNum();
if (sel >= 1 && sel <= n) {
replayMatch(files[n - sel]);
}
}
void help() {
cls();
setColor(14);
printf("============== 恶魔轮盘 - 规则说明 ==============\n");
setColor(7);
printf("\n* 玩法\n");
printf(" 一局共3大轮。每大轮开始时双方生命重置, 向霰弹枪装填若干实弹\n");
printf(" 和空弹(数量随机), 双方轮流行动:\n");
printf(" - 射击自己: 实弹→自己掉血, 行动权移交; 空弹→毫发无损, 保留行动权(赚回合)\n");
printf(" - 射击对方: 实弹→对方掉血, 行动权移交对方; 空弹→白送对方回合\n");
printf(" 大轮内谁生命先归零谁输本轮, 率先赢下2轮者赢得整局。\n");
printf(" 装弹时会告知本轮实弹/空弹总数, 界面实时显示(可在[6]设置中关闭)。\n");
printf("\n* 道具(每大轮随机发2个)\n");
printf(" 放大镜: 查看当前下一发是实弹还是空弹, 信息在本回合内一直显示\n");
printf(" 小刀: 下一发子弹伤害翻倍(空包弹伤害仍为0)\n");
printf(" 手铐: 跳过对方下一个回合\n");
printf(" 啤酒: 退出当前下一发子弹(实空都弹掉)\n");
printf(" 香烟: 恢复1点生命(使用时会显示恢复血量)\n");
printf(" 肾上腺素: 自主选择偷对方1个道具并立即使用(不能偷肾上腺素), 你的回合继续\n");
printf(" 逆转器: 将枪膛内剩余所有子弹翻转(实弹与空弹互换)\n");
printf(" 电话: 只预测某一发未发射的子弹, 该发被发射后才失效\n");
printf(" 过期药品: 等概率恢复2点或扣除1点生命(会显示恢复/扣除血量)\n");
printf("\n* 模式\n");
printf(" 经典: 每轮随机发2个道具\n");
printf(" 开局供给: 第1轮每人4个道具, 之后每轮2个\n");
printf(" 无道具: 纯对拼, 没有道具\n");
printf(" 无限道具: 每轮道具补满到8个\n");
printf("\n* 难度\n");
printf(" 简单: 恶魔随机开枪 (双方各4血)\n");
printf(" 中等: 恶魔计算剩余实弹概率, 分析后做选择 (双方各4血)\n");
printf(" 困难: 恶魔直接透视枪膛, 知道每一发子弹是实是空 (双方各4血)\n");
printf("\n* 其他功能\n");
printf(" 局中[4]可保存到1~9号存档槽; 每局结束自动记录得分并可进入\n");
printf(" 上帝模式复盘查看枪膛内真实子弹排列; 主菜单可回放历史对局。\n");
printf(" 装填配置: 每大轮子弹数与实/空比例全部随机, 但保证至少1实弹和1空弹;\n");
printf(" 每大轮装弹后必定告知本轮实弹与空弹的数量。\n");
printf(" 主菜单[6]设置: 可开关'实时显示子弹总数'、'剩余实/空弹辅助计算'、\n");
printf(" '全空/全实自动执行'(全空自动打自己, 全实自动打对方)三个辅助功能。\n");
printf("==================================================\n");
pause1();
}
int modeSelect() {
while (1) {
cls();
setColor(14);
printf("==== 选择模式 ====\n");
setColor(7);
printf(" [1] 经典 (每轮随机发2个道具)\n");
printf(" [2] 开局供给 (第1轮每人4个道具, 之后每轮2个)\n");
printf(" [3] 无道具 (纯对拼)\n");
printf(" [4] 无限道具 (每轮道具补满到8个)\n");
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') return MODE_CLASSIC;
if (c == '2') return MODE_HEADSTART;
if (c == '3') return MODE_NOITEM;
if (c == '4') return MODE_INFINITE;
if (c == '0') return -1;
}
}
int diffSelect() {
while (1) {
cls();
setColor(14);
printf("==== 选择难度 ====\n");
setColor(7);
printf(" [1] 简单 (恶魔随机开枪, 双方各4血)\n");
printf(" [2] 中等 (恶魔概率分析选择, 双方各4血)\n");
printf(" [3] 困难 (恶魔透视枪膛内子弹, 双方各4血)\n");
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') return DIFF_EASY;
if (c == '2') return DIFF_NORMAL;
if (c == '3') return DIFF_HARD;
if (c == '0') return -1;
}
}
void settingsMenu() {
while (1) {
cls();
setColor(14);
printf("==== 设置 ====\n");
setColor(7);
printf(" [1] 实时显示本轮子弹总数(实弹/空弹): %s\n",
g_showShell ? "开" : "关");
printf(" (关闭后仅装填瞬间告知总数, 界面不再持续显示)\n");
printf(" [2] 辅助计算剩余实弹/空弹数量: %s\n",
g_showRemain ? "开" : "关");
printf(" (实时帮你看清还剩几发真弹几发空弹)\n");
printf(" [3] 自动执行全空弹/全实弹确定局面: %s\n",
g_autoCertain ? "开" : "关");
printf(" (剩余全是空弹自动打自己保回合, 全是实弹自动打对方)\n");
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') g_showShell = !g_showShell;
if (c == '2') g_showRemain = !g_showRemain;
if (c == '3') g_autoCertain = !g_autoCertain;
if (c == '0') return;
}
}
int mainMenu() {
while (1) {
cls();
setColor(15);
printf("==========================================\n");
setColor(12);
printf(" ####### 恶 魔 轮 盘 #######\n");
setColor(7);
printf(" Buckshot Roulette (文字版)\n");
printf("==========================================\n");
setColor(14);
printf(" [1] 开始新对局\n");
printf(" [2] 读取存档(继续对局)\n");
printf(" [3] 回放历史对局\n");
printf(" [4] 分数排行榜\n");
printf(" [5] 游戏帮助(规则)\n");
printf(" [6] 设置\n");
printf(" [0] 退出\n");
setColor(7);
printf(" 输入: ");
fflush(stdout);
int c = my_getch();
printf("\n");
if (c == '1') {
int mode = modeSelect();
if (mode < 0) continue;
int diff = diffSelect();
if (diff < 0) continue;
char name[24];
cls();
printf(" 输入你的名字(直接回车=玩家): ");
fflush(stdout);
int i = 0;
int ch = getchar();
while (i < 23 && ch != '\n' && ch != '\r') { name[i++] = (char)ch; ch = getchar(); }
name[i] = 0;
while (ch != '\n' && ch != '\r' && (ch = getchar()) != '\n');
if (i == 0) strcpy(name, "玩家");
Game g;
resetGame(g, mode, diff, name);
runMatch(g);
} else if (c == '2') {
cls();
setColor(14);
printf("==== 读取存档 ====\n");
setColor(7);
int s;
for (s = 1; s <= MAXSLOT; s++) {
char fn[64];
sprintf(fn, "slot%d.brs", s);
FILE* f = fopen(fn, "rb");
if (f) {
Game g;
fread(&g, 1, sizeof(Game), f);
fclose(f);
if (strncmp(g.magic, "BRSAVE5", 7) == 0)
printf(" [%d] %s 模式:%s 难度:%s 第%d轮 HP:%d/%d\n",
s, g.playerName, modeName(g.mode), diffName(g.difficulty),
g.roundNum, g.hp[0], g.maxHp[0]);
else
printf(" [%d] (损坏)\n", s);
} else {
printf(" [%d] (空)\n", s);
}
}
printf(" [0] 返回\n 输入: ");
fflush(stdout);
int sel = my_getch();
printf("\n");
if (sel >= '1' && sel <= '0' + MAXSLOT) {
Game g;
int slot = sel - '0';
if (loadSlot(g, slot) == 0) {
printf(" 已读取存档槽 %d, 继续对局!\n", slot);
pause1();
g_resume = 1;
runMatch(g);
} else {
printf(" 读取失败!\n");
pause1();
}
}
} else if (c == '3') {
selectReplay();
} else if (c == '4') {
showScores();
} else if (c == '5') {
help();
} else if (c == '6') {
settingsMenu();
} else if (c == '0') {
cls();
setColor(12);
printf(" 感谢游玩恶魔轮盘, 再见!\n");
setColor(7);
break;
}
}
return 0;
}
void autoPlayer(Game& g) {
int r = rand() % 10;
if (r < 5) fire(g, 1);
else fire(g, 0);
}
void runAutoTest() {
int m;
for (m = 0; m < 4; m++) {
int mode = 1 + m;
int diff = (m == 0) ? DIFF_EASY : ((m == 1) ? DIFF_NORMAL : DIFF_HARD);
int wins[2] = {0, 0};
int t;
for (t = 0; t < 30; t++) {
Game g;
resetGame(g, mode, diff, "AutoTest");
newRound(g);
int guard = 0;
while (guard++ < 2000) {
if (g.hp[0] <= 0) { g.over = 1; g.winner = 1; break; }
if (g.hp[1] <= 0) { g.over = 1; g.winner = 0; break; }
if (g.pos >= g.nShells) { newRound(g); continue; }
if (g.playerTurn) autoPlayer(g);
else dealerAction(g);
}
if (g.winner == 0) wins[0]++; else if (g.winner == 1) wins[1]++;
}
printf("模式=%s 难度=%s: 玩家胜%d 恶魔胜%d\n",
modeName(mode), diffName(diff), wins[0], wins[1]);
}
printf("AutoTest 完成(无崩溃)。\n");
}
void runFlowTest() {
g_auto = 1;
g_noPause = 1;
int m, ok = 1;
remove("scores.txt");
remove("matches/index.txt");
for (m = 0; m < 4; m++) {
Game g;
int mode = 1 + m;
int diff = (m == 0) ? DIFF_EASY : ((m == 1) ? DIFF_NORMAL : DIFF_HARD);
resetGame(g, mode, diff, "FlowTest");
runMatch(g);
if (g.over != 1) { printf(" 第%d局: 对局未正常结束 over=%d\n", m + 1, g.over); ok = 0; }
if (saveSlot(g, 1) != 0) { printf(" saveSlot FAIL\n"); ok = 0; }
Game g2;
if (loadSlot(g2, 1) != 0 || g2.score != g.score || g2.roundNum != g.roundNum || g2.hp[0] != g.hp[0]) {
printf(" loadSlot 往返不一致\n"); ok = 0;
} else {
printf(" 第%d局 OK: %s 难度=%s 结果=%s 得分=%d\n",
m + 1, modeName(mode), diffName(diff),
g.winner == 0 ? "胜" : "负", g.score);
}
}
int lines = 0;
FILE* f = fopen("matches/index.txt", "r");
if (f) { char b[512]; while (fgets(b, sizeof(b), f)) lines++; fclose(f); }
if (lines != 4) { printf(" matches/index.txt 行数=%d (期望4)\n", lines); ok = 0; }
else printf(" matches/index.txt 行数=%d OK\n", lines);
int sl = 0;
f = fopen("scores.txt", "r");
if (f) { char b[512]; while (fgets(b, sizeof(b), f)) sl++; fclose(f); }
if (sl != 4) { printf(" scores.txt 行数=%d (期望4)\n", sl); ok = 0; }
else printf(" scores.txt 行数=%d OK\n", sl);
f = fopen("matches/index.txt", "r");
if (f) {
char line[256], file[96];
if (fgets(line, sizeof(line), f)) {
int p = 0;
while (line[p] && line[p] != '|') { file[p] = line[p]; p++; }
file[p] = 0;
Game r;
FILE* rf = fopen(file, "rb");
if (rf) {
fread(&r, 1, sizeof(Game), rf);
fclose(rf);
if (strncmp(r.magic, "BRSAVE5", 7) != 0 || r.logLen <= 0) { printf(" 回放文件读取异常\n"); ok = 0; }
else printf(" 回放文件OK: %s 日志%d条\n", file, r.logLen);
} else { printf(" 回放文件不存在: %s\n", file); ok = 0; }
}
fclose(f);
} else { printf(" matches/index.txt 无法读取\n"); ok = 0; }
{
Game g; resetGame(g, MODE_CLASSIC, DIFF_EASY, "FlowTest");
newRound(g);
g.pos = 1;
godReview(g);
printf(" 上帝模式复盘 OK\n");
}
printf(ok ? "\nFlowTest 全部通过\n" : "\nFlowTest 存在失败项\n");
}
void runItemTest() {
Game g;
resetGame(g, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
newRound(g);
int ok = 1;
int before[MAXSHELL], i;
memcpy(before, g.shells, sizeof(before));
g.itemCnt[0][ITEM_INVERT] = 1;
int rc = applyItem(g, 0, ITEM_INVERT);
if (rc != 0) { printf("逆转器返回值错误\n"); ok = 0; }
for (i = g.pos; i < g.nShells; i++)
if (g.shells[i] != 1 - before[i]) { printf("逆转器翻转错误 pos=%d\n", i); ok = 0; }
printf("逆转器: %s (剩余%d发已实/空翻转)\n", ok ? "OK" : "FAIL", g.nShells - g.pos);
g.itemCnt[0][ITEM_PHONE] = 1;
rc = applyItem(g, 0, ITEM_PHONE);
if (rc != 0 || g.phoneN != 1 || g.phonePosArr[0] < g.pos || g.phonePosArr[0] >= g.nShells) {
printf("电话位置错误\n"); ok = 0;
}
else if (g.phoneValArr[0] != g.shells[g.phonePosArr[0]]) { printf("电话值错误\n"); ok = 0; }
printf("电话: %s (告知第%d发=%s)\n", ok ? "OK" : "FAIL",
g.phonePosArr[0] + 1, g.phoneValArr[0] ? "实弹" : "空弹");
{
g.phoneN = 0;
g.itemCnt[0][ITEM_PHONE] = 2;
rc = applyItem(g, 0, ITEM_PHONE);
if (rc != 0) { printf("第2个电话使用失败\n"); ok = 0; }
rc = applyItem(g, 0, ITEM_PHONE);
if (rc != 0) { printf("第3个电话使用失败\n"); ok = 0; }
if (g.phoneN != 2) { printf("连续使用多个手机应全部保留提示, 实际%d条\n", g.phoneN); ok = 0; }
int i2;
for (i2 = 0; i2 < g.phoneN; i2++)
if (g.phonePosArr[i2] < g.pos || g.phonePosArr[i2] >= g.nShells ||
g.phoneValArr[i2] != g.shells[g.phonePosArr[i2]]) {
printf("多条电话提示内容错误 i=%d\n", i2); ok = 0;
}
printf("多电话保留: %s (连续%d个手机全部保留)\n", ok ? "OK" : "FAIL", g.phoneN);
}
g.hp[0] = 1;
g.maxHp[0] = 5;
int up = 0, down = 0, t;
for (t = 0; t < 200; t++) {
int bh = g.hp[0];
g.itemCnt[0][ITEM_PILL] = 1;
rc = applyItem(g, 0, ITEM_PILL);
if (rc != 0) { printf("药品返回值错误\n"); ok = 0; }
if (g.hp[0] == bh + 2) up++;
else if (g.hp[0] == bh - 1) down++;
else { printf("药品结果异常 %d->%d\n", bh, g.hp[0]); ok = 0; }
g.hp[0] = 1;
}
printf("过期药品: %s (恢复+2:%d次 扣除-1:%d次)\n", ok ? "OK" : "FAIL", up, down);
{
int up2 = 0, down2 = 0;
g_pillLuck = 1;
for (t = 0; t < 300; t++) {
g.hp[0] = 1;
g.itemCnt[0][ITEM_PILL] = 1;
applyItem(g, 0, ITEM_PILL);
if (g.hp[0] == 3) up2++; else if (g.hp[0] == 0) down2++;
g.hp[0] = 1;
}
g_pillLuck = 0;
if (up2 < 180) { printf("困难药品回血概率未提升: 回血%d/300\n", up2); ok = 0; }
printf("困难药品概率: %s (回血%d/300, 期望~210)\n", ok ? "OK" : "FAIL", up2);
}
newRound(g);
if (g.pos < g.nShells && g.shells[g.pos] == 0) {
g.knifeReady = 0;
g.itemCnt[0][ITEM_KNIFE] = 1;
applyItem(g, 0, ITEM_KNIFE);
int dmg = fire(g, 1);
if (dmg != 0) { printf("小刀对空弹应造成0伤害, 实际%d\n", dmg); ok = 0; }
printf("小刀空弹测试: %s (空弹伤害=%d)\n", ok ? "OK" : "FAIL", dmg);
}
{
Game t;
resetGame(t, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
newRound(t);
if (t.nShells >= 3) {
t.phoneN = 0;
t.phonePosArr[0] = t.pos + 2;
t.phoneValArr[0] = t.shells[t.phonePosArr[0]];
t.phoneN = 1;
int before = t.phonePosArr[0];
int firedPos0 = t.pos;
fire(t, 1);
if (t.phoneN == 1 && t.phonePosArr[0] == before) {
printf("电话延迟: OK (发射第%d发后, 预测的第%d发仍显示)\n", firedPos0 + 1, before + 1);
} else {
printf("电话应在预测子弹发射后才消失, 提前消失了\n"); ok = 0;
}
while (t.pos <= before && t.pos < t.nShells) fire(t, 1);
if (t.pos > before && t.phoneN == 0)
printf("电话延迟: OK (预测的第%d发被发射后消失)\n", before + 1);
else { printf("电话应在预测子弹发射后消失\n"); ok = 0; }
} else {
printf("电话延迟: 跳过(本轮子弹不足3发)\n");
}
}
{
Game t;
resetGame(t, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
t.hp[0] = 1;
t.itemCnt[1][ITEM_CIG] = 1;
t.itemCnt[1][ITEM_ADREN] = 2;
t.itemCnt[0][ITEM_ADREN] = 1;
int rc = applyItem(t, 0, ITEM_ADREN);
if (rc != 1) { printf("肾上腺素应正常使用(返回1), 实际%d\n", rc); ok = 0; }
if (t.itemCnt[0][ITEM_CIG] != 0 || t.itemCnt[1][ITEM_CIG] != 0) {
printf("未偷到香烟或未立即使用: 玩家=%d 恶魔=%d\n",
t.itemCnt[0][ITEM_CIG], t.itemCnt[1][ITEM_CIG]);
ok = 0;
}
if (t.hp[0] != 2) { printf("偷到的香烟未立即生效, HP=%d\n", t.hp[0]); ok = 0; }
if (t.itemCnt[0][ITEM_ADREN] != 0 || t.itemCnt[1][ITEM_ADREN] != 2) {
printf("肾上腺素不应被偷取(无法盗取肾上腺素), 玩家=%d 恶魔=%d\n",
t.itemCnt[0][ITEM_ADREN], t.itemCnt[1][ITEM_ADREN]);
ok = 0;
}
t.itemCnt[0][ITEM_ADREN] = 1;
t.itemCnt[1][ITEM_CIG] = 0;
rc = applyItem(t, 0, ITEM_ADREN);
if (rc != 2) { printf("对方只有肾上腺素时应无法使用(返回2), 实际%d\n", rc); ok = 0; }
if (t.itemCnt[0][ITEM_ADREN] != 1) { printf("无效使用后肾上腺素应返还\n"); ok = 0; }
printf("肾上腺素: %s (自主偷取、立即使用、排除肾上腺素)\n", ok ? "OK" : "FAIL");
}
{
Game t;
resetGame(t, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
newRound(t);
t.itemCnt[0][ITEM_BEER] = 1;
int nBefore = t.nShells - t.pos;
int rc = applyItem(t, 0, ITEM_BEER);
if (rc != 0 || (t.nShells - t.pos) != nBefore - 1) { printf("啤酒未正确弹出\n"); ok = 0; }
if (strstr(g_lastMsg, "弹出了第") == NULL ||
(strstr(g_lastMsg, "实弹") == NULL && strstr(g_lastMsg, "空弹") == NULL)) {
printf("啤酒文案不明确: %s\n", g_lastMsg); ok = 0;
}
printf("啤酒: %s (%s)\n", ok ? "OK" : "FAIL", g_lastMsg);
}
{
Game t;
resetGame(t, MODE_CLASSIC, DIFF_EASY, "ItemTest");
newRound(t);
int oldAuto = g_auto, oldPause = g_noPause, oldCert = g_autoCertain;
g_auto = 0; g_noPause = 1; g_autoCertain = 1;
int i;
for (i = t.pos; i < t.nShells; i++) t.shells[i] = 0;
t.playerTurn = 1;
int hpBefore = t.hp[0];
int rc = playerAction(t);
if (rc != 0 || t.hp[0] != hpBefore) { printf("全空自动执行异常\n"); ok = 0; }
if (t.playerTurn != 1) { printf("全空自动打自己后应保留回合\n"); ok = 0; }
printf("全空自动执行: %s (自动打自己保留回合)\n", ok ? "OK" : "FAIL");
newRound(t);
for (i = t.pos; i < t.nShells; i++) t.shells[i] = 1;
t.playerTurn = 1;
int dhp = t.hp[1];
rc = playerAction(t);
if (rc != 0 || t.hp[1] != dhp - 1) { printf("全实自动执行异常\n"); ok = 0; }
if (t.playerTurn != 0) { printf("全实自动打对方后应移交回合\n"); ok = 0; }
printf("全实自动执行: %s (自动打对方造成1伤)\n", ok ? "OK" : "FAIL");
g_auto = oldAuto; g_noPause = oldPause; g_autoCertain = oldCert;
}
{
Game t;
resetGame(t, MODE_CLASSIC, DIFF_EASY, "ItemTest");
newRound(t);
int oldPause = g_noPause; g_noPause = 1;
t.cuffed[1] = 1;
t.playerTurn = 1;
int i;
for (i = t.pos; i < t.nShells; i++) t.shells[i] = 0;
fire(t, 1);
if (t.playerTurn != 0) { printf("射击对方后应移交恶魔回合\n"); ok = 0; }
dealerAction(t);
if (t.playerTurn != 1) { printf("恶魔被铐跳过应把回合还给玩家\n"); ok = 0; }
if (strstr(g_lastMsg, "手铐") == NULL || strstr(g_lastMsg, "你射击") != NULL) {
printf("手铐跳过反馈错误(残留上一步文案): %s\n", g_lastMsg); ok = 0;
}
printf("手铐跳过反馈: %s (%s)\n", ok ? "OK" : "FAIL", g_lastMsg);
g_noPause = oldPause;
}
printf(ok ? "\nItemTest 全部通过\n" : "\nItemTest 存在失败项\n");
}
int main(int argc, char** argv) {
srand((unsigned)time(NULL));
ensureDirs();
if (argc > 1 && strcmp(argv[1], "autotest") == 0) {
runAutoTest();
return 0;
}
if (argc > 1 && strcmp(argv[1], "flowtest") == 0) {
runFlowTest();
return 0;
}
if (argc > 1 && strcmp(argv[1], "itemtest") == 0) {
runItemTest();
return 0;
}
mainMenu();
return 0;
}