attacklog

package
v0.6.0-beta Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package attacklog turns a bot run log into a per-unit account of what the deploy phase actually did.

It exists because the aggregate line is not evidence. A real 1280x720 attack logged `all units successfully deployed` and `remaining=0` while the same log recorded, further up, that the Barbarian King and Grand Warden had failed to deploy, that the Balloon and Electro Dragon reconciles had exhausted and been marked deployed on an OCR reading of zero *against a slot that still looked non-empty*, and that five Ice Spells had been tapped inside a 42x29 px box.

The gap was in how the run was checked, not in the parser: the geometry tools answer "are the taps inside the deployable band", which is necessary and nowhere near sufficient. This package answers "which units actually deployed, and were the spells placed somewhere they do work".

It reads the log's *intent and outcome lines* — what the bot decided and what its own reconcile concluded. It cannot see the game. A unit it calls unverified is one whose completion the bot could not establish either, which is exactly the category worth reporting.

Index

Constants

View Source
const DefaultSpellRadius = 100.0

DefaultSpellRadius is the reach a single spell placement is assumed to cover, in live pixels. It is the yardstick for "did these taps overlap": the village view spans roughly 44 tiles across 1280 px at the 720p device profile, so ~29 px per tile, and a freeze covers a handful of tiles. The number is a heuristic on purpose — the report prints the measured spacing beside it so a reader can judge for themselves instead of trusting a verdict.

View Source
const MinCastSpacing = DefaultSpellRadius * stackFactor

MinCastSpacing is the smallest live-pixel gap at which two casts of the same spell are working adjacent ground instead of the same ground. It is exported because it is not only a reporting threshold: the spell deployer enforces it as an invariant (see internal/attack's minCastSpacingRef), so that the tool which measures the defect and the code that would commit it agree on one number. Five Ice Spells tapped 11 px apart is how this was found.

Variables

This section is empty.

Functions

func IsDeployLine

func IsDeployLine(line string) bool

IsDeployLine reports whether a log line describes a deploy tap carrying coordinates — the lines this package reads coordinates from.

Types

type Placement

type Placement struct {
	Unit       string
	Taps       []Point
	MinSpacing float64
	Span       float64
	// Overlapped is true when two casts landed within half a cast's reach of
	// each other, i.e. one of them was spent on ground the other already covers.
	Overlapped bool
	// Tight is the milder case: closer than a full reach, but not stacked.
	Tight bool
	// Stacked is how many taps have another tap within half a reach.
	Stacked int
}

Placement is one spell unit's tap geometry, with the analysis applied.

type Point

type Point struct{ X, Y int }

Point is a tap coordinate in screen pixels.

type Report

type Report struct {
	Path        string
	Edge        string
	Projection  string
	DisplayK    float64
	ClampedTop  int
	ClampedBtm  int
	ClampedPts  int
	BandKnown   bool
	Units       []Unit
	SpellRadius float64
	// CountFabricated names the units whose troop count could not be read, so
	// the deploy used a fallback number instead of what the card held.
	CountFabricated []string
	ZeroCountSlots  int
	TotalSlots      int
}

Report is the parsed run.

func Parse

func Parse(path string) (*Report, error)

Parse reads a run log and produces the report.

func (*Report) Counts

func (r *Report) Counts() map[Verdict]int

Counts returns how many units landed in each verdict.

func (*Report) FailedUnits

func (r *Report) FailedUnits() []Unit

FailedUnits returns the units whose deployment the run did not establish, excluding the ones that were never in the bar.

func (*Report) OverlappedSpells

func (r *Report) OverlappedSpells() []Placement

OverlappedSpells returns the spell units whose taps are stacked within half a cast's reach, which means part of the cast landed on ground a previous cast already covers.

func (*Report) Placements

func (r *Report) Placements() []Placement

Placements returns the spell units with their spacing analysis, which is where the report's "did the spells overlap" answer comes from.

func (*Report) Problem

func (r *Report) Problem() bool

Problem reports whether anything in the run must not be called a success: an unestablished deployment, an overlapped spell cast, or a fabricated troop count (which is what makes the other two possible).

type Unit

type Unit struct {
	Name    string
	Kind    string // troop | siege | hero | spell | event
	Verdict Verdict
	Reason  string

	// Taps are the coordinates the log says were fired for this unit.
	Taps []Point
	// MinSpacing is the smallest distance between any two of its taps, and Span
	// the largest. For a spell fired several times these are the whole question:
	// five freezes 23 px apart are one freeze.
	MinSpacing float64
	Span       float64
}

Unit is one line of the report.

type Verdict

type Verdict string

Verdict is what the log lets us say about one unit.

const (
	// ConfirmedDeploy means the bot observed the card leave the bar (an explicit
	// success signal, e.g. "sweep reconciled slot empty" or "hero deployed").
	ConfirmedDeploy Verdict = "deployed"
	// Failed means the bot said so ("will be marked failed").
	Failed Verdict = "FAILED"
	// Unverified means the bot believed it deployed but the evidence in the log
	// is a fallback or a contradiction: a zero-OCR "spent" verdict against a
	// slot its own visual check called non-empty, or a reconcile that exhausted
	// and was handed on. These are the ones that turn into "it didn't use all
	// the troops" with no error anywhere.
	Unverified Verdict = "UNVERIFIED"
	// NotInBar means the card was never in the army bar, so skipping it is
	// correct rather than a failure.
	NotInBar Verdict = "not in bar"
)

Jump to

Keyboard shortcuts

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