diff --git a/README.rst b/README.rst index f4bc7d8..15cf784 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,6 @@ Ultimate TicTacToe - Python Player & Game Engine ================================================ -|Travis| |PyPI| |Coverage| - This repo contains a sample player and game engine for our `Ultimate Tic-Tac-Toe workshop_`. Welcome! @@ -11,28 +9,9 @@ Welcome! Welcome! You're probably here because you're in a hackathon we're running. -If so, make sure you've read our `Documentation`_. - -Once you're ready to write your player, you can find functions that will help you write your player in `API Reference`_ - -Contributors -============ - -If you're interested in contributing a player or even features to the game engine: +If so, make sure you've read our [docs](https://github.com/joffat/docs) . -1. **Thank you**! -2. Please read the `Contribution Guide`_ - -.. Images and Links - -.. |Travis| image:: https://travis-ci.org/socialgorithm/ultimate-ttt-py.svg?branch=master - :target: https://travis-ci.org/socialgorithm/ultimate-ttt-py -.. |PyPI| image:: https://badge.fury.io/py/ultimate_ttt.svg - :target: https://badge.fury.io/py/ultimate_ttt -.. |Coverage| image:: https://coveralls.io/repos/github/socialgorithm/ultimate-ttt-py/badge.svg?branch=master - :target: https://coveralls.io/github/socialgorithm/ultimate-ttt-py?branch=master +Run! +======== -.. _Ultimate Tic-Tac-Toe workshop: https://socialgorithm.org/workshops/ -.. _Documentation: https://socialgorithm.org/docs -.. _API Reference: https://ultimate-ttt-py.readthedocs.io/en/latest/ -.. _Contribution Guide: https://github.com/socialgorithm/ultimate-ttt-py/blob/master/CONTRIBUTING.rst +From the folder run `uabc --host {server host} --token {your token} -f "node run_player.js"` diff --git a/players/player.py b/players/player.py index 0e74654..dfa6145 100755 --- a/players/player.py +++ b/players/player.py @@ -12,7 +12,23 @@ def __init__(self): self.main_board = MainBoard(self.board_size) @abstractmethod - def get_my_move(self): + def onMove(self): + pass + + @abstractmethod + def onOpponent(self, board, move): + pass + + @abstractmethod + def gameOver(self, result, board, move): + pass + + @abstractmethod + def matchOver(self, result): + pass + + @abstractmethod + def timeout(self): pass @property diff --git a/players/random.py b/players/random.py index f73e81b..7c3a71c 100755 --- a/players/random.py +++ b/players/random.py @@ -5,14 +5,89 @@ class Random(StdOutPlayer): + # + # New Game Has Been Started. + # Do Whatever You Need To Do For The Player To Be Ready To Play + # def __init__(self): super().__init__() - def get_my_move(self): # -> Tuple[MainBoardCoords, SubBoardCoords] - main_board_coords = self.pick_next_main_board_coords() - sub_board = self.main_board.get_sub_board(main_board_coords) + # + # You are the First To Move + # Place Whereever You Want + # You Get To Pick The Board + # + # Returns Tuple[MainBoardCoords, SubBoardCoords] Position coordinates {board: [row, col], cell: [row, col]} + # + def onMove(self): + board = self.pick_next_main_board_coords() + sub_board = self.main_board.get_sub_board(board) + move = self.pick_random_sub_board_coords(sub_board) + return board, move + + # + # Opponent Has Moved. + # You Have Opponents Board + # You Have Opponents Move + # + # You Must Respond With Board Matching Opponents Move + # Unless That Board Is Complete + # + # Returns Tuple[MainBoardCoords, SubBoardCoords] Position coordinates {board: [row, col], cell: [row, col]} + # + def onOpponent(self, board, move): + sub_board = self.main_board.get_sub_board(move) + if sub_board.is_finished == True: + return self.onMove() sub_board_coords = self.pick_random_sub_board_coords(sub_board) - return main_board_coords, sub_board_coords + return move, sub_board_coords + + # + # Game Is Over. + # You May Wish To Change Stratagy For The New Game. + # Your Opponent Has Not Changed. + # + # result Either 'win' | 'lose' | 'tie' + # board Last Opponent Board identifier [row, col] + # move Last Opponent Cell identifier [row, col] + # + def gameOver(self, result, board, move): + if result == "win": + # DO SOMETHING FOR WIN + pass + elif result == "lose": + # DO SOMETHING FOR LOST + pass + elif result == "tie": + # DO SOMETHING FOR TIE + pass + return + + # + # Match Is Over. + # You Will Soon Have A New Opponent. + # + # result Either 'win' | 'lose' | 'tie' + # + def matchOver(self, result): + if result == "win": + # DO SOMETHING FOR WIN + pass + elif result == "lose": + # DO SOMETHING FOR LOST + pass + elif result == "tie": + # DO SOMETHING FOR TIE + pass + return + + # + # Game Lost. + # You Have Timed Out. + # + def timeout(self): + # DO SOMETHING FOR TIMEOUT + return def pick_next_main_board_coords(self) -> MainBoardCoords: if self.main_board.sub_board_next_player_must_play is None: diff --git a/players/stdout.py b/players/stdout.py index bdb4d2e..75e663c 100644 --- a/players/stdout.py +++ b/players/stdout.py @@ -11,22 +11,67 @@ def __init__(self): super().__init__() @abstractmethod - def get_my_move(self): + def onMove(self): + pass + + @abstractmethod + def onOpponent(self, board, move): + pass + + @abstractmethod + def gameOver(self, result, board, move): + pass + + @abstractmethod + def matchOver(self, result): + pass + + @abstractmethod + def timeout(self): pass def process_input(self, input_line: str) -> None: - if input_line == "init": - self.__init__() - elif input_line == "move": - self.get_and_publish_player_move() - elif input_line.startswith("opponent"): - self.react_to_opponent_move(input_line) - - def get_and_publish_player_move(self) -> None: - main_board_coords, sub_board_coords = self.get_my_move() + try: + if input_line == "init": + self.__init__() + elif input_line == "move": + self.react_to_move() + elif input_line.startswith("opponent"): + self.react_to_opponent_move(input_line) + elif input_line.startswith("game"): + self.react_to_game_over(input_line) + elif input_line.startswith("match"): + self.react_to_match_over(input_line) + elif input_line == "timeout": + self.react_to_timeout(input_line) + except: + print("ERROR: Failed To Process Message. May Forfit Game, But Should Reinitilize On New Game.") + + def react_to_move(self) -> None: + main_board_coords, sub_board_coords = self.onMove() self.add_my_move(main_board_coords, sub_board_coords) self.write_move(main_board_coords, sub_board_coords) + def react_to_opponent_move(self, input_line: str): + main_board_coords, sub_board_coords = self.read_move_opponent(input_line) + self.add_opponent_move(main_board_coords, sub_board_coords) + my_main_board_coords, my_sub_board_coords = self.onOpponent(main_board_coords, sub_board_coords) + self.add_my_move(my_main_board_coords, my_sub_board_coords) + self.write_move(my_main_board_coords, my_sub_board_coords) + + def react_to_game_over(self, input_line: str): + result, main_board_coords, sub_board_coords = self.read_result_move_game(input_line) + if main_board_coords is not None and sub_board_coords is not None: + self.add_opponent_move(main_board_coords, sub_board_coords) + self.gameOver(result, main_board_coords, sub_board_coords) + + def react_to_match_over(self, input_line: str): + result = self.read_result_match(input_line) + self.matchOver(result) + + def react_to_timeout(self, input_line: str): + self.timeout() + @staticmethod def write_move(main_board_coords: MainBoardCoords, sub_board_coords: SubBoardCoords): move_str = "send:%d,%d;%d,%d" % ( @@ -34,16 +79,27 @@ def write_move(main_board_coords: MainBoardCoords, sub_board_coords: SubBoardCoo print(move_str) sys.stdout.flush() - def react_to_opponent_move(self, input_line: str): - main_board_coords, sub_board_coords = self.read_move(input_line) - self.add_opponent_move(main_board_coords, sub_board_coords) - if not self.is_game_finished: - self.get_and_publish_player_move() - @staticmethod - def read_move(input_line: str): - received_move = input_line.split(" ")[1] + def parse_move(received_move: str): main_board_coords_str, opponent_move_str = received_move.split(";") main_board_coords = MainBoardCoords(*map(int, main_board_coords_str.split(","))) sub_board_coords = SubBoardCoords(*map(int, opponent_move_str.split(","))) return main_board_coords, sub_board_coords + + @staticmethod + def read_move_opponent(input_line: str): + received_move = input_line.split(" ")[1] + return StdOutPlayer.parse_move(received_move) + + @staticmethod + def read_result_move_game(input_line: str): + message_split = input_line.split(" ") + if len(message_split) == 3: + received_move = message_split[2] + main_board_coords, sub_board_coords = StdOutPlayer.parse_move(received_move) + return message_split[1], main_board_coords, sub_board_coords + return message_split[1], None, None + + @staticmethod + def read_result_match(input_line: str): + return input_line.split(" ")[1]