#P52071. 「THUPC 2021 初赛」麻将模拟器
「THUPC 2021 初赛」麻将模拟器
题目描述
麻将是一种休闲的四人博弈游戏。你的任务是写一个模拟器来模拟一局游戏的进程。
接下来将详细介绍游戏规则和每个玩家的决策。注意:为了实现方便和使游戏更加有趣味,这里介绍的规则和主流的几种麻将规则均略有不同。
基础规则:
- 一副麻将由 张牌组成,其中包含 种不同的牌,每种各 张。
- 这 种牌分别是:一万到九万(
1M ~ 9M
)、一筒到九筒(1P ~ 9P
)、一索到九索(1S ~ 9S
)、东(E
)、南(S
)、西(W
)、北(N
)、白(B
)、发(F
)、中(Z
),以及 种特殊牌:跳过(PASS
),反向(REVERSE
),双重回合(DOUBLE
)。 - 游戏共有 名玩家,不妨称其为
A
,B
,C
,D
。 - 游戏开始前,将 张牌随机洗乱后摆成一排,称为牌堆。此后玩家摸牌一定是从牌堆中摸取最靠前的一张牌。
- 从
A
开始按照ABCDABCD...
的顺序,每人依次从牌堆中摸一张牌,直到每人都有 张牌,这些牌组成每个玩家的手牌。 - 再从
A
开始按照ABCDABCD...
的顺序,依次进入每人的回合: - 在一个回合中,玩家先摸一张牌进入自己的手牌,再从自己的手牌中打出一张牌。
- 依次进行直到有人和牌或者无牌可摸时游戏结束。
特殊牌:
- 跳过(
PASS
):在出牌时打出这张牌,可以指定一名玩家,跳过他的下一个回合。 - 反向(
REVERSE
):在出牌时打出这张牌,反转进行回合的顺序,即由ABCDABCD...
变为ADCBADCB... 或由
ADCBADCB...变为
ABCDABCD...`。出牌后即按照反转后的顺序,从出牌者原先的上家开始进行回合。 - 双重回合(
DOUBLE
):在出牌时打出这张牌,该名玩家立即进入一个额外的回合。
牌型:有如下 种牌型:
- 顺子: 张数字连续的万,或 张数字连续的筒,或 张数字连续的索,如
4P 5P 6P
。 - 刻子: 张完全一样的非特殊牌,如
B B B
。 - 对子: 张完全一样的非特殊牌,如
9M 9M
。
吃、碰:
- 当一名玩家打出一张非特殊牌时,其他玩家可以进行吃或碰:
- 吃(
CHOW
):当打出的这张牌跟自己的手牌中的某两张牌能组成一个顺子时,可以将手牌中能与之组成顺子的其余两张牌取出,与这张牌一起摆在旁边。 - 注意只有上一名出牌玩家的下家(按当前顺序原本应在下一个进行回合的玩家)才能吃。
- 碰(
PONG
):当打出的这张牌跟自己的手牌中的某两张牌能组成一个刻子时,可以将手牌中能与之组成刻子的其余两张牌取出,与这张牌一起摆在旁边。 - 碰没有吃的上述限制,任意其他玩家都能碰。
- 如果既有玩家能吃又有玩家能碰,则碰优先于吃。
- 吃(或碰)不是强制性的,也就是说玩家满足吃(或碰)的条件时,可以选择不吃(或碰)。
- 吃和碰统称为副露。为方便起见,不将副露视为手牌的一部分。
- 在任意一名玩家吃(或碰)后,跳过从上一名出牌的玩家到这名玩家之间的所有玩家的回合,直接从当前玩家开始进行新的回合。但该玩家在这一回合中跳过摸牌直接出牌,在下一回合(如果没有吃碰的话)恢复正常。
- 注意在本规则中不能杠。
胡牌规则:
- 称一名玩家的牌能和,当且仅当满足如下条件:
- 牌数为 ,其中 为该玩家副露(即吃碰)的个数;
- 这些牌中无特殊牌;
- 这些牌能够被分成 组,其中 组均为 张且均为顺子或刻子,其余一组为 张且为对子。
- 注意本规则中不支持七对子、十三幺、全不靠等特殊的和牌规则。
- 另外,定义一组包含 张牌的手牌的和牌距离为最小的 ,使得向这些牌中加入特定的 张牌,再去掉 张手牌后,每种牌仍不超过 张且能和。
- 定义一组包含 张牌的手牌的和牌距离为最小的 ,使得向这些牌中加入特定的 张牌,再去掉 张手牌后,每种牌仍不超过 张且能和。
- 特别地,一手能和的牌的和牌距离为 ;和牌距离为 的牌称为听牌。
- 注意这里的“每种牌仍不超过 张”的限制:如果一手牌是
1M 1M 1M 1M
且副露数为 ,再向其中加入一张1M
就能和,但是由于有 张1M
所以是不被允许的,故不认为其和牌距离为 。 - 但如果一手牌是
1M
且副露数为 ,但是曾进行过一次1M 1M 1M
的碰,仍然认为其和牌距离为 (虽然缺的这张1M
永远也等不到)。
终局:
- 荣和(
RON
):当一名玩家出牌后,某名其他玩家的手牌加上这张牌能和,则称这名玩家荣和。荣和优先于吃碰。 - 如果有多名玩家同时达到荣和的标准,规定只有从上一名出牌玩家开始,沿回合进行顺序的第一名能荣和的玩家才能荣和,其余玩家荣和不了,称这种情况为截和。
- 自摸(
SELFDRAWN
):一名玩家摸牌后其手牌能和,称这名玩家自摸。 - 一旦有一名玩家荣和或自摸,游戏立即结束,该名玩家胜利。
- 如果某名玩家摸牌时发现牌堆中已经无牌可摸,游戏立即结束,称此种情况为流局。
出牌策略:每名玩家的出牌策略相同且固定:
- 出牌时,若手里有特殊牌一定会优先出,且如果有多种特殊牌,按照
PASS
、REVERSE
、DOUBLE
的优先顺序;出的PASS
一定指定下家。 - 出牌时若手里没有特殊牌,则会对于每一种可能的出牌方法计算出完牌后的和牌距离,选择和牌距离最小的一种方案。如果有并列最小,按照
Z
,F
,B
,N
,W
,S
,E
,9S
,8S
,……,1S
,9P
,……,1P
,9M
,……,1M
的优先顺序出牌。 - 同一个人能吃且能碰时,优先考虑碰;因为每种牌只有 张所以不会有两名玩家同时可以碰的情况;当且仅当吃(或碰)后能使得和牌距离严格减小才会去吃(或碰);如果有多种吃的方案使得和牌距离严格减小,优先选择数字较大的方案。
- 能荣和一定荣和(除非被截和),能自摸一定自摸,不会拒和(能和时故意选择不和)。
输入格式
输入共 行,按照牌堆从前到后的顺序输入每一张牌。
每行输入一个字符串表示这一张牌。
用 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
为吃涉及到的 张牌,按数字递增的顺序输出。 - 当任意一名玩家碰时,输出一行:
x PONG y1 y2 y3
其中x
为玩家名称,y1
,y2
,y3
为碰涉及到的 张牌,根据碰的规则,y1
,y2
,y3
应相同。 - 当任意一名玩家荣和时,输出一行:
x RON
其中x
为玩家名称。 - 当任意一名玩家自摸时,输出一行:
x SELFDRAWN
其中x
为玩家名称。 - 游戏的最后,如果某名玩家获得胜利,输出一行:
x WIN
其中x
为玩家名称。
如果出现流局,输出一行:
DRAW
需要特别注意的是,输入输出中出现的英文字母均为大写。
样例
见附加文件中的 [1.in](file:1.in) 与 [1.ans](file:1.ans)。
来源
来自 2021 清华大学学生程序设计竞赛暨高校邀请赛(THUPC2021)初赛。
题解等资源可在 https://github.com/THUSAAC/THUPC2021-pre 查看。