From 3a2bfc3a5fdb221545fb010333ecb44d47740d3c Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Tue, 21 Oct 2025 13:56:20 +0200 Subject: [PATCH] ass3: finish bucket_game --- assignment3/bucket_game.py | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/assignment3/bucket_game.py b/assignment3/bucket_game.py index ea431ac..8b6a747 100644 --- a/assignment3/bucket_game.py +++ b/assignment3/bucket_game.py @@ -1,3 +1,5 @@ +import typing + State = tuple[int, list[str | int]] # Tuple of player (whose turn it is), # and the buckets (as str) # or the number in a bucket @@ -42,3 +44,44 @@ class Game: print(f"P1's utility is {self.utility(state, 0)}") else: print(f"it is P{self.to_move(state)+1}'s turn") + + +def minimax_search(game: Game, state: State) -> Action | None: + _, result = max_value(game, state) + return result + + +def max_value(game: Game, state: State) -> tuple[float, Action | None]: + if game.is_terminal(state): + return game.utility(state, player), None + v, move = float("-inf"), float("-inf") + for a in game.actions(state): + v2, a2 = min_value(game, game.result(state, a)) + if v2 > v: + v, move = v2, a + return typing.cast(float, v), typing.cast(Action, move) + + +def min_value(game: Game, state: State) -> tuple[float, Action | None]: + if game.is_terminal(state): + return game.utility(state, player), None + v, move = float("+inf"), float("+inf") + for a in game.actions(state): + v2, a2 = max_value(game, game.result(state, a)) + if v2 < v: + v, move = v2, a + return typing.cast(float, v), typing.cast(Action, move) + + +game = Game() + +state = game.initial_state() +game.print(state) +while not game.is_terminal(state): + player = game.to_move(state) + action = minimax_search(game, state) # The player whose turn it is + # is the MAX player + print(f"P{player+1}'s action: {action}") + assert action is not None + state = game.result(state, action) + game.print(state)