1
This commit is contained in:
2024-12-01 22:43:40 +08:00
parent 9aec69bb46
commit 96f0fbdcd7
9 changed files with 0 additions and 0 deletions

View File

View 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}"

View 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()

View 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("测试通过:金钩吊胡牌成功!")

View 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("所有龙七对测试通过!")

View 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()

View 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("所有测试通过!")

View 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}"

View 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()