diff --git a/catanatron_core/catanatron/models/actions.py b/catanatron_core/catanatron/models/actions.py index 9bb414dc..1657ccaa 100644 --- a/catanatron_core/catanatron/models/actions.py +++ b/catanatron_core/catanatron/models/actions.py @@ -51,14 +51,26 @@ def generate_playable_actions(state) -> List[Action]: elif action_prompt == ActionPrompt.MOVE_ROBBER: return robber_possibilities(state, color) elif action_prompt == ActionPrompt.PLAY_TURN: + actions = [] + # Allow playing dev cards before and after rolling + if player_can_play_dev(state, color, "YEAR_OF_PLENTY"): + actions.extend(year_of_plenty_possibilities(color, state.resource_freqdeck)) + if player_can_play_dev(state, color, "MONOPOLY"): + actions.extend(monopoly_possibilities(color)) + if player_can_play_dev(state, color, "KNIGHT"): + actions.append(Action(color, ActionType.PLAY_KNIGHT_CARD, None)) + if ( + player_can_play_dev(state, color, "ROAD_BUILDING") + and len(road_building_possibilities(state, color, False)) > 0 + ): + actions.append(Action(color, ActionType.PLAY_ROAD_BUILDING, None)) + if state.is_road_building: actions = road_building_possibilities(state, color, False) elif not player_has_rolled(state, color): - actions = [Action(color, ActionType.ROLL, None)] - if player_can_play_dev(state, color, "KNIGHT"): - actions.append(Action(color, ActionType.PLAY_KNIGHT_CARD, None)) + actions.append(Action(color, ActionType.ROLL, None)) else: - actions = [Action(color, ActionType.END_TURN, None)] + actions.append(Action(color, ActionType.END_TURN, None)) actions.extend(road_building_possibilities(state, color)) actions.extend(settlement_possibilities(state, color)) actions.extend(city_possibilities(state, color)) @@ -70,21 +82,6 @@ def generate_playable_actions(state) -> List[Action]: if can_buy_dev_card: actions.append(Action(color, ActionType.BUY_DEVELOPMENT_CARD, None)) - # Play Dev Cards - if player_can_play_dev(state, color, "YEAR_OF_PLENTY"): - actions.extend( - year_of_plenty_possibilities(color, state.resource_freqdeck) - ) - if player_can_play_dev(state, color, "MONOPOLY"): - actions.extend(monopoly_possibilities(color)) - if player_can_play_dev(state, color, "KNIGHT"): - actions.append(Action(color, ActionType.PLAY_KNIGHT_CARD, None)) - if ( - player_can_play_dev(state, color, "ROAD_BUILDING") - and len(road_building_possibilities(state, color, False)) > 0 - ): - actions.append(Action(color, ActionType.PLAY_ROAD_BUILDING, None)) - # Trade actions.extend(maritime_trade_possibilities(state, color)) return actions diff --git a/catanatron_core/catanatron/state.py b/catanatron_core/catanatron/state.py index 665eab8c..2a3c98d5 100644 --- a/catanatron_core/catanatron/state.py +++ b/catanatron_core/catanatron/state.py @@ -76,6 +76,10 @@ # de-normalized features (for performance since we think they are good features) "ACTUAL_VICTORY_POINTS": 0, "LONGEST_ROAD_LENGTH": 0, + "KNIGHT_PURCHASED_THIS_TURN": 0, + "MONOPOLY_PURCHASED_THIS_TURN": 0, + "YEAR_OF_PLENTY_PURCHASED_THIS_TURN": 0, + "ROAD_BUILDING_PURCHASED_THIS_TURN": 0, } for resource in RESOURCES: PLAYER_INITIAL_STATE[f"{resource}_IN_HAND"] = 0 diff --git a/catanatron_core/catanatron/state_functions.py b/catanatron_core/catanatron/state_functions.py index acf71799..4b71391d 100644 --- a/catanatron_core/catanatron/state_functions.py +++ b/catanatron_core/catanatron/state_functions.py @@ -225,9 +225,17 @@ def player_resource_freqdeck_contains(state, color, freqdeck): def player_can_play_dev(state, color, dev_card): key = player_key(state, color) + + cards_in_hand = state.player_state[f"{key}_{dev_card}_IN_HAND"] + cards_purchased_this_turn = state.player_state[ + f"{key}_{dev_card}_PURCHASED_THIS_TURN" + ] + playable_cards = cards_in_hand - cards_purchased_this_turn + return ( not state.player_state[f"{key}_HAS_PLAYED_DEVELOPMENT_CARD_IN_TURN"] - and state.player_state[f"{key}_{dev_card}_IN_HAND"] >= 1 + # Must have at least 1 card that wasn't purchased this turn + and playable_cards > 0 ) @@ -259,6 +267,9 @@ def buy_dev_card(state, color, dev_card): state.player_state[f"{key}_{dev_card}_IN_HAND"] += 1 if dev_card == VICTORY_POINT: state.player_state[f"{key}_ACTUAL_VICTORY_POINTS"] += 1 + else: + # Mark as purchased this turn + state.player_state[f"{key}_{dev_card}_PURCHASED_THIS_TURN"] += 1 state.player_state[f"{key}_SHEEP_IN_HAND"] -= 1 state.player_state[f"{key}_WHEAT_IN_HAND"] -= 1 @@ -334,3 +345,8 @@ def player_clean_turn(state, color): key = player_key(state, color) state.player_state[f"{key}_HAS_PLAYED_DEVELOPMENT_CARD_IN_TURN"] = False state.player_state[f"{key}_HAS_ROLLED"] = False + # Reset all purchased-this-turn counters + state.player_state[f"{key}_KNIGHT_PURCHASED_THIS_TURN"] = 0 + state.player_state[f"{key}_MONOPOLY_PURCHASED_THIS_TURN"] = 0 + state.player_state[f"{key}_YEAR_OF_PLENTY_PURCHASED_THIS_TURN"] = 0 + state.player_state[f"{key}_ROAD_BUILDING_PURCHASED_THIS_TURN"] = 0 diff --git a/tests/test_game.py b/tests/test_game.py index c00d7561..80b4f503 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -327,10 +327,7 @@ def test_play_road_building(fake_roll_dice): ): game.play_tick() - # roll not a 7 - fake_roll_dice.return_value = (1, 2) - game.play_tick() # roll - + # Play Road Building before rolling game.execute(Action(p0.color, ActionType.PLAY_ROAD_BUILDING, None)) assert game.state.is_road_building assert game.state.free_roads_available == 2 diff --git a/ui/src/pages/ActionsToolbar.js b/ui/src/pages/ActionsToolbar.js index 74285758..2933c844 100644 --- a/ui/src/pages/ActionsToolbar.js +++ b/ui/src/pages/ActionsToolbar.js @@ -47,7 +47,7 @@ function PlayButtons() { [enqueueSnackbar, closeSnackbar] ); - const { gameState, isPlayingMonopoly, isPlayingYearOfPlenty } = state; + const { gameState, isPlayingMonopoly, isPlayingYearOfPlenty, isRoadBuilding } = state; const key = playerKey(gameState, gameState.current_color); const isRoll = gameState.current_prompt === "PLAY_TURN" && @@ -198,7 +198,7 @@ function PlayButtons() { return ( <> } items={useItems} @@ -206,7 +206,7 @@ function PlayButtons() { Use } items={buildItems} @@ -214,7 +214,7 @@ function PlayButtons() { Buy } items={tradeItems} @@ -222,27 +222,27 @@ function PlayButtons() { Trade