# src/game/game_controller.gd # Glue layer: connects core logic to UI and AI class_name GameController extends Node signal state_changed() signal turn_ready(player_idx: int, is_human: bool) signal game_ended(winner_team: int, reason: String) var game_state: GameState var ai_players: Dictionary = {} var is_processing: bool = false func start_game(config: RuleConfig, human_idx: int = 0, seed_: int = -1) -> void: game_state = GameState.create(config, seed_) game_state.player_human[human_idx] = true for i in range(4): if not game_state.player_human[i]: ai_players[i] = L2RuleAI.new() var deck := Deck.create(game_state.seed) game_state.deal_cards(deck) game_state.phase = GameState.Phase.PLAY game_state.round.active_player_idx = 0 state_changed.emit() func handle_human_play(cards: Array) -> Dictionary: if is_processing: return {"ok": false, "error_code": 1, "data": null} var hand := game_state.get_hand(game_state.round.active_player_idx) var play := HandEvaluator.evaluate(cards, game_state.current_rank, game_state.rule_config) if play == null or play.type == -1: return {"ok": false, "error_code": 1, "data": null} var last_idx := game_state.round.last_non_pass_player() var result := RuleEngine.can_play(hand, play, game_state.round.table, last_idx, game_state.current_rank, game_state.rule_config) if not result.ok: return result _apply_play(game_state.round.active_player_idx, play) _advance_turn() return {"ok": true, "error_code": 0, "data": null} func handle_human_pass() -> Dictionary: if is_processing: return {"ok": false, "error_code": 1, "data": null} var old_last := game_state.round.last_non_pass_player() if old_last >= 0 and old_last == game_state.round.active_player_idx: return {"ok": false, "error_code": 3, "data": null} var pass_play := HandEvaluator.EvaluatedPlay.new() pass_play.type = -1 pass_play.primary_rank = 0 _apply_play(game_state.round.active_player_idx, pass_play) _advance_turn() return {"ok": true, "error_code": 0, "data": null} func _apply_play(player_idx: int, play: HandEvaluator.EvaluatedPlay) -> void: if play.type != -1: game_state.remove_cards_from_hand(player_idx, play.cards) var action := Actions.Action.new() action.player_idx = player_idx action.action_type = "PASS" if play.type == -1 else "PLAY" action.cards = play.cards.duplicate(false) action.seq_id = game_state.round.next_seq() action.timestamp = Time.get_unix_time_from_system() game_state.action_log.append(action) game_state.round.add_play(play, player_idx) var hand: Array = game_state.get_hand(player_idx) if hand.is_empty() and not game_state.is_player_finished(player_idx): game_state.add_finished_player(player_idx) if game_state.all_hands_empty(): _end_game() func _advance_turn() -> void: var hand: Array = game_state.get_hand(game_state.round.active_player_idx) if hand.is_empty(): var partner := game_state.get_partner(game_state.round.active_player_idx) if not game_state.is_player_finished(partner): game_state.round.active_player_idx = partner game_state.round.reset_for_new_round() else: _next_alive_player() elif game_state.round.is_cleared: game_state.round.reset_for_new_round() else: _next_alive_player() var current := game_state.round.active_player_idx turn_ready.emit(current, game_state.player_human[current]) if not game_state.player_human[current]: _trigger_ai(current) func _next_alive_player() -> void: for _i in range(4): game_state.round.active_player_idx = (game_state.round.active_player_idx + 1) % 4 if not game_state.is_player_finished(game_state.round.active_player_idx): return func _trigger_ai(player_idx: int) -> void: var ai := ai_players.get(player_idx) if ai == null: return var hand8: Array = game_state.get_hand(player_idx) var decision := ai.decide(hand8, game_state.round.table, game_state.current_rank, game_state.rule_config) if decision.type == -1: _apply_play(player_idx, decision) else: _apply_play(player_idx, decision) _advance_turn() func _end_game() -> void: game_state.phase = GameState.Phase.GAME_OVER var team_counts := [0, 0] for fp in game_state.finished_players: var t := game_state.get_team(fp) if t != null: team_counts[t.team_id] += 1 if team_counts[0] >= 2: game_state.current_winner_team = 0 elif team_counts[1] >= 2: game_state.current_winner_team = 1 else: game_state.current_winner_team = 0 if team_counts[0] > team_counts[1] else 1 game_state.game_end_reason = "NORMAL" game_ended.emit(game_state.current_winner_team, "NORMAL")