Initial commit

This commit is contained in:
2022-01-24 15:30:05 +01:00
commit ad77bbc1f2
15 changed files with 545 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
*.DS_Store
*.pyc
.cmd
.idea
.vscode
+2
View File
@@ -0,0 +1,2 @@
language = "python3"
run = "python server.py"
+1
View File
@@ -0,0 +1 @@
* @chris-bsnake @aurorawalker @bvanvugt
+3
View File
@@ -0,0 +1,3 @@
# Battlesnake Code of Conduct
Please see https://docs.battlesnake.com/policies/conduct
+8
View File
@@ -0,0 +1,8 @@
# Contributing
We welcome contributions from the community!
Please see https://docs.battlesnake.com/community/contributing
We track all issues/discussions for all our open source repos at https://github.com/BattlesnakeOfficial/feedback/discussions
Any item tagged with "flag/help-wanted ✋" is a great place to start, as it highlights any issues where we'd love to get some help!
+21
View File
@@ -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.
+1
View File
@@ -0,0 +1 @@
web: python server.py
+97
View File
@@ -0,0 +1,97 @@
# Getting started with [Battlesnake](http://play.battlesnake.com?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=homepage) and Python
![Battlesnake Logo](https://media.battlesnake.com/social/StarterSnakeGitHubRepos_Python.png)
This is a basic implementation of the [Battlesnake API](https://docs.battlesnake.com/references/api) in Python. It's a great starting point for anyone wanting to program their first Battlesnake using Python, and comes ready to deploy with [Replit](https://repl.it) and [Heroku](https://heroku.com), or you can use any other cloud provider you'd like.
## Technologies Used
* [Python3](https://www.python.org/)
* [Flask](https://flask.palletsprojects.com/)
## Quickstart
The [Quick Start Coding Guide](https://docs.battlesnake.com/guides/getting-started) provides the full set of instructions to customize, register, and create your first games with your Battlesnake! While the guide uses [Repl.it](https://repl.it) as an example host, the instructions can be modified to work with any hosting provider. You can also find advice on other hosting providers on our [Hosting Suggestions](https://docs.battlesnake.com/references/hosting-suggestions) page.
### Prerequisites
* A free [Battlesnake Account](https://play.battlesnake.com/?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=homepage)
---
## Customizing Your Battlesnake
Locate the `handle_info` function inside [server.py](server.py#L15). At the end of that function you should see a line that looks like this:
```python
return {
"apiversion": "1",
"author": "",
"color": "#888888",
"head": "default",
"tail": "default",
}
```
This function is called by the game engine periodically to make sure your Battlesnake is healthy, responding correctly, and to determine how your Battlesnake will appear on the game board. See [Battlesnake Personalization](https://docs.battlesnake.com/references/personalization) for how to customize your Battlesnake's appearance using these values.
Whenever you update these values, go to the page for your Battlesnake and select 'Refresh Metadata' from the option menu. This will update your Battlesnake to use your latest configuration and those changes should be reflected in the UI as well as any new games created.
## Changing Behavior
On every turn of each game your Battlesnake receives information about the game board and must decide its next move.
Locate the `handle_move` function inside [server.py](server.py#L48). Possible moves are "up", "down", "left", or "right". To start your Battlesnake will choose a move randomly. Your goal as a developer is to read information sent to you about the board (available in the `data` variable) and decide where your Battlesnake should move next. Your Battlesnakes move logic lives in [server_logic.py](server_logic.py#L37). This is the code you will want to edit.
See the [Battlesnake Game Rules](https://docs.battlesnake.com/references/rules) for more information on playing the game, moving around the board, and improving your algorithm.
## (Optional) Running Your Battlesnake Locally
Eventually you might want to run your Battlesnake server locally for faster testing and debugging. You can do this by installing [Python 3.8](https://www.python.org/downloads/) and running:
```shell
python server.py
```
**Note:** You cannot create games on [play.battlesnake.com](https://play.battlesnake.com) using a locally running Battlesnake unless you install and use a port forwarding tool like [ngrok](https://ngrok.com/). See [Hosting Suggestions.](https://docs.battlesnake.com/references/hosting-suggestions#local)
## Running Tests
This Starter Project comes with a very simple test suite for you to expand! Located in `tests.py` you can run them using the following command:
```python tests.py -v```
---
## Playing Battlesnake
### Completing Challenges
If you're looking for the Single Player Mode of Battlesnake, or something to practice with between events, check out [Challenges.](https://docs.battlesnake.com/guides/quick-start-challenges-guide)
### Joining a Battlesnake Arena
Once you've made your Battlesnake behave and survive on its own, you can enter it into the [Global Battlesnake Arena](https://play.battlesnake.com/arena/global) to see how it performs against other Battlesnakes worldwide.
Arenas will regularly create new games and rank Battlesnakes based on their results. They're a good way to get regular feedback on how well your Battlesnake is performing, and a fun way to track your progress as you develop your algorithm.
### Joining a Battlesnake League
Want to get out there to compete and win prizes? Check out the [Quick Start League Guide](https://docs.battlesnake.com/guides/quick-start-league-guide) for information on the how and when of our competitive seasons.
---
## Resources
All documentation is available at [docs.battlesnake.com](https://docs.battlesnake.com), including detailed Guides, API References, and Tips.
You can also join the Battlesnake Developer Community on [Discord](https://play.battlesnake.com/discord?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=discord). We have a growing community of Battlesnake developers of all skill levels wanting to help everyone succeed and have fun with Battlesnake :)
Check out live Battlesnake events on [Twitch](https://www.twitch.tv/battlesnakeofficial) and see what is happening when on the [Calendar.](https://play.battlesnake.com/calendar?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=calendar)
Want to contribute to Battlesnake? We have a number of open-source codebases and would love for you to get involved! Check out our page on [Contributing.](https://docs.battlesnake.com/guides/contributing)
## Feedback
**Do you have an issue or suggestions for this repository?** Head over to our [Feedback Repository](https://play.battlesnake.com/feedback?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=feedback) today and let us know!
Generated
+144
View File
@@ -0,0 +1,144 @@
[[package]]
name = "click"
version = "8.0.1"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "flask"
version = "2.0.1"
description = "A simple framework for building complex web applications."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
click = ">=7.1.2"
itsdangerous = ">=2.0"
Jinja2 = ">=3.0"
Werkzeug = ">=2.0"
[package.extras]
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
[[package]]
name = "itsdangerous"
version = "2.0.1"
description = "Safely pass data to untrusted environments and back."
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "jinja2"
version = "3.0.1"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "werkzeug"
version = "2.0.1"
description = "The comprehensive WSGI web application library."
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
watchdog = ["watchdog"]
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "cb806ee633cb53933ae5610583ae98ba3574ebb5c0d2a215cc8602e7af663e18"
[metadata.files]
click = [
{file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
{file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
flask = [
{file = "Flask-2.0.1-py3-none-any.whl", hash = "sha256:a6209ca15eb63fc9385f38e452704113d679511d9574d09b2cf9183ae7d20dc9"},
{file = "Flask-2.0.1.tar.gz", hash = "sha256:1c4c257b1892aec1398784c63791cbaa43062f1f7aeb555c4da961b20ee68f55"},
]
itsdangerous = [
{file = "itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"},
{file = "itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"},
]
jinja2 = [
{file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
{file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
]
markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
werkzeug = [
{file = "Werkzeug-2.0.1-py3-none-any.whl", hash = "sha256:6c1ec500dcdba0baa27600f6a22f6333d8b662d22027ff9f6202e3367413caa8"},
{file = "Werkzeug-2.0.1.tar.gz", hash = "sha256:1de1db30d010ff1af14a009224ec49ab2329ad2cde454c8a708130642d579c42"},
]
+9
View File
@@ -0,0 +1,9 @@
[tool.poetry]
name = "starter-snake-python"
version = "1.0.0"
description = "A simple Battlesnake written in Python"
authors = ["Battlesnake Developers <developers@battlesnake.com>"]
[tool.poetry.dependencies]
python = "^3.8"
Flask = "^2.0.1"
+1
View File
@@ -0,0 +1 @@
Flask==2.0.1
+1
View File
@@ -0,0 +1 @@
python-3.9.6
+77
View File
@@ -0,0 +1,77 @@
import logging
import os
from flask import Flask
from flask import request
import server_logic
app = Flask(__name__)
@app.get("/")
def handle_info():
"""
This function is called when you register your Battlesnake on play.battlesnake.com
See https://docs.battlesnake.com/guides/getting-started#step-4-register-your-battlesnake
It controls your Battlesnake appearance and author permissions.
For customization options, see https://docs.battlesnake.com/references/personalization
TIP: If you open your Battlesnake URL in browser you should see this data.
"""
print("INFO")
return {
"apiversion": "1",
"author": "", # TODO: Your Battlesnake Username
"color": "#888888", # TODO: Personalize
"head": "default", # TODO: Personalize
"tail": "default", # TODO: Personalize
}
@app.post("/start")
def handle_start():
"""
This function is called everytime your snake is entered into a game.
request.json contains information about the game that's about to be played.
"""
data = request.get_json()
print(f"{data['game']['id']} START")
return "ok"
@app.post("/move")
def handle_move():
"""
This function is called on every turn of a game. It's how your snake decides where to move.
Valid moves are "up", "down", "left", or "right".
"""
data = request.get_json()
# TODO - look at the server_logic.py file to see how we decide what move to return!
move = server_logic.choose_move(data)
return {"move": move}
@app.post("/end")
def end():
"""
This function is called when a game your snake was in ends.
It's purely for informational purposes, you don't have to make any decisions here.
"""
data = request.get_json()
print(f"{data['game']['id']} END")
return "ok"
if __name__ == "__main__":
logging.getLogger("werkzeug").setLevel(logging.ERROR)
print("Starting Battlesnake Server...")
port = int(os.environ.get("PORT", "8080"))
app.run(host="0.0.0.0", port=port, debug=True)
+79
View File
@@ -0,0 +1,79 @@
import random
from typing import List, Dict
"""
This file can be a nice home for your move logic, and to write helper functions.
We have started this for you, with a function to help remove the 'neck' direction
from the list of possible moves!
"""
def avoid_my_neck(my_head: Dict[str, int], my_body: List[dict], possible_moves: List[str]) -> List[str]:
"""
my_head: Dictionary of x/y coordinates of the Battlesnake head.
e.g. {"x": 0, "y": 0}
my_body: List of dictionaries of x/y coordinates for every segment of a Battlesnake.
e.g. [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 2, "y": 0} ]
possible_moves: List of strings. Moves to pick from.
e.g. ["up", "down", "left", "right"]
return: The list of remaining possible_moves, with the 'neck' direction removed
"""
my_neck = my_body[1] # The segment of body right after the head is the 'neck'
if my_neck["x"] < my_head["x"]: # my neck is left of my head
possible_moves.remove("left")
elif my_neck["x"] > my_head["x"]: # my neck is right of my head
possible_moves.remove("right")
elif my_neck["y"] < my_head["y"]: # my neck is below my head
possible_moves.remove("down")
elif my_neck["y"] > my_head["y"]: # my neck is above my head
possible_moves.remove("up")
return possible_moves
def choose_move(data: dict) -> str:
"""
data: Dictionary of all Game Board data as received from the Battlesnake Engine.
For a full example of 'data', see https://docs.battlesnake.com/references/api/sample-move-request
return: A String, the single move to make. One of "up", "down", "left" or "right".
Use the information in 'data' to decide your next move. The 'data' variable can be interacted
with as a Python Dictionary, and contains all of the information about the Battlesnake board
for each move of the game.
"""
my_head = data["you"]["head"] # A dictionary of x/y coordinates like {"x": 0, "y": 0}
my_body = data["you"]["body"] # A list of x/y coordinate dictionaries like [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 2, "y": 0} ]
# TODO: uncomment the lines below so you can see what this data looks like in your output!
# print(f"~~~ Turn: {data['turn']} Game Mode: {data['game']['ruleset']['name']} ~~~")
# print(f"All board data this turn: {data}")
# print(f"My Battlesnakes head this turn is: {my_head}")
# print(f"My Battlesnakes body this turn is: {my_body}")
possible_moves = ["up", "down", "left", "right"]
# Don't allow your Battlesnake to move back in on it's own neck
possible_moves = avoid_my_neck(my_head, my_body, possible_moves)
# TODO: Using information from 'data', find the edges of the board and don't let your Battlesnake move beyond them
# board_height = ?
# board_width = ?
# TODO Using information from 'data', don't let your Battlesnake pick a move that would hit its own body
# TODO: Using information from 'data', don't let your Battlesnake pick a move that would collide with another Battlesnake
# TODO: Using information from 'data', make your Battlesnake move towards a piece of food on the board
# Choose a random direction from the remaining possible_moves to move in, and then return that move
move = random.choice(possible_moves)
# TODO: Explore new strategies for picking a move that are better than random
print(f"{data['game']['id']} MOVE {data['turn']}: {move} picked from all valid options in {possible_moves}")
return move
+96
View File
@@ -0,0 +1,96 @@
"""
Starter Unit Tests using the built-in Python unittest library.
See https://docs.python.org/3/library/unittest.html
You can expand these to cover more cases!
To run the unit tests, use the following command in your terminal,
in the folder where this file exists:
python tests.py -v
"""
import unittest
from server_logic import avoid_my_neck
class AvoidNeckTest(unittest.TestCase):
def test_avoid_neck_all(self):
"""
The possible move set should be all moves.
In the starter position, a Battlesnake body is 'stacked' in a
single place, and thus all directions are valid.
"""
# Arrange
test_head = {"x": 5, "y": 5}
test_body = [{"x": 5, "y": 5}, {"x": 5, "y": 5}, {"x": 5, "y": 5}]
possible_moves = ["up", "down", "left", "right"]
# Act
result_moves = avoid_my_neck(test_head, test_body, possible_moves)
# Assert
self.assertEqual(len(result_moves), 4)
self.assertEqual(possible_moves, result_moves)
def test_avoid_neck_left(self):
# Arrange
test_head = {"x": 5, "y": 5}
test_body = [{"x": 5, "y": 5}, {"x": 4, "y": 5}, {"x": 3, "y": 5}]
possible_moves = ["up", "down", "left", "right"]
expected = ["up", "down", "right"]
# Act
result_moves = avoid_my_neck(test_head, test_body, possible_moves)
# Assert
self.assertEqual(len(result_moves), 3)
self.assertEqual(expected, result_moves)
def test_avoid_neck_right(self):
# Arrange
test_head = {"x": 5, "y": 5}
test_body = [{"x": 5, "y": 5}, {"x": 6, "y": 5}, {"x": 7, "y": 5}]
possible_moves = ["up", "down", "left", "right"]
expected = ["up", "down", "left"]
# Act
result_moves = avoid_my_neck(test_head, test_body, possible_moves)
# Assert
self.assertEqual(len(result_moves), 3)
self.assertEqual(expected, result_moves)
def test_avoid_neck_up(self):
# Arrange
test_head = {"x": 5, "y": 5}
test_body = [{"x": 5, "y": 5}, {"x": 5, "y": 6}, {"x": 5, "y": 7}]
possible_moves = ["up", "down", "left", "right"]
expected = ["down", "left", "right"]
# Act
result_moves = avoid_my_neck(test_head, test_body, possible_moves)
# Assert
self.assertEqual(len(result_moves), 3)
self.assertEqual(expected, result_moves)
def test_avoid_neck_down(self):
# Arrange
test_head = {"x": 5, "y": 5}
test_body = [{"x": 5, "y": 5}, {"x": 5, "y": 4}, {"x": 5, "y": 3}]
possible_moves = ["up", "down", "left", "right"]
expected = ["up", "left", "right"]
# Act
result_moves = avoid_my_neck(test_head, test_body, possible_moves)
# Assert
self.assertEqual(len(result_moves), 3)
self.assertEqual(expected, result_moves)
if __name__ == "__main__":
unittest.main()