diff --git a/src/core/move_generator.gd b/src/core/move_generator.gd index 381ab07..03064aa 100644 --- a/src/core/move_generator.gd +++ b/src/core/move_generator.gd @@ -9,157 +9,157 @@ const MAX_ENUM_NODES := 10000 const BEAM_WIDTH := 50 static func generate(hand: Array[Card], current_rank: int, config: RuleConfig) -> Array[HandEvaluator.EvaluatedPlay]: - var results: Array[HandEvaluator.EvaluatedPlay] = [] - var pass_play := HandEvaluator.EvaluatedPlay.new() - pass_play.type = -1 - pass_play.primary_rank = 0 - results.append(pass_play) - if hand.is_empty(): - return results - var sorted: Array[Card] = [] - for item: Card in hand: - sorted.append(item) - _sort(sorted) - var seen_ranks := {} - for c: Card in sorted: - var rk := c.rank() - if seen_ranks.get(rk, 0) < 4: - var ep := HandEvaluator.evaluate([c], current_rank, config) - if ep != null and ep.type != HandEvaluator.INVALID: - if not _contains_duplicate(results, ep): - results.append(ep) - seen_ranks[rk] = seen_ranks.get(rk, 0) + 1 - _gen_pairs(sorted, results, current_rank, config) - _gen_triples(sorted, results, current_rank, config) - _gen_bombs(sorted, results, current_rank, config) - _gen_straights(sorted, results, current_rank, config) - _gen_rocket(sorted, results, current_rank, config) - if results.size() > MAX_ENUM_NODES: - results = results.slice(0, MAX_ENUM_NODES) - return results + var results: Array[HandEvaluator.EvaluatedPlay] = [] + var pass_play := HandEvaluator.EvaluatedPlay.new() + pass_play.type = -1 + pass_play.primary_rank = 0 + results.append(pass_play) + if hand.is_empty(): + return results + var sorted: Array[Card] = [] + for item: Card in hand: + sorted.append(item) + _sort(sorted) + var seen_ranks := {} + for c: Card in sorted: + var rk := c.rank() + if seen_ranks.get(rk, 0) < 4: + var ep := HandEvaluator.evaluate([c], current_rank, config) + if ep != null and ep.type != HandEvaluator.INVALID: + if not _contains_duplicate(results, ep): + results.append(ep) + seen_ranks[rk] = seen_ranks.get(rk, 0) + 1 + _gen_pairs(sorted, results, current_rank, config) + _gen_triples(sorted, results, current_rank, config) + _gen_bombs(sorted, results, current_rank, config) + _gen_straights(sorted, results, current_rank, config) + _gen_rocket(sorted, results, current_rank, config) + if results.size() > MAX_ENUM_NODES: + results = results.slice(0, MAX_ENUM_NODES) + return results static func _gen_pairs(sorted: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var rank_counts := {} - for c in sorted: - var rk := c.rank() - if not rank_counts.has(rk): rank_counts[rk] = [] - rank_counts[rk].append(c) - for rk in rank_counts: - var cards: Array = rank_counts[rk] - if cards.size() >= 2: - var ep := HandEvaluator.evaluate(cards.slice(0, 2), current_rank, config) - if ep != null: results.append(ep) + var rank_counts := {} + for c in sorted: + var rk := c.rank() + if not rank_counts.has(rk): rank_counts[rk] = [] + rank_counts[rk].append(c) + for rk in rank_counts: + var cards: Array = rank_counts[rk] + if cards.size() >= 2: + var ep := HandEvaluator.evaluate(cards.slice(0, 2), current_rank, config) + if ep != null: results.append(ep) static func _gen_triples(sorted: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var rank_counts := {} - for c in sorted: - var rk := c.rank() - if not rank_counts.has(rk): rank_counts[rk] = [] - rank_counts[rk].append(c) - for rk in rank_counts: - var cards: Array = rank_counts[rk] - if cards.size() >= 3: - var ep := HandEvaluator.evaluate(cards.slice(0, 3), current_rank, config) - if ep != null: results.append(ep) - if cards.size() >= 3: - _gen_triple_plus_kickers(sorted, cards.slice(0, 3), results, current_rank, config) + var rank_counts := {} + for c in sorted: + var rk := c.rank() + if not rank_counts.has(rk): rank_counts[rk] = [] + rank_counts[rk].append(c) + for rk in rank_counts: + var cards: Array = rank_counts[rk] + if cards.size() >= 3: + var ep := HandEvaluator.evaluate(cards.slice(0, 3), current_rank, config) + if ep != null: results.append(ep) + if cards.size() >= 3: + _gen_triple_plus_kickers(sorted, cards.slice(0, 3), results, current_rank, config) static func _gen_triple_plus_kickers(hand: Array[Card], triple: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var remaining: Array[Card] = [] - for c in hand: - if not _card_in(triple, c): remaining.append(c) - var pairs := _find_pairs(remaining) - for pair in pairs: - var ep := HandEvaluator.evaluate(triple + pair, current_rank, config) - if ep != null: results.append(ep) + var remaining: Array[Card] = [] + for c in hand: + if not _card_in(triple, c): remaining.append(c) + var pairs := _find_pairs(remaining) + for pair in pairs: + var ep := HandEvaluator.evaluate(triple + pair, current_rank, config) + if ep != null: results.append(ep) static func _gen_bombs(sorted: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var rank_counts := {} - for c in sorted: - var rk := c.rank() - if not rank_counts.has(rk): rank_counts[rk] = [] - rank_counts[rk].append(c) - for rk in rank_counts: - var cards: Array = rank_counts[rk] - if cards.size() >= 4: - var ep := HandEvaluator.evaluate(cards.slice(0, 4), current_rank, config) - if ep != null: results.append(ep) - for count in range(5, cards.size() + 1): - var ep := HandEvaluator.evaluate(cards.slice(0, count), current_rank, config) - if ep != null: results.append(ep) + var rank_counts := {} + for c in sorted: + var rk := c.rank() + if not rank_counts.has(rk): rank_counts[rk] = [] + rank_counts[rk].append(c) + for rk in rank_counts: + var cards: Array = rank_counts[rk] + if cards.size() >= 4: + var ep := HandEvaluator.evaluate(cards.slice(0, 4), current_rank, config) + if ep != null: results.append(ep) + for count in range(5, cards.size() + 1): + var ep := HandEvaluator.evaluate(cards.slice(0, count), current_rank, config) + if ep != null: results.append(ep) static func _gen_straights(sorted: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var unique := _unique_ranks(sorted) - for start in unique: - for length in range(5, 13): - var needed: Array[int] = [] - for r in range(start, start + length): needed.append(r) - var found := _pick_consecutive(sorted, needed) - if found.size() == length: - var ep := HandEvaluator.evaluate(found, current_rank, config) - if ep != null: results.append(ep) + var unique := _unique_ranks(sorted) + for start in unique: + for length in range(5, 13): + var needed: Array[int] = [] + for r in range(start, start + length): needed.append(r) + var found := _pick_consecutive(sorted, needed) + if found.size() == length: + var ep := HandEvaluator.evaluate(found, current_rank, config) + if ep != null: results.append(ep) static func _gen_rocket(sorted: Array[Card], results: Array[HandEvaluator.EvaluatedPlay], current_rank: int, config: RuleConfig) -> void: - var sj_indices: Array[int] = [] - var bj_indices: Array[int] = [] - for i in range(sorted.size()): - if sorted[i].rank() == 15: sj_indices.append(i) - if sorted[i].rank() == 16: bj_indices.append(i) - if sj_indices.size() >= 2 and bj_indices.size() >= 2: - var cards: Array[Card] = [] - for idx in sj_indices.slice(0, 2) + bj_indices.slice(0, 2): - cards.append(sorted[idx]) - var ep := HandEvaluator.evaluate(cards, current_rank, config) - if ep != null: results.append(ep) + var sj_indices: Array[int] = [] + var bj_indices: Array[int] = [] + for i in range(sorted.size()): + if sorted[i].rank() == 15: sj_indices.append(i) + if sorted[i].rank() == 16: bj_indices.append(i) + if sj_indices.size() >= 2 and bj_indices.size() >= 2: + var cards: Array[Card] = [] + for idx in sj_indices.slice(0, 2) + bj_indices.slice(0, 2): + cards.append(sorted[idx]) + var ep := HandEvaluator.evaluate(cards, current_rank, config) + if ep != null: results.append(ep) static func _find_pairs(hand: Array[Card]) -> Array: - var result := [] - var rank_groups := {} - for c in hand: - var rk := c.rank() - if not rank_groups.has(rk): rank_groups[rk] = [] - rank_groups[rk].append(c) - for rk in rank_groups: - if rank_groups[rk].size() >= 2: - result.append(rank_groups[rk].slice(0, 2)) - return result + var result := [] + var rank_groups := {} + for c in hand: + var rk := c.rank() + if not rank_groups.has(rk): rank_groups[rk] = [] + rank_groups[rk].append(c) + for rk in rank_groups: + if rank_groups[rk].size() >= 2: + result.append(rank_groups[rk].slice(0, 2)) + return result static func _card_in(cards: Array[Card], target: Card) -> bool: - for c in cards: - if c.card_id == target.card_id: return true - return false + for c in cards: + if c.card_id == target.card_id: return true + return false static func _unique_ranks(hand: Array[Card]) -> Array: - var seen := {} - var result: Array = [] - for c in hand: - var rk := c.rank() - if not seen.has(rk): - seen[rk] = true - result.append(rk) - result.sort() - return result + var seen := {} + var result: Array = [] + for c in hand: + var rk := c.rank() + if not seen.has(rk): + seen[rk] = true + result.append(rk) + result.sort() + return result static func _pick_consecutive(hand: Array[Card], needed: Array[int]) -> Array[Card]: - var result: Array[Card] = [] - var used := {} - for target in needed: - var found := false - for c in hand: - if c.rank() == target and not used.has(c.card_id): - result.append(c) - used[c.card_id] = true - found = true - break - if not found: return [] - return result + var result: Array[Card] = [] + var used := {} + for target in needed: + var found := false + for c in hand: + if c.rank() == target and not used.has(c.card_id): + result.append(c) + used[c.card_id] = true + found = true + break + if not found: return [] + return result static func _contains_duplicate(results: Array[HandEvaluator.EvaluatedPlay], ep: HandEvaluator.EvaluatedPlay) -> bool: - for existing in results: - if existing.type == ep.type and existing.primary_rank == ep.primary_rank: - if existing.cards.size() == ep.cards.size(): - return true - return false + for existing in results: + if existing.type == ep.type and existing.primary_rank == ep.primary_rank: + if existing.cards.size() == ep.cards.size(): + return true + return false static func _sort(cards: Array[Card]) -> void: - cards.sort_custom(func(a: Card, b: Card): return a.compare_to(b) < 0) + cards.sort_custom(func(a: Card, b: Card): return a.compare_to(b) < 0) diff --git a/src/game/game_controller.gd b/src/game/game_controller.gd index 7d637d5..feb149b 100644 --- a/src/game/game_controller.gd +++ b/src/game/game_controller.gd @@ -4,6 +4,8 @@ class_name GameController extends Node +const _C = preload("res://src/core/constants.gd") + signal state_changed() signal turn_ready(player_idx: int, is_human: bool) signal game_ended(winner_team: int, reason: String) @@ -66,8 +68,13 @@ func _apply_play(player_idx: int, play: HandEvaluator.EvaluatedPlay) -> void: 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) + EventBus.player_finished.emit(player_idx, game_state.finished_players.size()) if game_state.all_hands_empty(): _end_game() + if play.type != -1: + EventBus.player_played_cards.emit(player_idx, play.type, play.cards) + if play.type == _C.TYPE_BOMB or play.type == _C.TYPE_ROCKET: + EventBus.bomb_detonated.emit(player_idx, play.primary_rank) func _advance_turn() -> void: var hand: Array = game_state.get_hand(game_state.round.active_player_idx) @@ -84,6 +91,7 @@ func _advance_turn() -> void: _next_alive_player() var current := game_state.round.active_player_idx turn_ready.emit(current, game_state.player_human[current]) + EventBus.turn_changed.emit(current) if not game_state.player_human[current]: _trigger_ai(current) @@ -120,3 +128,4 @@ func _end_game() -> void: 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") + EventBus.game_over.emit(game_state.current_winner_team, "NORMAL")