diff --git a/agents/naive_line_counter.py b/agents/naive_line_counter.py new file mode 100644 index 0000000..2656f04 --- /dev/null +++ b/agents/naive_line_counter.py @@ -0,0 +1,117 @@ +""" + The naive line counter aims to calculate the best move by comparing the + lines and checking how many are filled. +""" + +from __future__ import annotations + +import random + +from .agent import Agent, Payload + + +class NaiveLineCounter(Agent): + """ + Tic-tac-toe has 8 possible lines where you can get 3 in a row. + + For all 8 lines, count how many would benefit from the addition of a + single sign. In priority, count how many lines are added. + """ + + def __init__(self) -> None: + super().__init__( + name="Naive line counter", + author="Bram", + version="1.0.0", + profile={}, + ) + + self.add_tic_tac_toe(on_move=self.play_tic_tac_toe, profile={}) + + def play_tic_tac_toe(self, payload : Payload) -> Payload: + """ + For each line: + + [X][X][_] -> 3 in a row + [O][O][_] -> ruin an opponent's 3 in a row + [X][_][ ] -> 2 in a row + [O][_][ ] -> ruin an opponent's 2 in a row + [_][ ][ ] -> 1 in a row + + Count how many they occur, then sort based on priority. + """ + lines = [ + [ 1, 2, 3, ], + [ 4, 5, 6, ], + [ 7, 8, 9, ], + + [ 1, 4, 7, ], + [ 2, 5, 8, ], + [ 3, 6, 9, ], + + [ 1, 5, 9, ], + [ 3, 5, 7, ], + ] + + my_piece = payload["your_token"] + moves, value = [], ( 0, 0, 0, 0, 0 ) + + for i in range(1, 10): + i_val = field_score(payload=payload, field=i, my_piece=my_piece) + + if i_val > value: + moves, value = [i], i_val + elif i_val == value: + moves.append(i) + + return dict(move=random.choice(moves)) + +def field_score(payload : Payload, field : int, my_piece : str) -> tuple[int, int, int, int, int]: + """ + Determine the score of a specific field in the grid. + """ + a, b, c, d, e = 0, 0, 0, 0, 0 + lines = [ + [ 1, 2, 3, ], + [ 4, 5, 6, ], + [ 7, 8, 9, ], + + [ 1, 4, 7, ], + [ 2, 5, 8, ], + [ 3, 6, 9, ], + + [ 1, 5, 9, ], + [ 3, 5, 7, ], + ] + + if payload[str(field)] != "": + return ( a, b, c, d, e ) + + for line in lines: + if field not in line: + continue + + values = [ payload[str(f)] for f in line if field != f ] + x = values.count("X") + o = values.count("O") + + s = ( x, o ) if my_piece == "X" else ( o, x ) + match s: + case ( 2, 0 ): + a += 1 + case ( 0, 2 ): + b += 1 + case ( 1, 0 ): + c += 1 + case ( 0, 1 ): + d += 1 + case ( 0, 0 ): + e += 1 + case ( 1, 1 ): + pass + case _: + raise ValueError( + f"Unknown number of items: {s}" + ) + + return ( a, b, c, d, e ) diff --git a/server.py b/server.py index 9ed7be0..315107d 100644 --- a/server.py +++ b/server.py @@ -6,6 +6,7 @@ from __future__ import annotations import agents +from agents.naive_line_counter import NaiveLineCounter from pyserver import PyServer def main() -> int: @@ -15,7 +16,7 @@ def main() -> int: :return: Exit code :rtype: int """ - player = PyServer(agents.RemoteAgent(url="https://bmt001.noordstar.me/")) + player = PyServer(NaiveLineCounter()) # Start listening for games player.start(