feat: Initial commit of Clutch-IQ project

This commit is contained in:
xunyulin230420
2026-02-05 23:26:03 +08:00
commit a355239861
66 changed files with 12922 additions and 0 deletions

9
tests/README.md Normal file
View File

@@ -0,0 +1,9 @@
# tests/
面向脚本执行的验证用例集合(以 `test_*.py` 为主)。
## 常用用法
- 本地启动推理服务后,运行 `test_inference_client.py` 验证接口联通与返回结构。
- 其余脚本用于验证特征计算与推理流程的基本正确性。

View File

@@ -0,0 +1,53 @@
import requests
import json
# URL of the local inference service
url = "http://127.0.0.1:5000/predict"
# Scenario: 2v2 Clutch
# T side: 2 players, low cash, AK47s
# CT side: 2 players, high cash, M4A1s + Defuser
# Spatial: T grouped (spread low), CT spread out (spread high)
payload = {
"game_time": 90.0,
"is_bomb_planted": 1,
"site": 401, # Example site ID
"players": [
# T Players (Team 2)
{
"team_num": 2, "is_alive": True, "health": 100,
"X": -1000, "Y": 2000, "Z": 0,
"active_weapon_name": "ak47", "balance": 1500, "armor_value": 100, "has_helmet": True,
"rating": 1.05
},
{
"team_num": 2, "is_alive": True, "health": 100,
"X": -1050, "Y": 2050, "Z": 0,
"active_weapon_name": "ak47", "balance": 2000, "armor_value": 100, "has_helmet": True,
"rating": 0.95
},
# CT Players (Team 3)
{
"team_num": 3, "is_alive": True, "health": 100,
"X": 0, "Y": 0, "Z": 0,
"active_weapon_name": "m4a1", "balance": 5000, "armor_value": 100, "has_helmet": True, "has_defuser": True,
"rating": 1.10
},
{
"team_num": 3, "is_alive": True, "health": 100,
"X": -2000, "Y": 3000, "Z": 0,
"active_weapon_name": "awp", "balance": 4750, "armor_value": 100, "has_helmet": True,
"rating": 1.20
}
]
}
print(f"Sending payload to {url}...")
try:
response = requests.post(url, json=payload)
print(f"Status Code: {response.status_code}")
print("Response JSON:")
print(json.dumps(response.json(), indent=2))
except Exception as e:
print(f"Request failed: {e}")

61
tests/test_inference.py Normal file
View File

@@ -0,0 +1,61 @@
import requests
import json
import time
import subprocess
import sys
# Start API in background
print("Starting API...")
api_process = subprocess.Popen([sys.executable, "src/inference/app.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Wait for startup
time.sleep(5)
url = "http://localhost:5000/predict"
# Test Case 1: CT Advantage (3v1, high health)
payload_ct_win = {
"game_time": 60.0,
"players": [
{"team_num": 3, "is_alive": True, "health": 100},
{"team_num": 3, "is_alive": True, "health": 100},
{"team_num": 3, "is_alive": True, "health": 90},
{"team_num": 2, "is_alive": True, "health": 50}
]
}
# Test Case 2: T Advantage (1v3)
payload_t_win = {
"game_time": 45.0,
"players": [
{"team_num": 3, "is_alive": True, "health": 10},
{"team_num": 2, "is_alive": True, "health": 100},
{"team_num": 2, "is_alive": True, "health": 100},
{"team_num": 2, "is_alive": True, "health": 100}
]
}
def test_payload(name, payload):
print(f"\n--- Testing {name} ---")
try:
response = requests.post(url, json=payload, timeout=2)
print("Status Code:", response.status_code)
if response.status_code == 200:
print("Response:", json.dumps(response.json(), indent=2))
else:
print("Error:", response.text)
except Exception as e:
print(f"Request failed: {e}")
try:
test_payload("CT Advantage Scenario", payload_ct_win)
test_payload("T Advantage Scenario", payload_t_win)
finally:
print("\nStopping API...")
api_process.terminate()
try:
outs, errs = api_process.communicate(timeout=2)
print("API Output:", outs.decode())
print("API Errors:", errs.decode())
except:
api_process.kill()

View File

@@ -0,0 +1,45 @@
import requests
import json
import time
url = "http://localhost:5000/predict"
# Test Case 1: CT Advantage (3v1, high health)
payload_ct_win = {
"game_time": 60.0,
"players": [
{"team_num": 3, "is_alive": True, "health": 100},
{"team_num": 3, "is_alive": True, "health": 100},
{"team_num": 3, "is_alive": True, "health": 90},
{"team_num": 2, "is_alive": True, "health": 50}
]
}
# Test Case 2: T Advantage (1v3)
payload_t_win = {
"game_time": 45.0,
"players": [
{"team_num": 3, "is_alive": True, "health": 10},
{"team_num": 2, "is_alive": True, "health": 100},
{"team_num": 2, "is_alive": True, "health": 100},
{"team_num": 2, "is_alive": True, "health": 100}
]
}
def test_payload(name, payload):
print(f"\n--- Testing {name} ---")
try:
response = requests.post(url, json=payload, timeout=2)
print("Status Code:", response.status_code)
if response.status_code == 200:
print("Response:", json.dumps(response.json(), indent=2))
else:
print("Error:", response.text)
except Exception as e:
print(f"Request failed: {e}")
if __name__ == "__main__":
# Wait a bit to ensure server is ready if run immediately after start
time.sleep(1)
test_payload("CT Advantage Scenario", payload_ct_win)
test_payload("T Advantage Scenario", payload_t_win)

View File

@@ -0,0 +1,44 @@
import requests
import json
import time
url = "http://localhost:5000/predict"
# Scenario: 2v2 Clutch
# T Team: Together (Planting B site?)
# CT Team: Separated (Retaking?)
payload_spatial = {
"game_time": 90.0,
"players": [
# T Team (Team 2) - Clumped together
{"team_num": 2, "is_alive": True, "health": 100, "X": -1000, "Y": 2000, "Z": 0},
{"team_num": 2, "is_alive": True, "health": 100, "X": -1050, "Y": 2050, "Z": 0},
# CT Team (Team 3) - Far apart (Retaking from different angles)
{"team_num": 3, "is_alive": True, "health": 100, "X": 0, "Y": 0, "Z": 0}, # Mid
{"team_num": 3, "is_alive": True, "health": 100, "X": -2000, "Y": 3000, "Z": 0} # Flanking
]
}
def test_payload(name, payload):
print(f"\n--- Testing {name} ---")
try:
response = requests.post(url, json=payload, timeout=2)
print("Status Code:", response.status_code)
if response.status_code == 200:
data = response.json()
print("Response Prediction:", data['prediction'])
print("Win Probability:", json.dumps(data['win_probability'], indent=2))
print("Spatial Features Calculated:")
feats = data['features_used']
print(f" Team Distance: {feats.get('team_distance', 'N/A'):.2f}")
print(f" T Spread: {feats.get('t_spread', 'N/A'):.2f}")
print(f" CT Spread: {feats.get('ct_spread', 'N/A'):.2f}")
else:
print("Error:", response.text)
except Exception as e:
print(f"Request failed: {e}")
if __name__ == "__main__":
test_payload("Spatial 2v2 Scenario", payload_spatial)