Spaces:
Running
Running
| """Tests for features/clusters.py.""" | |
| from __future__ import annotations | |
| from werewolftown.features.clusters import ( | |
| achievable_size, | |
| calc_cluster_gain, | |
| find_full_count_gaps, | |
| find_typed_clusters, | |
| ) | |
| class TestFindTypedClusters: | |
| def test_single_cluster(self): | |
| built = {4: "Happy", 5: "Happy"} | |
| clusters = find_typed_clusters(built, "Happy") | |
| assert len(clusters) == 1 | |
| assert clusters[0] == [4, 5] | |
| def test_multiple_clusters(self): | |
| built = {4: "Happy", 5: "Happy", 8: "Happy"} | |
| # 4 adj 5 (same row), 8 adj 4 (vertical) → all connected | |
| clusters = find_typed_clusters(built, "Happy") | |
| assert len(clusters) == 1 | |
| assert sorted(clusters[0]) == [4, 5, 8] | |
| def test_empty_built(self): | |
| assert find_typed_clusters({}, "Happy") == [] | |
| def test_wrong_type(self): | |
| built = {4: "Happy", 5: "猫天天"} | |
| assert find_typed_clusters(built, "Happy") == [[4]] | |
| class TestCalcClusterGain: | |
| def test_gain_from_extending_cluster(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| # Building Happy on 6 (adjacent to 5): 3-cluster vs 2-cluster | |
| # rev(3,4)=50000 - rev(2,4)=30000 = 20000 | |
| gain = calc_cluster_gain(state, 6, "Happy") | |
| assert gain == 20000 | |
| def test_zero_for_already_built(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| ) | |
| assert calc_cluster_gain(state, 4, "Happy") == 0 | |
| def test_zero_for_isolated_plot(self, make_state): | |
| # Plot 7 is not adjacent to 4/5 | |
| state = make_state( | |
| plots=frozenset({4, 5, 7}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| ) | |
| gain = calc_cluster_gain(state, 7, "Happy") | |
| # 7 is isolated → building creates a new 1-cluster = 10000 revenue | |
| assert gain == 10000 | |
| class TestAchievableSize: | |
| def test_with_adjacent_empty_and_cards(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy", "Happy"), | |
| ) | |
| # existing=2, adjacent_empties={6}, cards=2 → min(1,2)=1 → total=3 | |
| assert achievable_size(state, "Happy") == 3 | |
| def test_fc5_capped_at_3(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6, 1, 2}), | |
| built_on={4: "淘小宝", 5: "淘小宝"}, | |
| shops=("淘小宝", "淘小宝", "淘小宝"), | |
| ) | |
| # fc=5 but capped at 3 | |
| assert achievable_size(state, "淘小宝") == 3 | |
| def test_zero_for_no_cards_no_built(self, make_state): | |
| state = make_state(plots=frozenset({4, 5})) | |
| assert achievable_size(state, "Happy") == 0 | |
| class TestFindFullCountGaps: | |
| def test_finds_2_cluster_gap(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5, 6}), | |
| built_on={4: "Happy", 5: "Happy"}, | |
| shops=("Happy",), | |
| ) | |
| gaps = find_full_count_gaps(state) | |
| assert len(gaps) >= 1 | |
| gap = gaps[0] | |
| assert gap.shop_type == "Happy" | |
| assert gap.gap == 2 # fc=4, size=2, gap=2 | |
| assert gap.gain_if_filled > 0 | |
| def test_empty_for_full_cluster(self, make_state): | |
| state = make_state( | |
| plots=frozenset({4, 5}), | |
| built_on={4: "钉三多", 5: "钉三多"}, | |
| ) | |
| # fc=2, 2-cluster = full → no gap | |
| gaps = find_full_count_gaps(state) | |
| assert len(gaps) == 0 | |
| def test_empty_for_no_built(self, make_state): | |
| state = make_state(plots=frozenset({4, 5})) | |
| assert find_full_count_gaps(state) == [] | |