Skip to content

Instantly share code, notes, and snippets.

@RyanFleck
Last active January 27, 2021 03:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RyanFleck/b86cd1bbf9a14bb150f19456c79f53ed to your computer and use it in GitHub Desktop.
Save RyanFleck/b86cd1bbf9a14bb150f19456c79f53ed to your computer and use it in GitHub Desktop.
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