1
1
This commit is contained in:
0
tests/mahjong/__init__.py
Normal file
0
tests/mahjong/__init__.py
Normal file
272
tests/mahjong/test_calculate_fan.py
Normal file
272
tests/mahjong/test_calculate_fan.py
Normal file
@@ -0,0 +1,272 @@
|
||||
from src import calculate_fan, is_seven_pairs, is_cleared, is_big_pairs
|
||||
|
||||
from src import Hand
|
||||
from src import MahjongTile
|
||||
|
||||
# 测试用例
|
||||
|
||||
def test_basic_win():
|
||||
"""
|
||||
测试平胡(基本胡)计分
|
||||
"""
|
||||
hand = Hand()
|
||||
# 模拟平胡手牌:四组顺子 + 一对将
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1)) # 将
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3)) # 顺子
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6)) # 顺子
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 9)) # 顺子
|
||||
hand.add_tile(MahjongTile("万", 1))
|
||||
hand.add_tile(MahjongTile("万", 2))
|
||||
hand.add_tile(MahjongTile("万", 3)) # 顺子
|
||||
|
||||
melds = []
|
||||
conditions = {}
|
||||
|
||||
fan = calculate_fan(hand.tiles, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 1, f"Expected 1 fan, got {fan}"
|
||||
|
||||
|
||||
def test_clear_win():
|
||||
"""
|
||||
测试清一色计分(不加自摸番)
|
||||
"""
|
||||
hand = [0] * 108
|
||||
# 模拟清一色手牌
|
||||
hand[0] = 2
|
||||
hand[4] = 1
|
||||
hand[5] = 1
|
||||
hand[6] = 1
|
||||
hand[10] = 1
|
||||
hand[11] = 1
|
||||
hand[12] = 1
|
||||
hand[20] = 1
|
||||
hand[21] = 1
|
||||
hand[22] = 1
|
||||
hand[23] = 1
|
||||
|
||||
melds = []
|
||||
conditions = {"is_seven_pairs": False, "add_self_draw": False}
|
||||
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=is_cleared(hand), conditions=conditions)
|
||||
assert fan == 3, f"Expected 3 fans (1 basic + 2 cleared), got {fan}"
|
||||
|
||||
def test_pure_cleared():
|
||||
"""
|
||||
测试清对计分
|
||||
"""
|
||||
hand = [2] * 6 + [0] * 102 # 手牌:清对
|
||||
melds = [0, 1, 2] # 模拟碰
|
||||
conditions = {"is_pure_cleared": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=True, conditions=conditions)
|
||||
assert fan == 3, f"Expected 3 fans (pure cleared), got {fan}"
|
||||
|
||||
|
||||
|
||||
def test_pure_cleared():
|
||||
"""
|
||||
测试清对计分
|
||||
"""
|
||||
hand = [2] * 6 + [0] * 102 # 手牌:清对
|
||||
melds = [0, 1, 2] # 模拟碰
|
||||
conditions = {"is_pure_cleared": True} # 明确条件
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=True, conditions=conditions)
|
||||
assert fan == 3, f"Expected 3 fans (pure cleared), got {fan}"
|
||||
|
||||
def test_seven_pairs():
|
||||
"""
|
||||
测试七对计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
# 模拟七对手牌
|
||||
hand[0] = 2 # 1条
|
||||
hand[4] = 2 # 2条
|
||||
hand[8] = 2 # 3条
|
||||
hand[12] = 2 # 4条
|
||||
hand[16] = 2 # 5条
|
||||
hand[20] = 2 # 6条
|
||||
hand[24] = 2 # 7条
|
||||
|
||||
melds = []
|
||||
conditions = {"is_seven_pairs": is_seven_pairs(hand)}
|
||||
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 2, f"Expected 2 fans (7 pairs), got {fan}"
|
||||
|
||||
|
||||
def test_small_pairs():
|
||||
"""
|
||||
测试小七对计分
|
||||
"""
|
||||
hand = [2] * 6 + [0] * 102
|
||||
melds = []
|
||||
conditions = {"is_small_pairs": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 2, f"Expected 2 fans (small pairs), got {fan}"
|
||||
|
||||
|
||||
def test_clear_seven_pairs():
|
||||
"""
|
||||
测试清七对计分
|
||||
"""
|
||||
hand = [2] * 7 + [0] * 101
|
||||
melds = []
|
||||
conditions = {"is_clear_seven_pairs": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=True, conditions=conditions)
|
||||
assert fan == 12, f"Expected 12 fans (clear seven pairs), got {fan}"
|
||||
|
||||
|
||||
|
||||
def test_full_request():
|
||||
"""
|
||||
测试全求人计分
|
||||
"""
|
||||
hand = [2] + [0] * 107
|
||||
melds = []
|
||||
conditions = {"is_full_request": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 6, f"Expected 6 fans (full request), got {fan}"
|
||||
|
||||
|
||||
def test_big_pairs():
|
||||
"""
|
||||
测试大对子计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
# 模拟大对子手牌
|
||||
hand[0] = 3 # 1条
|
||||
hand[4] = 3 # 2条
|
||||
hand[8] = 3 # 3条
|
||||
hand[12] = 2 # 将: 4条
|
||||
|
||||
melds = []
|
||||
conditions = {"is_big_pairs": is_big_pairs(hand)}
|
||||
|
||||
# Debug output
|
||||
print(f"Conditions: {conditions}")
|
||||
|
||||
# 确保大对子检测正确
|
||||
assert is_big_pairs(hand), "The hand is not identified as a big pairs hand."
|
||||
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 2, f"Expected 2 fans (big pairs), got {fan}"
|
||||
|
||||
|
||||
|
||||
def test_gang_flower():
|
||||
"""
|
||||
测试杠上开花计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
# 模拟杠上开花手牌
|
||||
hand[0] = 2 # 将: 两张1条
|
||||
hand[3] = 1 # 2条
|
||||
hand[4] = 1 # 3条
|
||||
hand[5] = 1 # 4条
|
||||
hand[10] = 1 # 5条
|
||||
hand[11] = 1 # 6条
|
||||
hand[12] = 1 # 7条
|
||||
hand[20] = 1 # 8条
|
||||
hand[21] = 1 # 9条
|
||||
hand[22] = 1 # 1筒
|
||||
|
||||
melds = []
|
||||
conditions = {"is_gang_flower": True}
|
||||
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=False, conditions=conditions)
|
||||
assert fan == 2, f"Expected 2 fans (1 basic + 1 gang flower), got {fan}"
|
||||
|
||||
|
||||
def test_rob_gang():
|
||||
"""
|
||||
测试抢杠胡计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {"is_rob_gang": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 1, f"Expected 1 fan (rob gang), got {fan}"
|
||||
|
||||
def test_under_the_sea():
|
||||
"""
|
||||
测试海底捞月计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {"is_under_the_sea": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=False, conditions=conditions)
|
||||
assert fan == 1, f"Expected 1 fan (under the sea), got {fan}"
|
||||
|
||||
|
||||
|
||||
def test_cannon():
|
||||
"""
|
||||
测试放炮计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {"is_cannon": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 1, f"Expected 1 fan (cannon), got {fan}"
|
||||
|
||||
|
||||
def test_tian_hu():
|
||||
"""
|
||||
测试天胡计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {"is_tian_hu": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=False, conditions=conditions)
|
||||
assert fan == 12, f"Expected 12 fans (tian hu), got {fan}"
|
||||
|
||||
def test_di_hu():
|
||||
"""
|
||||
测试地胡计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {"is_di_hu": True}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=False, is_cleared=False, conditions=conditions)
|
||||
assert fan == 12, f"Expected 12 fans (di hu), got {fan}"
|
||||
|
||||
def test_dragon_seven_pairs():
|
||||
"""
|
||||
测试龙七对计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
# 模拟龙七对手牌
|
||||
hand[0] = 2 # 1条
|
||||
hand[4] = 2 # 2条
|
||||
hand[8] = 2 # 3条
|
||||
hand[12] = 2 # 4条
|
||||
hand[16] = 2 # 5条
|
||||
hand[20] = 2 # 6条
|
||||
hand[24] = 4 # 龙: 7条
|
||||
|
||||
melds = []
|
||||
conditions = {
|
||||
"is_dragon_seven_pairs": True,
|
||||
"is_seven_pairs": True,
|
||||
}
|
||||
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=False, conditions=conditions)
|
||||
assert fan == 12, f"Expected 13 fans (1 self-draw + 12 dragon seven pairs), got {fan}"
|
||||
|
||||
def test_self_draw():
|
||||
"""
|
||||
测试自摸计分
|
||||
"""
|
||||
hand = [0] * 108
|
||||
melds = []
|
||||
conditions = {}
|
||||
fan = calculate_fan(hand, melds, is_self_draw=True, is_cleared=False, conditions=conditions)
|
||||
assert fan == 1, f"Expected 1 fan (self-draw), got {fan}"
|
||||
|
||||
|
||||
48
tests/mahjong/test_chengdu_mahjong_engine.py
Normal file
48
tests/mahjong/test_chengdu_mahjong_engine.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from src import ChengduMahjongEngine
|
||||
from loguru import logger
|
||||
|
||||
def test_mahjong_engine():
|
||||
"""
|
||||
测试成都麻将引擎,包括初始化、发牌、轮次逻辑等。
|
||||
"""
|
||||
# 初始化麻将引擎
|
||||
engine = ChengduMahjongEngine()
|
||||
|
||||
# 初始化游戏
|
||||
engine.initialize_game()
|
||||
|
||||
# 发牌
|
||||
engine.deal_tiles()
|
||||
|
||||
# 检查发牌后的状态
|
||||
logger.info(f"庄家: 玩家 {engine.state.current_player}")
|
||||
for player in range(4):
|
||||
hand = engine.state.hands[player]
|
||||
logger.info(f"玩家 {player} 的手牌: {hand}")
|
||||
logger.info(f"玩家 {player} 的缺门: {engine.state.missing_suits[player]}")
|
||||
|
||||
# 模拟游戏主循环
|
||||
try:
|
||||
engine.run()
|
||||
except Exception as e:
|
||||
logger.error(f"测试引擎时出错: {e}")
|
||||
|
||||
# 打印游戏结束后的状态
|
||||
logger.info("游戏结束!")
|
||||
for player in range(4):
|
||||
logger.info(
|
||||
f"玩家 {player}: 分数={engine.state.scores[player]}, "
|
||||
f"手牌数量={len(engine.state.hands[player].tiles)}, 明牌数量={len(engine.state.melds[player])}, "
|
||||
f"缺门={engine.state.missing_suits[player]}, 手牌={engine.state.hands[player]}, 明牌={engine.state.melds[player]}"
|
||||
)
|
||||
|
||||
# 记录赢家信息
|
||||
if engine.state.winners:
|
||||
logger.info(f"赢家: {engine.state.winners}")
|
||||
else:
|
||||
logger.info("没有赢家!")
|
||||
|
||||
|
||||
# 运行测试
|
||||
if __name__ == "__main__":
|
||||
test_mahjong_engine()
|
||||
514
tests/mahjong/test_chengdu_mahjong_states.py
Normal file
514
tests/mahjong/test_chengdu_mahjong_states.py
Normal file
@@ -0,0 +1,514 @@
|
||||
from src import ChengduMahjongState
|
||||
from src import Hand
|
||||
from src import MahjongTile
|
||||
from src import Meld
|
||||
|
||||
|
||||
def test_set_missing_suit():
|
||||
"""测试设置缺门功能"""
|
||||
state = ChengduMahjongState()
|
||||
state.set_missing_suit(0, "条")
|
||||
assert state.missing_suits[0] == "条", "测试失败:缺门设置为 '条' 后未正确更新"
|
||||
state.set_missing_suit(1, "筒")
|
||||
assert state.missing_suits[1] == "筒", "测试失败:缺门设置为 '筒' 后未正确更新"
|
||||
state.set_missing_suit(2, "万")
|
||||
assert state.missing_suits[2] == "万", "测试失败:缺门设置为 '万' 后未正确更新"
|
||||
|
||||
try:
|
||||
state.set_missing_suit(0, "花")
|
||||
except ValueError:
|
||||
print("测试通过:设置无效缺门 '花' 抛出异常")
|
||||
else:
|
||||
raise AssertionError("测试失败:设置无效缺门 '花' 未抛出异常")
|
||||
|
||||
|
||||
def test_can_win_with_pure_sequences():
|
||||
"""测试纯顺子胡牌"""
|
||||
hand = Hand()
|
||||
# 添加牌到手牌中
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("万", 3))
|
||||
hand.add_tile(MahjongTile("万", 4))
|
||||
hand.add_tile(MahjongTile("万", 5))
|
||||
|
||||
state = ChengduMahjongState()
|
||||
state.melds[0] = []
|
||||
state.hands[0] = hand
|
||||
|
||||
# 设置缺门为 "条",因为手牌中没有 "条"
|
||||
missing_suit = "条"
|
||||
print(f"\n,state.hand[0]: {state.hands[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0],missing_suit) == True, "测试失败:纯顺子应该可以胡牌"
|
||||
|
||||
|
||||
def test_can_win_with_sequence_and_triplet():
|
||||
"""测试顺子 + 刻子胡牌"""
|
||||
hand = Hand()
|
||||
# 添加牌到手牌中
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("万", 3))
|
||||
hand.add_tile(MahjongTile("万", 4))
|
||||
hand.add_tile(MahjongTile("万", 5))
|
||||
hand.add_tile(MahjongTile("万", 7))
|
||||
hand.add_tile(MahjongTile("万", 8))
|
||||
hand.add_tile(MahjongTile("万", 9))
|
||||
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
state.melds[0] = []
|
||||
|
||||
# 设置缺门为 "条",因为手牌中没有 "条"
|
||||
missing_suit = "条"
|
||||
print(f"\n,state.hand[0]: {state.hands[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0],missing_suit) == True, "测试失败:顺子 + 刻子应该可以胡牌"
|
||||
|
||||
def test_can_win_with_triplets_and_pair():
|
||||
"""测试刻子和对子胡牌"""
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
state.melds[0] = []
|
||||
# 设置缺门为 "万",因为手牌中没有 "万"
|
||||
missing_suit = "万"
|
||||
print(f"\n,state.hand[0]: {state.hands[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0],missing_suit) == True, "测试失败:刻子和对子应该可以胡牌"
|
||||
|
||||
|
||||
def test_can_win_with_pure_one_suit():
|
||||
"""测试清一色不带杠胡牌"""
|
||||
hand = Hand()
|
||||
# 添加牌到手牌中
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
state.melds[0] = []
|
||||
|
||||
# 设置缺门为 "万",因为手牌中只有 "筒"
|
||||
missing_suit = "万"
|
||||
print(f"\n,state.hand[0]: {state.hands[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0],missing_suit) == True, "测试失败:清一色不带杠应该可以胡牌"
|
||||
|
||||
|
||||
def test_can_win_with_pure_one_suit_and_gang():
|
||||
"""测试带杠的清一色胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加暗牌
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
# 添加对子
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 设置明牌(杠)
|
||||
melds_list = [
|
||||
Meld(MahjongTile("筒", 9), "杠") # 表示明杠了4张9筒
|
||||
]
|
||||
state.melds[0] = melds_list # 确保 state.melds[0] 是一个列表
|
||||
|
||||
# 设置缺门为 "万"
|
||||
missing_suit = "万"
|
||||
print(f"\n当前手牌: {state.hands[0]}, 明牌: {state.melds[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:带杠的清一色应该可以胡牌"
|
||||
|
||||
|
||||
def test_can_win_with_yaojiu_sequences():
|
||||
"""测试带幺九的顺子胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加顺子和对子
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 8))
|
||||
hand.add_tile(MahjongTile("条", 9))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
# 添加对子
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 无明牌
|
||||
melds_list = []
|
||||
state.melds[0] = melds_list
|
||||
|
||||
# 设置缺门为 "万"
|
||||
missing_suit = "万"
|
||||
print(f"\n当前手牌: {state.hands[0]}, 明牌: {state.melds[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:带幺九顺子应该可以胡牌"
|
||||
|
||||
def test_can_win_with_seven_pairs():
|
||||
"""测试清一色七对胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加 7 对牌
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 无明牌
|
||||
melds_list = []
|
||||
state.melds[0] = melds_list
|
||||
|
||||
# 设置缺门为 "条"
|
||||
missing_suit = "条"
|
||||
print(f"\n当前手牌: {state.hands[0]}, 明牌: {state.melds[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:七对应该可以胡牌"
|
||||
|
||||
def test_can_win_with_dragon_seven_pairs():
|
||||
"""测试带暗杠的龙七对胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加手牌
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1)) # 暗杠
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 无明牌
|
||||
melds_list = []
|
||||
state.melds[0] = melds_list
|
||||
|
||||
# 设置缺门为 "万"
|
||||
missing_suit = "万"
|
||||
print(f"\n当前手牌: {state.hands[0]}, 明牌: {state.melds[0]}")
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:带暗杠的龙七对应该可以胡牌"
|
||||
|
||||
def test_can_win_with_mixed_seven_pairs():
|
||||
"""测试混合七对胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加手牌
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 无明牌
|
||||
melds_list = []
|
||||
state.melds[0] = melds_list
|
||||
|
||||
# 设置缺门为 "万"
|
||||
missing_suit = "万"
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:混合七对应该可以胡牌"
|
||||
|
||||
|
||||
def test_can_win_after_ming_gang():
|
||||
"""测试明杠后杠上开花胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加暗牌
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 8))
|
||||
hand.add_tile(MahjongTile("条", 9))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 8))
|
||||
hand.add_tile(MahjongTile("条", 9))
|
||||
hand.add_tile(MahjongTile("万", 2))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 设置明牌(明杠)
|
||||
melds_list = []
|
||||
state.melds[0] = melds_list
|
||||
|
||||
# 别人打出3条,明杠了3条
|
||||
state.hands[0].add_tile(MahjongTile("条", 3))
|
||||
# 设置明杠
|
||||
state.melds[0].append(Meld(MahjongTile("条", 3), "杠"))
|
||||
|
||||
# 从手牌中移除明杠的牌
|
||||
for _ in range(4):
|
||||
state.hands[0].remove_tile(MahjongTile("条", 3))
|
||||
|
||||
# 模拟杠上开花,自摸一张3万
|
||||
hand.add_tile(MahjongTile("万", 2))
|
||||
# 设置缺门
|
||||
state.missing_suits = "筒"
|
||||
|
||||
# 打印手牌,暗牌,明牌,缺门
|
||||
state.print_game_state(0)
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], state.missing_suits), "测试失败:明杠后杠上开花应该可以胡牌"
|
||||
print("测试通过:明杠后杠上开花胡牌成功!")
|
||||
|
||||
def test_qiang_gang_hu():
|
||||
"""测试抢杠胡"""
|
||||
# 玩家 A 的手牌
|
||||
player_a_hand = Hand()
|
||||
player_a_hand.add_tile(MahjongTile("筒", 2))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 3))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 5))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 6))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 7))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 9))
|
||||
player_a_hand.add_tile(MahjongTile("筒", 9))
|
||||
player_a_hand.add_tile(MahjongTile("万", 1))
|
||||
player_a_hand.add_tile(MahjongTile("万", 1))
|
||||
player_a_hand.add_tile(MahjongTile("万", 2))
|
||||
player_a_hand.add_tile(MahjongTile("万", 2))
|
||||
player_a_hand.add_tile(MahjongTile("万", 3))
|
||||
player_a_hand.add_tile(MahjongTile("万", 3))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = player_a_hand # 玩家 A 的手牌
|
||||
state.missing_suits[0] = "条" # 玩家 A 的缺门
|
||||
|
||||
# 玩家 B 的明牌和手牌
|
||||
player_b_hand = Hand()
|
||||
player_b_hand.add_tile(MahjongTile("筒", 1))
|
||||
player_b_hand.add_tile(MahjongTile("筒", 1))
|
||||
player_b_hand.add_tile(MahjongTile("筒", 1))
|
||||
state.hands[1] = player_b_hand
|
||||
state.missing_suits[1] = "万"
|
||||
|
||||
# 玩家 B 尝试杠 1 筒
|
||||
melds_b = [Meld(MahjongTile("筒", 1), "碰")]
|
||||
state.melds[1] = melds_b
|
||||
|
||||
# 玩家 A 抢杠胡
|
||||
gang_tile = MahjongTile("筒", 1) # 玩家 B 打出用于杠的牌
|
||||
player_a_hand.add_tile(gang_tile)
|
||||
can_qiang_gang_hu = state.can_win(player_a_hand, state.melds[0], state.missing_suits[0])
|
||||
|
||||
# 打印状态
|
||||
state.print_game_state(player_index=0) # 打印玩家 A 的状态
|
||||
state.print_game_state(player_index=1) # 打印玩家 B 的状态
|
||||
|
||||
assert can_qiang_gang_hu, "测试失败:玩家 A 应该可以抢杠胡"
|
||||
print("测试通过:抢杠胡成功!")
|
||||
|
||||
def test_can_win_with_big_pairs():
|
||||
"""测试大对子胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加对子
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
|
||||
# 添加刻子
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 设置缺门为 "条",因为手牌和明牌中没有 "条"
|
||||
missing_suit = "条"
|
||||
|
||||
# 打印当前玩家状态
|
||||
state.print_game_state(player_index=0)
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:大对子应该可以胡牌"
|
||||
print("测试通过:大对子胡牌成功!")
|
||||
|
||||
|
||||
def test_can_win_with_small_seven_pairs():
|
||||
"""测试小七对胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加七对
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
|
||||
# 设置缺门为 "条",因为手牌和明牌中没有 "条"
|
||||
missing_suit = "条"
|
||||
|
||||
# 打印当前玩家状态
|
||||
state.print_game_state(player_index=0)
|
||||
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
assert state.can_win(state.hands[0], state.melds[0], missing_suit), "测试失败:小七对应该可以胡牌"
|
||||
print("测试通过:小七对胡牌成功!")
|
||||
|
||||
def test_can_win_with_jin_gou_diao():
|
||||
"""测试金钩吊胡牌"""
|
||||
hand = Hand()
|
||||
|
||||
# 添加仅剩的一张牌
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
|
||||
# 初始化游戏状态
|
||||
state = ChengduMahjongState()
|
||||
state.hands[0] = hand
|
||||
state.melds[0] = [
|
||||
Meld(MahjongTile("筒", 8), "碰"), # 8筒碰
|
||||
Meld(MahjongTile("筒", 7), "碰"), # 7筒碰
|
||||
Meld(MahjongTile("筒", 9), "杠"), # 9筒杠
|
||||
Meld(MahjongTile("筒", 3), "碰") # 3筒碰
|
||||
]
|
||||
# 设置缺门为 "条",因为手牌和明牌中没有 "条"
|
||||
missing_suit = "条"
|
||||
|
||||
# 打印当前玩家状态
|
||||
state.print_game_state(player_index=0)
|
||||
|
||||
# 模拟别人打出一张 "筒3",胡牌
|
||||
winning_tile = MahjongTile("筒", 3)
|
||||
|
||||
state.hands[0].add_tile(winning_tile)
|
||||
# 调用 can_win 方法并断言胡牌
|
||||
can_win = state.can_win(state.hands[0], state.melds[0], missing_suit)
|
||||
assert can_win, f"测试失败:金钩吊未能胡 {winning_tile}"
|
||||
print("测试通过:金钩吊胡牌成功!")
|
||||
245
tests/mahjong/test_fan_type.py
Normal file
245
tests/mahjong/test_fan_type.py
Normal file
@@ -0,0 +1,245 @@
|
||||
from src import Hand
|
||||
from src import MahjongTile
|
||||
from src import is_basic_win,is_cleared,is_terminal_fan,is_seven_pairs,is_full_request,is_dragon_seven_pairs
|
||||
from src import Meld
|
||||
|
||||
def test_is_basic_win():
|
||||
"""
|
||||
测试平胡(基本胡)的逻辑。
|
||||
"""
|
||||
hand = Hand()
|
||||
# 添加牌到手牌中
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("万", 3))
|
||||
hand.add_tile(MahjongTile("万", 4))
|
||||
hand.add_tile(MahjongTile("万", 5))
|
||||
|
||||
# 打印当前手牌
|
||||
print(f"测试手牌: {hand}")
|
||||
|
||||
# 调用平胡逻辑函数
|
||||
result = is_basic_win(hand)
|
||||
|
||||
# 使用断言验证
|
||||
assert result, "测试失败:此手牌应该符合平胡(基本胡)规则"
|
||||
print("测试通过:平胡(基本胡)逻辑正确")
|
||||
|
||||
def test_is_cleared_basic():
|
||||
"""测试素清(不带杠的清一色)"""
|
||||
hand = Hand()
|
||||
# 添加手牌
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 8))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
|
||||
melds = [] # 无杠
|
||||
assert is_cleared(hand, melds) == 2, "测试失败:素清应为 2 番"
|
||||
print("测试通过:素清")
|
||||
|
||||
def test_is_cleared_with_one_gang():
|
||||
"""测试极品(带 1 杠的清一色)"""
|
||||
hand = Hand()
|
||||
# 添加手牌
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
|
||||
# 添加明牌(1 杠)
|
||||
melds = [Meld(MahjongTile("筒", 7), "杠")]
|
||||
|
||||
# 检查是否为极品(带 1 杠的清一色)
|
||||
assert is_cleared(hand, melds) == 3, "测试失败:极品应为 3 番"
|
||||
print("测试通过:极品")
|
||||
|
||||
def test_calculate_terminal_fan():
|
||||
"""测试带幺九番型"""
|
||||
|
||||
# 示例1:基本带幺九
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 8))
|
||||
hand.add_tile(MahjongTile("条", 9))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("筒", 9))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
melds = []
|
||||
assert is_terminal_fan(hand, melds) == 3, "测试失败:基本带幺九应为 3 番"
|
||||
|
||||
def test_is_seven_pairs():
|
||||
"""测试七对番型"""
|
||||
hand = Hand()
|
||||
|
||||
# 示例1:符合七对
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
|
||||
assert is_seven_pairs(hand) == 2, "测试失败:符合七对,应为 2 番"
|
||||
|
||||
# 示例2:不符合七对(少一个对子)
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 1))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 2))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 3))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 4))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 5))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 6))
|
||||
hand.add_tile(MahjongTile("条", 7))
|
||||
|
||||
assert is_seven_pairs(hand) == 0, "测试失败:不符合七对,应为 0 番"
|
||||
|
||||
print("所有七对测试通过!")
|
||||
|
||||
def test_is_full_request():
|
||||
"""测试全求人番型"""
|
||||
hand = Hand()
|
||||
|
||||
# 示例1:符合全求人
|
||||
hand.add_tile(MahjongTile("筒", 5)) # 玩家手中只剩下 1 张牌
|
||||
melds = [
|
||||
Meld(MahjongTile("筒", 1), "碰"),
|
||||
Meld(MahjongTile("筒", 2), "杠"),
|
||||
Meld(MahjongTile("筒", 3), "碰"),
|
||||
Meld(MahjongTile("筒", 4), "碰"),
|
||||
]
|
||||
winning_tile = MahjongTile("筒", 5) # 胡牌通过别人打出的牌
|
||||
assert is_full_request(hand, melds, winning_tile) == 6, "测试失败:符合全求人,应为 6 番"
|
||||
|
||||
# 示例2:不符合全求人(玩家手上有多张牌)
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
assert is_full_request(hand, melds, winning_tile) == 0, "测试失败:不符合全求人,应为 0 番"
|
||||
|
||||
# 示例3:不符合全求人(没有碰或杠的明牌)
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
melds = []
|
||||
assert is_full_request(hand, melds, winning_tile) == 0, "测试失败:不符合全求人,应为 0 番"
|
||||
|
||||
print("所有全求人测试通过!")
|
||||
|
||||
def test_is_dragon_seven_pairs():
|
||||
"""测试龙七对番型计算"""
|
||||
|
||||
# 示例1:符合龙七对
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7)) # 四张7筒
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
|
||||
melds = [] # 没有明牌
|
||||
fan, root_adjustment = is_dragon_seven_pairs(hand, melds)
|
||||
assert fan == 12 and root_adjustment == -1, "测试失败:符合龙七对,应为 12 番,并减少 1 根"
|
||||
|
||||
# 示例2:不符合龙七对(只有七对,没有四张)
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
|
||||
fan, root_adjustment = is_dragon_seven_pairs(hand, melds)
|
||||
assert fan == 0 and root_adjustment == 0, "测试失败:不符合龙七对,应为 0 番,根数不变"
|
||||
|
||||
# 示例3:不符合龙七对(有明牌)
|
||||
hand = Hand()
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 1))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 2))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 3))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 4))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 5))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 6))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
hand.add_tile(MahjongTile("筒", 7))
|
||||
|
||||
melds = [Meld(MahjongTile("筒", 8), "碰")] # 有明牌
|
||||
fan, root_adjustment = is_dragon_seven_pairs(hand, melds)
|
||||
assert fan == 0 and root_adjustment == 0, "测试失败:有明牌,不符合龙七对,应为 0 番,根数不变"
|
||||
|
||||
print("所有龙七对测试通过!")
|
||||
|
||||
|
||||
87
tests/mahjong/test_hand.py
Normal file
87
tests/mahjong/test_hand.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from src import Hand
|
||||
from src import MahjongTile
|
||||
|
||||
|
||||
def test_add_tile():
|
||||
"""测试添加牌功能"""
|
||||
hand = Hand()
|
||||
tile1 = MahjongTile("条", 1)
|
||||
tile2 = MahjongTile("条", 2)
|
||||
|
||||
hand.add_tile(tile1)
|
||||
hand.add_tile(tile1)
|
||||
hand.add_tile(tile2)
|
||||
print("\n测试添加牌功能,当前手牌:", hand)
|
||||
|
||||
assert hand.get_tile_count(tile1) == 2, f"测试失败:{tile1} 应该有 2 张"
|
||||
assert hand.get_tile_count(tile2) == 1, f"测试失败:{tile2} 应该有 1 张"
|
||||
|
||||
|
||||
def test_remove_tile():
|
||||
"""测试移除牌功能"""
|
||||
hand = Hand()
|
||||
tile1 = MahjongTile("条", 1)
|
||||
|
||||
hand.add_tile(tile1)
|
||||
hand.add_tile(tile1)
|
||||
hand.remove_tile(tile1)
|
||||
print("\n测试移除牌功能,移除一张 1条 后的手牌:", hand)
|
||||
|
||||
assert hand.get_tile_count(tile1) == 1, f"测试失败:{tile1} 应该有 1 张"
|
||||
|
||||
|
||||
def test_can_peng():
|
||||
"""测试是否可以碰"""
|
||||
hand = Hand()
|
||||
tile1 = MahjongTile("条", 1)
|
||||
tile2 = MahjongTile("条", 2)
|
||||
|
||||
hand.add_tile(tile1)
|
||||
hand.add_tile(tile1)
|
||||
print("\n测试碰功能,当前手牌:", hand)
|
||||
|
||||
assert hand.can_peng(tile1) == True, f"测试失败:{tile1} 应该可以碰"
|
||||
assert hand.can_peng(tile2) == False, f"测试失败:{tile2} 不可以碰"
|
||||
|
||||
print(f"可以碰 {tile1} 的牌:", hand.can_peng(tile1))
|
||||
print(f"不可以碰 {tile2} 的牌:", hand.can_peng(tile2))
|
||||
|
||||
|
||||
def test_can_gang():
|
||||
"""测试是否可以杠"""
|
||||
hand = Hand()
|
||||
tile2 = MahjongTile("条", 2)
|
||||
|
||||
hand.add_tile(tile2)
|
||||
hand.add_tile(tile2)
|
||||
hand.add_tile(tile2)
|
||||
print("\n测试杠功能,当前手牌:", hand)
|
||||
|
||||
assert hand.can_gang(tile2) == False, f"测试失败:{tile2} 不可以杠"
|
||||
|
||||
# 添加更多牌来形成杠
|
||||
hand.add_tile(tile2)
|
||||
print("再添加一张 2条 后:", hand)
|
||||
|
||||
assert hand.can_gang(tile2) == True, f"测试失败:{tile2} 应该可以杠"
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""运行所有测试"""
|
||||
test_add_tile()
|
||||
print("测试添加牌功能通过!")
|
||||
|
||||
test_remove_tile()
|
||||
print("测试移除牌功能通过!")
|
||||
|
||||
test_can_peng()
|
||||
print("测试碰功能通过!")
|
||||
|
||||
test_can_gang()
|
||||
print("测试杠功能通过!")
|
||||
|
||||
print("\n所有测试通过!")
|
||||
|
||||
|
||||
# 运行测试
|
||||
run_all_tests()
|
||||
45
tests/mahjong/test_mahjong_tile.py
Normal file
45
tests/mahjong/test_mahjong_tile.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from src import MahjongTile
|
||||
|
||||
def test_mahjong_tile():
|
||||
# 测试合法的牌
|
||||
tile1 = MahjongTile("条", 5)
|
||||
assert tile1.suit == "条", f"测试失败:预期花色是 '条',但实际是 {tile1.suit}"
|
||||
assert tile1.value == 5, f"测试失败:预期面值是 5,但实际是 {tile1.value}"
|
||||
assert repr(tile1) == "5条", f"测试失败:预期牌名是 '5条',但实际是 {repr(tile1)}"
|
||||
|
||||
tile2 = MahjongTile("筒", 3)
|
||||
assert tile2.suit == "筒", f"测试失败:预期花色是 '筒',但实际是 {tile2.suit}"
|
||||
assert tile2.value == 3, f"测试失败:预期面值是 3,但实际是 {tile2.value}"
|
||||
assert repr(tile2) == "3筒", f"测试失败:预期牌名是 '3筒',但实际是 {repr(tile2)}"
|
||||
|
||||
tile3 = MahjongTile("万", 9)
|
||||
assert tile3.suit == "万", f"测试失败:预期花色是 '万',但实际是 {tile3.suit}"
|
||||
assert tile3.value == 9, f"测试失败:预期面值是 9,但实际是 {tile3.value}"
|
||||
assert repr(tile3) == "9万", f"测试失败:预期牌名是 '9万',但实际是 {repr(tile3)}"
|
||||
|
||||
# 测试非法的牌
|
||||
try:
|
||||
MahjongTile("条", 10) # 面值超出范围
|
||||
assert False, "测试失败:面值为 10 的牌应该抛出异常"
|
||||
except ValueError:
|
||||
pass # 正确抛出异常
|
||||
|
||||
try:
|
||||
MahjongTile("花", 5) # 花色无效
|
||||
assert False, "测试失败:花色为 '花' 的牌应该抛出异常"
|
||||
except ValueError:
|
||||
pass # 正确抛出异常
|
||||
|
||||
# 测试相等判断
|
||||
tile4 = MahjongTile("条", 5)
|
||||
assert tile1 == tile4, f"测试失败:预期 {tile1} 和 {tile4} 相等"
|
||||
tile5 = MahjongTile("筒", 5)
|
||||
assert tile1 != tile5, f"测试失败:预期 {tile1} 和 {tile5} 不相等"
|
||||
|
||||
# 测试哈希
|
||||
tile_set = {tile1, tile4, tile2}
|
||||
assert len(tile_set) == 2, f"测试失败:集合中应该有 2 张牌,而实际有 {len(tile_set)} 张"
|
||||
|
||||
print("所有测试通过!")
|
||||
|
||||
|
||||
19
tests/mahjong/test_scoring.py
Normal file
19
tests/mahjong/test_scoring.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
from src import calculate_score
|
||||
|
||||
@pytest.mark.parametrize("fan, is_self_draw, base_score, expected_scores", [
|
||||
# 测试用例 1: 自摸,总番数 3
|
||||
(3, True, 5, {"winner": 120, "loser": [-40, -40, -40]}),
|
||||
|
||||
# 测试用例 2: 点炮,总番数 2
|
||||
(2, False, 5, {"winner": 20, "loser": [-20, 0, 0]}),
|
||||
|
||||
# 测试用例 3: 自摸,总番数 4
|
||||
(4, True, 5, {"winner": 240, "loser": [-80, -80, -80]}),
|
||||
|
||||
# 测试用例 4: 点炮,总番数 1
|
||||
(1, False, 5, {"winner": 10, "loser": [-10, 0, 0]}),
|
||||
])
|
||||
def test_calculate_score(fan, is_self_draw, base_score, expected_scores):
|
||||
scores = calculate_score(fan, base_score, is_self_draw)
|
||||
assert scores == expected_scores, f"测试失败: {scores} != {expected_scores}"
|
||||
36
tests/mahjong/test_utils.py
Normal file
36
tests/mahjong/test_utils.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from src import get_suit,get_tile_name
|
||||
|
||||
def test_get_suit():
|
||||
# 测试条花色(0-35)
|
||||
for i in range(36):
|
||||
assert get_suit(i) == "条", f"测试失败:索引 {i} 应该是 '条'"
|
||||
|
||||
# 测试筒花色(36-71)
|
||||
for i in range(36, 72):
|
||||
assert get_suit(i) == "筒", f"测试失败:索引 {i} 应该是 '筒'"
|
||||
|
||||
# 测试万花色(72-107)
|
||||
for i in range(72, 108):
|
||||
assert get_suit(i) == "万", f"测试失败:索引 {i} 应该是 '万'"
|
||||
|
||||
# 测试无效索引
|
||||
try:
|
||||
get_suit(108)
|
||||
assert False, "测试失败:索引 108 应该抛出 ValueError"
|
||||
except ValueError:
|
||||
pass # 如果抛出 ValueError,测试通过
|
||||
|
||||
print("get_suit 测试通过!")
|
||||
|
||||
def test_get_tile_name():
|
||||
# 测试每个牌的名称是否正确
|
||||
for i in range(108):
|
||||
tile_name = get_tile_name(i)
|
||||
assert tile_name == f"{i % 36 + 1}{get_suit(i)}", \
|
||||
f"测试失败:索引 {i} 应该是 '{i % 36 + 1}{get_suit(i)}',但实际返回 '{tile_name}'"
|
||||
|
||||
print("get_tile_name 测试通过!")
|
||||
|
||||
# 运行测试
|
||||
test_get_suit()
|
||||
test_get_tile_name()
|
||||
Reference in New Issue
Block a user