回精華區
作者itoc (站上人數:802), 信區: plan
標題[功能] 橋牌打譜
時間2004/10/25 Mon 02:27:37 Updated: 2004/10/25
: menu.c 適當的選單加入

  "bin/bridge.so:main_bridge", 0, - M_XMODE,
  "Bridge     ♂ 橋牌打譜 ♀",

: src/so/Makefile

SO =    .... bridge

: src/so/bridge.c 新增此程式

/*-------------------------------------------------------*/
/* bridge.c     ( NTHU CS MapleBBS Ver 3.10 )            */
/*-------------------------------------------------------*/
/* target : 橋牌打譜                                     */
/* create : 04/10/24                                     */
/* update :   /  /                                       */
/* author : itoc.bbs@bbs.tnfsh.tn.edu.tw                 */
/*-------------------------------------------------------*/


#include "bbs.h"

enum {NORTH, WEST, EAST, SOUTH, TOTAL_PLAYER};
enum {SPADE, HEART, DIAMOND, CLUB, TOTAL_SUIT};

#define SUIT_LEN        13      /* 每種花色 13 張牌 */
#define CYCLE_MAX       10      /* 假設每人最多叫 10 次 */

/* 各家牌的坐標 */
static const int XPOS[TOTAL_PLAYER] = {2, 6, 6, 10};
static const int YPOS[TOTAL_PLAYER] = {8, 0, 16, 8};

/* 叫牌坐標 */
static const int XPOS_BID = 6;
static const int YPOS_BID = 32;

static const char MSGPLAYER[TOTAL_PLAYER][3] = {"北", "西", "東", "南"};
static const char MSGSUIT[TOTAL_PLAYER][5] = {"黑桃", "紅心", "磚塊", "梅花"};
static const char MSGPOINT[SUIT_LEN][3] = {"A", "2", "3", "4", "5",
  "6", "7", "8", "9", "T", "J", "Q", "K"};

static char cards[TOTAL_PLAYER][TOTAL_SUIT][SUIT_LEN + 1];
static char bids[TOTAL_PLAYER][CYCLE_MAX][3];
static char leader[3];
static char result[8];

static int Open;        /* 開叫 */

static int Debug;       /* 1:會檢查輸入牌組是否正確 */


static int              /* 1:正確 */
valid_suit(str)         /* 檢查是否為正確的輸入 */
  char *str;
{
  int ch;
  char *ptr;

  if (!Debug)
    return 1;

  ptr = str;
  while (ch = *ptr)
  {
    if (ch == 't' || ch == 'j' || ch == 'q' || ch == 'k' || ch == 'a')
      *ptr = ch & ~0x20;        /* 換大寫 */
    else if (!(ch >= '2' && ch <= '9') &&
      !(ch == 'T' || ch == 'J' || ch == 'Q' || ch == 'K' || ch == 'A'))
      return 0;
    ptr++;
  }

  return 1;
}


static int
suit_cmp(a, b)          /* 由大至小排序 */
  char *a;
  char *b;
{
  int ac, bc;

  ac = (*a == 'T') ? ('0' + 10) :
       (*a == 'J') ? ('0' + 11) :
       (*a == 'Q') ? ('0' + 12) :
       (*a == 'K') ? ('0' + 13) :
       (*a == 'A') ? ('0' + 14) :
       *a;
  bc = (*b == 'T') ? ('0' + 10) :
       (*b == 'J') ? ('0' + 11) :
       (*b == 'Q') ? ('0' + 12) :
       (*b == 'K') ? ('0' + 13) :
       (*b == 'A') ? ('0' + 14) :
       *b;

  return bc - ac;
}


static void
input_cards(echo)       /* 輸入各家的牌 */
  int echo;
{
  int x, y;
  int player, suit;
  char msg[32], *str;

  for (player = 0; player < TOTAL_PLAYER; player++)
  {
    x = XPOS[player];
    y = YPOS[player] << 1;

    for (suit = 0; suit < TOTAL_SUIT; suit++)
    {
      move(x + suit, 0);
      clrtoeol();
      sprintf(msg, "%s家%s的牌張:", MSGPLAYER[player], MSGSUIT[suit]);
      str = cards[player][suit];

      do
      {
        vget(x + suit, y, msg, str, SUIT_LEN + 1, echo);
      } while (!valid_suit(str));

      if (Debug)
        qsort(str, strlen(str), sizeof(char), suit_cmp);
    }
  }
}


static int              /* 1:過 0:沒過 */
check_cards()           /* 檢查各家的牌 */
{
  char total[TOTAL_SUIT][SUIT_LEN];
  int player, suit, ch, success;
  char msg[32], *ptr;

  if (!Debug)
    return 1;

  /* 檢查每人張數 */

  for (player = 0; player < TOTAL_PLAYER; player++)
  {
    ch = 0;
    for (suit = 0; suit < TOTAL_SUIT; suit++)
      ch += strlen(cards[player][suit]);
    if (ch != TOTAL_SUIT * SUIT_LEN / TOTAL_PLAYER)
    {
      sprintf(msg, "%s家的總張數不對", MSGPLAYER[player]);
      vmsg(msg);
    }
  }


  /* 檢查牌是否有重覆 */

  memset(total, 0, sizeof(total));

  for (player = 0; player < TOTAL_PLAYER; player++)
  {
    for (suit = 0; suit < TOTAL_SUIT; suit++)
    {
      ptr = cards[player][suit];
      while (ch = *ptr)
      {
        ch = (ch == 'T') ? (10 - 1) :
             (ch == 'J') ? (11 - 1) :
             (ch == 'Q') ? (12 - 1) :
             (ch == 'K') ? (13 - 1) :
             (ch == 'A') ? (1 - 1) :
             ch - '1';

        total[suit][ch]++;
        ptr++;
      }
    }
  }

  success = 1;
  for (suit = 0; suit < TOTAL_SUIT; suit++)
  {
    for (ch = 0; ch < SUIT_LEN; ch++)
    {
      player = total[suit][ch] - 1;      /* 借用 player */
      if (player)
      {
        sprintf(msg, "%s%s %s %d 張", MSGSUIT[suit], MSGPOINT[ch],
          player > 0 ? "多" : "少", abs(player));
        vmsg(msg);
        success = 0;
      }
    }
  }

  return success;
}


static void
outs_cards()            /* 印出各家的牌 */
{
  int x, y;
  int player, suit;
  char *str;

  move(1, 0);
  clrtobot();

  for (player = 0; player < TOTAL_PLAYER; player++)
  {
    x = XPOS[player];
    y = YPOS[player];

    for (suit = 0; suit < TOTAL_SUIT; suit++)
    {
      str = cards[player][suit];
      move(x + suit, y);
      outs(*str ? str : "-");
    }
  }
}


static int              /* 1:正確 */
valid_call(str)         /* 檢查是否為正確的輸入 */
  char *str;
{
  int ch;

  /* 叫品只檢查筆誤,而不檢查是否有邏輯上的錯誤(太複雜) */

  ch = str[0];
  if (!ch)      /* pass */
  {
    str[0] = '-';
    str[1] = '\0';
    return 1;
  }

  if (!Debug)
    return 1;

  if ((ch < '1' || ch > '7') && (ch != 'x' && ch != 'X'))
    return 0;
  if (ch == 'x')
    str[0] = 'X';       /* 換大寫 */

  ch = str[1];
  if (ch != 'c' && ch != 'C' &&
      ch != 'd' && ch != 'D' &&
      ch != 'h' && ch != 'H' &&
      ch != 's' && ch != 'S' &&
      ch != 'n' && ch != 'N')
    return 0;
  str[1] = ch & ~0x20;  /* 換大寫 */

  return 1;
}


static void
input_bids()
{
  int player, cycle, pass;
  char msg[32], *str;

  while (1)     /* 借用 pass */
  {
    pass = vans("開叫的是 (1)北 (2)東 (3)南 (4)西:");
    if (pass < '1' || pass > '4')
      continue;
    Open = (pass == '1') ? NORTH :
           (pass == '2') ? EAST :
           (pass == '3') ? SOUTH :
                         WEST;
    break;
  }

  player = Open;
  cycle = 0;
  pass = -1;            /* 第一圈要四個人都 pass 才結束 */
  do
  {
    sprintf(msg, "%s家叫品 (Enter=Pass):", MSGPLAYER[player]);
    str = bids[player][cycle];

    do
    {
      vget(b_lines, 0, msg, str, 3, DOECHO);
    } while (!valid_call(str));

    if (str[0] == '-')
    {
      if (++pass >= 3)
        str[0] = '=';   /* 最後一家 */
    }
    else
      pass = 0;

    /* 輪下一家叫 */
    if (player == NORTH)
      player = EAST;
    else if (player == EAST)
      player = SOUTH;
    else if (player == SOUTH)
      player = WEST;
    else
      player = NORTH;

    if (player == Open)
    {
      if (++cycle >= CYCLE_MAX)
        return;
    }
  } while (pass < 3);

  /* Pass 到結束 */
  bids[player][cycle][0] = '\0';

  vget(b_lines, 0, "首引:", leader, 3, DOECHO);
  vget(b_lines, 0, "結果:", result, 8, DOECHO);
}


static void
outs_bids()
{
  int x;
  int player, cycle;
  char *str;

  x = XPOS_BID;

  move(x++, YPOS_BID);
  outs(Open == NORTH ? "North   East    South   West" :
       Open == EAST  ? "East    South   West    North" :
       Open == SOUTH ? "South   West    North   East" :
                       "West    North   East    South");

  player = Open;
  cycle = 0;
  move(x++, YPOS_BID);
  while (1)
  {
    str = bids[player][cycle];
    if (!*str)          /* 印完了 */
      break;

    prints("%-8s", str);

    /* 輪下一家叫 */
    if (player == NORTH)
      player = EAST;
    else if (player == EAST)
      player = SOUTH;
    else if (player == SOUTH)
      player = WEST;
    else
      player = NORTH;

    if (player == Open)
    {
      if (++cycle >= CYCLE_MAX)
        break;
      move(x++, YPOS_BID);
    }
  }

  x += 2;
  move(x, YPOS_BID);
  prints("Leader: %-8sResult:%s", leader, result);
}


int
main_bridge()
{
  switch (vans("◎橋牌打譜 1)希望 2)不希望 程式檢查輸入牌組 [Q] "))
  {
  case '1':
    Debug = 1;
    break;
  case '2':
    Debug = 0;
    break;
  default:
    return XEASY;
  }

  do
  {
    vs_bar("橋牌打譜");
    refresh();

    input_cards(DOECHO);
    while (!check_cards())      /* 重新輸入 */
    {
      vmsg("牌組輸入有誤,請重新輸入");
      move(b_lines, 0);
      clrtoeol();
      input_cards(GCARRY);
    }

    input_bids();

    outs_cards();
    outs_bids();
  } while (vmsg("按 q 離開,其他鍵繼續打譜") != 'q');

  return 0;
}

--
□ 本文章由 itocitoc.Dorm11.NCTU.edu.tw 發表
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻