agents.manual_agent

 1import asyncio
 2from typing import List, Optional, Tuple, Union
 3
 4from agents.base_agent import BaseUTTTAgent
 5
 6
 7class ManualUTTTAgent(BaseUTTTAgent):
 8    """
 9    An agent that allows manual control via the CLI.
10    """
11
12    async def deliberate(
13        self,
14        board: List[List[int]],
15        macro_board: List[List[int]],
16        active_macro: Optional[List[int]],
17        valid_actions: List[List[int]],
18    ) -> Optional[Union[List[int], Tuple[int, int]]]:
19        """
20        Prompts the user to enter a move via the command line.
21
22        Args:
23            board (List[List[int]]): The current 9x9 board state.
24            macro_board (List[List[int]]): The current 3x3 macro board state.
25            active_macro (Optional[List[int]]): The active macro board coordinates [my, mx].
26            valid_actions (List[List[int]]): A list of valid moves [x, y].
27
28        Returns:
29            Optional[Union[List[int], Tuple[int, int]]]: The chosen move [x, y] or None.
30        """
31        print(f"\n--- YOUR TURN (Player {self.player_id}) ---")
32
33        # If no active macro, we default to showing the whole board,
34        # but for a 'Free Move', relative coordinates are harder to define.
35        # We will allow the user to pick the macro-board first.
36        if active_macro is None:
37            print(">>> FREE MOVE! Pick any macro-board.")
38            while True:
39                m_input = await asyncio.to_thread(
40                    input, "Select Macro-Board [mx,my] (0-2): "
41                )
42                try:
43                    parts = m_input.strip().split(",")
44                    if len(parts) != 2:
45                        raise ValueError
46                    mx, my = [int(i) for i in parts]
47                    if 0 <= mx <= 2 and 0 <= my <= 2 and macro_board[my][mx] == 0:
48                        active_macro = [my, mx]
49                        break
50                    print("That macro-board is already finished or invalid.")
51                except ValueError:
52                    print("Use format 'mx,my' (e.g., 1,1 for center).")
53
54        my, mx = active_macro
55        print(f">>> Playing in Macro-Board [{mx}, {my}]")
56
57        while True:
58            user_input = await asyncio.to_thread(
59                input, "Enter local move 'x,y' (0-2): "
60            )
61
62            try:
63                parts = user_input.strip().split(",")
64                if len(parts) != 2:
65                    raise ValueError
66                lx, ly = [int(i) for i in parts]
67                # Convert Relative (0-2) to Global (0-8)
68                gx = mx * 3 + lx
69                gy = my * 3 + ly
70
71                if [gx, gy] in valid_actions:
72                    return [gx, gy]
73                else:
74                    print(
75                        f"Invalid local move. Cell ({lx},{ly}) is either taken or out of bounds."
76                    )
77            except (ValueError, IndexError):
78                print("Invalid format. Use 'x,y' where x and y are 0, 1, or 2.")
79
80
81if __name__ == "__main__":
82    agent = ManualUTTTAgent()
83    print("Starting Relative-Coordinate Manual Agent...")
84    asyncio.run(agent.run())
class ManualUTTTAgent(agents.base_agent.BaseUTTTAgent):
 8class ManualUTTTAgent(BaseUTTTAgent):
 9    """
10    An agent that allows manual control via the CLI.
11    """
12
13    async def deliberate(
14        self,
15        board: List[List[int]],
16        macro_board: List[List[int]],
17        active_macro: Optional[List[int]],
18        valid_actions: List[List[int]],
19    ) -> Optional[Union[List[int], Tuple[int, int]]]:
20        """
21        Prompts the user to enter a move via the command line.
22
23        Args:
24            board (List[List[int]]): The current 9x9 board state.
25            macro_board (List[List[int]]): The current 3x3 macro board state.
26            active_macro (Optional[List[int]]): The active macro board coordinates [my, mx].
27            valid_actions (List[List[int]]): A list of valid moves [x, y].
28
29        Returns:
30            Optional[Union[List[int], Tuple[int, int]]]: The chosen move [x, y] or None.
31        """
32        print(f"\n--- YOUR TURN (Player {self.player_id}) ---")
33
34        # If no active macro, we default to showing the whole board,
35        # but for a 'Free Move', relative coordinates are harder to define.
36        # We will allow the user to pick the macro-board first.
37        if active_macro is None:
38            print(">>> FREE MOVE! Pick any macro-board.")
39            while True:
40                m_input = await asyncio.to_thread(
41                    input, "Select Macro-Board [mx,my] (0-2): "
42                )
43                try:
44                    parts = m_input.strip().split(",")
45                    if len(parts) != 2:
46                        raise ValueError
47                    mx, my = [int(i) for i in parts]
48                    if 0 <= mx <= 2 and 0 <= my <= 2 and macro_board[my][mx] == 0:
49                        active_macro = [my, mx]
50                        break
51                    print("That macro-board is already finished or invalid.")
52                except ValueError:
53                    print("Use format 'mx,my' (e.g., 1,1 for center).")
54
55        my, mx = active_macro
56        print(f">>> Playing in Macro-Board [{mx}, {my}]")
57
58        while True:
59            user_input = await asyncio.to_thread(
60                input, "Enter local move 'x,y' (0-2): "
61            )
62
63            try:
64                parts = user_input.strip().split(",")
65                if len(parts) != 2:
66                    raise ValueError
67                lx, ly = [int(i) for i in parts]
68                # Convert Relative (0-2) to Global (0-8)
69                gx = mx * 3 + lx
70                gy = my * 3 + ly
71
72                if [gx, gy] in valid_actions:
73                    return [gx, gy]
74                else:
75                    print(
76                        f"Invalid local move. Cell ({lx},{ly}) is either taken or out of bounds."
77                    )
78            except (ValueError, IndexError):
79                print("Invalid format. Use 'x,y' where x and y are 0, 1, or 2.")

An agent that allows manual control via the CLI.

async def deliberate( self, board: List[List[int]], macro_board: List[List[int]], active_macro: Optional[List[int]], valid_actions: List[List[int]]) -> Union[List[int], Tuple[int, int], NoneType]:
13    async def deliberate(
14        self,
15        board: List[List[int]],
16        macro_board: List[List[int]],
17        active_macro: Optional[List[int]],
18        valid_actions: List[List[int]],
19    ) -> Optional[Union[List[int], Tuple[int, int]]]:
20        """
21        Prompts the user to enter a move via the command line.
22
23        Args:
24            board (List[List[int]]): The current 9x9 board state.
25            macro_board (List[List[int]]): The current 3x3 macro board state.
26            active_macro (Optional[List[int]]): The active macro board coordinates [my, mx].
27            valid_actions (List[List[int]]): A list of valid moves [x, y].
28
29        Returns:
30            Optional[Union[List[int], Tuple[int, int]]]: The chosen move [x, y] or None.
31        """
32        print(f"\n--- YOUR TURN (Player {self.player_id}) ---")
33
34        # If no active macro, we default to showing the whole board,
35        # but for a 'Free Move', relative coordinates are harder to define.
36        # We will allow the user to pick the macro-board first.
37        if active_macro is None:
38            print(">>> FREE MOVE! Pick any macro-board.")
39            while True:
40                m_input = await asyncio.to_thread(
41                    input, "Select Macro-Board [mx,my] (0-2): "
42                )
43                try:
44                    parts = m_input.strip().split(",")
45                    if len(parts) != 2:
46                        raise ValueError
47                    mx, my = [int(i) for i in parts]
48                    if 0 <= mx <= 2 and 0 <= my <= 2 and macro_board[my][mx] == 0:
49                        active_macro = [my, mx]
50                        break
51                    print("That macro-board is already finished or invalid.")
52                except ValueError:
53                    print("Use format 'mx,my' (e.g., 1,1 for center).")
54
55        my, mx = active_macro
56        print(f">>> Playing in Macro-Board [{mx}, {my}]")
57
58        while True:
59            user_input = await asyncio.to_thread(
60                input, "Enter local move 'x,y' (0-2): "
61            )
62
63            try:
64                parts = user_input.strip().split(",")
65                if len(parts) != 2:
66                    raise ValueError
67                lx, ly = [int(i) for i in parts]
68                # Convert Relative (0-2) to Global (0-8)
69                gx = mx * 3 + lx
70                gy = my * 3 + ly
71
72                if [gx, gy] in valid_actions:
73                    return [gx, gy]
74                else:
75                    print(
76                        f"Invalid local move. Cell ({lx},{ly}) is either taken or out of bounds."
77                    )
78            except (ValueError, IndexError):
79                print("Invalid format. Use 'x,y' where x and y are 0, 1, or 2.")

Prompts the user to enter a move via the command line.

Args: board (List[List[int]]): The current 9x9 board state. macro_board (List[List[int]]): The current 3x3 macro board state. active_macro (Optional[List[int]]): The active macro board coordinates [my, mx]. valid_actions (List[List[int]]): A list of valid moves [x, y].

Returns: Optional[Union[List[int], Tuple[int, int]]]: The chosen move [x, y] or None.