A passion project for Graphwar — turn what you see on the battlefield into a paste-ready mathematical function.
GraphBot watches the game field and builds Graphwar-compatible formulas. The recommended workflow is the local web UI (approximator_server.py) — Click mode (default), Draw mode with five approximation methods, and an animated genetic Dot mode. GraphBot.py still offers click mode (OpenCV overlay) and an automatic mode prototype that is not production-ready yet.
Project status: web UI click + draw = ready to use · Dot mode = first playable GA version ·
GraphBot.pyauto mode = work in progress (see Auto mode)
How GraphBot touches Graphwar: GraphBot is an external helper — it does not modify game files, inject into the game process, read game memory, or automate gameplay. The only direct interaction with the Graphwar window is moving it to a fixed corner so screen capture aligns with the configured field region. Everything else is: screenshot → math → copy a formula to your clipboard. You paste it into Graphwar yourself.
graphbot_kroshechka_github.mp4
- What is Graphwar?
- Getting started
- Web UI (recommended)
- How GraphBot works
- Click mode
- Draw mode
- Dot mode
- Auto mode (work in progress)
- Project layout
- Roadmap
- More to come
- Feedback & issues
- License
Graphwar is an artillery game on a Cartesian plane. You type a function; the game fires along that curve (with a vertical shift so the shot passes through your soldier). Hit enemies, avoid teammates and black obstacle circles.
GraphBot does not replace the game — it helps you derive functions faster. See GAME_RULES.md for full Graphwar rules and syntax.
Field limits (approx.): x ∈ [-25, 25], y ∈ [-15, 15].
| Requirement | Notes |
|---|---|
| Windows | Screen capture and window APIs are Win32-specific (pywin32). |
| Python 3.10+ | Tested with dependencies in requirements.txt. |
| Graphwar | Window title must be Graphwar. Keep it visible while the bot runs. |
git clone https://github.com/KroSheChKa/GraphBot.git
cd GraphBot
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txtLegacy / alternative entry point — click mode on the live game window with F-keys:
python GraphBot.py- Choose
1(click mode) or0(auto — experimental). - Press F1 to start, F2 to quit.
- In click mode: F3 start recording clicks, F4 finish.
See Click mode → GraphBot.py for how formula building differs from the web UI.
Tip: If detection looks wrong, tune capture with python tools/preview_capture.py and calibration tools under tools/.
The main tool is a local p5.js app served by Python:
python tools/approximator_server.pyThe server automatically opens http://127.0.0.1:8765/ in your default browser.
| Mode | What it does |
|---|---|
| 1. Click mode (default) | Place waypoints on the canvas; get a piecewise direct_line formula |
| 2. Draw mode | Sketch a curve, resample to a dataset, approximate with 5 methods |
| 3. Dot mode | Place the active soldier and unordered enemies; evolve a population of left-to-right trajectories |
| Action | Control |
|---|---|
| Capture Graphwar field as background | Capture field (Graphwar must be running; window moved to corner for alignment) |
| Active player source | Auto-detect active player toggle; disable it to place A manually |
| Clear current path / stroke | C (background screenshot stays) |
| Copy formula | Copy y |
| Reset sliders & canvas state | Reset |
After «Capture field»: any previous clicks, drawn curve, or Dot-mode population are cleared automatically — you start fresh on the new screenshot. With Auto-detect active player enabled, GraphBot finds the stable yellow body of each sprite and then matches the circular red active marker around it; red name outlines are not used as the anchor. The result is shown as A and seeds Click/Dot mode. In Draw mode the stroke starts only where you first touch the canvas; A never creates a line to that first touch. If A falls inside the drawn x-range, it receives a soft training weight; otherwise it is ignored for that stroke. Disable the toggle to hide the automatic marker and place A manually. The marker is not draggable, so drawing over it remains an ordinary canvas action.
Every successful Capture field also stores the clean raw field crop as a lossless PNG in the local, Git-ignored folder data/field_captures/. The archive is written before detection and contains no points, formulas, trajectories, masks, or UI overlays. These files are reserved for later regression tests and detector tuning.
Run python tools/preview_capture.py, adjust left, top, right, and bottom until the grid follows the playable area, then press s to save config/capture_config.json. The preview shows x=-25,0,25, y=15,0,-15, field size, and game-units-per-pixel.
For the active player, run python tools/calibrate_active.py. Its separate ×10 ROI window shows the refined center (green), Hough source center (yellow), and red-ring estimate (magenta). The web capture now primarily uses a yellow-body candidate plus a circular red-ring match; the old red-glow path remains a fallback for unusual frames. Press d to save outputs/active_debug.png. The ±0.05 game-unit value is a review threshold, not extra information created by enlargement; uncertainty remains limited by the original screenshot.
| Action | Control |
|---|---|
| Place active soldier | 1st click — purple marker A |
| Place targets | 2nd, 3rd… clicks — orange markers 2, 3… |
| Undo last click | Right-click, Backspace, or Undo last click |
Formula output: expression only, no y= prefix — paste into Graphwar as-is.
Switch to 2. Draw mode in the side panel, then pick an approximation method:
| Method | Idea |
|---|---|
| 2.1 Linear (segments) | Exact piecewise lines through the dataset |
| 2.2 Sigmoid network | Sum of shifted sigmoids (universal approximation) |
| 2.3 Taylor (polynomial) | Polynomial features ± MLP (beta) |
| 2.4 Fourier (harmonics) | Harmonic features ± MLP |
| 2.5 Cubic spline | C²-smooth cubic interpolation or B-spline fit |
| Action | Control |
|---|---|
| Draw target curve | Click and drag on the canvas |
| Adjust dataset density | Dataset step slider |
| Hidden-layer activation (Taylor / Fourier) | Hidden-layer activation — only when hidden layers ≥ 1 |
| Retrain after parameter change | Retrain |
Formula output: y=... (Graphwar syntax). Compare MSE in the status line before copying.
Taylor and Fourier methods with ≥ 1 hidden layer use a small MLP on feature vector
flowchart LR
GW[Graphwar window] --> CAP[Screen capture]
CAP --> DET[Player / obstacle detection]
DET --> PATH[Waypoints, drawn curve, or evolved population]
PATH --> FMT[Graphwar formula]
FMT --> CLIP[Clipboard]
- Capture — crop the game field via Win32 window rect + margins from
config/capture_config.json. - Detect — find allies, enemies, the active-player center (red glow + refined player circle), and black obstacles (OpenCV + Hough) on the screenshot only.
- Plan — build waypoints (click), freehand draw + resample (draw), or evolve left-to-right control-point paths (dot). (Auto planners in
GraphBot.py— A, polynomial search, symbolic GA — are experimental.)* - Encode — convert segments or approximations into Graphwar syntax and copy to clipboard.
GraphBot stays outside Graphwar:
| GraphBot does | GraphBot does not |
|---|---|
| Take a screenshot of the visible game field | Edit, patch, or replace any game files |
| Move the Graphwar window to a known screen position for consistent capture | Inject DLLs, hooks, or code into the game process |
| Run OpenCV on the captured image | Send keystrokes/clicks into the game to play for you |
| Copy a formula to the clipboard | Read game memory or network traffic |
There is no autopilot that fires shots or submits functions. You still aim by typing (or pasting) the formula in Graphwar’s own UI — GraphBot only helps you derive that formula faster.
The piecewise building block is shared between click mode and draw mode (linear segments):
def direct_line(p1, p2):
x1, y1 = fmt_game(p1[0]), fmt_game(p1[1])
x2, y2 = fmt_game(p2[0]), fmt_game(p2[1])
dx = x2 - x1
if abs(dx) < 1e-12:
dx = fmt_game(vertical_eps(y1, y2)) if y1 != y2 else VERTICAL_MIN_EPS
x2 = fmt_game(x1 + dx)
dist = fmt_game(-((y1 - y2) / 2) / dx)
return f"{dist}*(abs(x - {x1}) - abs(x - {x2}))"Each segment is a V-shaped absolute-value line between two points. A full path is the sum of segments.
Both click workflows build a path from direct_line segments — V-shaped absolute-value pieces between waypoints. The core formula for one segment:
For endpoints
Full path:
Vertical segments: if the next waypoint has GraphBot.py's process_clicks_to_waypoints.
- Run the web UI, optionally Capture field.
- 1st click — your active soldier (purple A). You choose the position manually on the screenshot.
- Next clicks — targets (enemies, detour points) in click order.
- If a click lands left of the previous waypoint → vertical segment is inserted automatically.
- Copy y — copies the expression without
y=, e.g.:
-1.2*(abs(x - -18.5) - abs(x - -5.2)) + 0.8*(abs(x - -5.2) - abs(x - 12.1))
Paste into Graphwar. In normal mode the game still adds its own vertical shift (+c) so the shot passes through your soldier.
- Start
GraphBot.py, choose mode1, press F1. - F3 — start recording clicks on the live game field; F4 — done.
- Click targets only on the field (clicks outside the capture region are ignored).
- GraphBot auto-detects the active soldier (red glow + OpenCV), sorts targets by
x, buildssoldier → target₁ → target₂ → …. - Formula copied to clipboard — no
y=prefix.
If active-player detection fails, tune tools/calibrate_active.py.
| Web UI click mode | GraphBot.py click mode |
|
|---|---|---|
| Where you click | Canvas (after screenshot) | Live Graphwar window |
| Active soldier | Manual 1st click (A) | Auto-detected from screenshot |
| Target order | Click order + vertical-left rule | Sorted by x |
| Formula prefix | none | none |
Draw mode lives in the web UI only. Sketch a curve, sample it into a dataset, and approximate with one of five methods.
Draw mode — sampled curve points and the resulting Fourier approximation on a captured Graphwar field.
flowchart TD
A[Mouse draw on canvas] --> B[Merge points with same x]
B --> C[Uniform resample with sample step]
C --> D[Training dataset blue points]
D --> E{Approximation method}
E --> L[Linear segments]
E --> S[Sigmoid network]
E --> T[Taylor MLP]
E --> F[Fourier MLP]
E --> C[Cubic spline / B-spline]
- Draw — freehand stroke in game coordinates (
x: -25…25,y: -15…15). - Merge — points with nearly equal
xare averaged (stable vertical strokes). - Resample — uniform steps along
xcontrolled by dataset step (sampleStep). More points → more linear segments; smoother target for neural approximators. - Approximate — pick a method; compare MSE in the panel; copy the winning formula.
Enable Prevent backward drawing (x only increases) when the stroke must behave like a function moving from left to right. If the cursor goes left, its x position is locked to the furthest point already reached while y can still move vertically; the stroke never creates a backward segment.
The red curve is your intent; blue dots are the dataset; green is the approximation.
Connect consecutive dataset points with the same direct_line formula as click mode. Segment count ≈ dataset points − 1.
When to use: You want an exact piecewise path through the samples — same math as click mode, but waypoints come from drawing instead of clicking.
A shallow network of shifted sigmoids — inspired by the universal approximation theorem: a sum of sigmoids can approximate wide classes of curves.
Model:
Graphwar export uses the logistic form:
| Parameter | Role |
|---|---|
numNeurons |
Number of sigmoid steps |
sigmoidK |
Sharpness of each step |
stepHeights |
Initialize |
freezeX0 |
Keep uniform neuron positions while training weights |
Polynomial features around a scaled origin — related to a Taylor expansion mindset: local behavior encoded by powers of
Features:
With hidden layers:
With 0 hidden layers: pure polynomial in
| Parameter | Role |
|---|---|
taylorOrder |
Highest power |
taylorHiddenLayers |
0 = pure polynomial; >0 = MLP on features |
taylorHiddenSize |
Width of hidden layers |
mlpActivation |
Nonlinearity between hidden layers (tanh, ReLU, Swish, …) |
Trigonometric basis — same spirit as a Fourier series on a normalized interval:
With 0 hidden layers: linear combination of harmonics (Fourier-like sum).
With hidden layers: richer expressivity via MLP on
| Parameter | Role |
|---|---|
fourierHarmonics |
Number of harmonic pairs |
fourierHiddenLayers |
0 = pure harmonic sum |
fourierHiddenSize |
Hidden layer width when MLP is used |
mlpActivation |
Nonlinearity between hidden layers |
The default cubic spline is an interpolating, piecewise-cubic curve with continuous first and second derivatives (C²). The Natural boundary condition sets the second derivative to zero at both ends; Clamped uses zero first derivatives at the ends. This follows the standard cubic-spline boundary-condition formulation. SciPy CubicSpline reference
Enable Use B-spline basis for a compact least-squares spline with a uniform clamped knot vector. Its controls are:
| Parameter | Role |
|---|---|
| B-spline control points | More points increase detail/fit capacity; fewer points smooth the curve |
| B-spline smoothing λ | Ridge regularization; 0 follows the data most closely, larger values smooth more |
| Curve precision (plot step) | Preview sampling step; smaller values make the displayed curve denser |
| Formula decimals | Number of decimal places retained in the copied Graphwar formula; spline export defaults to 14 because basis terms accumulate rounding errors |
The copied result is converted to Graphwar-safe arithmetic using abs, +, -, *, /, and ^; it does not require a piecewise-function operator.
When Taylor or Fourier uses ≥ 1 hidden layer, the web UI trains a small MLP on feature vector
| Activation | Notes |
|---|---|
| tanh | Default; smooth, bounded |
| sigmoid (σ) | Classic logistic |
| ReLU | Common in modern nets; see Graphwar export below |
| Leaky ReLU | Small slope on |
| Softplus | Smooth ReLU-like: |
| Swish / SiLU |
|
| GELU (approx) | Transformer-style nonlinearity |
| Mish |
|
All exported formulas use only Graphwar builtins: +, -, *, /, ^, sqrt, log, ln, abs, sin, cos, tan, exp. There is no max() or min() in the game (GAME_RULES.md).
Standard ReLU is max, so GraphBot exports the equivalent form:
For
Solid blue:
Example in a formula (hidden pre-activation
((z)+abs(z))/2
Leaky ReLU uses the same trick: abs(z) only — no max/min.
Dot mode is the animated genetic-search workflow in the web UI:
- Switch to 3. Dot mode.
- Click the active soldier first (A), then click enemy targets in any order.
- Press Start evolution and watch each population grow from A across the field.
- Choose Straight segments or Cubic spline, stop when satisfied, and use Copy y to copy the best agent as a Graphwar expression.
Each genome stores y values at a fixed, increasing sequence of x control points. The first gene is locked to the active soldier and every other value is clamped to [-15, 15]. For cubic splines, the sampled curve is checked as well: any trajectory that leaves the field is killed and never displayed or selected. Selection is lexicographic: keep in-bounds agents alive, maximize target hits, minimize distance to missed targets, avoid the optional outer edge strips, then prefer shorter curves. Targets use the configurable Hit radius rather than exact point equality.
| Control | Effect |
|---|---|
| Population | Number of visible agents per generation |
| Control points | Genome/path resolution and spline control-point count |
| Trajectory | Evaluate and export either straight segments or a natural cubic spline through the evolved control points |
| Spline samples / segment | Collision and target-distance sampling density for cubic-spline trajectories |
| Hit radius | Circle around each enemy that counts as a hit (0.05–0.25 game units) |
| Mutation scale | Size of random changes between generations |
| Edge penalty offset | Places neutral-zone lines inward from y = ±15; only trajectory samples beyond them are penalized |
| Generation time | How long one animated generation remains on screen |
Blue trails are the current population; the green trail is the current champion. Right-click, Backspace, or Ctrl+Z removes the last point; Space toggles evolution. Targets left of A are marked as unreachable because this first version never moves backward in x.
1. Planner result — the evolved champion hits all targets while avoiding the detected forbidden mask.
2. In-game result — the exported piecewise function reproduced the planned trajectory in Graphwar.
After Capture field, Python extracts a raster forbidden-mask from black pixels, removes detected players and thin graph strokes, adds a safety margin, and sends a compact occupancy grid to the browser. Dot mode shows it as a translucent red overlay. Safe agents lexicographically outrank every colliding agent; hit count and missed-target distance still outrank the edge-strip penalty, so a necessary border route remains available.
Tune and inspect the exact data used by Dot mode:
python tools/calibrate_forbidden_mask.pyYou can also open a saved field image:
python tools/calibrate_forbidden_mask.py path\to\field.pngThe dashboard keeps four views together:
- Original field with final forbidden area in red.
- Raw pixels accepted by the black threshold.
- Clean connected areas in green plus the safety expansion in red.
- The exact occupancy grid transferred to JavaScript.
| Key | Action |
|---|---|
| Space | Freeze/unfreeze the current live field |
| F | Toggle removal of detected players |
| S | Save config/forbidden_config.json |
| D | Export source, intermediate masks, dashboard and JSON report to outputs/ |
| R | Restore default mask parameters |
The raster mask is the collision source of truth: overlapping or nested circles may merge into one connected area without losing their forbidden pixels. Hough circle reconstruction is not used by Dot mode.
Current boundary: player filtering still depends partly on the existing player-circle detector. Dot mode additionally ignores a small area around manually clicked A/enemy points so imperfect player removal does not make valid hits impossible.
Status: in development. Auto mode is not the main focus of the project yet. Core pieces exist (screen capture, player detection, preview overlay, prototype planners), but gameplay-critical behavior is still missing or unreliable — teammate filtering, accurate enemy radius, black-circle avoidance, and stable active-player detection are all on the roadmap.
Automatic mode (0 at startup) tries to detect enemies and build formulas without manual input. Treat it as a preview of what's coming, not a finished autopilot.
| Area | Current state |
|---|---|
| Teammates | Left/right split only — may route through allies |
| Enemies | Aims at circle centers, not full hit radius |
| Black obstacles | Detection exists but auto routing is not fully wired |
| Active player | Yellow-body + circular red-marker matching; red-glow path remains a fallback |
| UX | Busy-wait on F-keys; formula loop is rough around the edges |
| Planner | Description | Maturity |
|---|---|---|
| A chain* | Path through enemy centers; obstacle avoidance partially implemented | Prototype |
| Polynomial search | Sample and mutate polynomials anchored at your soldier; score by hits and penalties | Experimental |
| Symbolic GA | Evolve Graphwar-like expressions on live scene data | Experimental |
Polynomial candidate form:
Updates roughly every second while Graphwar is visible. Press F2 to quit.
For reliable results right now, use the web UI or GraphBot.py click mode.
GraphBot/
├── GraphBot.py # Main bot (auto + click modes)
├── core/ # Capture, detection, pathfinding, planners
├── config/ # JSON configs (capture, players, obstacles)
├── tools/
│ ├── approximator_server.py # Web UI server (click + draw + dot modes)
│ ├── calibrate_forbidden_mask.py # Raster forbidden-area dashboard
│ ├── preview_capture.py # Debug capture region
│ └── calibrate_*.py # Tune detection parameters
├── Visuals in p5.js/
│ └── universal-approximator/ # Web UI (p5.js + training + Dot-mode GA)
├── docs/images/ # README screenshots (add yours here)
├── GAME_RULES.md # Graphwar rules reference
├── TODO.md # Detailed dev notes
└── outputs/ # Local logs / temp artifacts (gitignored)
High-level checklist distilled from TODO.md. Detailed notes stay in that file.
Focus: most open items below are auto mode blockers. Click mode and draw mode are usable today; Dot mode is an evolving first version; auto mode should not be expected to play rounds reliably until these land.
- Teammate avoidance (auto) — distinguish allies from enemies beyond left/right split; never route through teammates.
- Enemy as a circle — use radius from Hough, not just center; one segment may hit multiple nearby enemies.
- Black obstacle avoidance (auto) — enable
detect_black_circles()in auto mode; pathfind around lethal circles. - Active player detection — match the circular active marker around color-stable player candidates; keep manual toggle/fallback.
- Keyboard UX — replace F1/F3/F4 busy-wait with OpenCV
waitKey; stay alive after click-mode formula instead of exiting. - Calibration suite — sliders for Hough thresholds, glow mask, field margins; export JSON for
GraphBot.py. - Dynamic field bounds — derive capture rect from window size instead of hard-coded margins.
- Dot mode v1: animated populations, lexicographic fitness, start/stop controls, and champion formula export
- Dot obstacle avoidance: calibrated raster mask, compact grid transfer, safety-first GA fitness
- Draw mode: activation picker for Taylor / Fourier MLP (
tanh,ReLU,Swish,GELU,Mish, …) - Active-player detection v2: yellow-body candidates + circular red-marker matching; manual/automatic UI toggle
- Graphwar-safe ReLU export —
max(0,x)→(x+|x|)/2in copied formulas - Web UI with Click mode (default) + Draw mode (5 approximation methods) + Dot mode
- Click mode: manual soldier (A), vertical segments on left-click, formula without
y= - Field capture resets previous clicks / strokes in the web UI
- Graceful handling when no players are detected (
GraphBot.py) - Click-mode vertical segments in
GraphBot.py(process_clicks_to_waypoints) - Win32 field capture +
capture_config.json - Partial calibration tools (
preview_capture,calibrate_active,calibrate_players)
This repo is actively evolving — a pet project built for fun and learning, not a finished product.
The biggest active effort is auto mode — obstacle routing, teammate logic, and trustworthy detection. Draw mode and click mode will keep improving too. If you have ideas (especially for auto planners), I'd love to hear them.
Something broken? Open an Issue with steps to reproduce, your Windows version, and a screenshot if possible.
Have a feature idea or math trick worth adding? Same place — Issues or a PR. All constructive feedback welcome.
MIT — see LICENSE.
Built with curiosity for Graphwar, OpenCV, and a bit of approximation theory.










