diff --git a/main.py b/main.py index 8ac4cf2..a60a41e 100644 --- a/main.py +++ b/main.py @@ -10,56 +10,22 @@ # To get you started we've included code to prevent your Battlesnake from moving backwards. # For more info see docs.battlesnake.com -from server.Files import read_file, save_file -from server.GameStorage import GameStorage -from config import SNAKE +from server.SnakeBuilder import SnakeBuilder +from server.Server import Server from dotenv import load_dotenv, find_dotenv -from datetime import datetime -import json, os - -# info is called when you create your Battlesnake on play.battlesnake.com -# and controls your Battlesnake's appearance -# TIP: If you open your Battlesnake URL in a browser you should see this data -game_state_storage = GameStorage(SNAKE.__class__.__name__) - -def info() -> dict: - CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'data', 'snake-config.json') - snake = read_file(CONFIG_PATH, json.load) - if not snake: - snake = {"apiversion":"1","author":"","color":"#888888","head":"default","tail":"default"} - save_file(CONFIG_PATH, snake, callback=json.dump, indent=2, ensure_ascii=False) - print("INFO", snake) - return snake - - print("INFO Snake:", snake) - return snake - -# start is called when your Battlesnake begins a game -def start(game_state:dict): - game_state_storage.start_new_game(game_state["game"], game_state["board"], game_state["you"]) - SNAKE.clear_history() - print("GAME START:", game_state["game"]) - -# move is called when your Battlesnake game is running game -def move(game_state:dict) -> dict: - next_move = SNAKE.choose_move(game_state) - game_state_storage.add_moves(game_state["board"], next_move) - print("MOVE:", f"{next_move:5},", "Me:", {"head": game_state["you"]["head"], "length": game_state["you"]["length"]}) - return {"move": next_move} - -# end is called when your Battlesnake finishes a game -def end(game_state:dict): - HISTORY_PATH = os.path.join(os.path.dirname(__file__), 'data', 'history') - - game_state_storage.add_end_state(game_state["board"], SNAKE.get_history()) - game_state_storage.save(os.path.join(HISTORY_PATH, f"{SNAKE.__class__.__name__}_{datetime.now().strftime('%d.%m.%Y_%H%M%S')}_{game_state['game']['id']}.json"), callback=json.dump, indent=2, ensure_ascii=False) - - print("GAME OVER\n") +import os # Start server when `python main.py` is run if __name__ == "__main__": load_dotenv(find_dotenv()) - from server.server import run_server - run_server({"info": info, "start": start, "move": move, "end": end}) + SNAKE = SnakeBuilder.build(os.environ.get("snake", "DummSnake")) + + server = Server( + data_path=os.path.dirname(__file__), + snake=SnakeBuilder.build(os.environ.get("snake", "DummSnake")), + port=int(os.environ.get("PORT", "8000")), + debug=bool(os.environ.get("debug", False)), + ) + server.run() diff --git a/server/Server.py b/server/Server.py new file mode 100644 index 0000000..bb41a35 --- /dev/null +++ b/server/Server.py @@ -0,0 +1,91 @@ +from server.Files import read_file, save_file +from server.GameStorage import GameStorage +from snakes.TemplateSnake import TemplateSnake + +from datetime import datetime +from flask import Flask +from flask import request +import logging, json, os + +class Server: + default_snake_config = {"apiversion":"1","author":"","color":"#888888","head":"default","tail":"default"} + + def __init__(self, data_path:str, snake:TemplateSnake, host:str="0.0.0.0", port:str="8000", debug:bool=False): + self.host = host + self.port = port + self.debug = debug + self.snake = snake + self.game_state_storage = GameStorage(snake.__class__.__name__) + + self.config_file = os.path.join(data_path, 'data', 'snake-config.json') + self.history_path = os.path.join(data_path, 'data', 'history') + + self.app = Flask("Battlesnake") + + @self.app.get("/") + def on_info(): + return self._info() + + @self.app.post("/start") + def on_start(): + game_state = request.get_json() + self._start(game_state) + return "ok" + + @self.app.post("/move") + def on_move(): + game_state = request.get_json() + return self._move(game_state) + + @self.app.post("/end") + def on_end(): + game_state = request.get_json() + self._end(game_state) + return "ok" + + @self.app.after_request + def identify_server(response): + response.headers.set( + "server", "battlesnake/github/starter-snake-python" + ) + return response + + def run(self): + logging.getLogger("werkzeug").setLevel(logging.ERROR) + + print(f"\nRunning Battlesnake at http://{self.host}:{self.port} with the {self.snake.__class__.__name__.replace('Snake', '')} Snake") + self.app.run(host=self.host, port=self.port, debug=self.debug) + + def _read_json_config_or_create(self): + snake_config = read_file(self.config_file, json.load) + if not snake_config: + snake_config = self.default_snake_config + save_file(self.config_file, snake_config, callback=json.dump, indent=2, ensure_ascii=False) + return snake_config + + # info is called when you create your Battlesnake on play.battlesnake.com + # and controls your Battlesnake's appearance + # TIP: If you open your Battlesnake URL in a browser you should see this data + def _info(self) -> dict: + snake_config = self._read_json_config_or_create() + print("INFO Snake:", snake_config) + return snake_config + + # start is called when your Battlesnake begins a game + def _start(self, game_state:dict): + self.game_state_storage.start_new_game(game_state["game"], game_state["board"], game_state["you"]) + self.snake.clear_history() + print("GAME START:", game_state["game"]) + + # move is called when your Battlesnake game is running game + def _move(self, game_state:dict) -> dict: + next_move = self.snake.choose_move(game_state) + self.game_state_storage.add_moves(game_state["board"], next_move) + print("MOVE:", f"{next_move:5},", "Me:", {"head": game_state["you"]["head"], "length": game_state["you"]["length"]}) + return {"move": next_move} + + # end is called when your Battlesnake finishes a game + def _end(self, game_state:dict): + self.game_state_storage.add_end_state(game_state["board"], self.snake.get_history()) + self.game_state_storage.save(os.path.join(self.history_path, f"{self.snake.__class__.__name__}_{datetime.now().strftime('%d.%m.%Y_%H%M%S')}_{game_state['game']['id']}.json"), callback=json.dump, indent=2, ensure_ascii=False) + print("GAME OVER\n") diff --git a/server/server.py b/server/server.py deleted file mode 100644 index 214166c..0000000 --- a/server/server.py +++ /dev/null @@ -1,45 +0,0 @@ -import logging -import os - -from flask import Flask -from flask import request - -def run_server(handlers:dict): - app = Flask("Battlesnake") - - @app.get("/") - def on_info(): - return handlers["info"]() - - @app.post("/start") - def on_start(): - game_state = request.get_json() - handlers["start"](game_state) - return "ok" - - @app.post("/move") - def on_move(): - game_state = request.get_json() - return handlers["move"](game_state) - - @app.post("/end") - def on_end(): - game_state = request.get_json() - handlers["end"](game_state) - return "ok" - - @app.after_request - def identify_server(response): - response.headers.set( - "server", "battlesnake/github/starter-snake-python" - ) - return response - - host = "0.0.0.0" - port = int(os.environ.get("PORT", "8000")) - debug = bool(os.environ.get("debug", False)) - - logging.getLogger("werkzeug").setLevel(logging.ERROR) - - print(f"\nRunning Battlesnake at http://{host}:{port}") - app.run(host=host, port=port, debug=debug)