Files
snake-python/server/Server.py
T

131 lines
4.9 KiB
Python

from server.Files import read_file
from server.GameStorage import GameStorage
from snakes.TemplateSnake import TemplateSnake
from server.SnakeBuilder import SnakeBuilder
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_type:str, debug:bool=False, store_game_when_win_and_moves_are_bigger_as:int=10):
self.debug = debug
self.snake_type = snake_type
self.config_file = os.path.join(data_path, 'data', 'snake-config.json')
self.data_path = data_path
self.store_game_state = False
self.store_game_when_win_and_moves_are_bigger_as = store_game_when_win_and_moves_are_bigger_as
self.running_games:dict[str, GameStorage] = {}
self.running_snake:dict[str, TemplateSnake] = {}
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, host:str="0.0.0.0", port:str="8000", debug:bool=False):
logging.getLogger("werkzeug").setLevel(logging.ERROR)
print(f"\nRunning Battlesnake at http://{host}:{port} with the {self.snake_type.replace('Snake', '')} Snake")
self.app.run(host=host, port=port, debug=debug)
def _read_json_config_or_create(self):
snake_config = read_file(self.config_file, json.load)
if not snake_config:
snake_config = self._override_snake_config_with_environment_variables(self.default_snake_config)
return self._override_snake_config_with_environment_variables(snake_config)
def _override_snake_config_with_environment_variables(self, config:dict[str]):
for key in ["author", "color", "head", "tail"]:
if os.environ.get(f"SNAKE_{key.upper()}", None):
config[key.lower()] = os.environ.get(f"SNAKE_{key.upper()}")
return config
def enable_store_game_state(self):
self.store_game_state = True
# 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):
if self.store_game_state:
self.running_games[game_state["game"]["id"]] = GameStorage(
self.snake_type,
path=os.path.join(self.data_path, 'data', 'history'),
no_store_turns=self.store_game_when_win_and_moves_are_bigger_as
)
self.running_games[game_state["game"]["id"]].start_new_game(game_state["game"], game_state["board"], game_state["you"])
self.running_snake[game_state["game"]["id"]] = SnakeBuilder.build(self.snake_type)
print("GAME START:", game_state["game"])
# move is called when your Battlesnake game is running game
def _move(self, game_state:dict) -> dict:
try:
next_move = self.running_snake[game_state["game"]["id"]].choose_move(game_state)
except KeyError:
self.running_snake[game_state["game"]["id"]] = SnakeBuilder.build(self.snake_type)
next_move = self.running_snake[game_state["game"]["id"]].choose_move(game_state)
if self.store_game_state:
self.running_games[game_state["game"]["id"]].add_moves(game_state["turn"], game_state["board"], next_move)
if self.debug:
print(self.running_games[game_state["game"]["id"]])
print("TURN:", f'{game_state["turn"]:3},', "MOVE:", f"{next_move:5}")
return {"move": next_move}
# end is called when your Battlesnake finishes a game
def _end(self, game_state:dict):
if self.store_game_state:
snake = self.running_snake[game_state["game"]["id"]]
self.running_games[game_state["game"]["id"]].add_end_state(game_state["board"], snake.get_history(), game_state["turn"])
self.running_games[game_state["game"]["id"]].save(
f"{snake.__class__.__name__}_{datetime.now().strftime('%H-%M-%S')}_{game_state['game']['id']}.json",
callback=json.dump, indent=2, ensure_ascii=False
)
del self.running_games[game_state["game"]["id"]]
print("GAME OVER:\n- Winner is", [ x["name"] for x in game_state["board"]['snakes']])
del self.running_snake[game_state["game"]["id"]]