Spaces:
Sleeping
Sleeping
| """Tests for features/plot_value.py.""" | |
| from __future__ import annotations | |
| from werewolftown.features.plot_value import ( | |
| find_best_buy_candidate, | |
| plot_buyer_gain, | |
| plot_opportunity_cost, | |
| plot_value, | |
| ) | |
| from werewolftown.state.game_state import PlayerView | |
| class TestPlotValue: | |
| def test_empty_plot_best_shop(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| # Plot 6 adjacent to 5 (Happy) → gain = rev(3,4) - rev(2,4) = 50000-30000 | |
| val = plot_value(state, 6, viewer="me") | |
| assert val == 20000 | |
| def test_built_plot_fixed_type(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy", 6: "猫天天"}, | |
| shops=(), | |
| ) | |
| # Plot 6 is built with 猫天天 → gain from acquiring = 0 (already built, fixed type) | |
| # Actually, gain is the marginal revenue from having this plot | |
| # For a built plot, it's the cluster gain for that type | |
| val = plot_value(state, 6, viewer="me") | |
| # 6 is isolated 猫天天 (no adjacent 猫天天) → gain = 0 | |
| assert val == 0 | |
| def test_isolated_empty_plot(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 7}), | |
| built_on={4: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| # Plot 7 not adjacent to any Happy → can still build 1-cluster = 10000 | |
| val = plot_value(state, 7, viewer="me") | |
| assert val == 10000 | |
| class TestPlotOpportunityCost: | |
| def test_built_plot_loss(self, make_state, cfg): | |
| state = make_state( | |
| round=1, | |
| plots=frozenset({4, 5}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=(), | |
| ) | |
| # Selling plot 4: lose 2-cluster → 1-cluster | |
| # rev(2,4)=30000, rev(1,4)=10000 → loss=20000/round * 4 rounds = 80000 | |
| # + extra_penalty 5000*4 = 20000 → total = 100000 | |
| cost = plot_opportunity_cost(state, 4, cfg) | |
| assert cost == 100000 | |
| def test_empty_plot_adjacent(self, make_state, cfg): | |
| state = make_state( | |
| round=1, | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| # Empty plot 6, adjacent to my plots → floor=10000 * 4 = 40000 | |
| cost = plot_opportunity_cost(state, 6, cfg) | |
| assert cost == 40000 | |
| def test_empty_plot_isolated(self, make_state, cfg): | |
| state = make_state( | |
| round=1, | |
| plots=frozenset({4, 5, 7}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| # Empty plot 7, not adjacent → floor=5000 * 4 = 20000 | |
| cost = plot_opportunity_cost(state, 7, cfg) | |
| assert cost == 20000 | |
| class TestPlotBuyerGain: | |
| def test_buyer_gain_with_penalty(self, make_state, cfg): | |
| state = make_state( | |
| round=1, | |
| player_id="子涵", | |
| plots=frozenset({4, 5}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| other_players={"浩宇": PlayerView( | |
| name="浩宇", cash=50000, | |
| plots=frozenset({6, 10}), | |
| shops=("Happy",), | |
| )}, | |
| ) | |
| # 浩宇 has Happy card, plot 6 → can build 1-cluster = 10000/round | |
| # penalty ratio R1 = 0.4, remaining = 4 → 10000 * 4 * 0.4 = 16000 | |
| gain = plot_buyer_gain(state, 6, "浩宇", cfg) | |
| assert gain == 16000 | |
| def test_zero_for_no_gain(self, make_state, cfg): | |
| state = make_state(round=1) | |
| gain = plot_buyer_gain(state, 4, "浩宇", cfg) | |
| assert gain == 0 | |
| class TestFindBestBuyCandidate: | |
| def test_finds_adjacent_opp_plot(self, make_state, cfg): | |
| state = make_state( | |
| round=1, | |
| player_id="子涵", | |
| plots=frozenset({4, 5}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| other_players={"浩宇": PlayerView( | |
| name="浩宇", cash=50000, | |
| plots=frozenset({6}), # 6 adj 5 | |
| shops=(), | |
| )}, | |
| ) | |
| cand = find_best_buy_candidate(state, cfg) | |
| assert cand is not None | |
| assert cand.plot_id == 6 | |
| assert cand.owner == "浩宇" | |
| assert cand.is_adjacent is True | |
| assert cand.gain > 0 | |
| def test_none_when_no_adjacent(self, make_state, cfg): | |
| state = make_state( | |
| plots=frozenset({4, 5}), | |
| other_players={"浩宇": PlayerView( | |
| name="浩宇", cash=50000, | |
| plots=frozenset({10}), # 10 adj 9, not adj to 4/5 | |
| shops=(), | |
| )}, | |
| ) | |
| assert find_best_buy_candidate(state, cfg) is None | |
| def test_none_when_no_opponents(self, make_state, cfg): | |
| state = make_state(plots=frozenset({4, 5})) | |
| assert find_best_buy_candidate(state, cfg) is None | |