diff --git a/main.py b/main.py index 4ca21971..d075e3c5 100644 --- a/main.py +++ b/main.py @@ -23,9 +23,9 @@ def info() -> typing.Dict: return { "apiversion": "1", "author": "", # TODO: Your Battlesnake Username - "color": "#888888", # TODO: Choose color - "head": "default", # TODO: Choose head - "tail": "default", # TODO: Choose tail + "color": "#3333ff", # TODO: Choose color + "head": "bonhomme", # TODO: Choose head + "tail": "nr-booster", # TODO: Choose tail } @@ -44,56 +44,120 @@ def end(game_state: typing.Dict): # See https://docs.battlesnake.com/api/example-move for available data def move(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} + 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'] + + # at the top + if my_head['y'] + 1 == game_state['board']['height']: + is_move_safe['up'] = False + # at the bottom + if my_head['y'] - 1 < 0: + is_move_safe['down'] = False + # right edge + if my_head['x'] + 1 == game_state['board']['width']: + is_move_safe['right'] = False + # left edge + if my_head['x'] - 1 < 0: + is_move_safe['left'] = False + + # TODO: Step 2 - Prevent your Battlesnake from colliding with itself + # my_body = game_state['you']['body'] + + my_body = game_state['you']['body'] + + for segment in my_body: + if my_head['x'] - 1 == segment['x']: + is_move_safe['left'] = False + if my_head['x'] + 1 == segment['x']: + is_move_safe['right'] = False + if my_head['y'] - 1 == segment['y']: + is_move_safe['down'] = False + if my_head['y'] + 1 == segment['y']: + is_move_safe['up'] = False + + + + + # TODO: Step 3 - Prevent your Battlesnake from colliding with other Battlesnakes + + # top left corner + if my_head['x'] == 0 and my_head['y'] == 10: + is_move_safe['down'] = True + is_move_safe['right'] = True + + # top right corner + if my_head['x'] == 10 and my_head['y'] == 10: + is_move_safe['left'] = True + is_move_safe['down'] = True + + # bottom left corner + if my_head['x'] == 0 and my_head['y'] == 0: + is_move_safe['right'] = True + is_move_safe['up'] = True + + # bottom right corner + if my_head['x'] == 10 and my_head['y'] == 0: + is_move_safe['left'] = True + is_move_safe['up'] = True + # 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") + print(is_move_safe) + print("------------------------------") + 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}") + #print(game_state) + #print("Your snake's body: ", game_state['you']['body']) + print(is_move_safe) + print("------------------------------") + return {"move": next_move} # Start server when `python main.py` is run if __name__ == "__main__": from server import run_server - run_server({"info": info, "start": start, "move": move, "end": end}) + run_server({ + "info": info, + "start": start, + "move": move, + "end": end + }) diff --git a/starter-snake-python/Dockerfile b/starter-snake-python/Dockerfile new file mode 100644 index 00000000..1b06fd3d --- /dev/null +++ b/starter-snake-python/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10.6-slim + +# Install app +COPY . /usr/app +WORKDIR /usr/app + +# Install dependencies +RUN pip install --upgrade pip && pip install -r requirements.txt + +# Run Battlesnake +CMD [ "python", "main.py" ] diff --git a/starter-snake-python/LICENSE b/starter-snake-python/LICENSE new file mode 100644 index 00000000..1abdb9c9 --- /dev/null +++ b/starter-snake-python/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Battlesnake Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/starter-snake-python/README.md b/starter-snake-python/README.md new file mode 100644 index 00000000..0c13ab26 --- /dev/null +++ b/starter-snake-python/README.md @@ -0,0 +1,59 @@ +# Battlesnake Python Starter Project + +An official Battlesnake template written in Python. Get started at [play.battlesnake.com](https://play.battlesnake.com). + +![Battlesnake Logo](https://media.battlesnake.com/social/StarterSnakeGitHubRepos_Python.png) + +This project is a great starting point for anyone wanting to program their first Battlesnake in Python. It can be run locally or easily deployed to a cloud provider of your choosing. See the [Battlesnake API Docs](https://docs.battlesnake.com/api) for more detail. + +[![Run on Replit](https://repl.it/badge/github/BattlesnakeOfficial/starter-snake-python)](https://replit.com/@Battlesnake/starter-snake-python) + +## Technologies Used + +This project uses [Python 3](https://www.python.org/) and [Flask](https://flask.palletsprojects.com/). It also comes with an optional [Dockerfile](https://docs.docker.com/engine/reference/builder/) to help with deployment. + +## Run Your Battlesnake + +Install dependencies using pip + +```sh +pip install -r requirements.txt +``` + +Start your Battlesnake + +```sh +python main.py +``` + +You should see the following output once it is running + +```sh +Running your Battlesnake at http://0.0.0.0:8000 + * Serving Flask app 'My Battlesnake' + * Debug mode: off +``` + +Open [localhost:8000](http://localhost:8000) in your browser and you should see + +```json +{"apiversion":"1","author":"","color":"#888888","head":"default","tail":"default"} +``` + +## Play a Game Locally + +Install the [Battlesnake CLI](https://github.com/BattlesnakeOfficial/rules/tree/main/cli) +* You can [download compiled binaries here](https://github.com/BattlesnakeOfficial/rules/releases) +* or [install as a go package](https://github.com/BattlesnakeOfficial/rules/tree/main/cli#installation) (requires Go 1.18 or higher) + +Command to run a local game + +```sh +battlesnake play -W 11 -H 11 --name 'Python Starter Project' --url http://localhost:8000 -g solo --browser +``` + +## Next Steps + +Continue with the [Battlesnake Quickstart Guide](https://docs.battlesnake.com/quickstart) to customize and improve your Battlesnake's behavior. + +**Note:** To play games on [play.battlesnake.com](https://play.battlesnake.com) you'll need to deploy your Battlesnake to a live web server OR use a port forwarding tool like [ngrok](https://ngrok.com/) to access your server locally. diff --git a/starter-snake-python/__pycache__/server.cpython-310.pyc b/starter-snake-python/__pycache__/server.cpython-310.pyc new file mode 100644 index 00000000..231f4e18 Binary files /dev/null and b/starter-snake-python/__pycache__/server.cpython-310.pyc differ diff --git a/starter-snake-python/battlesnake.exe b/starter-snake-python/battlesnake.exe new file mode 100644 index 00000000..5a4eb17c Binary files /dev/null and b/starter-snake-python/battlesnake.exe differ diff --git a/starter-snake-python/main.py b/starter-snake-python/main.py new file mode 100644 index 00000000..e1cc4616 --- /dev/null +++ b/starter-snake-python/main.py @@ -0,0 +1,99 @@ +# Welcome to +# __________ __ __ .__ __ +# \______ \_____ _/ |__/ |_| | ____ ______ ____ _____ | | __ ____ +# | | _/\__ \\ __\ __\ | _/ __ \ / ___// \\__ \ | |/ // __ \ +# | | \ / __ \| | | | | |_\ ___/ \___ \| | \/ __ \| <\ ___/ +# |________/(______/__| |__| |____/\_____>______>___|__(______/__|__\\_____> +# +# This file can be a nice home for your Battlesnake logic and helper functions. +# +# To get you started we've included code to prevent your Battlesnake from moving backwards. +# For more info see docs.battlesnake.com + +import random +import typing + + +# 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() -> 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 + } + + +# start is called when your Battlesnake begins a game +def start(game_state: typing.Dict): + print("GAME START") + + +# end is called when your Battlesnake finishes a game +def end(game_state: typing.Dict): + print("GAME OVER\n") + + +# 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 +def move(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} + + +# Start server when `python main.py` is run +if __name__ == "__main__": + from server import run_server + + run_server({"info": info, "start": start, "move": move, "end": end}) diff --git a/starter-snake-python/requirements.txt b/starter-snake-python/requirements.txt new file mode 100644 index 00000000..d6112355 --- /dev/null +++ b/starter-snake-python/requirements.txt @@ -0,0 +1 @@ +Flask==2.3.2 diff --git a/starter-snake-python/server.py b/starter-snake-python/server.py new file mode 100644 index 00000000..1e7da5e2 --- /dev/null +++ b/starter-snake-python/server.py @@ -0,0 +1,46 @@ +import logging +import os +import typing + +from flask import Flask +from flask import request + + +def run_server(handlers: typing.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")) + + logging.getLogger("werkzeug").setLevel(logging.ERROR) + + print(f"\nRunning Battlesnake at http://{host}:{port}") + app.run(host=host, port=port)