parent
14c811f6b9
commit
2a5680fae9
|
|
@ -2,57 +2,87 @@ from loguru import logger
|
|||
from utils import get_tile_name
|
||||
|
||||
|
||||
def draw_tile(state):
|
||||
def draw_tile(self):
|
||||
"""
|
||||
当前玩家摸牌的动作逻辑。
|
||||
|
||||
参数:
|
||||
- state: ChengduMahjongState 实例,表示当前游戏状态。
|
||||
|
||||
返回:
|
||||
- tile: 当前玩家摸到的牌的索引。
|
||||
|
||||
异常:
|
||||
- ValueError: 如果牌堆已经空了(流局条件)。
|
||||
当前玩家摸牌逻辑,记录牌的详细信息和游戏状态。
|
||||
"""
|
||||
if state.remaining_tiles == 0:
|
||||
logger.warning("牌堆已空,无法摸牌!")
|
||||
raise ValueError("牌堆已空") # 牌堆为空时不能摸牌
|
||||
if self.state.remaining_tiles == 0:
|
||||
logger.warning("牌堆已空,游戏结束!")
|
||||
self.game_over = True
|
||||
return "牌堆已空"
|
||||
|
||||
tile = state.deck.pop(0) # 从牌堆顶取出一张牌
|
||||
state.remaining_tiles -= 1 # 更新牌堆剩余数量
|
||||
state.hands[state.current_player][tile] += 1 # 将摸到的牌添加到当前玩家的手牌中
|
||||
tile = self.state.deck.pop(0) # 从牌堆中取出一张牌
|
||||
self.state.remaining_tiles -= 1 # 更新剩余牌数
|
||||
self.state.hands[self.state.current_player][tile] += 1 # 加入当前玩家手牌
|
||||
|
||||
tile_name = get_tile_name(tile) # 获取牌的具体名称
|
||||
tile_name = get_tile_name(tile) # 获取具体的牌名
|
||||
logger.info(
|
||||
f"玩家 {state.current_player} 摸到一张牌: {tile_name}(索引 {tile})。剩余牌堆数量: {state.remaining_tiles}"
|
||||
f"玩家 {self.state.current_player} 摸到一张牌: {tile_name}(索引 {tile})。剩余牌堆数量: {self.state.remaining_tiles}"
|
||||
)
|
||||
return tile
|
||||
|
||||
|
||||
def discard_tile(state, tile):
|
||||
def discard_tile(self, tile):
|
||||
"""
|
||||
当前玩家打出一张牌的动作逻辑。
|
||||
|
||||
参数:
|
||||
- state: ChengduMahjongState 实例,表示当前游戏状态。
|
||||
- tile: 玩家想要打出的牌的索引。
|
||||
|
||||
操作:
|
||||
- 从当前玩家手牌中移除指定的牌。
|
||||
- 将指定牌添加到当前玩家的牌河中。
|
||||
|
||||
异常:
|
||||
- ValueError: 如果当前玩家的手牌中没有这张牌。
|
||||
当前玩家打牌逻辑,记录打出的牌和当前牌河信息。
|
||||
"""
|
||||
if state.hands[state.current_player][tile] == 0:
|
||||
logger.error(f"玩家 {state.current_player} 尝试打出不存在的牌: 索引 {tile}")
|
||||
raise ValueError("玩家没有这张牌") # 防止打出不存在的牌
|
||||
if self.state.hands[self.state.current_player][tile] == 0:
|
||||
logger.error(f"玩家 {self.state.current_player} 尝试打出不存在的牌: 索引 {tile}")
|
||||
raise ValueError("玩家没有这张牌")
|
||||
|
||||
state.hands[state.current_player][tile] -= 1 # 从手牌中移除该牌
|
||||
state.discards[state.current_player].append(tile) # 将牌添加到牌河
|
||||
self.state.hands[self.state.current_player][tile] -= 1 # 从手牌中移除
|
||||
self.state.discards[self.state.current_player].append(tile) # 加入牌河
|
||||
|
||||
tile_name = get_tile_name(tile) # 获取牌的具体名称
|
||||
tile_name = get_tile_name(tile) # 获取具体的牌名
|
||||
logger.info(
|
||||
f"玩家 {state.current_player} 打出一张牌: {tile_name}(索引 {tile})。当前牌河: {[get_tile_name(t) for t in state.discards[state.current_player]]}"
|
||||
f"玩家 {self.state.current_player} 打出一张牌: {tile_name}(索引 {tile})。当前牌河: {[get_tile_name(t) for t in self.state.discards[self.state.current_player]]}"
|
||||
)
|
||||
|
||||
|
||||
def peng(self, tile):
|
||||
"""
|
||||
当前玩家碰牌逻辑,记录碰牌操作和手牌状态。
|
||||
"""
|
||||
player = self.state.current_player
|
||||
if self.state.hands[player][tile] < 2:
|
||||
logger.error(f"玩家 {player} 尝试碰牌失败: {get_tile_name(tile)}(索引 {tile})")
|
||||
raise ValueError("碰牌条件不满足")
|
||||
|
||||
self.state.hands[player][tile] -= 2 # 减去两张牌
|
||||
self.state.melds[player].append(("peng", tile)) # 加入明牌列表
|
||||
|
||||
tile_name = get_tile_name(tile)
|
||||
logger.info(f"玩家 {player} 碰了一张牌: {tile_name}(索引 {tile})。当前明牌: {self.state.melds[player]}")
|
||||
|
||||
|
||||
def gang(self, tile, mode="ming"):
|
||||
"""
|
||||
当前玩家杠牌逻辑,记录杠牌类型和状态更新。
|
||||
"""
|
||||
player = self.state.current_player
|
||||
tile_name = get_tile_name(tile)
|
||||
|
||||
if mode == "ming" and self.state.hands[player][tile] == 3:
|
||||
self.state.hands[player][tile] -= 3
|
||||
self.state.melds[player].append(("ming_gang", tile))
|
||||
logger.info(f"玩家 {player} 明杠: {tile_name}(索引 {tile})")
|
||||
elif mode == "an" and self.state.hands[player][tile] == 4:
|
||||
self.state.hands[player][tile] -= 4
|
||||
self.state.melds[player].append(("an_gang", tile))
|
||||
logger.info(f"玩家 {player} 暗杠: {tile_name}(索引 {tile})")
|
||||
else:
|
||||
logger.error(f"玩家 {player} 尝试杠牌失败: {tile_name}(索引 {tile}),条件不满足")
|
||||
raise ValueError("杠牌条件不满足")
|
||||
|
||||
|
||||
def check_blood_battle(self):
|
||||
"""
|
||||
检查游戏是否流局或血战结束,记录状态。
|
||||
"""
|
||||
if any(score <= 0 for score in self.state.scores):
|
||||
logger.info(f"游戏结束,某玩家分数小于等于0: {self.state.scores}")
|
||||
self.game_over = True
|
||||
|
||||
if len(self.state.winners) >= 3 or self.state.remaining_tiles == 0:
|
||||
logger.info(f"游戏结束,赢家列表: {self.state.winners}")
|
||||
self.game_over = True
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
from .game_state import ChengduMahjongState
|
||||
from .utils import get_tile_name # 确保 utils 中有 get_tile_name 定义
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class ChengduMahjongEngine:
|
||||
|
|
@ -8,79 +6,3 @@ class ChengduMahjongEngine:
|
|||
self.state = ChengduMahjongState()
|
||||
self.game_over = False
|
||||
|
||||
def draw_tile(self):
|
||||
"""
|
||||
当前玩家摸牌逻辑,记录牌的详细信息和游戏状态。
|
||||
"""
|
||||
if self.state.remaining_tiles == 0:
|
||||
logger.warning("牌堆已空,游戏结束!")
|
||||
self.game_over = True
|
||||
return "牌堆已空"
|
||||
|
||||
tile = self.state.deck.pop(0) # 从牌堆中取出一张牌
|
||||
self.state.remaining_tiles -= 1 # 更新剩余牌数
|
||||
self.state.hands[self.state.current_player][tile] += 1 # 加入当前玩家手牌
|
||||
|
||||
tile_name = get_tile_name(tile) # 获取具体的牌名
|
||||
logger.info(
|
||||
f"玩家 {self.state.current_player} 摸到一张牌: {tile_name}(索引 {tile})。剩余牌堆数量: {self.state.remaining_tiles}"
|
||||
)
|
||||
return tile
|
||||
|
||||
def discard_tile(self, tile):
|
||||
"""
|
||||
当前玩家打牌逻辑,记录打出的牌和当前牌河信息。
|
||||
"""
|
||||
if self.state.hands[self.state.current_player][tile] == 0:
|
||||
logger.error(f"玩家 {self.state.current_player} 尝试打出不存在的牌: 索引 {tile}")
|
||||
raise ValueError("玩家没有这张牌")
|
||||
|
||||
self.state.hands[self.state.current_player][tile] -= 1 # 从手牌中移除
|
||||
self.state.discards[self.state.current_player].append(tile) # 加入牌河
|
||||
|
||||
tile_name = get_tile_name(tile) # 获取具体的牌名
|
||||
logger.info(
|
||||
f"玩家 {self.state.current_player} 打出一张牌: {tile_name}(索引 {tile})。当前牌河: {[get_tile_name(t) for t in self.state.discards[self.state.current_player]]}"
|
||||
)
|
||||
|
||||
def peng(self, tile):
|
||||
"""
|
||||
当前玩家碰牌逻辑,记录碰牌操作和手牌状态。
|
||||
"""
|
||||
player = self.state.current_player
|
||||
if self.state.hands[player][tile] < 2:
|
||||
logger.error(f"玩家 {player} 尝试碰牌失败: {get_tile_name(tile)}(索引 {tile})")
|
||||
raise ValueError("碰牌条件不满足")
|
||||
|
||||
self.state.hands[player][tile] -= 2 # 减去两张牌
|
||||
self.state.melds[player].append(("peng", tile)) # 加入明牌列表
|
||||
|
||||
tile_name = get_tile_name(tile)
|
||||
logger.info(f"玩家 {player} 碰了一张牌: {tile_name}(索引 {tile})。当前明牌: {self.state.melds[player]}")
|
||||
|
||||
def gang(self, tile, mode="ming"):
|
||||
"""
|
||||
当前玩家杠牌逻辑,记录杠牌类型和状态更新。
|
||||
"""
|
||||
player = self.state.current_player
|
||||
tile_name = get_tile_name(tile)
|
||||
|
||||
if mode == "ming" and self.state.hands[player][tile] == 3:
|
||||
self.state.hands[player][tile] -= 3
|
||||
self.state.melds[player].append(("ming_gang", tile))
|
||||
logger.info(f"玩家 {player} 明杠: {tile_name}(索引 {tile})")
|
||||
elif mode == "an" and self.state.hands[player][tile] == 4:
|
||||
self.state.hands[player][tile] -= 4
|
||||
self.state.melds[player].append(("an_gang", tile))
|
||||
logger.info(f"玩家 {player} 暗杠: {tile_name}(索引 {tile})")
|
||||
else:
|
||||
logger.error(f"玩家 {player} 尝试杠牌失败: {tile_name}(索引 {tile}),条件不满足")
|
||||
raise ValueError("杠牌条件不满足")
|
||||
|
||||
def check_blood_battle(self):
|
||||
"""
|
||||
检查游戏是否流局或血战结束,记录状态。
|
||||
"""
|
||||
if len(self.state.winners) >= 3 or self.state.remaining_tiles == 0:
|
||||
logger.info(f"游戏结束,赢家列表: {self.state.winners}")
|
||||
self.game_over = True
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import gym
|
|||
import numpy as np
|
||||
from gym import spaces
|
||||
|
||||
|
||||
from src.engine.calculate_fan import calculate_fan, is_seven_pairs, is_cleared, is_big_pairs
|
||||
from src.engine.chengdu_mahjong_engine import ChengduMahjongEngine
|
||||
from src.engine.scoring import calculate_score
|
||||
|
|
@ -14,12 +13,15 @@ class MahjongEnv(gym.Env):
|
|||
self.engine = ChengduMahjongEngine()
|
||||
self.scores = [100, 100, 100, 100] # 四位玩家初始分数
|
||||
self.base_score = 1 # 底分
|
||||
self.max_rounds = 100 # 最大轮数,防止游戏无限进行
|
||||
self.current_round = 0 # 当前轮数
|
||||
self.action_space = spaces.Discrete(108) # 动作空间:打牌的索引
|
||||
self.observation_space = spaces.Box(low=0, high=4, shape=(108,), dtype=np.int32)
|
||||
|
||||
def reset(self):
|
||||
self.engine = ChengduMahjongEngine()
|
||||
self.scores = [100, 100, 100, 100] # 每局重置分数
|
||||
self.current_round = 0
|
||||
return self.engine.state.hands[self.engine.state.current_player]
|
||||
|
||||
def step(self, action):
|
||||
|
|
@ -31,9 +33,23 @@ class MahjongEnv(gym.Env):
|
|||
reward, done = self.handle_win()
|
||||
else:
|
||||
reward, done = -1, False # 默认小惩罚
|
||||
|
||||
# 检查是否有玩家分数 <= 0
|
||||
if any(score <= 0 for score in self.scores):
|
||||
done = True
|
||||
reward = -100 # 游戏结束的惩罚(可根据需求调整)
|
||||
except ValueError:
|
||||
reward, done = -10, False # 非法操作扣分
|
||||
|
||||
# 切换到下一个玩家
|
||||
self.engine.state.current_player = (self.engine.state.current_player + 1) % 4
|
||||
self.current_round += 1
|
||||
|
||||
# 如果达到最大轮数,结束游戏
|
||||
if self.current_round >= self.max_rounds:
|
||||
done = True
|
||||
reward = 0 # 平局奖励或惩罚(可调整)
|
||||
|
||||
return self.engine.state.hands[self.engine.state.current_player], reward, done, {}
|
||||
|
||||
def handle_win(self):
|
||||
|
|
@ -44,8 +60,9 @@ class MahjongEnv(gym.Env):
|
|||
hand = self.engine.state.hands[winner]
|
||||
melds = self.engine.state.melds[winner]
|
||||
is_self_draw = True # 假设自摸(后续可动态判断)
|
||||
is_cleared = is_cleared(hand)
|
||||
|
||||
conditions = {
|
||||
"is_cleared": is_cleared(hand),
|
||||
"is_seven_pairs": is_seven_pairs(hand),
|
||||
"is_big_pairs": is_big_pairs(hand),
|
||||
# 添加其他条件...
|
||||
|
|
@ -62,9 +79,10 @@ class MahjongEnv(gym.Env):
|
|||
|
||||
# 奖励设置为赢家得分
|
||||
reward = scores["winner"]
|
||||
done = True # 游戏结束
|
||||
done = True # 胡牌结束当前局
|
||||
return reward, done
|
||||
|
||||
def render(self, mode="human"):
|
||||
print("当前分数:", self.scores)
|
||||
print("当前状态:", self.engine.state.hands[self.engine.state.current_player])
|
||||
print(f"当前轮数: {self.current_round}")
|
||||
print("玩家分数:", self.scores)
|
||||
print("当前玩家状态:", self.engine.state.hands[self.engine.state.current_player])
|
||||
|
|
|
|||
Loading…
Reference in New Issue