-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathsample_agent.py
More file actions
154 lines (122 loc) · 4.72 KB
/
Copy pathsample_agent.py
File metadata and controls
154 lines (122 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""
Sample agent for Case Closed Challenge - Works with Judge Protocol
This agent runs as a Flask server and responds to judge requests.
"""
import os
from flask import Flask, request, jsonify
from collections import deque
app = Flask(__name__)
# Basic identity
PARTICIPANT = os.getenv("PARTICIPANT", "SampleParticipant")
AGENT_NAME = os.getenv("AGENT_NAME", "SampleAgent")
# Track game state
game_state = {
"board": None,
"agent1_trail": [],
"agent2_trail": [],
"agent1_length": 0,
"agent2_length": 0,
"agent1_alive": True,
"agent2_alive": True,
"agent1_boosts": 3,
"agent2_boosts": 3,
"turn_count": 0,
"player_number": 1,
}
@app.route("/", methods=["GET"])
def info():
"""Basic health/info endpoint used by the judge to check connectivity."""
return jsonify({"participant": PARTICIPANT, "agent_name": AGENT_NAME}), 200
@app.route("/send-state", methods=["POST"])
def receive_state():
"""Judge calls this to push the current game state to the agent server."""
data = request.get_json()
if not data:
return jsonify({"error": "no json body"}), 400
# Update our local game state
game_state.update(data)
return jsonify({"status": "state received"}), 200
@app.route("/send-move", methods=["GET"])
def send_move():
"""Judge calls this (GET) to request the agent's move for the current tick.
Return format: {"move": "DIRECTION"} or {"move": "DIRECTION:BOOST"}
"""
player_number = request.args.get("player_number", default=1, type=int)
turn_count = game_state.get("turn_count", 0)
# Get our current state
if player_number == 1:
my_trail = game_state.get("agent1_trail", [])
my_boosts = game_state.get("agent1_boosts", 3)
other_trail = game_state.get("agent2_trail", [])
else:
my_trail = game_state.get("agent2_trail", [])
my_boosts = game_state.get("agent2_boosts", 3)
other_trail = game_state.get("agent1_trail", [])
# Simple decision logic
move = decide_move(my_trail, other_trail, turn_count, my_boosts)
return jsonify({"move": move}), 200
@app.route("/end", methods=["POST"])
def end_game():
"""Judge notifies agent that the match finished and provides final state."""
data = request.get_json()
if data:
result = data.get("result", "UNKNOWN")
print(f"\nGame Over! Result: {result}")
return jsonify({"status": "acknowledged"}), 200
def decide_move(my_trail, other_trail, turn_count, my_boosts):
"""Simple decision logic for the agent.
Strategy:
- Move in a direction that doesn't immediately hit a trail
- Use boost if we have them and it's mid-game (turns 30-80)
"""
if not my_trail:
return "RIGHT"
# Get current head position and direction
head = my_trail[-1] if my_trail else (0, 0)
# Calculate current direction if we have at least 2 positions
current_dir = "RIGHT"
if len(my_trail) >= 2:
prev = my_trail[-2]
dx = head[0] - prev[0]
dy = head[1] - prev[1]
# Normalize for torus wrapping
if abs(dx) > 1:
dx = -1 if dx > 0 else 1
if abs(dy) > 1:
dy = -1 if dy > 0 else 1
if dx == 1:
current_dir = "RIGHT"
elif dx == -1:
current_dir = "LEFT"
elif dy == 1:
current_dir = "DOWN"
elif dy == -1:
current_dir = "UP"
# Simple strategy: try to avoid trails, prefer continuing straight
# Check available directions (not opposite to current)
directions = ["UP", "DOWN", "LEFT", "RIGHT"]
opposite = {"UP": "DOWN", "DOWN": "UP", "LEFT": "RIGHT", "RIGHT": "LEFT"}
# Remove opposite direction
if current_dir in opposite:
try:
directions.remove(opposite[current_dir])
except ValueError:
pass
# Prefer current direction if still available
if current_dir in directions:
chosen_dir = current_dir
else:
# Pick first available
chosen_dir = directions[0] if directions else "RIGHT"
# Decide whether to use boost
# Use boost in mid-game when we still have them
use_boost = my_boosts > 0 and 30 <= turn_count <= 80
if use_boost:
return f"{chosen_dir}:BOOST"
else:
return chosen_dir
if __name__ == "__main__":
# For development only. Port can be overridden with the PORT env var.
port = int(os.environ.get("PORT", "5009"))
print(f"Starting {AGENT_NAME} ({PARTICIPANT}) on port {port}...")
app.run(host="0.0.0.0", port=port, debug=False)