j124 spinner claim: add ConstantSpinner adversary + spinner A/B arms, panel, analyzer
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "ConstantSpinner",
|
||||
"version": "0.1.0",
|
||||
"authors": ["Davide Cappellini"],
|
||||
"description": "True constant-turn spinner adversary — constant speed + constant body turn rate, head-on firing",
|
||||
"homepage": "",
|
||||
"countryCodes": ["IT"],
|
||||
"gameTypes": ["classic", "1v1"],
|
||||
"platform": "Nim",
|
||||
"programmingLang": "Nim"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Package
|
||||
version = "0.1.0"
|
||||
author = "Davide Cappellini"
|
||||
description = "True constant-turn spinner adversary — constant speed + constant body turn rate"
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
bin = @["ConstantSpinner"]
|
||||
|
||||
# Dependencies
|
||||
requires "nim >= 2.0.0"
|
||||
requires "robocode_tankroyale_botapi >= 1.0.7"
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
exec ./out/ConstantSpinner 2>> /tmp/constantspinner_stderr.log
|
||||
@@ -0,0 +1,2 @@
|
||||
--path:"../../.."
|
||||
switch("outdir", "out")
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# make_variant.sh — generate a spin-rate preset of the ConstantSpinner adversary.
|
||||
#
|
||||
# make_variant.sh <NAME> <TURN_RATE_DEG_PER_TICK> <SPEED> [OUTDIR]
|
||||
#
|
||||
# e.g.
|
||||
# make_variant.sh ConstantSpinner_s3 3 5 /tmp/tr_spinners
|
||||
# make_variant.sh ConstantSpinner_s6 6 5 /tmp/tr_spinners
|
||||
#
|
||||
# The variant dir is a normal Tank Royale bot directory named <NAME>: the runner
|
||||
# matches the connecting bot's identity against <OUTDIR>/<NAME>/<NAME>.json, so
|
||||
# the launcher exports BOT_JSON to make the binary handshake under <NAME>.
|
||||
#
|
||||
# The single committed ConstantSpinner binary is reused (no per-variant build);
|
||||
# only SPIN_TURN_RATE / SPIN_SPEED differ.
|
||||
set -euo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BIN="$HERE/out/ConstantSpinner"
|
||||
|
||||
NAME="${1:?usage: make_variant.sh <NAME> <TURN_RATE> <SPEED> [OUTDIR]}"
|
||||
TURN="${2:?usage: make_variant.sh <NAME> <TURN_RATE> <SPEED> [OUTDIR]}"
|
||||
SPEED="${3:?usage: make_variant.sh <NAME> <TURN_RATE> <SPEED> [OUTDIR]}"
|
||||
OUTDIR="${4:-/tmp/tr_spinners}"
|
||||
|
||||
[[ -x "$BIN" ]] || { echo "ERROR: build first: nim c -d:release src/ConstantSpinner.nim" >&2; exit 1; }
|
||||
|
||||
DIR="$OUTDIR/$NAME"
|
||||
mkdir -p "$DIR"
|
||||
|
||||
cat > "$DIR/$NAME.json" <<JSON
|
||||
{
|
||||
"name": "$NAME",
|
||||
"version": "0.1.0",
|
||||
"authors": ["Davide Cappellini"],
|
||||
"description": "ConstantSpinner preset: constant turn ${TURN} deg/tick at speed ${SPEED}",
|
||||
"homepage": "",
|
||||
"countryCodes": ["IT"],
|
||||
"gameTypes": ["classic", "1v1"],
|
||||
"platform": "Nim",
|
||||
"programmingLang": "Nim"
|
||||
}
|
||||
JSON
|
||||
|
||||
cat > "$DIR/$NAME.sh" <<SH
|
||||
#!/bin/sh
|
||||
cd "\$(dirname "\$0")"
|
||||
export BOT_JSON="$DIR/$NAME.json"
|
||||
export SPIN_TURN_RATE="$TURN"
|
||||
export SPIN_SPEED="$SPEED"
|
||||
exec "$BIN" 2>> /tmp/constantspinner_stderr.log
|
||||
SH
|
||||
chmod +x "$DIR/$NAME.sh"
|
||||
|
||||
echo "generated $DIR (turn=$TURN deg/tick, speed=$SPEED)"
|
||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "ConstantSpinner",
|
||||
"version": "0.1.0",
|
||||
"authors": ["Davide Cappellini"],
|
||||
"description": "True constant-turn spinner adversary — constant speed + constant body turn rate, head-on firing",
|
||||
"homepage": "",
|
||||
"countryCodes": ["IT"],
|
||||
"gameTypes": ["classic", "1v1"],
|
||||
"platform": "Nim",
|
||||
"programmingLang": "Nim"
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# ConstantSpinner — a TRUE constant-turn spinner.
|
||||
#
|
||||
# Purpose: the legacy roster has no constant-turn spinner. `SpinBot` (the sample
|
||||
# bot) and the in-repo movers are periodic/oscillating, but this fixture is the
|
||||
# degenerate case the owner's gun claim is really about: a body that moves at a
|
||||
# CONSTANT forward speed while turning at a CONSTANT rate, forever. Its path is a
|
||||
# perfect circle of radius v / omega, i.e. a single-frequency, low-entropy,
|
||||
# perfectly periodic movement — the easiest possible target for a fast-adapting
|
||||
# or pattern-matching gun.
|
||||
#
|
||||
# It is a MOVEMENT FIXTURE, not a fighter: the gun is plain head-on, fired at low
|
||||
# power when the turret happens to be on target. No adaptation, no randomness, no
|
||||
# wall logic (the server's wall collision is the only non-periodic perturbation).
|
||||
#
|
||||
# Env (read at round start, so one binary serves every spin rate):
|
||||
# SPIN_TURN_RATE body turn rate, deg/tick (default 6.0)
|
||||
# SPIN_SPEED forward speed, units/tick (default 5.0)
|
||||
# SPIN_FIRE_POWER bullet power (default 1.0)
|
||||
#
|
||||
# The requested turn rate is clamped to the maximum reachable at the target
|
||||
# speed (`MAX_TURN_RATE - 0.75 * speed`), so the ACTUAL turn rate stays exactly
|
||||
# constant instead of being server-clamped while the bot accelerates or slides
|
||||
# along a wall.
|
||||
|
||||
import std/[math, os, strutils]
|
||||
import robocode_tankroyale_botapi
|
||||
|
||||
# The identity JSON. A generated spin-rate variant directory (see
|
||||
# `make_variant.sh`) sets BOT_JSON so the bot handshakes under the variant's own
|
||||
# name, which is what the Tank Royale runner matches against `<dir>/<dir>.json`.
|
||||
const defaultBotJsonPath = currentSourcePath().parentDir / "ConstantSpinner.json"
|
||||
|
||||
proc envFloat(name: string, default: float): float =
|
||||
let v = getEnv(name)
|
||||
if v.len == 0: return default
|
||||
try:
|
||||
parseFloat(v)
|
||||
except ValueError:
|
||||
default
|
||||
|
||||
type ConstantSpinner = ref object of Bot
|
||||
turnRate: float
|
||||
speed: float
|
||||
firePower: float
|
||||
|
||||
method onRoundStarted*(bot: ConstantSpinner, e: RoundStartedEvent) =
|
||||
# Gun and radar stay independent of the (continuously turning) body.
|
||||
setAdjustGunForBodyTurn(true)
|
||||
setAdjustRadarForBodyTurn(true)
|
||||
setAdjustRadarForGunTurn(true)
|
||||
|
||||
bot.speed = envFloat("SPIN_SPEED", 5.0)
|
||||
bot.turnRate = envFloat("SPIN_TURN_RATE", 6.0)
|
||||
bot.firePower = envFloat("SPIN_FIRE_POWER", 1.0)
|
||||
|
||||
# A turn rate above the speed-dependent maximum would be clamped by the
|
||||
# server, making the rate non-constant during acceleration / wall slides.
|
||||
let maxAtSpeed = MAX_TURN_RATE - 0.75 * abs(bot.speed)
|
||||
if bot.turnRate > maxAtSpeed:
|
||||
bot.turnRate = maxAtSpeed
|
||||
if bot.turnRate < 0.0:
|
||||
bot.turnRate = 0.0
|
||||
|
||||
method onScannedBot*(bot: ConstantSpinner, e: ScannedBotEvent) =
|
||||
# Head-on: aim at where the enemy is right now, fire low power whenever the
|
||||
# gun is cool. It is a movement fixture, so the shot goes wherever the turret
|
||||
# happens to point; the radar sweep keeps scans coming.
|
||||
let bearing = directionTo(getX(), getY(), e.x, e.y)
|
||||
let gunDelta = normalizeRelativeAngle(bearing - getGunDirection())
|
||||
setGunTurnRate(gunDelta.clamp(-MAX_GUN_TURN_RATE, MAX_GUN_TURN_RATE))
|
||||
if getGunHeat() <= 0.0:
|
||||
discard setFire(bot.firePower)
|
||||
|
||||
method run*(bot: ConstantSpinner) =
|
||||
while isRunning():
|
||||
# The whole movement: constant turn, constant speed. Nothing else.
|
||||
setRadarTurnRate(45.0)
|
||||
setTurnRate(bot.turnRate)
|
||||
setTargetSpeed(bot.speed)
|
||||
go()
|
||||
|
||||
when isMainModule:
|
||||
var bot = ConstantSpinner()
|
||||
let jsonPath = getEnv("BOT_JSON", defaultBotJsonPath)
|
||||
start(bot, jsonPath)
|
||||
Reference in New Issue
Block a user