[THUPC2021 初赛] 麻将模拟器

题目描述

麻将是一种休闲的四人博弈游戏。你的任务是写一个模拟器来模拟一局游戏的进程。 接下来将详细介绍游戏规则和每个玩家的决策。注意:为了实现方便和使游戏更加有趣味,这里介绍的规则和主流的几种麻将规则均略有不同。 **基础规则:** - 一副麻将由 $148$ 张牌组成,其中包含 $37$ 种不同的牌,每种各 $4$ 张。 - 这 $37$ 种牌分别是:一万到九万(`1M ~ 9M`)、一筒到九筒(`1P ~ 9P`)、一索到九索(`1S ~ 9S`)、东(`E`)、南(`S`)、西(`W`)、北(`N`)、白(`B`)、发(`F`)、中(`Z`),以及 $3$ 种特殊牌:跳过(`PASS`),反向(`REVERSE`),双重回合(`DOUBLE`)。 - 游戏共有 $4$ 名玩家,不妨称其为 `A`,`B`,`C`,`D`。 - 游戏开始前,将 $148$ 张牌随机洗乱后摆成一排,称为牌堆。此后玩家摸牌一定是从牌堆中摸取最靠前的一张牌。 - 从 `A` 开始按照 `ABCDABCD...` 的顺序,每人依次从牌堆中摸一张牌,直到每人都有 $13$ 张牌,这些牌组成每个玩家的手牌。 - 再从 `A` 开始按照 `ABCDABCD...` 的顺序,依次进入每人的回合: - 在一个回合中,玩家先摸一张牌进入自己的手牌,再从自己的手牌中打出一张牌。 - 依次进行直到有人和牌或者无牌可摸时游戏结束。 **特殊牌:** - 跳过(`PASS`):在出牌时打出这张牌,可以指定一名玩家,跳过他的下一个回合。 - 反向(`REVERSE`):在出牌时打出这张牌,反转进行回合的顺序,即由 `ABCDABCD...` 变为 `ADCBADCB...` 或由 `ADCBADCB...` 变为 `ABCDABCD...`。出牌后即按照反转后的顺序,从出牌者原先的上家开始进行回合。 - 双重回合(`DOUBLE`):在出牌时打出这张牌,该名玩家立即进入一个额外的回合。 **牌型:**​有如下 $3$ 种牌型: - 顺子:$3$ 张数字连续的万,或 $3$ 张数字连续的筒,或 $3$ 张数字连续的索,如 `4P 5P 6P`。 - 刻子:$3$ 张完全一样的非特殊牌,如 `B B B`。 - 对子:$2$ 张完全一样的非特殊牌,如 `9M 9M`。 **吃、碰:** - 当一名玩家打出一张非特殊牌时,其他玩家可以进行吃或碰: - 吃(`CHOW`):当打出的这张牌跟自己的手牌中的某两张牌能组成一个顺子时,可以将手牌中能与之组成顺子的其余两张牌取出,与这张牌一起摆在旁边。 - **注意只有上一名出牌玩家的下家(按当前顺序原本应在下一个进行回合的玩家)才能吃。** - 碰(`PONG`):当打出的这张牌跟自己的手牌中的某两张牌能组成一个刻子时,可以将手牌中能与之组成刻子的其余两张牌取出,与这张牌一起摆在旁边。 - 碰没有吃的上述限制,任意其他玩家都能碰。 - 如果既有玩家能吃又有玩家能碰,则碰优先于吃。 - 吃(或碰)不是强制性的,也就是说玩家满足吃(或碰)的条件时,可以选择不吃(或碰)。 - 吃和碰统称为副露。为方便起见,不将副露视为手牌的一部分。 - 在任意一名玩家吃(或碰)后,跳过从上一名出牌的玩家到这名玩家之间的所有玩家的回合,直接从当前玩家开始进行新的回合。但该玩家在这一回合中**跳过摸牌直接出牌**,在下一回合(如果没有吃碰的话)恢复正常。 - **注意在本规则中不能杠。** **胡牌规则:** - 称一名玩家的牌能和,当且仅当满足如下条件: - 牌数为 $14 - 3 n$,其中 $n$ 为该玩家副露(即吃碰)的个数; - 这些牌中无特殊牌; - 这些牌能够被分成 $(5 - n)$ 组,其中 $(4 - n)$ 组均为 $3$ 张且均为顺子或刻子,其余一组为 $2$ 张且为对子。 - **注意本规则中不支持七对子、十三幺、全不靠等特殊的和牌规则。** - 另外,定义一组包含 $13 - 3 n$ 张牌的手牌的和牌距离为最小的 $x$,使得向这些牌中加入特定的 $x$ 张牌,再去掉 $x - 1$ 张手牌后,每种牌仍不超过 $4$ 张且能和。 - 定义一组包含 $14 - 3 n$ 张牌的手牌的和牌距离为最小的 $x$,使得向这些牌中加入特定的 $x$ 张牌,再去掉 $x$ 张手牌后,每种牌仍不超过 $4$ 张且能和。 - 特别地,一手能和的牌的和牌距离为 $0$;和牌距离为 $1$ 的牌称为听牌。 - 注意这里的“**每种牌仍不超过** $\boldsymbol{4}$ **张**”的限制:如果一手牌是 `1M 1M 1M 1M` 且副露数为 $3$,再向其中加入一张 `1M` 就能和,但是由于有 $5$ 张 `1M` 所以是不被允许的,故不认为其和牌距离为 $1$。 - 但如果一手牌是 `1M` 且副露数为 $4$,但是曾进行过一次 `1M 1M 1M` 的碰,仍然认为其和牌距离为 $1$(虽然缺的这张 `1M` 永远也等不到)。 **终局:** - 荣和(`RON`):当一名玩家出牌后,某名其他玩家的手牌加上这张牌能和,则称这名玩家荣和。荣和优先于吃碰。 - 如果有多名玩家同时达到荣和的标准,规定只有从上一名出牌玩家开始,沿回合进行顺序的第一名能荣和的玩家才能荣和,其余玩家荣和不了,称这种情况为截和。 - 自摸(`SELFDRAWN`):一名玩家摸牌后其手牌能和,称这名玩家自摸。 - 一旦有一名玩家荣和或自摸,游戏立即结束,该名玩家胜利。 - 如果某名玩家摸牌时发现牌堆中已经无牌可摸,游戏立即结束,称此种情况为流局。 **出牌策略:**​每名玩家的出牌策略相同且固定: - 出牌时,若手里有特殊牌一定会优先出,且如果有多种特殊牌,按照 `PASS`、`REVERSE`、`DOUBLE` 的优先顺序;出的 `PASS` 一定指定下家。 - 出牌时若手里没有特殊牌,则会对于每一种可能的出牌方法计算出完牌后的和牌距离,选择和牌距离最小的一种方案。如果有并列最小,按照 `Z`,`F`,`B`,`N`,`W`,`S`,`E`,`9S`,`8S`,……,`1S`,`9P`,……,`1P`,`9M`,……,`1M` 的优先顺序出牌。 - 同一个人能吃且能碰时,优先考虑碰;因为每种牌只有 $4$ 张所以不会有两名玩家同时可以碰的情况;当且仅当吃(或碰)后能使得和牌距离严格减小才会去吃(或碰);如果有多种吃的方案使得和牌距离严格减小,优先选择数字较大的方案。 - 能荣和一定荣和(除非被截和),能自摸一定自摸,不会拒和(能和时故意选择不和)。

输入输出格式

输入格式


输入共 $148$ 行,按照牌堆从前到后的顺序输入每一张牌。 每行输入一个字符串表示这一张牌。 用 `1M`,`2M`,……,`9M` 代表万,`1P`,`2P`,……,`9P` 代表筒,`1S`,`2S`,……,`9S` 代表索,`E`,`S`,`W`,`N`,`B`,`F`,`Z` 分别代表东、南、西、北、白、发、中,`PASS` 代表跳过,`REVERSE` 表示反向,`DOUBLE` 表示双重回合。

输出格式


按照如下几条规则进行输出: - 当任意一名玩家摸牌时(**包括游戏最开始的摸牌**),输出一行: `x IN y` 其中 `x` 为玩家名称,`y` 为摸到的牌。 - 当任意一名玩家出牌时,如果出的牌不是 `PASS`,输出一行: `x OUT y` 其中 `x` 为玩家名称,`y` 为出的牌。 如果出的牌是 `PASS`,应当输出一行: `x OUT PASS z` 其中 `z` 为 `PASS` 指定的对象。 - 当任意一名玩家吃时,输出一行: `x CHOW y1 y2 y3` 其中 `x` 为玩家名称,`y1`,`y2`,`y3` 为吃涉及到的 $3$ 张牌,按数字递增的顺序输出。 - 当任意一名玩家碰时,输出一行: `x PONG y1 y2 y3` 其中 `x` 为玩家名称,`y1`,`y2`,`y3` 为碰涉及到的 $3$ 张牌,根据碰的规则,`y1`,`y2`,`y3` 应相同。 - 当任意一名玩家荣和时,输出一行: `x RON` 其中 `x` 为玩家名称。 - 当任意一名玩家自摸时,输出一行: `x SELFDRAWN` 其中 `x` 为玩家名称。 - 游戏的最后,如果某名玩家获得胜利,输出一行: `x WIN` 其中 `x` 为玩家名称。 如果出现流局,输出一行: `DRAW` **需要特别注意的是,输入输出中出现的英文字母均为大写。**

输入输出样例

输入样例 #1

8M
Z
E
9P
3P
9S
5P
W
3M
8P
DOUBLE
5P
Z
2P
3M
8S
2S
5P
5M
E
6M
9S
6P
5S
7M
4S
3S
6M
3S
2M
9M
5S
Z
7P
5P
8M
3M
F
7M
2S
N
4P
3S
S
PASS
1P
6S
3P
9P
9S
4M
8P
N
Z
N
5M
DOUBLE
REVERSE
S
3P
4M
4S
1S
PASS
4P
6S
7S
7P
6S
9M
REVERSE
3P
7P
DOUBLE
B
9P
4S
5S
7S
7S
7P
6S
9S
B
9M
S
F
2P
1P
PASS
9P
DOUBLE
4P
PASS
5S
2M
2P
6P
W
1M
8S
REVERSE
8M
6M
5M
F
4M
F
8P
2S
1M
2M
3M
7M
3S
B
7S
1S
REVERSE
8P
6M
4S
2M
B
1M
S
6P
5M
W
7M
2S
8S
8M
1S
4P
E
4M
9M
1M
8S
1S
1P
2P
1P
W
6P
N
E

输出样例 #1

A IN 8M
B IN Z
C IN E
D IN 9P
A IN 3P
B IN 9S
C IN 5P
D IN W
A IN 3M
B IN 8P
C IN DOUBLE
D IN 5P
A IN Z
B IN 2P
C IN 3M
D IN 8S
A IN 2S
B IN 5P
C IN 5M
D IN E
A IN 6M
B IN 9S
C IN 6P
D IN 5S
A IN 7M
B IN 4S
C IN 3S
D IN 6M
A IN 3S
B IN 2M
C IN 9M
D IN 5S
A IN Z
B IN 7P
C IN 5P
D IN 8M
A IN 3M
B IN F
C IN 7M
D IN 2S
A IN N
B IN 4P
C IN 3S
D IN S
A IN PASS
B IN 1P
C IN 6S
D IN 3P
A IN 9P
B IN 9S
C IN 4M
D IN 8P
A IN N
A OUT PASS B
C IN Z
C OUT DOUBLE
C IN N
C OUT Z
A PONG Z Z Z
A OUT 9P
B CHOW 7P 8P 9P
B OUT Z
C IN 5M
C OUT N
A PONG N N N
A OUT 3P
B CHOW 3P 4P 5P
B OUT F
C IN DOUBLE
C OUT DOUBLE
C IN REVERSE
C OUT REVERSE
B IN S
B OUT S
A IN 3P
A OUT 3P
D IN 4M
D OUT W
C IN 4S
C OUT E
B IN 1S
B OUT 4S
A RON
A WIN

说明

**【题目来源】** 来自 2021 清华大学学生程序设计竞赛暨高校邀请赛(THUPC2021)初赛。 题解等资源可在 <https://github.com/THUSAAC/THUPC2021-pre> 查看。