dev
parent
c3dc6cf234
commit
ccbe55dbc7
|
|
@ -2,7 +2,7 @@ import random
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from .game_state import ChengduMahjongState
|
from .chengdu_mahjong_state import ChengduMahjongState
|
||||||
|
|
||||||
|
|
||||||
class ChengduMahjongEngine:
|
class ChengduMahjongEngine:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from .hand import Hand
|
from .hand import Hand
|
||||||
from .mahjong_tile import MahjongTile
|
from .mahjong_tile import MahjongTile
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
class ChengduMahjongState:
|
class ChengduMahjongState:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -40,28 +40,33 @@ class ChengduMahjongState:
|
||||||
raise ValueError("缺门设置无效")
|
raise ValueError("缺门设置无效")
|
||||||
self.missing_suits[player] = missing_suit
|
self.missing_suits[player] = missing_suit
|
||||||
|
|
||||||
def can_win(self, hand: Hand):
|
def can_win(self, hand: Hand, missing_suit: str):
|
||||||
"""
|
"""
|
||||||
判断是否满足胡牌条件:四组(顺子或刻子)+ 一对将。
|
判断玩家是否能胡牌。
|
||||||
|
:param hand: 玩家手牌(Hand 对象)。
|
||||||
|
:param missing_suit: 玩家设置的缺门花色。
|
||||||
|
:return: True 表示能胡牌,False 表示不能胡牌。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def is_valid_group(tiles):
|
def is_valid_group(tiles):
|
||||||
"""
|
"""
|
||||||
判断是否为合法的顺子或刻子。
|
检查是否是合法组(AAA 或 ABC)。
|
||||||
"""
|
"""
|
||||||
if len(tiles) != 3:
|
if len(tiles) != 3:
|
||||||
return False
|
return False
|
||||||
tiles.sort(key=lambda t: (t.suit, t.value))
|
tiles.sort(key=lambda t: (t.suit, t.value)) # 按花色和数值排序
|
||||||
return (tiles[0] == tiles[1] == tiles[2]) or \
|
return (tiles[0].value == tiles[1].value == tiles[2].value and # AAA
|
||||||
(tiles[0].value + 1 == tiles[1].value and tiles[1].value + 1 == tiles[2].value and
|
tiles[0].suit == tiles[1].suit == tiles[2].suit) or \
|
||||||
|
(tiles[0].value + 1 == tiles[1].value and # ABC
|
||||||
|
tiles[1].value + 1 == tiles[2].value and
|
||||||
tiles[0].suit == tiles[1].suit == tiles[2].suit)
|
tiles[0].suit == tiles[1].suit == tiles[2].suit)
|
||||||
|
|
||||||
def try_win(remaining_tiles, depth=0):
|
def try_win(remaining_tiles, depth=0):
|
||||||
"""
|
"""
|
||||||
递归检查是否可以将剩余牌分为合法组合。
|
尝试将剩余牌分组,必须满足 n * AAA + m * ABC。
|
||||||
"""
|
"""
|
||||||
if not remaining_tiles:
|
if not remaining_tiles:
|
||||||
return depth == 4 # 必须分成四组
|
return True # 所有牌成功分组
|
||||||
|
|
||||||
for i in range(len(remaining_tiles)):
|
for i in range(len(remaining_tiles)):
|
||||||
for j in range(i + 1, len(remaining_tiles)):
|
for j in range(i + 1, len(remaining_tiles)):
|
||||||
for k in range(j + 1, len(remaining_tiles)):
|
for k in range(j + 1, len(remaining_tiles)):
|
||||||
|
|
@ -73,18 +78,27 @@ class ChengduMahjongState:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 统计每张牌的数量
|
# **第一步:检查花色限制**
|
||||||
|
suits = {tile.suit for tile in hand.tiles}
|
||||||
|
if len(suits) > 2:
|
||||||
|
logger.info("花色超过两种,不能胡牌")
|
||||||
|
return False # 花色超过两种,不能胡牌
|
||||||
|
|
||||||
|
# 检查是否打完缺门的花色
|
||||||
|
if any(tile.suit == missing_suit for tile in hand.tiles):
|
||||||
|
return False # 仍有缺门的花色,不能胡牌
|
||||||
|
|
||||||
|
# **第二步:寻找对子并分组**
|
||||||
|
# 找到所有对子(至少两张相同的牌)
|
||||||
tile_counter = Counter(hand.tiles)
|
tile_counter = Counter(hand.tiles)
|
||||||
pairs = [tile for tile, count in tile_counter.items() if count >= 2]
|
pairs = [tile for tile, count in tile_counter.items() if count >= 2]
|
||||||
|
|
||||||
|
# 遍历所有对子,尝试用剩余牌分组
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
# 移除一对将牌
|
|
||||||
temp_tiles = hand.tiles[:]
|
temp_tiles = hand.tiles[:]
|
||||||
temp_tiles.remove(pair)
|
temp_tiles.remove(pair) # 移除一张将牌
|
||||||
temp_tiles.remove(pair)
|
temp_tiles.remove(pair) # 再移除一张将牌
|
||||||
|
if try_win(temp_tiles): # 检查是否可以分组
|
||||||
# 检查剩余牌是否能分组
|
|
||||||
if try_win(temp_tiles):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False # 如果没有找到符合条件的组合,则不能胡牌
|
||||||
|
|
@ -27,7 +27,7 @@ def test_discard_tile():
|
||||||
|
|
||||||
|
|
||||||
def test_set_missing_suit():
|
def test_set_missing_suit():
|
||||||
from src.engine.game_state import ChengduMahjongState
|
from src.engine.chengdu_mahjong_state import ChengduMahjongState
|
||||||
|
|
||||||
state = ChengduMahjongState()
|
state = ChengduMahjongState()
|
||||||
player = 0
|
player = 0
|
||||||
|
|
@ -41,7 +41,7 @@ def test_set_missing_suit():
|
||||||
|
|
||||||
|
|
||||||
def test_can_win():
|
def test_can_win():
|
||||||
from src.engine.game_state import ChengduMahjongState
|
from src.engine.chengdu_mahjong_state import ChengduMahjongState
|
||||||
|
|
||||||
state = ChengduMahjongState()
|
state = ChengduMahjongState()
|
||||||
hand = [0] * 108
|
hand = [0] * 108
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
from src.engine.chengdu_mahjong_state import ChengduMahjongState
|
||||||
|
from src.engine.hand import Hand
|
||||||
|
from src.engine.mahjong_tile import MahjongTile
|
||||||
|
|
||||||
|
|
||||||
|
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_sequence_and_pair():
|
||||||
|
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("条", 4))
|
||||||
|
hand.add_tile(MahjongTile("筒", 5))
|
||||||
|
hand.add_tile(MahjongTile("筒", 6))
|
||||||
|
hand.add_tile(MahjongTile("筒", 7))
|
||||||
|
hand.add_tile(MahjongTile("万", 1))
|
||||||
|
hand.add_tile(MahjongTile("万", 2))
|
||||||
|
hand.add_tile(MahjongTile("万", 3))
|
||||||
|
hand.add_tile(MahjongTile("筒", 8))
|
||||||
|
hand.add_tile(MahjongTile("筒", 8))
|
||||||
|
state = ChengduMahjongState()
|
||||||
|
state.hands[0] = hand
|
||||||
|
assert state.can_win(hand), "测试失败:顺子和对子应该可以胡牌"
|
||||||
|
print("测试通过:顺子和对子可以胡牌")
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
print(f"\n,state.hand[0]: {state.hands[0]}")
|
||||||
|
assert state.can_win(state.hands[0]) == True, "测试失败:刻子和对子应该可以胡牌"
|
||||||
|
|
||||||
|
|
||||||
|
def test_can_win_with_mixed_groups():
|
||||||
|
"""测试顺子、刻子和对子混合胡牌"""
|
||||||
|
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("筒", 4))
|
||||||
|
hand.add_tile(MahjongTile("筒", 4))
|
||||||
|
hand.add_tile(MahjongTile("万", 7))
|
||||||
|
hand.add_tile(MahjongTile("万", 8))
|
||||||
|
hand.add_tile(MahjongTile("万", 9))
|
||||||
|
hand.add_tile(MahjongTile("筒", 6))
|
||||||
|
hand.add_tile(MahjongTile("筒", 6))
|
||||||
|
state = ChengduMahjongState()
|
||||||
|
assert state.can_win(hand), "测试失败:顺子、刻子和对子混合应该可以胡牌"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cannot_win():
|
||||||
|
"""测试不能胡牌"""
|
||||||
|
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))
|
||||||
|
state = ChengduMahjongState()
|
||||||
|
assert not state.can_win(hand), "测试失败:当前手牌不应可以胡牌"
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue