Last active
January 27, 2021 03:18
-
-
Save RyanFleck/b86cd1bbf9a14bb150f19456c79f53ed to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import random | |
import enum | |
import cherrypy | |
""" | |
Dumb battlesnake, picks a clear path. | |
For instructions see https://github.com/BattlesnakeOfficial/starter-snake-python/README.md | |
""" | |
class I(enum.Enum): | |
BODY = 1 | |
HEAD = 2 | |
APPLE = 3 | |
ENEMY = 4 | |
X_HEAD_OF_ENEMY = 5 | |
x_AROUND_HEAD_OF_ENEMY = 6 | |
def is_food_around(grid, width, height, x, y): | |
around = [[0, -1], [-1, 0], [-1, -1], [0, 1], [1, 0], [1, 1], [-1, 1], | |
[1, -1]] | |
for location in around: | |
newx = x + location[0] | |
newy = y + location[1] | |
if newx >= width or newy >= height or newx < 0 or newy < 0: | |
continue | |
content = grid[newy][newx] | |
if content == I.APPLE: | |
return True | |
return False | |
def nothing_else_around(grid, width, height, x, y): | |
around = [[0, -1], [-1, 0], [-1, -1], [0, 1], [1, 0], [1, 1], [-1, 1], | |
[1, -1]] | |
score = 0 # Higher is worse. | |
for location in around: | |
newx = x + location[0] | |
newy = y + location[1] | |
if newx >= width or newy >= height or newx < 0 or newy < 0: | |
continue | |
content = grid[newy][newx] | |
if content != 0: | |
score = score + 1 | |
return score | |
def mark_around(grid, width, height, x, y): | |
around = [[0, -1], [-1, 0], [-1, -1], [0, 1], [1, 0], [1, 1], [-1, 1], | |
[1, -1]] | |
for location in around: | |
newx = x + location[0] | |
newy = y + location[1] | |
if newx >= width or newy >= height or newx < 0 or newy < 0: | |
continue | |
grid[newy][newx] = I.x_AROUND_HEAD_OF_ENEMY | |
class Battlesnake(object): | |
@cherrypy.expose | |
@cherrypy.tools.json_out() | |
def index(self): | |
# This function is called when you register your Battlesnake on play.battlesnake.com | |
# It controls your Battlesnake appearance and author permissions. | |
# TIP: If you open your Battlesnake URL in browser you should see this data | |
return { | |
"apiversion": "1", | |
"author": "", # TODO: Your Battlesnake Username | |
"color": "#888888", # TODO: Personalize | |
"head": "default", # TODO: Personalize | |
"tail": "default", # TODO: Personalize | |
} | |
@cherrypy.expose | |
@cherrypy.tools.json_in() | |
def start(self): | |
# This function is called everytime your snake is entered into a game. | |
# cherrypy.request.json contains information about the game that's about to be played. | |
# data = cherrypy.request.json | |
print("START") | |
return "ok" | |
@cherrypy.expose | |
@cherrypy.tools.json_in() | |
@cherrypy.tools.json_out() | |
def move(self): | |
# 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". | |
# TODO: Use the information in cherrypy.request.json to decide your next move. | |
data = cherrypy.request.json | |
board = data["board"] | |
snakes = board["snakes"] | |
food = board["food"] | |
me = data["you"] | |
x = me['head']['x'] | |
y = me['head']['y'] | |
height = int(board["height"]) | |
width = int(board["width"]) | |
# Create a 2D plane of 0. | |
plane = [] | |
for y in range(height): | |
plane.append([0] * width) | |
elems = len(plane) - 1 | |
# Set snakes on plane. | |
for snake in snakes: | |
body = snake["body"] | |
head = snake["head"] | |
sx = head["x"] | |
sy = head["y"] | |
is_self = snake["id"] == me["id"] | |
if (is_self): | |
print("I am snek" + snake["name"]) | |
for elem in body: | |
ex = elem["x"] | |
ey = elem["y"] | |
if is_self: | |
plane[ey][ex] = I.BODY | |
else: | |
plane[ey][ex] = I.ENEMY | |
if is_self: | |
plane[sy][sx] = I.HEAD | |
else: | |
plane[sy][sx] = I.X_HEAD_OF_ENEMY | |
mark_around(plane, width, height, sx, sy) | |
for apple in food: | |
ax = apple["x"] | |
ay = apple["y"] | |
plane[ay][ax] = I.APPLE | |
# Print the plane. | |
if (True): | |
print("\nT:\tPlane:") | |
for row in range(len(plane)): | |
print( | |
f"{elems-row+1}:\t{' '.join(map(lambda x: '.' if x == 0 else x.name[0], plane[elems-row]))}" | |
) | |
print() | |
print("\nSnakes:") | |
for snake in snakes: | |
print(" - " + snake["name"]) | |
x = me['head']['x'] | |
y = me['head']['y'] | |
print(f"\nMy Head is at: {x},{y}") | |
# Choose a random direction to move in | |
possible_moves = [] | |
# Up safe? | |
if (y + 1 < height): | |
up = plane[y + 1][x] | |
if (up == 0 or up == I.APPLE): | |
possible_moves.append("up") | |
# Down safe? | |
if (y - 1 >= 0): | |
down = plane[y - 1][x] | |
if (down == 0 or down == I.APPLE): | |
possible_moves.append("down") | |
# Left safe? | |
if (x - 1 >= 0): | |
left = plane[y][x - 1] | |
if (left == 0 or left == I.APPLE): | |
possible_moves.append("left") | |
# Right safe? | |
if (x + 1 < width): | |
right = plane[y][x + 1] | |
if (right == 0 or right == I.APPLE): | |
possible_moves.append("right") | |
# Suicide if no moves left. | |
if (len(possible_moves) == 0): | |
return {"move": "up"} | |
# Additional processing- decide which move is best. | |
# Check 4 Apples: | |
# Up safe? | |
if (y + 1 < height): | |
up = plane[y + 1][x] | |
if (up == I.APPLE and "up" in possible_moves): | |
return {"move": "up"} | |
# Down safe? | |
if (y - 1 >= 0): | |
down = plane[y - 1][x] | |
if (down == I.APPLE and "down" in possible_moves): | |
return {"move": "down"} | |
# Left safe? | |
if (x - 1 >= 0): | |
left = plane[y][x - 1] | |
if (left == I.APPLE and "left" in possible_moves): | |
return {"move": "left"} | |
# Right safe? | |
if (x + 1 < width): | |
right = plane[y][x + 1] | |
if (right == I.APPLE and "right" in possible_moves): | |
return {"move": "right"} | |
# Check 4 Apples II: | |
# Up safe? | |
if (y + 1 < height): | |
up = plane[y + 1][x] | |
if (is_food_around(plane, width, height, x, y+1) and "up" in possible_moves): | |
print("Food up.") | |
return {"move": "up"} | |
# Down safe? | |
if (y - 1 >= 0): | |
down = plane[y - 1][x] | |
if (is_food_around(plane, width, height, x, y-1) and "down" in possible_moves): | |
print("Food down.") | |
return {"move": "down"} | |
# Left safe? | |
if (x - 1 >= 0): | |
left = plane[y][x - 1] | |
if (is_food_around(plane, width, height, x-1, y) and "left" in possible_moves): | |
print("Food to left.") | |
return {"move": "left"} | |
# Right safe? | |
if (x + 1 < width): | |
right = plane[y][x + 1] | |
if (is_food_around(plane, width, height, x+1, y) and "right" in possible_moves): | |
print("Food to right.") | |
return {"move": "right"} | |
# Check 4 Empty Space: | |
scores = [] # ("direction", score) | |
# Up safe? | |
if (y + 1 < height): | |
score = nothing_else_around(plane, width, height, x, y+1) | |
scores.append(("up", score)) | |
# Down safe? | |
if (y - 1 >= 0): | |
score = nothing_else_around(plane, width, height, x, y-1) | |
scores.append(("down", score)) | |
# Left safe? | |
if (x - 1 >= 0): | |
score = nothing_else_around(plane, width, height, x-1, y) | |
scores.append(("left", score)) | |
# Right safe? | |
if (x + 1 < width): | |
score = nothing_else_around(plane, width, height, x+1, y) | |
scores.append(("right", score)) | |
# Sort and filter scores | |
sorted_scores = sorted(scores, key=lambda x: x[1]) | |
filtered_scores = list(filter(lambda x: x[0] in possible_moves, sorted_scores)) | |
print("Sorted scores:") | |
print(sorted_scores) | |
print("Filtered scores:") | |
print(filtered_scores) | |
if len(filtered_scores) > 0: | |
direction = filtered_scores[0][0] | |
print("Ideal direction to move is "+direction) | |
return {"move": direction} | |
# If nothing else is done, pick from available moves. | |
move = random.choice(possible_moves) | |
print("\nPossible Moves:") | |
print(possible_moves) | |
print(f"MOVE: {move}") | |
return {"move": move} | |
@cherrypy.expose | |
@cherrypy.tools.json_in() | |
def end(self): | |
# 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. | |
print("\nGAME IS OVER\n") | |
data = cherrypy.request.json | |
print(data) | |
print("END") | |
return "ok" | |
if __name__ == "__main__": | |
server = Battlesnake() | |
cherrypy.config.update({"server.socket_host": "0.0.0.0"}) | |
cherrypy.config.update({ | |
"server.socket_port": | |
int(os.environ.get("PORT", "8080")), | |
}) | |
print("Starting Battlesnake Server...") | |
cherrypy.quickstart(server) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment