Add some snakes
This commit is contained in:
parent
4c6b305aca
commit
6de4111b05
16
Dockerfile
Normal file
16
Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
FROM python:3.11.4-slim
|
||||||
|
# FROM continuumio/conda-ci-linux-64-python3.8
|
||||||
|
# FROM continuumio/miniconda3
|
||||||
|
|
||||||
|
# Install app
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN pip install --upgrade pip && pip install -r requirements.txt
|
||||||
|
# RUN conda env create -f environment.yaml
|
||||||
|
# ENV CONDA_DEFAULT_ENV battlesnake
|
||||||
|
|
||||||
|
# Run Battlesnake
|
||||||
|
ENTRYPOINT [ "python", "server.py" ]
|
||||||
|
CMD [ "StarterSnake" ]
|
62
server.py
Normal file
62
server.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from snakes.snake import Snake
|
||||||
|
|
||||||
|
|
||||||
|
def run_server(snake: Snake, host: str, port: int):
|
||||||
|
app = Flask("Battlesnake")
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def on_info():
|
||||||
|
return snake.info()
|
||||||
|
|
||||||
|
@app.post("/start")
|
||||||
|
def on_start():
|
||||||
|
game_state = request.get_json()
|
||||||
|
snake.start(game_state)
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
@app.post("/move")
|
||||||
|
def on_move():
|
||||||
|
game_state = request.get_json()
|
||||||
|
return snake.move(game_state)
|
||||||
|
|
||||||
|
@app.post("/end")
|
||||||
|
def on_end():
|
||||||
|
game_state = request.get_json()
|
||||||
|
snake.end(game_state)
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def identify_server(response):
|
||||||
|
response.headers.set(
|
||||||
|
"server", "shillerben/gitea/starter-snake-python"
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
print(f"\nRunning Battlesnake at http://{host}:{port}")
|
||||||
|
app.run(host=host, port=port)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("-b", "--bind", default="0.0.0.0", help="host ip to listen on")
|
||||||
|
parser.add_argument("-p", "--port", type=int, default=8000, help="host port to listen on")
|
||||||
|
parser.add_argument("snake", help="snake to run")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
host = args.bind
|
||||||
|
port = args.port
|
||||||
|
snake_name = args.snake
|
||||||
|
|
||||||
|
snake = Snake.SNAKES[snake_name]()
|
||||||
|
|
||||||
|
run_server(snake, host, port)
|
2
snakes/__init__.py
Normal file
2
snakes/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
from .snake import Snake
|
||||||
|
from .starter_snake import StarterSnake
|
32
snakes/snake.py
Normal file
32
snakes/snake.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
class Snake:
|
||||||
|
|
||||||
|
SNAKES: typing.Dict[str, typing.Type["Snake"]] = {}
|
||||||
|
|
||||||
|
def __init_subclass__(cls) -> None:
|
||||||
|
cls.SNAKES[cls.__name__] = cls
|
||||||
|
|
||||||
|
def info(self) -> typing.Dict:
|
||||||
|
'''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
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def start(self, game_state: typing.Dict) -> None:
|
||||||
|
'''start is called when your Battlesnake begins a game'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def end(self, game_state: typing.Dict) -> None:
|
||||||
|
'''end is called when your Battlesnake finishes a game'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def move(self, game_state: typing.Dict) -> typing.Dict:
|
||||||
|
'''move is called on every turn and returns your next move
|
||||||
|
Valid moves are "up", "down", "left", or "right"
|
||||||
|
See https://docs.battlesnake.com/api/example-move for available data
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
74
snakes/starter_snake.py
Normal file
74
snakes/starter_snake.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
import random
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from .snake import Snake
|
||||||
|
|
||||||
|
|
||||||
|
class StarterSnake(Snake):
|
||||||
|
'''Snake from Battlesnake's starter snake repo'''
|
||||||
|
|
||||||
|
def info(self) -> typing.Dict:
|
||||||
|
print("INFO")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"apiversion": "1",
|
||||||
|
"author": "", # TODO: Your Battlesnake Username
|
||||||
|
"color": "#888888", # TODO: Choose color
|
||||||
|
"head": "default", # TODO: Choose head
|
||||||
|
"tail": "default", # TODO: Choose tail
|
||||||
|
}
|
||||||
|
|
||||||
|
def start(self, game_state: typing.Dict):
|
||||||
|
print("GAME START")
|
||||||
|
|
||||||
|
def end(self, game_state: typing.Dict):
|
||||||
|
print("GAME OVER\n")
|
||||||
|
|
||||||
|
def move(self, game_state: typing.Dict) -> typing.Dict:
|
||||||
|
is_move_safe = {"up": True, "down": True, "left": True, "right": True}
|
||||||
|
|
||||||
|
# We've included code to prevent your Battlesnake from moving backwards
|
||||||
|
my_head = game_state["you"]["body"][0] # Coordinates of your head
|
||||||
|
my_neck = game_state["you"]["body"][1] # Coordinates of your "neck"
|
||||||
|
|
||||||
|
if my_neck["x"] < my_head["x"]: # Neck is left of head, don't move left
|
||||||
|
is_move_safe["left"] = False
|
||||||
|
|
||||||
|
elif my_neck["x"] > my_head["x"]: # Neck is right of head, don't move right
|
||||||
|
is_move_safe["right"] = False
|
||||||
|
|
||||||
|
elif my_neck["y"] < my_head["y"]: # Neck is below head, don't move down
|
||||||
|
is_move_safe["down"] = False
|
||||||
|
|
||||||
|
elif my_neck["y"] > my_head["y"]: # Neck is above head, don't move up
|
||||||
|
is_move_safe["up"] = False
|
||||||
|
|
||||||
|
# TODO: Step 1 - Prevent your Battlesnake from moving out of bounds
|
||||||
|
# board_width = game_state['board']['width']
|
||||||
|
# board_height = game_state['board']['height']
|
||||||
|
|
||||||
|
# TODO: Step 2 - Prevent your Battlesnake from colliding with itself
|
||||||
|
# my_body = game_state['you']['body']
|
||||||
|
|
||||||
|
# TODO: Step 3 - Prevent your Battlesnake from colliding with other Battlesnakes
|
||||||
|
# opponents = game_state['board']['snakes']
|
||||||
|
|
||||||
|
# Are there any safe moves left?
|
||||||
|
safe_moves = []
|
||||||
|
for move, isSafe in is_move_safe.items():
|
||||||
|
if isSafe:
|
||||||
|
safe_moves.append(move)
|
||||||
|
|
||||||
|
if len(safe_moves) == 0:
|
||||||
|
print(f"MOVE {game_state['turn']}: No safe moves detected! Moving down")
|
||||||
|
return {"move": "down"}
|
||||||
|
|
||||||
|
# Choose a random move from the safe ones
|
||||||
|
next_move = random.choice(safe_moves)
|
||||||
|
|
||||||
|
# TODO: Step 4 - Move towards food instead of random, to regain health and survive longer
|
||||||
|
# food = game_state['board']['food']
|
||||||
|
|
||||||
|
print(f"MOVE {game_state['turn']}: {next_move}")
|
||||||
|
return {"move": next_move}
|
Loading…
Reference in New Issue
Block a user