working on implementing server.js
This commit is contained in:
parent
4ccc534f3d
commit
55b2398370
13 changed files with 2320 additions and 0 deletions
15
deployment-service/.env
Normal file
15
deployment-service/.env
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Fly API token for Management Service
|
||||
FLY_API_TOKEN=FlyV1 fm2_lJPECAAAAAAACJJHxBBwbuk1FNDzBQl/dWFv18aswrVodHRwczovL2FwaS5mbHkuaW8vdjGUAJLOAA//nh8Lk7lodHRwczovL2FwaS5mbHkuaW8vYWFhL3YxxDy2Mf28JyaaU50pzS95pUibO6PCjFa7jJ6l+HdBg1NN3XaM4fRmQX5UnSgoKGTY+q+K+9LEdgeKZ6/jM5PETm7WQZcDR+6sy+8HcD0D+cO9zFyJ1/SnS9hrdSTilS++SrvxdeKswcBZbJs1hDLZ28BNXAtf86/urf+qVJiet4RVZhwmm/+AkgsPV29N98Qg8sBjIe2AJxoGDkUFFMW+uBBabRn+yOiIFmqRpSxuUbs=,fm2_lJPETm7WQZcDR+6sy+8HcD0D+cO9zFyJ1/SnS9hrdSTilS++SrvxdeKswcBZbJs1hDLZ28BNXAtf86/urf+qVJiet4RVZhwmm/+AkgsPV29N98QQwSdewQC1cShau0sg8G0QJcO5aHR0cHM6Ly9hcGkuZmx5LmlvL2FhYS92MZgEks5oC3chzwAAAAEkA5U/F84AD2FZCpHOAA9hWQzEEFT5imWgo8DLSNPluL+eVJfEIMFKlsEhtF+4ZGupW3XW9Yyr8hUFSQW0x4BfcB6zxHYU
|
||||
FLY_ORG=personal
|
||||
|
||||
# Docker image for each Notebook API server
|
||||
CONTAINER_IMAGE=python:3.11
|
||||
|
||||
# Common Tigris bucket name shared by all instances
|
||||
COMMON_BUCKET=snakeapi-deployment-test-bucket
|
||||
|
||||
# Tigris S3-compatible credentials (injected into A by Fly extension)
|
||||
AWS_ACCESS_KEY_ID=tid__NSmOVaGknqitaCySppZjqVTgJSdDFnFbWcQllkC_juHwkbQZO
|
||||
AWS_SECRET_ACCESS_KEY=tsec_6Bz1aMbfYQftuq5WfIVEDZkHwskU4MMjVywdtxSP6uxetEBvkSC2VHI9HfTeDgHr4D6kiz
|
||||
AWS_ENDPOINT_URL_S3=https://fly.storage.tigris.dev
|
||||
AWS_REGION=auto
|
||||
16
deployment-service/dockerfile
Normal file
16
deployment-service/dockerfile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY package.json ./
|
||||
RUN npm install --production
|
||||
|
||||
# Copy source and example env
|
||||
COPY src ./src
|
||||
COPY .env.example .env
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3006
|
||||
|
||||
# Start management service
|
||||
CMD ["npm", "start"]
|
||||
BIN
deployment-service/fly-org.token
Normal file
BIN
deployment-service/fly-org.token
Normal file
Binary file not shown.
19
deployment-service/fly.toml
Normal file
19
deployment-service/fly.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
app = "deployment-service"
|
||||
primary_region = "sea"
|
||||
|
||||
[build]
|
||||
|
||||
[http_service]
|
||||
internal_port = 3006
|
||||
|
||||
[[services]]
|
||||
protocol = "tcp"
|
||||
internal_port = 3006
|
||||
|
||||
[[services.ports]]
|
||||
port = 443
|
||||
handlers = ["tls","http"]
|
||||
|
||||
[[services.ports]]
|
||||
port = 80
|
||||
handlers = ["http"]
|
||||
1709
deployment-service/package-lock.json
generated
Normal file
1709
deployment-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
19
deployment-service/package.json
Normal file
19
deployment-service/package.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "deployment-service",
|
||||
"version": "1.0.0",
|
||||
"description": "Manage Fly.io Notebook API servers with Tigris storage",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "node src/index.js",
|
||||
"dev": "nodemon src/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"axios": "^1.4.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"aws-sdk": "^2.1420.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.22"
|
||||
}
|
||||
}
|
||||
19
deployment-service/snakeapi_service/dockerfile
Normal file
19
deployment-service/snakeapi_service/dockerfile
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
|
||||
# Install Jupyter, Flask, AWS CLI
|
||||
RUN pip install --no-cache-dir jupyter flask awscli
|
||||
|
||||
# Copy runner scripts and notebook (relative to this Dockerfile)
|
||||
COPY entrypoint.sh .
|
||||
COPY battlesnake_server.py .
|
||||
COPY notebook.ipynb .
|
||||
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
# Set and expose the port
|
||||
ENV PORT=3006
|
||||
EXPOSE 3006
|
||||
|
||||
# Start the notebook-to-Flask loop
|
||||
CMD ["./entrypoint.sh"]
|
||||
18
deployment-service/snakeapi_service/entrypoint.sh
Normal file
18
deployment-service/snakeapi_service/entrypoint.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
while true; do
|
||||
# Fetch the latest notebook from S3 (common bucket + prefix)
|
||||
aws --endpoint-url "$AWS_ENDPOINT_URL_S3" --region "$AWS_REGION" \
|
||||
s3 cp "s3://$BUCKET_NAME/$INSTANCE_PREFIX/notebook.ipynb" notebook.ipynb
|
||||
|
||||
# Execute all cells, including run_server(...) in the last cell
|
||||
jupyter nbconvert \
|
||||
--to notebook \
|
||||
--execute \
|
||||
--inplace \
|
||||
--ExecutePreprocessor.timeout=0 \
|
||||
notebook.ipynb
|
||||
|
||||
echo "Notebook executed; restarting..."
|
||||
sleep 1
|
||||
done
|
||||
363
deployment-service/snakeapi_service/notebook.ipynb
Normal file
363
deployment-service/snakeapi_service/notebook.ipynb
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "a9683468",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Defaulting to user installation because normal site-packages is not writeable\n",
|
||||
"Requirement already satisfied: flask in /home/student/.local/lib/python3.10/site-packages (2.3.2)\n",
|
||||
"Requirement already satisfied: Jinja2>=3.1.2 in /home/student/.local/lib/python3.10/site-packages (from flask) (3.1.4)\n",
|
||||
"Requirement already satisfied: itsdangerous>=2.1.2 in /home/student/.local/lib/python3.10/site-packages (from flask) (2.2.0)\n",
|
||||
"Requirement already satisfied: Werkzeug>=2.3.3 in /home/student/.local/lib/python3.10/site-packages (from flask) (3.0.3)\n",
|
||||
"Requirement already satisfied: click>=8.1.3 in /home/student/.local/lib/python3.10/site-packages (from flask) (8.1.7)\n",
|
||||
"Requirement already satisfied: blinker>=1.6.2 in /home/student/.local/lib/python3.10/site-packages (from flask) (1.8.2)\n",
|
||||
"Requirement already satisfied: MarkupSafe>=2.0 in /home/student/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!pip install flask"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "aeee415d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import logging\n",
|
||||
"import os\n",
|
||||
"import typing\n",
|
||||
"import flask\n",
|
||||
"\n",
|
||||
"from flask import Flask\n",
|
||||
"from flask import request\n",
|
||||
"\n",
|
||||
"print(\"Starting Battlesnake Server...\")\n",
|
||||
"\n",
|
||||
"def run_server(handlers: typing.Dict):\n",
|
||||
" app = Flask(__name__)\n",
|
||||
"\n",
|
||||
" @app.get(\"/\")\n",
|
||||
" def on_info():\n",
|
||||
" print(\"info\")\n",
|
||||
" return handlers[\"info\"]()\n",
|
||||
"\n",
|
||||
" @app.post(\"/start\")\n",
|
||||
" def on_start():\n",
|
||||
" print(\"go!\")\n",
|
||||
" game_state = request.get_json()\n",
|
||||
" handlers[\"start\"](game_state)\n",
|
||||
" return \"ok\"\n",
|
||||
"\n",
|
||||
" @app.post(\"/move\")\n",
|
||||
" def on_move():\n",
|
||||
" game_state = request.get_json()\n",
|
||||
" return handlers[\"move\"](game_state)\n",
|
||||
"\n",
|
||||
" @app.post(\"/end\")\n",
|
||||
" def on_end():\n",
|
||||
"# This isn't being called by the CLI atm\n",
|
||||
"# print(request.get_json())\n",
|
||||
"# end_game = request.get_json()\n",
|
||||
"# print(end_game[\"death\"])\n",
|
||||
" handlers[\"end\"](end_game)\n",
|
||||
" return \"ok\"\n",
|
||||
"\n",
|
||||
" @app.after_request\n",
|
||||
" def identify_server(response):\n",
|
||||
" response.headers.set(\n",
|
||||
" \"server\", \"battlesnake/github/starter-snake-python\"\n",
|
||||
" )\n",
|
||||
" return response\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" host = \"::\"\n",
|
||||
" port = int(os.environ.get(\"PORT\", \"8000\"))\n",
|
||||
"\n",
|
||||
" logging.getLogger(\"werkzeug\").setLevel(logging.ERROR)\n",
|
||||
"\n",
|
||||
" print(f\"\\nRunning Battlesnake at http://{host}:{port}\")\n",
|
||||
" app.run(host=host, port=port)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "11a8ff03",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Welcome to\n",
|
||||
"# __________ __ __ .__ __\n",
|
||||
"# \\______ \\_____ _/ |__/ |_| | ____ ______ ____ _____ | | __ ____\n",
|
||||
"# | | _/\\__ \\\\ __\\ __\\ | _/ __ \\ / ___// \\\\__ \\ | |/ // __ \\\n",
|
||||
"# | | \\ / __ \\| | | | | |_\\ ___/ \\___ \\| | \\/ __ \\| <\\ ___/\n",
|
||||
"# |________/(______/__| |__| |____/\\_____>______>___|__(______/__|__\\\\_____>\n",
|
||||
"#\n",
|
||||
"# This file can be a nice home for your Battlesnake logic and helper functions.\n",
|
||||
"#\n",
|
||||
"# To get you started we've included code to prevent your Battlesnake from moving backwards.\n",
|
||||
"# For more info see docs.battlesnake.com\n",
|
||||
"\n",
|
||||
"import random\n",
|
||||
"import typing\n",
|
||||
"\n",
|
||||
"# info is called when you create your Battlesnake on play.battlesnake.com\n",
|
||||
"# and controls your Battlesnake's appearance\n",
|
||||
"# TIP: If you open your Battlesnake URL in a browser you should see this data\n",
|
||||
"def info() -> typing.Dict:\n",
|
||||
" print(\"INFO\")\n",
|
||||
"#MMMM CUSTOMIZATIONS\n",
|
||||
" return {\n",
|
||||
" \"apiversion\": \"1\",\n",
|
||||
" \"author\": \"MaybeMadelyn\", # TODO: Your Battlesnake Username\n",
|
||||
" \"color\": \"#d5dbdb\", # TODO: Choose color\n",
|
||||
" \"head\": \"default\", # TODO: Choose head\n",
|
||||
" \"tail\": \"default\", # TODO: Choose tail\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# start is called when your Battlesnake begins a game\n",
|
||||
"def start(game_state: typing.Dict):\n",
|
||||
" global start_snake_count\n",
|
||||
" start_snake_count = len(game_state['board']['snakes'])\n",
|
||||
" print(\"GAME START with \", start_snake_count,\" snakes\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# end is called when your Battlesnake finishes a game\n",
|
||||
"# this is not happening right now :/\n",
|
||||
"def end(end_game: typing.Dict):\n",
|
||||
"# ending = end_game['death']\n",
|
||||
" print(\"GAME OVER. You died by: \",ending) \n",
|
||||
"\n",
|
||||
" \n",
|
||||
"def search(targets, my_head, move_points, points):\n",
|
||||
" if targets:\n",
|
||||
" closest = targets[0]\n",
|
||||
" distance = 100\n",
|
||||
" \n",
|
||||
" for target in targets:\n",
|
||||
" x_distance = abs(my_head[\"x\"] - target[\"x\"])\n",
|
||||
" y_distance = abs(my_head[\"y\"] - target[\"y\"])\n",
|
||||
" \n",
|
||||
" if x_distance + y_distance < distance:\n",
|
||||
" closest = target\n",
|
||||
" distance = x_distance + y_distance\n",
|
||||
" \n",
|
||||
" if closest[\"x\"] < my_head[\"x\"]:\n",
|
||||
" move_points[\"left\"] += points\n",
|
||||
" if closest[\"x\"] > my_head[\"x\"]:\n",
|
||||
" move_points[\"right\"] += points\n",
|
||||
" if closest[\"y\"] < my_head[\"y\"]:\n",
|
||||
" move_points[\"down\"] += points\n",
|
||||
" if closest[\"y\"] > my_head[\"y\"]:\n",
|
||||
" move_points[\"up\"] += points\n",
|
||||
" \n",
|
||||
" \n",
|
||||
"def search_head(targets, my_head, move_points, points):\n",
|
||||
" if targets:\n",
|
||||
" closest = targets[0]\n",
|
||||
" distance = 100\n",
|
||||
" \n",
|
||||
" for target in targets:\n",
|
||||
" x_distance = abs(my_head[\"x\"] - target[\"x\"])\n",
|
||||
" y_distance = abs(my_head[\"y\"] - target[\"y\"])\n",
|
||||
" \n",
|
||||
" if x_distance + y_distance < distance:\n",
|
||||
" closest = target\n",
|
||||
" distance = x_distance + y_distance\n",
|
||||
" \n",
|
||||
" if closest[\"x\"] < my_head[\"x\"]:\n",
|
||||
" move_points[\"left\"] += points\n",
|
||||
" if closest[\"x\"] > my_head[\"x\"]:\n",
|
||||
" move_points[\"right\"] += points\n",
|
||||
" if closest[\"y\"] < my_head[\"y\"]:\n",
|
||||
" move_points[\"down\"] += points\n",
|
||||
" if closest[\"y\"] > my_head[\"y\"]:\n",
|
||||
" move_points[\"up\"] += points\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" search(game_state[\"board\"][\"food\"], my_head, move_points, 20)\n",
|
||||
" search(game_state[\"board\"][\"snakes\"], my_head, move_points, 20) \n",
|
||||
"#mmmm FLOODFILL FUNCTION\n",
|
||||
"def floodfill(snakes,start):\n",
|
||||
" empty_spaces = 0\n",
|
||||
" visited = []\n",
|
||||
" to_visit = [start] \n",
|
||||
" unsafe = []\n",
|
||||
" for snake in snakes:\n",
|
||||
" for body_part in snake[\"body\"]:\n",
|
||||
" unsafe.append(body_part) #mmmm APPEND means adding something to the list\n",
|
||||
" in_bounds = True\n",
|
||||
" while to_visit: \n",
|
||||
" in_bounds = not(to_visit[0][\"y\"] < 0 or to_visit[0][\"y\"] > 10 or to_visit[0][\"x\"] < 0 or to_visit[0][\"x\"] > 10)\n",
|
||||
" if to_visit[0] not in unsafe and in_bounds and to_visit[0] not in visited:\n",
|
||||
" empty_spaces += 1\n",
|
||||
" to_visit.append({\"x\": to_visit[0][\"x\"] +1, \"y\": to_visit[0][\"y\"]})\n",
|
||||
" to_visit.append({\"x\": to_visit[0][\"x\"] -1, \"y\": to_visit[0][\"y\"]})\n",
|
||||
" to_visit.append({\"x\": to_visit[0][\"x\"], \"y\": to_visit[0][\"y\"] +1})\n",
|
||||
" to_visit.append({\"x\": to_visit[0][\"x\"], \"y\": to_visit[0][\"y\"] -1})\n",
|
||||
" \n",
|
||||
" visited.append(to_visit[0])\n",
|
||||
" to_visit.pop(0)\n",
|
||||
" \n",
|
||||
" return empty_spaces\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"# move is called on every turn and returns your next move\n",
|
||||
"# Valid moves are \"up\", \"down\", \"left\", or \"right\"\n",
|
||||
"# See https://docs.battlesnake.com/api/example-move for available data\n",
|
||||
"def move(game_state: typing.Dict) -> typing.Dict:\n",
|
||||
"\n",
|
||||
" move_points = {\"up\": 10, \"down\": 10, \"left\": 10, \"right\": 10}\n",
|
||||
" moves = [\"up\", \"down\", \"left\", \"right\"]\n",
|
||||
"\n",
|
||||
" # We've included code to prevent your Battlesnake from moving backwards\n",
|
||||
" my_head = game_state[\"you\"][\"body\"][0] # Coordinates of your head\n",
|
||||
" my_neck = game_state[\"you\"][\"body\"][1] # Coordinates of your \"neck\"\n",
|
||||
" my_body = game_state['you']['body']\n",
|
||||
"\n",
|
||||
" if my_neck[\"x\"] < my_head[\"x\"]: # Neck is left of head, don't move left\n",
|
||||
" move_points[\"left\"] = 0\n",
|
||||
"\n",
|
||||
" elif my_neck[\"x\"] > my_head[\"x\"]: # Neck is right of head, don't move right\n",
|
||||
" move_points[\"right\"] = 0\n",
|
||||
"\n",
|
||||
" elif my_neck[\"y\"] < my_head[\"y\"]: # Neck is below head, don't move down\n",
|
||||
" move_points[\"down\"] = 0\n",
|
||||
"\n",
|
||||
" elif my_neck[\"y\"] > my_head[\"y\"]: # Neck is above head, don't move up\n",
|
||||
" move_points[\"up\"] = 0\n",
|
||||
"\n",
|
||||
" #MMMM MAKE SURE YOU DONT HIT THE WALL\n",
|
||||
"\n",
|
||||
" game_height = game_state[\"board\"][\"height\"]\n",
|
||||
" game_width = game_state[\"board\"][\"width\"]\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" if my_head[\"y\"] == game_height - 1:\n",
|
||||
" move_points[\"up\"] -= 1000\n",
|
||||
" \n",
|
||||
" if my_head[\"y\"] == 0:\n",
|
||||
" move_points[\"down\"] -= 1000\n",
|
||||
" \n",
|
||||
" if my_head[\"x\"] == game_width - 1:\n",
|
||||
" move_points[\"right\"] -= 1000\n",
|
||||
" print(\"DONT GO RIGHT!\")\n",
|
||||
" print(f\"Right points: {move_points['right']}\")\n",
|
||||
"\n",
|
||||
" if my_head[\"x\"] == 0:\n",
|
||||
" move_points[\"left\"] -= 1000\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" #MMMM DONT RUN INTO YOURSELF\n",
|
||||
" \n",
|
||||
" for body_part in my_body:\n",
|
||||
" if body_part[\"y\"] == my_head[\"y\"]+1 and body_part[\"x\"] == my_head[\"x\"]:\n",
|
||||
" move_points[\"up\"] -= 1000\n",
|
||||
" \n",
|
||||
" if body_part[\"y\"] == my_head[\"y\"]-1 and body_part[\"x\"] == my_head[\"x\"]:\n",
|
||||
" move_points[\"down\"] -= 1000\n",
|
||||
" \n",
|
||||
" if body_part[\"x\"] == my_head[\"x\"]+1 and body_part[\"y\"] == my_head[\"y\"]:\n",
|
||||
" move_points[\"right\"] -= 1000\n",
|
||||
" \n",
|
||||
" if body_part[\"x\"] == my_head[\"x\"]-1 and body_part[\"y\"] == my_head[\"y\"]:\n",
|
||||
" move_points[\"left\"] -= 1000\n",
|
||||
" \n",
|
||||
" #MMMM DONT RUN INTO OTHER SNAKES\n",
|
||||
" \n",
|
||||
" all_snakes = game_state[\"board\"][\"snakes\"]\n",
|
||||
" for snakes in all_snakes:\n",
|
||||
" for body_part in snakes[\"body\"]:\n",
|
||||
" if body_part[\"y\"] == my_head[\"y\"]+1 and body_part[\"x\"] == my_head[\"x\"]:\n",
|
||||
" move_points[\"up\"] -= 1000\n",
|
||||
"\n",
|
||||
" if body_part[\"y\"] == my_head[\"y\"]-1 and body_part[\"x\"] == my_head[\"x\"]:\n",
|
||||
" move_points[\"down\"] -= 1000\n",
|
||||
"\n",
|
||||
" if body_part[\"x\"] == my_head[\"x\"]+1 and body_part[\"y\"] == my_head[\"y\"]:\n",
|
||||
" move_points[\"right\"] -= 1000\n",
|
||||
"\n",
|
||||
" if body_part[\"x\"] == my_head[\"x\"]-1 and body_part[\"y\"] == my_head[\"y\"]:\n",
|
||||
" move_points[\"left\"] -= 1000\n",
|
||||
" \n",
|
||||
" move_points[\"right\"] += floodfill(all_snakes, {\"x\": my_head[\"x\"] +1, \"y\": my_head[\"y\"]})\n",
|
||||
" move_points[\"left\"] += floodfill(all_snakes, {\"x\": my_head[\"x\"] -1, \"y\": my_head[\"y\"]})\n",
|
||||
" move_points[\"up\"] += floodfill(all_snakes, {\"x\": my_head[\"x\"] , \"y\": my_head[\"y\"] +1})\n",
|
||||
" move_points[\"down\"] += floodfill(all_snakes, {\"x\": my_head[\"x\"] , \"y\": my_head[\"y\"] -1})\n",
|
||||
"\n",
|
||||
" \n",
|
||||
" #mmmm EAT SOME FOOD \n",
|
||||
" #bfs\n",
|
||||
" \n",
|
||||
" # Compare all of the moves to create a list of all moves with the highest score\n",
|
||||
" print(f\"Right points: {move_points['right']}\")\n",
|
||||
" best_moves = []\n",
|
||||
" for move, points in move_points.items():\n",
|
||||
" if len(best_moves) == 0:\n",
|
||||
" best_moves.append(move)\n",
|
||||
" else:\n",
|
||||
" for good_move in best_moves:\n",
|
||||
" if points > move_points[good_move]:\n",
|
||||
" best_moves.clear()\n",
|
||||
" best_moves.append(move)\n",
|
||||
" elif points == move_points[good_move]:\n",
|
||||
" best_moves.append(move)\n",
|
||||
" break\n",
|
||||
" \n",
|
||||
" # Choose a random move from the best choices\n",
|
||||
" print(f\"Right points: {move_points['right']}\")\n",
|
||||
" next_move = random.choice(best_moves)\n",
|
||||
" # TODO: Step 4 - Move towards food instead of random, to regain health and survive longer\n",
|
||||
" # food = game_state['board']['food']\n",
|
||||
"\n",
|
||||
" print(f\"MOVE {game_state['turn']}: {next_move}\")\n",
|
||||
" print(f\"MOVE {game_state['turn']} POINTS: UP - {move_points['up']}, DOWN - {move_points['down']}, LEFT - {move_points['left']}, RIGHT - {move_points['right']}\")\n",
|
||||
" return {\"move\": next_move}\n",
|
||||
"\n",
|
||||
"run_server({\"info\": info, \"start\": start, \"move\": move, \"end\": end})\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "845e510c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
36
deployment-service/snakeapi_service/snakeapi_server.py
Normal file
36
deployment-service/snakeapi_service/snakeapi_server.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import os
|
||||
import logging
|
||||
import typing
|
||||
from flask import Flask, request
|
||||
from flask_cors import CORS
|
||||
|
||||
def run_server(handlers: typing.Dict):
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
@app.get("/")
|
||||
def on_info():
|
||||
return handlers["info"]()
|
||||
|
||||
@app.post("/start")
|
||||
def on_start():
|
||||
handlers["start"](request.get_json())
|
||||
return "ok"
|
||||
|
||||
@app.post("/move")
|
||||
def on_move():
|
||||
return handlers["move"](request.get_json())
|
||||
|
||||
@app.post("/end")
|
||||
def on_end():
|
||||
handlers["end"](request.get_json())
|
||||
return "ok"
|
||||
|
||||
@app.after_request
|
||||
def identify_server(response):
|
||||
response.headers["server"] = "battlesnake/github/starter-snake-python"
|
||||
return response
|
||||
|
||||
port = int(os.environ.get("PORT", "3006"))
|
||||
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||
app.run(host="0.0.0.0", port=port)
|
||||
105
deployment-service/src/index.js
Normal file
105
deployment-service/src/index.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const axios = require('axios');
|
||||
const AWS = require('aws-sdk');
|
||||
|
||||
const {
|
||||
FLY_API_TOKEN,
|
||||
FLY_ORG,
|
||||
CONTAINER_IMAGE,
|
||||
COMMON_BUCKET,
|
||||
AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY,
|
||||
AWS_ENDPOINT_URL_S3,
|
||||
AWS_REGION
|
||||
} = process.env;
|
||||
|
||||
if (
|
||||
!FLY_API_TOKEN ||
|
||||
!FLY_ORG ||
|
||||
!CONTAINER_IMAGE ||
|
||||
!COMMON_BUCKET ||
|
||||
!AWS_ACCESS_KEY_ID ||
|
||||
!AWS_SECRET_ACCESS_KEY ||
|
||||
!AWS_ENDPOINT_URL_S3 ||
|
||||
!AWS_REGION
|
||||
) {
|
||||
console.error('Missing required environment variables.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function createFlyClient() {
|
||||
const response = await axios.post(
|
||||
'https://api.fly.io/api/v1/cli_sessions',
|
||||
null,
|
||||
{ headers: { Authorization: `Bearer ${FLY_API_TOKEN}` } }
|
||||
);
|
||||
return axios.create({
|
||||
baseURL: 'https://api.machines.dev/v1',
|
||||
headers: {
|
||||
Authorization: `Bearer ${response.data.token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const s3 = new AWS.S3({
|
||||
endpoint: AWS_ENDPOINT_URL_S3,
|
||||
region: AWS_REGION,
|
||||
credentials: new AWS.Credentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY),
|
||||
s3ForcePathStyle: true
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const fly = await createFlyClient();
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
app.post('/servers', async (req, res) => {
|
||||
const { appName, region = 'sea' } = req.body;
|
||||
try {
|
||||
await fly.post('/apps', { name: appName, org_slug: FLY_ORG, primary_region: region });
|
||||
await fly.post(
|
||||
`/apps/${appName}/extensions`,
|
||||
{ name: 'tigris_object_storage', settings: { bucket_name: COMMON_BUCKET } }
|
||||
);
|
||||
await fly.post(
|
||||
`/apps/${appName}/secrets`,
|
||||
{ secrets: { INSTANCE_PREFIX: appName, BUCKET_NAME: COMMON_BUCKET } }
|
||||
);
|
||||
await fly.post(`/apps/${appName}/deploys`, { image: CONTAINER_IMAGE });
|
||||
res.json({ status: 'created', app: appName, url: `https://${appName}.fly.dev` });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.response?.data || error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/servers/:name/notebook', async (req, res) => {
|
||||
const name = req.params.name;
|
||||
try {
|
||||
await s3.putObject({
|
||||
Bucket: COMMON_BUCKET,
|
||||
Key: `${name}/notebook.ipynb`,
|
||||
Body: JSON.stringify(req.body),
|
||||
ContentType: 'application/json'
|
||||
}).promise();
|
||||
await fly.post(`/apps/${name}/deploys`, { image: CONTAINER_IMAGE });
|
||||
res.json({ status: 'updated', app: name });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/servers/:name', async (req, res) => {
|
||||
const name = req.params.name;
|
||||
try {
|
||||
await fly.delete(`/apps/${name}`);
|
||||
res.json({ status: 'deleted', app: name });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.response?.data || error.message });
|
||||
}
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3006;
|
||||
app.listen(port, () => console.log(`Listening on port ${port}`));
|
||||
})();
|
||||
BIN
deployment-service/user.token
Normal file
BIN
deployment-service/user.token
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue