更新内容见上级博客.

#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;
int g_anim = 1;
int g_titleShown = 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 int rawKey();
static void flushInput();
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 void delayMs(int ms) {
#ifdef _WIN32
 Sleep(ms);
#else
 usleep(ms * 1000);
#endif
}
static void animReload() {
 if (!g_anim || g_auto) return;
 const char* spin = "|/-\\";
 int k, f;
 for (k = 0; k < 4; k++) {
  printf("\r  装填中 %c [", spin[k % 4]);
  for (f = 0; f < 8; f++) printf(f < ((k + 1) * 8 / 4) ? "#" : ".");
  printf("] %d%%", (k + 1) * 25);
  fflush(stdout);
  delayMs(35);
 }
 printf("\r                    \r");
 fflush(stdout);
}
static void animStage(int st) {
 if (!g_anim || g_auto) return;
 setColor(14);
 printf("\n  ===== 第 %d/3 大轮 开始 =====\n", st);
 setColor(7);
 fflush(stdout);
 delayMs(160);
}
static void animShotFire() {
 if (!g_anim || g_auto) return;
 setColor(12);
 printf("  >>> 砰!!! <<<\n");
 setColor(7);
 fflush(stdout);
 delayMs(80);
}
static void animShotBlank() {
 if (!g_anim || g_auto) return;
 setColor(8);
 printf("  >>> 咔嗒 (空弹) <<<\n");
 setColor(7);
 fflush(stdout);
 delayMs(50);
}
static int readNum() {
 int val = 0, got = 0;
 while (1) {
  int ch = rawKey();
  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
 int c = _getch();
 while (_kbhit()) _getch();
 return c;
#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);
 tcflush(0, TCIFLUSH);
 return ch;
#endif
}
static void flushInput() {
#ifdef _WIN32
 while (_kbhit()) _getch();
#else
 tcflush(0, TCIFLUSH);
#endif
}
static int rawKey() {
#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 "系统";
}
static void printItemList(const Game& g, int who) {
 int k, any = 0;
 for (k = 0; k < NITEM; k++) if (g.itemCnt[who][k] > 0) { printf("%s x%d ", itemName(k), g.itemCnt[who][k]); any = 1; }
 if (!any) printf("(无)");
}
int  stageMaxHp(int st);
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] = stageMaxHp(1);
 g.maxHp[1] = stageMaxHp(1);
 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;
}
int stageMaxHp(int st) {
 if (st == 1) return 2;
 if (st == 2) return 4;
 return 6;
}
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;
 if (g.mode == MODE_INFINITE) give = 8;
 else give = 1 + g.stage;
 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;
 animReload();
 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 (dmg > 0) {
  if (g.stage >= 3 && g.hp[vic] <= 2) g.hp[vic] = 0;
  else g.hp[vic] -= dmg;
 }
 if (isLive) { animShotFire(); }
 else { animShotBlank(); }
 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.stage >= 3 && g.hp[who] <= 2) {
    cnt[it]++;
    sprintf(det, "[香烟] 第三大轮保护血区域, 无法恢复");
    strcpy(g_lastMsg, det);
    addLog(g, who, "香烟", det, -1, 0);
    return 2;
   }
   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: {
   if (g.stage >= 3 && g.hp[who] <= 2) {
    cnt[it]++;
    sprintf(det, "[过期药品] 第三大轮保护血区域, 药品无效");
    strcpy(g_lastMsg, det);
    addLog(g, who, "过期药品", det, -1, 0);
    return 2;
   }
   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) {
 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");
 if (g.stage >= 3 && g.hp[0] <= 2) { setColor(12); printf("  [保护血] 你已进入保护血: 无法恢复, 受伤害即死!\n"); setColor(7); }
 if (g.stage >= 3 && g.hp[1] <= 2) { setColor(12); printf("  [保护血] 恶魔进入保护血: 无法恢复, 受伤害即死!\n"); setColor(7); }
 setColor(11);
 printf(" 你的道具: ");
 setColor(7);
 printItemList(g, 0);
 printf("\n");
 setColor(11);
 printf(" 恶魔道具: ");
 setColor(7);
 printItemList(g, 1);
 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");
}
static void dealerNormal(Game& g) {
 int who = ACT_DEALER;
 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 && g.hp[who] >= 4 && g.hp[who] < g.maxHp[who]) {
   int hpBefore = g.hp[who];
   applyItem(g, who, ITEM_PILL);
   if (g.hp[who] < hpBefore && g.hp[who] > 2 && 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);
}
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) {
  dealerNormal(g);
  return;
 }
 if (rand() % 2 == 0) {
  int isLive = (g.pos < g.nShells) ? g.shells[g.pos] : -1;
  if (g.itemCnt[who][ITEM_PILL] > 0 && g.hp[who] >= 3 && g.hp[who] < g.maxHp[who]) {
   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);
    fire(g, 1);
    return;
   }
   if (g.hp[who] > 2 && g.hp[who] < g.maxHp[who] && g.itemCnt[who][ITEM_CIG] > 0)
    applyItem(g, who, ITEM_CIG);
   fire(g, 0);
  } else {
   if (g.itemCnt[who][ITEM_BEER] > 0) {
    applyItem(g, who, ITEM_BEER);
    if (g.pos >= g.nShells) return;
   } else fire(g, 1);
  }
 } else {
  dealerNormal(g);
 }
}
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;
 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("   你  : ");
 printItemList(g, 0);
 printf("\n   恶魔: ");
 printItemList(g, 1);
 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("  下一大轮双方生命重置为 %d 血, 重新装填子弹...\n", stageMaxHp(g.stage + 1));
 }
 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.maxHp[0] = stageMaxHp(g.stage);
   g.maxHp[1] = stageMaxHp(g.stage);
   g.hp[0] = g.maxHp[0];
   g.hp[1] = g.maxHp[1];
   g.blanksSelfHit = 0;
   animStage(g.stage);
  } else {
   if (g.hp[0] > g.maxHp[0]) g.hp[0] = g.maxHp[0];
   if (g.hp[1] > g.maxHp[1]) g.hp[1] = g.maxHp[1];
  }
  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");
 printf(" 一局3大轮, 生命上限第1/2/3大轮=2/4/6血, 率先赢下2轮者胜\n");
 printf(" 仅第3大轮有保护血: 最后2血不能用香烟/药品恢复, 受伤害即死\n");
 printf(" 装填实弹/空弹随机(至少各1), 装弹时告知总数(设置可关实时显示)\n");
 printf(" 打自己: 实弹→掉血换回合; 空弹→无伤保留回合\n");
 printf(" 打对方: 实弹→对方掉血换回合; 空弹→白送对方回合\n");
 printf("* 道具(每次装弹发放, 按大轮2/3/4个, 上限8)\n");
 printf(" 放大镜: 看下一发实/空, 本回合内一直显示\n");
 printf(" 小刀: 下一发伤害翻倍(空弹仍0)  手铐: 跳过对方下回合\n");
 printf(" 啤酒: 退出当前下一发(实空都弹掉)  香烟: 恢复1点(仅第3轮保护血内无效)\n");
 printf(" 肾上腺素: 偷对方1道具并立即用(不能偷肾上腺素), 回合继续\n");
 printf(" 逆转器: 剩余子弹全部实空互换  电话: 随机告知某发未发射子弹, 发射后失效\n");
 printf(" 过期药品: 等概率+2或-1生命(仅第3轮保护血内无效)\n");
 printf("* 模式: 经典/开局供给(按大轮发2/3/4) 无道具(纯对拼) 无限道具(补满8)\n");
 printf("* 难度: 简单(随机+用道具) 中等(连招+药品期望) 困难(随机透视, 透视禁用小刀/手铐)\n");
printf("* 其他: 局中[4]存档1-9, 主菜单[6]设置(显示/辅助/自动/动画, 自动保存)\n");
 printf("==============================\n");
 pause1();
}
int modeSelect() {
 while (1) {
  cls();
  setColor(14);
  printf("==== 选择模式 ====\n");
  setColor(7);
  printf("  [1] 经典     (每次装弹按大轮发2/3/4个道具)\n");
  printf("  [2] 开局供给 (同经典, 按大轮发2/3/4个)\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] 简单   (恶魔随机开枪, 也会使用道具)\n");
  printf("  [2] 中等   (恶魔道具连招+概率分析)\n");
  printf("  [3] 困难   (恶魔随机透视枪膛, 透视时禁用小刀/手铐)\n");
  printf("  双方生命按大轮: 第1/2/3大轮各2/4/6血\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 settingsSave() {
 FILE* f = fopen("settings.dat", "w");
 if (!f) return;
 fprintf(f, "showShell=%d\nshowRemain=%d\nautoCertain=%d\nanim=%d\n",
   g_showShell, g_showRemain, g_autoCertain, g_anim);
 fclose(f);
}
void settingsLoad() {
 FILE* f = fopen("settings.dat", "r");
 if (!f) return;
 char key[32];
 int val;
 while (fscanf(f, "%31[^=\n]=%d\n", key, &val) == 2) {
  if (strcmp(key, "showShell") == 0) g_showShell = val;
  else if (strcmp(key, "showRemain") == 0) g_showRemain = val;
  else if (strcmp(key, "autoCertain") == 0) g_autoCertain = val;
  else if (strcmp(key, "anim") == 0) g_anim = val;
 }
 fclose(f);
}
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("  [4] 过场动画: %s\n", g_anim ? "开" : "关");
  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 == '4') g_anim = !g_anim;
  if (c == '0') { settingsSave(); return; }
 }
}
int mainMenu() {
 while (1) {
  cls();
  if (!g_titleShown && g_anim) {
   setColor(12);
   printf("      恶");
   fflush(stdout); delayMs(180);
   printf("魔");
   fflush(stdout); delayMs(180);
   printf("轮");
   fflush(stdout); delayMs(180);
   printf("盘");
   fflush(stdout); delayMs(300);
   printf("\n");
   setColor(7);
   g_titleShown = 1;
   delayMs(300);
   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');
   flushInput();
   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() {
 g_auto = 1;
 g_noPause = 1;
 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() {
 g_anim = 0;
 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] = 3;
 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] = 3;
 }
 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] = 3;
   g.itemCnt[0][ITEM_PILL] = 1;
   applyItem(g, 0, ITEM_PILL);
   if (g.hp[0] == 5) up2++; else if (g.hp[0] == 2) down2++;
   g.hp[0] = 3;
  }
  g_pillLuck = 0;
  if (up2 < 180) { printf("困难药品回血概率未提升: 回血%d/300\n", up2); ok = 0; }
  printf("困难药品概率: %s (回血%d/300, 期望~210)\n", ok ? "OK" : "FAIL", up2);
 }
 {
  g.stage = 3;
  g.hp[0] = 2;
  g.itemCnt[0][ITEM_CIG] = 1;
  rc = applyItem(g, 0, ITEM_CIG);
  if (rc != 2 || g.hp[0] != 2 || g.itemCnt[0][ITEM_CIG] != 1) {
   printf("第三轮保护血香烟应无效并返还\n"); ok = 0;
  }
  g.itemCnt[0][ITEM_PILL] = 1;
  rc = applyItem(g, 0, ITEM_PILL);
  if (rc != 2 || g.hp[0] != 2 || g.itemCnt[0][ITEM_PILL] != 1) {
   printf("第三轮保护血药品应无效并返还\n"); ok = 0;
  }
  printf("第三轮保护血恢复无效: %s\n", ok ? "OK" : "FAIL");
  {
   Game t2;
   resetGame(t2, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
   newRound(t2);
   t2.stage = 3;
   t2.hp[1] = 2;
   t2.shells[t2.pos] = 1;
   t2.knifeReady = 0;
   int dm = fire(t2, 1);
   if (dm != 1 || t2.hp[1] != 0) {
    printf("第三轮保护血受伤害应立即死亡, hp=%d dmg=%d\n", t2.hp[1], dm); ok = 0;
   }
   printf("第三轮保护血即死: %s\n", ok ? "OK" : "FAIL");
   Game t3;
   resetGame(t3, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
   newRound(t3);
   t3.stage = 1;
   t3.hp[1] = 2;
   t3.maxHp[1] = 4;
   t3.shells[t3.pos] = 1;
   t3.knifeReady = 0;
   fire(t3, 1);
   if (t3.hp[1] != 1) { printf("前两轮无保护血: 2血受1伤应扣到1, 实际%d\n", t3.hp[1]); ok = 0; }
   printf("前两轮无保护血正常扣血: %s\n", ok ? "OK" : "FAIL");
   Game t4;
   resetGame(t4, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
   newRound(t4);
   t4.stage = 1;
   t4.hp[0] = 1;
   t4.maxHp[0] = 2;
   t4.itemCnt[0][ITEM_CIG] = 1;
   rc = applyItem(t4, 0, ITEM_CIG);
   if (rc != 0 || t4.hp[0] != 2) { printf("前两轮低血可用香烟恢复, 实际hp=%d rc=%d\n", t4.hp[0], rc); ok = 0; }
   printf("前两轮低血可恢复: %s\n", ok ? "OK" : "FAIL");
  }
 }
 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] = 3;
  t.maxHp[0] = 5;
  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] != 4) { 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;
  t.hp[1] = 4;
  t.maxHp[1] = 4;
  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;
 }
 {
  Game t;
  resetGame(t, MODE_CLASSIC, DIFF_NORMAL, "ItemTest");
  newRound(t);
  t.playerTurn = 1;
  t.shells[t.pos] = 0;
  fire(t, 0);
  if (strncmp(g_lastMsg, "你射击自己", 12) != 0) { printf("玩家文案错误: %s\n", g_lastMsg); ok = 0; }
  t.playerTurn = 0;
  t.shells[t.pos] = 0;
  fire(t, 0);
  if (strncmp(g_lastMsg, "恶魔射击自己", 12) != 0) { printf("恶魔文案错误(指代残留): %s\n", g_lastMsg); ok = 0; }
  t.shells[t.pos] = 1;
  fire(t, 1);
  if (strncmp(g_lastMsg, "恶魔射击你", 10) != 0) { printf("恶魔打玩家文案错误: %s\n", g_lastMsg); ok = 0; }
  printf("射击文案指代: %s\n", ok ? "OK" : "FAIL");
 }
 printf(ok ? "\nItemTest 全部通过\n" : "\nItemTest 存在失败项\n");
}
int main(int argc, char** argv) {
 srand((unsigned)time(NULL));
 ensureDirs();
 settingsLoad();
 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;
}