作者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;
}
--
□ 本文章由 itoc 從 itoc.Dorm11.NCTU.edu.tw 發表