立直麻将算点器,不排除 bug,欢迎来 hack
使用方法见代码开头注释
// 麻将算点器 by Zhengfourth
/*
格式:mjs [场风][门风][荣和R/自摸T/有自摸损的自摸S][立直R/非立直N]< +[附加役番数]>< D[宝牌数]>< 副露>... [手牌] [第14张手牌]
风:1234=东南西北
牌:1~9s=一到九索,1~9p=一到九筒,1~9m=一到九万,1~7z=东南西北白发中,不区分红宝牌
附加役番数包括:一发、岭上开花、抢杠、河底捞鱼、海底摸月、天和、地和、两立直,天地和输入+M,两立直算额外的一番
宝牌数包括宝牌,里宝牌,红宝牌
副露暗杠用--1p1p--表示
例子:
mjs 11RN --1z1z-- --2z2z-- --3z3z-- --4z4z-- 6z 6z
mjs 21TR +1 D1 1s2s3s4s4s5s5s6s7s8s9s9s9s 6s
mjs 11RN 6z6z6z6z 7z7z7z 2p3p4p5p6p5z5z 7p
mjs 12RN 4s2s3s 2p3p4p 6m7m8m 2m2m2s3s 4s
mjs 11RR +1 D1 --1m1m-- --1z1z-- --5z5z-- 6z6z7z7z 6z
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <list>
#include <algorithm>
[[noreturn]] void __ILLEGAL(int line) {
printf("%d:不合法的输入", line);
exit(-1);
}
#define ILLEGAL() __ILLEGAL(__LINE__)
int tonumber(char* s) {
int len = strlen(s);
if (len > 2) ILLEGAL();
int x = 0;
for (int i = 0; i < len; i++) {
if (!isdigit(s[i])) ILLEGAL();
x = 10 * x + s[i] - '0';
}
return x;
}
int ceil100(int x) {
return (x / 100 * 100) + (x % 100 ? 100 : 0);
}
int ceil10(int x) {
return (x / 10 * 10) + (x % 10 ? 10 : 0);
}
struct Pai { // 一张牌
int type; // s=0,p=1,m=2,1~7z对应3~9
int x; // 数字,字牌为1
bool iszipai() {
return type >= 3;
}
bool is19() {
return x == 1 || x == 9;
}
bool islaotou() {
return is19() && !iszipai();
}
bool islv() {
if (type == 8) return true;
if (type != 0) return false;
return x == 2 || x == 3 || x == 4 || x == 6 || x == 8;
}
bool operator == (Pai ot) {
return type == ot.type && x == ot.x;
}
bool operator < (Pai ot) {
if (type == ot.type) return x < ot.x;
return type < ot.type;
}
bool isnext(Pai ot) {
return type == ot.type && x+1 == ot.x;
}
};
struct Mianzi { // 面子
int type; // 0顺子,1刻子,2杠子
Pai s; // 如果是顺子就是数最小的那张牌
bool fulou; // 是否副露,暗杠是false
bool dai19() { // 是否带幺九
if (type) return s.is19();
else return (s.x == 1 || s.x == 7);
}
int fu() { // 面子的符数
if (type == 0) return 0;
int x;
if (type == 1 && fulou) x = 2;
if (type == 1 && !fulou) x = 4;
if (type == 2 && fulou) x = 8;
if (type == 2 && !fulou) x = 16;
if (s.is19()) x *= 2;
return x;
}
bool contain(Pai x) {
if (type) return s == x;
else return s.type == x.type && x.x-2 <= s.x && s.x <= x.x;
}
};
int zifeng, changfeng;
bool tsumo, riichi, sun; // sun表示有自摸损
int addfan = 0, dora = 0; // 附加番数-1表示天地和
Mianzi fulou[4]; int fulous, sps; // 副露区,组数,手牌张数
Pai sp[14], s14, all[18]; int acnt = 0; // 手牌,第14张手牌,所有牌,总张数
int parsePai(char *s, Pai* p, int n) { // 解析至多n张牌,返回实际解析的牌数
int r = 0;
while (s[2*r]) {
if (r >= n) ILLEGAL();
p[r].x = s[2*r] - '0';
if (p[r].x <= 0 || p[r].x > 9) ILLEGAL();
if (s[2*r+1] == 'z') {
p[r].type = s[2*r] - '0' + 2;
if (p[r].type < 3 || p[r].type > 9) ILLEGAL();
p[r].x = 1;
} else if (s[2*r+1] == 's') p[r].type = 0;
else if (s[2*r+1] == 'p') p[r].type = 1;
else if (s[2*r+1] == 'm') p[r].type = 2;
else ILLEGAL();
r++;
}
return r;
}
Mianzi parseFulou(char *s) { // 解析副露,同时输出到all里
bool angang = false;
if (s[0] == '-') {
if (s[1] == '-' && s[6] == '-' && s[7] == '-') {
s[0] = s[6] = s[2];
s[1] = s[7] = s[3];
angang = true;
} else ILLEGAL();
}
Mianzi ret;
int x = parsePai(s, all+acnt, 4);
if (x == 4) {
if (all[acnt] == all[acnt+1] && all[acnt] == all[acnt+2] && all[acnt] == all[acnt+3]) {
ret.type = 2; // 杠子
ret.s = all[acnt];
ret.fulou = !angang;
} else ILLEGAL();
} else if (x == 3) {
ret.fulou = true;
if (all[acnt] == all[acnt+1] && all[acnt] == all[acnt+2]) {
ret.type = 1; // 刻子
ret.s = all[acnt];
} else {
std::sort(all+acnt, all+acnt+3);
if (all[acnt].isnext(all[acnt+1]) && all[acnt+1].isnext(all[acnt+2])) {
ret.type = 0; // 顺子
ret.s = all[acnt];
} else ILLEGAL();
}
} else ILLEGAL();
acnt += x;
return ret;
}
// 参数格式:[场风][门风][荣和R/自摸T/有自摸损的自摸S][立直R/非立直N]< +[附加役番数]>< D[宝牌数]>< 副露>... [手牌] [第14张手牌]
int h_state = 0;
void handleArg(char* arg, int c) { // c代表这是倒数第几个参数
if (h_state == 0) {
if (strlen(arg) != 4) ILLEGAL();
if (arg[0] < '1' || '4' < arg[0]) ILLEGAL();
if (arg[1] < '1' || '4' < arg[1]) ILLEGAL();
if (arg[2] != 'R' && arg[2] != 'T' && arg[2] != 'S') ILLEGAL();
if (arg[3] != 'R' && arg[3] != 'N') ILLEGAL();
changfeng = arg[0] - '0';
zifeng = arg[1] - '0';
tsumo = (arg[2] == 'T' || arg[2] == 'S');
sun = (arg[2] == 'S');
riichi = (arg[3] == 'R');
h_state = 1;
return;
}
if (arg[0] == '+') {
if (h_state != 1) ILLEGAL();
if (arg[1] == 'M' && arg[2] == '\0')
addfan = -1;
else addfan = tonumber(arg+1);
h_state = 2;
return;
}
if (arg[0] == 'D') {
if (h_state != 1 && h_state != 2) ILLEGAL();
dora = tonumber(arg+1);
h_state = 3;
return;
}
// 处理副露
if (1 <= h_state && h_state <= 3) {
fulous = c-2;
if (fulous < 0 || fulous > 4) ILLEGAL();
h_state = 4;
}
if (h_state == 4) {
if (c > 2) {
fulou[fulous-c+2] = parseFulou(arg);
} else if (c == 2) {
int n = 13-3*fulous;
int x = parsePai(arg, sp, n);
if (n != x) ILLEGAL();
memcpy(all+acnt, sp, n*sizeof(Pai));
acnt += n;
} else if (c == 1) {
if (parsePai(arg, &s14, 1) != 1) ILLEGAL();
sp[13-3*fulous] = all[acnt++] = s14;
}
return;
}
ILLEGAL();
}
bool menqian; // 门前清
int sa, sb, ma; // 不包括宝牌的番数,符数,役满倍数
std::list<std::string> yiz; // 役种
void checkTiandi() {
if (addfan == -1) {
if (zifeng == 1) yiz.push_back("天和");
else yiz.push_back("地和");
ma += 1;
}
}
void checkAdd() {
if (menqian && tsumo) {
yiz.push_front("门前清自摸和 1 番");
sa++;
}
if (riichi) {
yiz.push_front("立直 1 番");
sa++;
}
if (addfan) {
char buf[20];
sprintf(buf, "附加役种 %d 番", addfan);
yiz.push_back(buf);
sa += addfan;
}
}
bool checkGuoshi() {
if (fulous) return false;
int db = 0;
for (int i = 0; i < 14; i++) {
if (!sp[i].is19()) return false;
if (i && sp[i] == sp[i-1]) {
if (db) return false;
else db = i;
}
}
checkTiandi();
if (sp[db] == s14) {
yiz.push_back("国士无双十三面");
ma += 2;
} else {
yiz.push_back("国士无双");
ma += 1;
}
sb = 25;
return true;
}
bool checkQidui() {
if (fulous) return false;
for (int i = 0; i < 7; i++) {
if (!(sp[2*i] == sp[2*i+1])) return false;
if (i && sp[2*i] == sp[2*i-1]) return false;
}
checkTiandi();
bool daqixing = true; // 大七星
for (int i = 0; daqixing && i < 7; i++)
if (!sp[2*i].iszipai()) daqixing = false;
if (daqixing) {
yiz.push_back("字一色");
ma++;
}
sb = 25;
if (ma) return true; // 役满
yiz.push_back("七对子 2 番");
sa += 2;
bool duanyao = true, laotou = true; // 断幺九,混老头
int col = -1; bool haszi = 0; // 混/清一色的颜色,是否包含字牌
for (int i = 0; i < 7; i++) {
if (sp[2*i].is19()) duanyao = false;
else laotou = false;
if (sp[2*i].iszipai()) haszi = true;
else if (col == -1) col = sp[2*i].type;
else if (col != sp[2*i].type) col = -2;
}
if (duanyao) {
yiz.push_back("断幺九 1 番");
sa += 1;
}
if (laotou) {
yiz.push_back("混老头 2 番");
sa += 2;
}
if (col >= 0) {
if (haszi) {
yiz.push_back("混一色 3 番");
sa += 3;
} else {
yiz.push_back("清一色 6 番");
sa += 6;
}
}
checkAdd();
return true;
}
int cs[10], s[10]; // 每个种类的张数(前缀和),当前种类每个数字的张数
Mianzi mian[4]; Pai quetou; int ms; // 搜出的面子和雀头,当前面子组数
bool td = false; // 是否划分成功
int s14pos; // 第14张牌在第几组面子(0到3),雀头为4
int calcFu0() {
int ret = 0;
for (int i = 0; i < 4; i++)
ret += mian[i].fu();
if (7 <= quetou.type && quetou.type <= 9) ret += 2; // 三元牌雀头
if (quetou.type == changfeng + 2) ret += 2; // 场风雀头
if (quetou.type == zifeng + 2) ret += 2; // 自风雀头
if (s14pos == 4) ret += 2; // 单骑听牌
else if (mian[s14pos].type == 0) {
if (mian[s14pos].s.x + 1 == s14.x) ret += 2; // 坎张
else if (mian[s14pos].s.x == 1 && s14.x == 3) ret += 2; // 边张
else if (mian[s14pos].s.x == 7 && s14.x == 7) ret += 2; // 边张
}
return ret;
}
int calcFu() {
int ret = calcFu0();
if (ret == 0) { // 平和
if (menqian && tsumo) ret -= 2; // 平和自摸不算符
if (!menqian && !tsumo) return 30; // 副露平和型点和固定30符
}
ret += 20;
if (tsumo) ret += 2;
else if (menqian) ret += 10;
return ceil10(ret);
}
void checkDasanyuan() {
int k = 0;
for (int i = 0; i < 4; i++) {
if (mian[i].type != 0 && 7 <= mian[i].s.type && mian[i].s.type <= 9) k++;
}
if (k == 3) {
yiz.push_back("大三元");
ma += 1;
}
}
void checkSixi() {
int k = 0;
for (int i = 0; i < 4; i++) {
if (mian[i].type != 0 && 3 <= mian[i].s.type && mian[i].s.type <= 6) k++;
}
if (k == 4) {
yiz.push_back("大四喜");
ma += 2;
}
if (k == 3 && 3 <= quetou.type && quetou.type <= 6) {
yiz.push_back("小四喜");
ma += 1;
}
}
void checkZiyise() {
if (!quetou.iszipai()) return;
for (int i = 0; i < 4; i++)
if (!mian[i].s.iszipai()) return;
yiz.push_back("字一色");
ma += 1;
}
void checkQinglaotou() {
if (!quetou.islaotou()) return;
for (int i = 0; i < 4; i++)
if (mian[i].type == 0 || !mian[i].s.islaotou()) return;
yiz.push_back("清老头");
ma += 1;
}
void checkLvyise() {
if (!quetou.islv()) return;
for (int i = 0; i < 4; i++) {
if (mian[i].type == 0) {
if (mian[i].s.type != 0 || mian[i].s.x != 2) return;
} else if (!mian[i].s.islv()) return;
}
yiz.push_back("绿一色");
ma += 1;
}
void checkSigangzi() {
for (int i = 0; i < 4; i++)
if (mian[i].type != 2) return;
yiz.push_back("四杠子");
ma += 1;
}
void checkJiulian() {
if (!menqian) return;
int ls[10];
for (int i = 1; i <= 9; i++) ls[i] = 0;
for (int i = 0; i < 14; i++) {
if (i && sp[i].type != sp[i-1].type) return;
ls[sp[i].x]++;
}
ls[1] -= 2;
ls[9] -= 2;
int p = 0;
for (int i = 1; i <= 9; i++) {
if (ls[i] < 1) return;
if (ls[i] > 1) p = i;
}
if (p == s14.x) {
yiz.push_back("纯正九莲宝灯");
ma += 2;
} else {
yiz.push_back("九莲宝灯");
ma += 1;
}
}
void checkSianke() {
if (!menqian) return;
for (int i = 0; i < 4; i++)
if (!mian[i].type) return;
if (s14 == quetou) {
yiz.push_back("四暗刻单骑");
ma += 2;
} else {
if (tsumo) {
yiz.push_back("四暗刻");
ma += 1;
} // else 荣和的刻子不算暗刻,是三暗刻对对和
}
}
void checkDuanyao() {
if (quetou.is19()) return;
for (int i = 0; i < 4; i++)
if (mian[i].dai19()) return;
yiz.push_back("断幺九 1 番");
sa += 1;
}
void checkYipai() {
for (int i = 0; i < 4; i++) if (mian[i].type) {
if (mian[i].s.type == 7) {
yiz.push_back("役牌:白 1 番");
sa++;
}
if (mian[i].s.type == 8) {
yiz.push_back("役牌:发 1 番");
sa++;
}
if (mian[i].s.type == 9) {
yiz.push_back("役牌:中 1 番");
sa++;
}
if (mian[i].s.type == changfeng + 2) {
yiz.push_back("场风牌 1 番");
sa++;
}
if (mian[i].s.type == zifeng + 2) {
yiz.push_back("自风牌 1 番");
sa++;
}
}
}
void checkSangangzi() {
int k = 0;
for (int i = 0; i < 4; i++)
if (mian[i].type == 2) k++;
if (k == 3) {
yiz.push_back("三杠子 2 番");
sa += 2;
}
}
void checkXiaosanyuan() {
int k = 0;
for (int i = 0; i < 4; i++)
if (7 <= mian[i].s.type && mian[i].s.type <= 9) k++;
if (7 <= quetou.type && quetou.type <= 9) k++;
if (k == 3) {
yiz.push_back("小三元 2 番");
sa += 2;
}
}
void checkHunlaotou() {
if (!quetou.is19()) return;
for (int i = 0; i < 4; i++)
if (mian[i].type == 0 || !mian[i].s.is19()) return;
yiz.push_back("混老头 2 番");
sa += 2;
}
void checkYise() {
int col = -1; bool haszi = 0; // 混/清一色的颜色,是否包含字牌
for (int i = 0; i < 4; i++) {
if (mian[i].s.iszipai()) haszi = true;
else if (col == -1) col = mian[i].s.type;
else if (col != mian[i].s.type) col = -2;
}
if (quetou.iszipai()) haszi = true;
else if (col == -1) col = quetou.type;
else if (col != quetou.type) col = -2;
if (col >= 0) {
if (haszi) {
if (menqian) {
yiz.push_back("混一色 3 番");
sa += 3;
} else { // 副露减一番
yiz.push_back("混一色 2 番");
sa += 2;
}
} else {
if (menqian) {
yiz.push_back("清一色 6 番");
sa += 6;
} else { // 副露减一番
yiz.push_back("清一色 5 番");
sa += 5;
}
}
}
}
int cmaxa = -1, cmaxb, cnta; // 划分方式有关的最大番数,最大符数,当前番数
std::list<std::string> cmaxyiz, cntyiz;
void checkBeikou() {
if (!menqian) return;
Pai tmp[4]; int sn = 0;
for (int i = 0; i < 4; i++)
if (mian[i].type == 0)
tmp[sn++] = mian[i].s;
std::sort(tmp, tmp+sn);
int k = 0;
for (int i = 1; i < sn; i++) {
if (tmp[i] == tmp[i-1]) {
k++;
i++;
}
}
if (k == 1) {
cntyiz.push_back("一杯口 1 番");
cnta += 1;
} else if (k == 2) {
cntyiz.push_back("二杯口 3 番");
cnta += 3;
}
}
void checkYiqitongguan() {
for (int c = 0; c <= 2; c++) {
int x = 0;
for (int i = 0; i < 4; i++) {
if (mian[i].type == 0 && mian[i].s.type == c) {
if (mian[i].s.x == 1) x |= 1;
if (mian[i].s.x == 4) x |= 2;
if (mian[i].s.x == 7) x |= 4;
}
}
if (x == 7) {
if (menqian) {
cntyiz.push_back("一气通贯 2 番");
cnta += 2;
} else { // 副露减一番
cntyiz.push_back("一气通贯 1 番");
cnta += 1;
}
return;
}
}
}
void checkDaiyaojiu() {
bool hasshun = false, haszi = false;
if (!quetou.is19()) return;
if (quetou.iszipai()) haszi = true;
for (int i = 0; i < 4; i++) {
if (!mian[i].dai19()) return;
if (mian[i].type == 0) hasshun = true;
if (mian[i].s.iszipai()) haszi = true;
}
if (!hasshun) return; // 混老头/清老头
if (haszi) {
if (menqian) {
cntyiz.push_back("混全带幺九 2 番");
cnta += 2;
} else { // 副露减一番
cntyiz.push_back("混全带幺九 1 番");
cnta += 1;
}
} else {
if (menqian) {
cntyiz.push_back("纯全带幺九 3 番");
cnta += 3;
} else { // 副露减一番
cntyiz.push_back("纯全带幺九 2 番");
cnta += 2;
}
}
}
void checkDuiduihe() {
for (int i = 0; i < 4; i++)
if (mian[i].type == 0) return;
cntyiz.push_back("对对和 2 番");
cnta += 2;
}
void checkTongshun() {
Pai tmp[4]; int sn = 0;
for (int i = 0; i < 4; i++)
if (mian[i].type == 0)
tmp[sn++] = mian[i].s;
for (int i = 0; i < sn; i++) {
int x = 0;
for (int j = i; j < sn; j++)
if (tmp[i].x == tmp[j].x)
x |= (1 << tmp[j].type);
if (x == 7) {
if (menqian) {
cntyiz.push_back("三色同顺 2 番");
cnta += 2;
} else { // 副露减一番
cntyiz.push_back("三色同顺 1 番");
cnta += 1;
}
return;
}
}
}
void checkTongke() {
Pai tmp[4]; int sn = 0;
for (int i = 0; i < 4; i++)
if (mian[i].type != 0 && !mian[i].s.iszipai())
tmp[sn++] = mian[i].s;
for (int i = 0; i < sn; i++) {
int x = 0;
for (int j = i; j < sn; j++)
if (tmp[i].x == tmp[j].x)
x |= (1 << tmp[j].type);
if (x == 7) {
cntyiz.push_back("三色同刻 2 番");
cnta += 2;
return;
}
}
}
int dmaxa, dmaxb, dnta; // 第14张牌位置有关的最大番数,最大符数,当前番数
std::list<std::string> dmaxyiz, dntyiz;
void checkPinghe() {
if (menqian && !calcFu0()) { // 门前清,数不出来符
dntyiz.push_back("平和 1 番");
dnta += 1;
}
}
void checkSananke() {
int k = 0;
for (int i = 0; i < 4; i++) {
if (mian[i].type && !mian[i].fulou) k++;
}
if (k == 3) {
dntyiz.push_back("三暗刻 2 番");
dnta += 2;
}
}
void checkPos() { // 检查和第14张手牌位置有关的役,并算符
dnta = 0; dntyiz.clear();
checkPinghe();
checkSananke();
int dntb = calcFu();
if (dnta > dmaxa || (dnta == dmaxa && dntb > dmaxb)) { // 第三种更新最大值
dmaxa = dnta;
dmaxb = dntb;
dmaxyiz = dntyiz;
}
}
void check() {
// 役分三次检测:
// 与划分方式无关:输出到sa,yiz
// 与划分方式有关,和第14张手牌位置无关:输出到cnta,cntyiz
// 与第14张手牌位置有关,输出到dnta,dntyiz
// 用后一种的最大值追加到前一种上
if (!td) {
td = true;
// 第一次划分成功,检查与划分方式无关的役种
// 检查役满役种
checkTiandi();
checkDasanyuan();
checkSixi();
checkZiyise();
checkQinglaotou();
checkLvyise();
checkSigangzi();
checkJiulian();
checkSianke();
if (ma) {
// 枚举第14张牌所在位置算符
for (s14pos = fulous; s14pos <= 3; s14pos++)
if (mian[s14pos].contain(s14)) {
if (!tsumo) mian[s14pos].fulou = true;
sb = std::max(sb, calcFu());
if (!tsumo) mian[s14pos].fulou = false;
}
if (quetou == s14) {
s14pos = 4;
sb = std::max(sb, calcFu());
}
return;
}
// 没有役满
checkDuanyao();
checkYipai();
checkSangangzi();
checkXiaosanyuan();
checkHunlaotou();
checkYise();
}
// 检查和划分方式有关,和第14张手牌位置无关的役
cnta = 0; cntyiz.clear();
checkBeikou();
checkYiqitongguan();
checkDaiyaojiu();
checkDuiduihe();
checkTongshun();
checkTongke();
// 枚举第14张牌所在位置
dmaxa = -1; dmaxyiz.clear();
for (s14pos = fulous; s14pos <= 3; s14pos++)
if (mian[s14pos].contain(s14)) {
if (!tsumo) mian[s14pos].fulou = true;
checkPos();
if (!tsumo) mian[s14pos].fulou = false;
}
if (quetou == s14) {
s14pos = 4;
checkPos();
}
cntyiz.splice(cntyiz.end(), dmaxyiz); // 第三种追加到第二种
cnta += dmaxa;
int cntb = dmaxb;
if (cnta > cmaxa || (cnta == cmaxa && cntb > cmaxb)) { // 第二种更新最大值
cmaxa = cnta;
cmaxb = cntb;
cmaxyiz = cntyiz;
}
}
void dfs(int c, int n, int x, bool sh) { // 搜索到种类c,种类剩余n张牌,数字x,是否重复搜索x
if (ma) return; // 有役满退出搜索
if (c == 10) {
check();
return;
}
if (x == 0) {
for (int i = (c ? cs[c-1] : 0); i < cs[c]; i++)
{s[sp[i].x]++; n++; }
x = 1;
}
if (n == 0) {
dfs(c+1, 0, 0, false);
for (int i = 1; i <= 9; i++)
s[i] = 0;
return;
}
while (s[x] == 0) {x++; sh = false; }
if (!sh && s[x] >= 3) { // 组成一组刻子
s[x] -= 3;
mian[ms].type = 1;
mian[ms].s.type = c;
mian[ms].s.x = x;
mian[ms].fulou = false;
ms++;
dfs(c, n-3, x, true);
s[x] += 3;
ms--;
}
if (!sh && (n % 3 == 2) && s[x] >= 2) { // 组成一组雀头
s[x] -= 2;
quetou.type = c;
quetou.x = x;
dfs(c, n-2, x, true);
s[x] += 2;
}
if (x <= 7 && s[x+1] && s[x+2]) { // 组成一组顺子
s[x]--;
s[x+1]--;
s[x+2]--;
mian[ms].type = 0;
mian[ms].s.type = c;
mian[ms].s.x = x;
mian[ms].fulou = false;
ms++;
dfs(c, n-3, x, true);
s[x]++;
s[x+1]++;
s[x+2]++;
ms--;
}
}
bool checkCommon() {
for (int i = 0; i < fulous; i++)
mian[i] = fulou[i];
ms = fulous;
for (int i = 0; i < sps; i++)
cs[sp[i].type]++;
for (int i = 0, haq = 0; i <= 9; i++) { // 检查每种牌张数是否可能和牌
if (cs[i] % 3 == 1) return false;
if (cs[i] % 3 == 2) {
if (haq) return false;
haq = true;
}
if (i) cs[i] += cs[i-1];
}
dfs(0, 0, 0, false);
if (!td) return false;
if (ma) return true;
yiz.splice(yiz.end(), cmaxyiz); // 第二种追加到第一种
sa += cmaxa;
sb = cmaxb;
checkAdd();
return true;
}
const std::string shuzi[] = {"", "", "两", "三", "四", "五", "六", "七", "八", "九", "十"};
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++)
handleArg(argv[i], argc-i);
if (h_state != 4) ILLEGAL();
if (addfan == -1 && fulous) ILLEGAL();
if (addfan == -1 && riichi) ILLEGAL();
std::sort(all, all+acnt);
for (int i = 1, t = 1; i < acnt; i++) {
if (all[i] == all[i-1]) t++;
else t = 1;
if (t >= 5) ILLEGAL(); // 不可以一张牌有五张
}
sps = 14-3*fulous;
std::sort(sp, sp+sps);
menqian = true;
for (int i = 0; menqian && i < fulous; i++)
if (fulou[i].fulou) menqian = false;
if (!menqian && riichi) ILLEGAL();
bool tingpai = false;
if (!tingpai) tingpai = checkGuoshi();
if (!tingpai) tingpai = checkCommon(); // 二杯口优先级高于七对子,故先解析
if (!tingpai) tingpai = checkQidui();
if (!tingpai) {printf("未听牌"); return 0; }
if (!sa && !ma) {printf("无役"); return 0; }
for (auto x : yiz)
puts(x.c_str());
int A; // 素点
if (ma) {
if (ma > 1) printf("%s倍", shuzi[ma].c_str());
printf("役满 %d 符\n", sb);
A = ma * 8000;
} else {
if (dora) printf("宝牌 %d 番\n", dora);
sa += dora;
printf("%d 番 %d 符\n", sa, sb);
if (sa <= 4) {
A = sb << (sa+2);
if (A >= 2000) {
puts("满贯");
A = 2000;
}
} else if (sa == 5) {
puts("满贯");
A = 2000;
} else if (6 <= sa && sa <= 7) {
puts("跳满");
A = 3000;
} else if (8 <= sa && sa <= 10) {
puts("倍满");
A = 4000;
} else if (11 <= sa && sa <= 12) {
puts("三倍满");
A = 6000;
} else if (sa >= 13) {
puts("累计役满");
A = 8000;
}
}
if (zifeng == 1 && tsumo) { // 亲家自摸
int dian = ceil100(2*A);
if (sun) {
printf("%d 点(每家 %d 点)", 2*dian, dian);
} else {
printf("%d 点(每家 %d 点)", 3*dian, dian);
}
}
if (zifeng != 1 && tsumo) { // 子家自摸
int qindian = ceil100(2*A);
int zidian = ceil100(A);
if (sun) {
printf("%d 点(亲家 %d 点,子家 %d 点)", qindian+zidian, qindian, zidian);
} else {
printf("%d 点(亲家 %d 点,子家 %d 点)", qindian+2*zidian, qindian, zidian);
}
}
if (zifeng == 1 && !tsumo) { // 亲家荣和
int dian = ceil100(6*A);
printf("%d 点", dian);
}
if (zifeng != 1 && !tsumo) { // 子家荣和
int dian = ceil100(4*A);
printf("%d 点", dian);
}
return 0;
}