File size: 12,842 Bytes
571f3d0
d10d3ce
f63cf4c
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
d10d3ce
 
571f3d0
 
 
 
 
 
d10d3ce
571f3d0
 
230696b
571f3d0
d10d3ce
571f3d0
 
 
 
 
 
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
 
 
 
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
 
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
 
230696b
 
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
d10d3ce
571f3d0
 
 
 
 
 
 
 
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
d10d3ce
 
 
 
571f3d0
 
 
 
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
d10d3ce
 
571f3d0
 
 
230696b
571f3d0
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
230696b
571f3d0
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
d10d3ce
571f3d0
 
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
 
 
d10d3ce
571f3d0
 
d10d3ce
571f3d0
 
d10d3ce
 
 
571f3d0
230696b
d10d3ce
571f3d0
d10d3ce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
import pytest

from domains.belief.belief_domain import BeliefUpdate
from domains.coordination.game_coordination import BayesianGame, GamePhase, GameState
from domains.environment.environment_domain import EnvironmentEvidence


class TestGameState:
    """Test the GameState dataclass."""

    def test_game_state_creation(self):
        """Test creating game state with required parameters."""
        state = GameState(round_number=5, max_rounds=10, phase=GamePhase.PLAYING)

        assert state.round_number == 5
        assert state.max_rounds == 10
        assert state.phase == GamePhase.PLAYING
        assert state.target_value is None
        assert state.evidence_history == []
        assert state.current_beliefs == []

    def test_game_state_with_optional_params(self):
        """Test creating game state with optional parameters."""
        evidence = [EnvironmentEvidence(dice_roll=3, comparison_results=["higher"])]
        beliefs = [0.2, 0.3, 0.5]

        state = GameState(
            round_number=2,
            max_rounds=5,
            phase=GamePhase.PLAYING,
            target_value=4,
            evidence_history=evidence,
            current_beliefs=beliefs,
            most_likely_target=3,
            belief_entropy=1.5,
        )

        assert state.target_value == 4
        assert state.evidence_history == evidence
        assert state.current_beliefs == beliefs
        assert state.most_likely_target == 3
        assert state.belief_entropy == 1.5


class TestBayesianGame:
    """Test the BayesianGame class."""

    def test_initialization_default(self):
        """Test game initialization with default parameters."""
        game = BayesianGame()

        assert game.dice_sides == 6
        assert game.max_rounds == 10
        assert game.environment.dice_sides == 6
        assert game.belief_state.dice_sides == 6
        assert game.game_state.phase == GamePhase.SETUP
        assert game.game_state.round_number == 0
        assert game.game_state.max_rounds == 10

    def test_initialization_custom(self):
        """Test game initialization with custom parameters."""
        game = BayesianGame(dice_sides=8, max_rounds=15, seed=42)

        assert game.dice_sides == 8
        assert game.max_rounds == 15
        assert game.environment.dice_sides == 8
        assert game.belief_state.dice_sides == 8
        assert game.game_state.max_rounds == 15

    def test_start_new_game_random_target(self):
        """Test starting new game with random target."""
        game = BayesianGame(seed=42)

        state = game.start_new_game()

        assert state.phase == GamePhase.PLAYING
        assert state.round_number == 0
        assert 1 <= state.target_value <= 6
        assert len(state.evidence_history) == 0
        assert len(state.current_beliefs) == 6
        assert state.most_likely_target in range(1, 7)
        assert state.belief_entropy > 0

    def test_start_new_game_specific_target(self):
        """Test starting new game with specific target."""
        game = BayesianGame()

        state = game.start_new_game(target_value=4)

        assert state.phase == GamePhase.PLAYING
        assert state.target_value == 4
        assert game.environment.get_target_value() == 4

    def test_start_new_game_resets_state(self):
        """Test that starting new game resets previous state."""
        game = BayesianGame(seed=42)

        # Start first game and play some rounds
        game.start_new_game(target_value=3)
        game.play_round()
        game.play_round()

        # Start new game
        state = game.start_new_game(target_value=5)

        assert state.target_value == 5
        assert state.round_number == 0
        assert len(state.evidence_history) == 0
        assert len(game.belief_state.evidence_history) == 0

    def test_play_round_not_playing(self):
        """Test playing round when not in playing phase."""
        game = BayesianGame()

        # Game starts in setup phase
        with pytest.raises(ValueError, match="Game is not in playing phase"):
            game.play_round()

    def test_play_round_game_finished(self):
        """Test playing round when game is already finished."""
        game = BayesianGame(max_rounds=1, seed=42)

        # Start game and play one round (should finish)
        game.start_new_game(target_value=3)
        game.play_round()

        # Try to play another round
        with pytest.raises(ValueError, match="Game is not in playing phase"):
            game.play_round()

    def test_play_round_updates_state(self):
        """Test that playing round updates game state correctly."""
        game = BayesianGame(seed=42)
        game.start_new_game(target_value=3)

        initial_round_number = game.get_current_state().round_number

        # Play one round
        updated_state = game.play_round()

        assert updated_state.round_number == initial_round_number + 1
        assert len(updated_state.evidence_history) == 1
        assert len(updated_state.current_beliefs) == 6
        assert updated_state.most_likely_target in range(1, 7)
        assert updated_state.belief_entropy >= 0

        # Evidence should be valid
        evidence = updated_state.evidence_history[0]
        assert 1 <= evidence.dice_roll <= 6
        # At least one basic comparison result should be present
        basic_results = {"higher", "lower", "same"}
        assert any(result in basic_results for result in evidence.comparison_results)

    def test_play_multiple_rounds(self):
        """Test playing multiple rounds."""
        game = BayesianGame(max_rounds=5, seed=42)
        game.start_new_game(target_value=4)

        for expected_round in range(1, 6):
            state = game.play_round()

            assert state.round_number == expected_round
            assert len(state.evidence_history) == expected_round

            if expected_round < 5:
                assert state.phase == GamePhase.PLAYING
            else:
                assert state.phase == GamePhase.FINISHED

    def test_get_current_state(self):
        """Test getting current game state."""
        game = BayesianGame()

        # Initial state
        state = game.get_current_state()
        assert state.phase == GamePhase.SETUP

        # After starting game
        game.start_new_game(target_value=2)
        state = game.get_current_state()
        assert state.phase == GamePhase.PLAYING
        assert state.target_value == 2

    def test_is_game_finished(self):
        """Test checking if game is finished."""
        game = BayesianGame(max_rounds=2, seed=42)

        # Initially not finished
        assert not game.is_game_finished()

        # Start game - still not finished
        game.start_new_game(target_value=3)
        assert not game.is_game_finished()

        # Play one round - still not finished
        game.play_round()
        assert not game.is_game_finished()

        # Play final round - now finished
        game.play_round()
        assert game.is_game_finished()

    def test_get_final_guess_accuracy_no_target(self):
        """Test getting final guess accuracy without target set."""
        game = BayesianGame()

        with pytest.raises(ValueError, match="Target value not set"):
            game.get_final_guess_accuracy()

    def test_get_final_guess_accuracy(self):
        """Test getting final guess accuracy."""
        game = BayesianGame(seed=42)
        game.start_new_game(target_value=3)

        # Play some rounds
        game.play_round()
        game.play_round()

        accuracy = game.get_final_guess_accuracy()

        # Should be probability assigned to target value 3
        assert 0 <= accuracy <= 1
        expected_accuracy = game.belief_state.get_belief_for_target(3)
        assert accuracy == expected_accuracy

    def test_was_final_guess_correct_no_target(self):
        """Test checking final guess correctness without target set."""
        game = BayesianGame()

        with pytest.raises(ValueError, match="Target value not set"):
            game.was_final_guess_correct()

    def test_was_final_guess_correct(self):
        """Test checking if final guess was correct."""
        game = BayesianGame(seed=42)
        game.start_new_game(target_value=3)

        # Play rounds until we get definitive evidence
        for _ in range(10):  # Play enough rounds to get clear evidence
            if game.is_game_finished():
                break
            game.play_round()

        is_correct = game.was_final_guess_correct()
        most_likely = game.game_state.most_likely_target

        assert isinstance(is_correct, bool)
        assert is_correct == (most_likely == 3)

    def test_get_game_summary(self):
        """Test getting game summary."""
        game = BayesianGame(max_rounds=3, seed=42)
        game.start_new_game(target_value=4)

        # Play all rounds
        while not game.is_game_finished():
            game.play_round()

        summary = game.get_game_summary()

        # Check all required fields
        assert summary["rounds_played"] == 3
        assert summary["max_rounds"] == 3
        assert summary["true_target"] == 4
        assert summary["final_guess"] in range(1, 7)
        assert isinstance(summary["guess_correct"], bool)
        assert 0 <= summary["final_accuracy"] <= 1
        assert summary["final_entropy"] >= 0
        assert summary["evidence_count"] == 3
        assert len(summary["final_beliefs"]) == 6

        # Check that final beliefs are properly indexed (1-6)
        for i in range(1, 7):
            assert i in summary["final_beliefs"]

    def test_belief_updates_with_evidence(self):
        """Test that belief updates properly reflect evidence."""
        game = BayesianGame(seed=42)
        game.start_new_game(target_value=1)  # Low target for predictable evidence

        # Store initial beliefs for comparison
        _initial_beliefs = game.belief_state.get_current_beliefs()

        # Play several rounds
        states = []
        for _ in range(5):
            if game.is_game_finished():
                break
            state = game.play_round()
            states.append(state)

        # Beliefs should change as evidence accumulates
        final_beliefs = game.belief_state.get_current_beliefs()

        # Should not be uniform anymore (unless very unlikely)
        assert not all(abs(b - 1 / 6) < 1e-10 for b in final_beliefs)

        # Evidence should influence beliefs correctly
        for state in states:
            for evidence in state.evidence_history:
                if "higher" in evidence.comparison_results:
                    # Target must be less than dice roll
                    for _target in range(evidence.dice_roll, 7):
                        # These targets should have reduced probability
                        pass  # Detailed verification would require complex logic

    def test_game_with_evidence_updates(self):
        """Test game behavior with evidence updates."""
        game = BayesianGame(seed=42)
        game.start_new_game(target_value=3)

        # Apply evidence that changes beliefs
        update = BeliefUpdate(comparison_results=["higher"])
        game.belief_state.update_beliefs(update)

        # Update game state to reflect the belief change
        game.game_state.most_likely_target = game.belief_state.get_most_likely_target()

        # Beliefs should have changed from uniform
        prob_1 = game.belief_state.get_belief_for_target(1)
        prob_6 = game.belief_state.get_belief_for_target(6)

        assert prob_1 > prob_6  # Lower targets should be more likely after "higher"
        assert game.belief_state.get_most_likely_target() in range(1, 7)
        assert 0 <= game.get_final_guess_accuracy() <= 1

    def test_reproducibility_with_seed(self):
        """Test that games are reproducible with same seed."""
        # Run two games with same seed
        game1 = BayesianGame(seed=42)
        game1.start_new_game(target_value=3)

        game2 = BayesianGame(seed=42)
        game2.start_new_game(target_value=3)

        # Play same number of rounds
        for _ in range(5):
            if game1.is_game_finished() or game2.is_game_finished():
                break

            state1 = game1.play_round()
            state2 = game2.play_round()

            # Evidence should be identical
            assert len(state1.evidence_history) == len(state2.evidence_history)
            for ev1, ev2 in zip(
                state1.evidence_history, state2.evidence_history, strict=False
            ):
                assert ev1.dice_roll == ev2.dice_roll
                assert ev1.comparison_results == ev2.comparison_results

            # Beliefs should be identical
            assert state1.current_beliefs == state2.current_beliefs