"""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) == []