立直麻将算点器

立直麻将算点器,不排除 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;
}