更新内容

#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;
}