formula

package
v0.2.0-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package formula defines the per-unit deploy coordinate schema produced by the `design_attack` interactive tool and consumed by the deploy pipeline in `internal/attack`.

The schema is intentionally side-based (single endpoints along a chosen SIDE of the screen — top, bottom, left, right) rather than the legacy corner-based system in `precision_config.json`. The user can pinpoint exact tap coordinates for every unit type so the bot no longer has to guess based on red zone detection or random interpolation.

Schema (1 line per field, see examples in cmd/design_attack README):

{
  "name":  "EDrag side formula",
  "screen": {"w": 860, "h": 732},
  "units": {
    "balloon":          {"type":"line", "p1":{"x":60,"y":110}, "p2":{"x":60,"y":542}, "count":10, "jitter":3},
    "stone slammer":    {"type":"point", "p":{"x":400,"y":326}, "jitter":6},
    "rage spell":       {"type":"lines", "lines":[
      {"p1":{"x":80,"y":200},"p2":{"x":130,"y":280},"count":3,"jitter":3},
      {"p1":{"x":80,"y":280},"p2":{"x":130,"y":380},"count":2,"jitter":3}
    ]}
  }
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Formula

type Formula struct {
	Name   string `json:"name"`
	Screen struct {
		W int `json:"w"`
		H int `json:"h"`
	} `json:"screen"`
	Units           map[string]UnitEntry            `json:"units"`
	CornerOverrides map[string]map[string]UnitEntry `json:"corner_overrides,omitempty"`
}

Formula is the top-level deploy plan.

`Units` is the per-unit deploy geometry authored for one canonical corner (BottomRight by convention). The orchestrator mirrors Units to the active target edge at runtime (see MirrorForCorner).

`CornerOverrides` is an optional map of per-corner partial overrides. When present, the orchestrator uses the override for that corner INSTEAD OF mirroring Units — because the user has authored explicit coordinates for that side (e.g. with `cmd/design_attack -corner BL`) that better match the base's actual red-line position on that side.

Keys are the canonical corner names produced by NextEdgeIndex: "TopLeft" / "TopRight" / "BottomRight" / "BottomLeft" (case sensitive — the orchestrator's switch uses the raw value). The per-corner value is a per-unit map with the SAME schema as Units; typically a PARTIAL formula (only the units that differ from the mirrored BR default). The orchestrator merges the override with Units per-unit, with the override winning.

`corner_overrides` is omitted from JSON when nil, so old formulas (with only Units) continue to round-trip cleanly.

func AutoPickFor

func AutoPickFor(s *strategy.DynamicStrategy, screenW, screenH int, targetEdge string) *Formula

AutoPickFor builds a *Formula for every unit in the strategy using per-class pinned geometry. No manual clicks required - the side is chosen from targetEdge (Random falls back to Left for determinism).

Per-class rules (4-side aware):

  • Hero (Point pattern, phase name contains "hero") → midpoint of the chosen side's deploy line, jitter 5.
  • Siege (Point pattern, phase name contains "siege") → dead center of map, jitter 6.
  • Line troop (Line pattern, "balloon"/"dragon"/etc) → full edge line endpoints, count=10, jitter 3.
  • Rage spell (Line pattern, name contains "rage") → 2 sub-lines: first 15%-60% (3 taps), second 40%-85% (2 taps).
  • Other spell (Line pattern, name contains "spell") → single edge line pulled slightly inward, count=5, jitter 3.
  • FourSides → point cluster at map center, jitter 5.

The 20-80 percent rule guarantees auto-pick geometry mathematically never lands in a screen corner (the regression that motivated this helper).

func Load

func Load(strategyPath string) (*Formula, bool, error)

Load finds and reads a formula file. Strategy path is the YAML file the bot is loading — we look for `<stem>_formula.json` next to it, also probing `assets/strategies/<basename>` via paths.Resolve.

func LoadFile

func LoadFile(path string) (*Formula, error)

LoadFile reads a formula from a direct path. Use Load() instead when the path is a strategy YAML (Load auto-resolves <stem>_formula.json). Used by cmd/design_attack -verify to load a previously-saved formula for visual inspection of the 4-corner mirror.

func (*Formula) ApplyScreenScale

func (f *Formula) ApplyScreenScale(srcW, srcH, dstW, dstH int)

ApplyScreenScale multiplies every coordinate in the formula by (dstW/srcW, dstH/srcH) so a JSON calibrated on one screen size still lands correctly on a different live resolution. Does NOT scale Count, Jitter, or Type. No-op when src dims are zero.

Callers should pass formulaPtr.Screen.W/H as the source dims (the dimensions the formula was authored against) and the live screen dims as the destination.

func (*Formula) LookUp

func (f *Formula) LookUp(unitName string) (UnitEntry, bool)

LookUp returns the entry for a unit name, normalizing underscores to spaces (Strategy stores "stone_slammer"; formula stores "stone slammer").

func (*Formula) MirrorForCorner

func (f *Formula) MirrorForCorner(targetEdge string)

MirrorForCorner reflects every P / P1 / P2 / Lines[i].P1 / Lines[i].P2 across the screen axes as needed so a formula authored for ONE corner (BottomRight by convention) applies to any of the 4 corners. Used by the orchestrator with `target_edge: "Rotate"` so the same authored attack lands on a different side each run without the user having to pin all 4 corners in `precision_config.json`.

Reflection rules (mirror around the formula's authored screen center):

BottomRight: identity (formula as-authored)        — no-op
BottomLeft:  reflect X (newX = W - X)              — flip horizontally
TopRight:    reflect Y (newY = H - Y)              — flip vertically
TopLeft:     reflect both (newX = W - X; newY = H - Y)

Accepts BOTH the full canonical names ("BottomLeft", "TopRight", etc. — used by the orchestrator via NextEdgeIndex) AND the abbreviated 2-letter forms ("BL", "TR", etc. — used by cmd/design_attack -verify for its 2x2 grid labels). Freeform values like "left" or "right" also work via the substring fallback so a user-authored strategy with `target_edge: "left"` still gets a reasonable mirror.

Designed to be called BEFORE ApplyScreenScale so the mirror uses the formula's authored 860x732 reference frame. After the mirror, ApplyScreenScale does the live-screen projection. The Grand Warden "always at screen center" hardcode in HeroManager is NOT mirrored — center stays center — so a pin authored at the formula's (430, 366) center mirrors to itself, which is the right behavior either way.

func (*Formula) Save

func (f *Formula) Save(path string) error

Save writes the formula as pretty JSON.

type LinePoint

type LinePoint struct {
	P1     Point `json:"p1"`
	P2     Point `json:"p2"`
	Count  int   `json:"count"`
	Jitter int   `json:"jitter"`
}

LinePoint is one segment of a multi-line deploy (e.g. rage spell 3+2 split). Count = number of taps distributed along P1->P2 for this segment.

type Point

type Point struct {
	X int `json:"x"`
	Y int `json:"y"`
}

Point is a tap-able 2D screen coordinate.

func (Point) Image

func (p Point) Image() image.Point

Image converts to image.Point for the deploy path.

type UnitEntry

type UnitEntry struct {
	Type string `json:"type,omitempty"`

	// point
	P      *Point `json:"p,omitempty"`
	Jitter int    `json:"jitter,omitempty"`

	// line
	P1    *Point `json:"p1,omitempty"`
	P2    *Point `json:"p2,omitempty"`
	Count int    `json:"count,omitempty"`

	// lines (rage-style)
	Lines []LinePoint `json:"lines,omitempty"`
}

UnitEntry is the per-unit deploy instruction.

Type discriminator (inferred from populated fields):

"point" - single tap target (heroes, siege)
"line"  - taps evenly distributed from P1 to P2 (balloons, EDrag)
"lines" - rage-style split (each LinePoint is one sub-line)
empty   - no entry; caller falls back to pCfg.Edges

func (UnitEntry) IsLine

func (e UnitEntry) IsLine() bool

IsLine returns true if the entry is "line".

func (UnitEntry) IsLines

func (e UnitEntry) IsLines() bool

IsLines returns true if the entry is "lines".

func (UnitEntry) IsPoint

func (e UnitEntry) IsPoint() bool

IsPoint returns true if the entry is "point".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL