///- --package .
hunting_pack (test)
test hunting_pack: prelude
///- --test static --symbol
:< testing
:= tick_ms () : i32 = 100
:= band_size () : i32 = 6
:= scenario_ticks () : i32 = 3000
:= escape_distance () : i32 = 8
:= sight_range () : i32 = 7
:= knight_max_health (level : i32) : i32 = 185 + 15 * (level - 8)
:= training_first () : u32 = 1
:= training_count () : i32 = 100
:= held_out_first () : u32 = 1001
:= resampled_first (block : i32) (generation : i32) : u32 = 10001u32 + conv (generation * block)
:= held_out_count () : i32 = 100
:= creature_strike_ms () : i32 = 2000
:= chase_step_factor () : i32 = 1
:= knight_sample_step_ms () : i32 = 650
:= parity_action_gap_ms () : i32 = 250
:= client_decision_ms () : i32 = 100
:= death_penalty () : i32 = 50
:= death_experience_penalty () : i32 = 5000
:< keyed_rng
:| pack_roll =
| PackPlayerHit
| PackPlayerHeal
| PackCreatureHit
| PackLoot
| PackRandomControl
| PackWander
| PackCreatureShot
| PackDance
| PackPoll
| PackEntry
| PackSearchTarget
| PackChangeTarget
:= target_code (r : pack_roll) : u32 =
?= r ?|
| PackCreatureShot => 17u32
| PackDance => 18u32
| PackPoll => 19u32
| PackEntry => 20u32
| PackSearchTarget => 40u32
| _ => 41u32
:= hunt_code (r : pack_roll) : u32 =
?= r ?|
| PackPlayerHit => 11u32
| PackPlayerHeal => 12u32
| PackCreatureHit => 13u32
| PackLoot => 14u32
| PackRandomControl => 15u32
| PackWander => 16u32
| _ => target_code r
:+ purpose_code pack_roll = \ r => hunt_code r
:= client_lag_ms () : i32 = 8
:= target_nearby_step_factor () : i32 = 2
:= canary_static_attack_chance () : i32 = 90
:== living = vitality::vitality vitality::alive
:== hit_points = units::amount quantity::hitpoints
:== foe_chase = { met_target : bool; nearby : i32; acquired_at : i32 }
:== foe_respawn = { returns_at : i32; check_until : i32 }
:== foe_aim =
{
aims_at : i32
entrant : i32
in_view : i32
damage_first : i32
damage_second : i32
retarget_at : i32
last_x : i32
last_y : i32
}
:== foe =
{
at : geometry::position
spawn : geometry::position
life : living
strikes_at : i32
next_step_at : i32
chase : foe_chase
respawn : foe_respawn
aim : foe_aim
}
:== hunter_clock =
{
timers : cooldowns::timers
block_charges : i32
next_charge_at : i32
next_action_at : units::amount quantity::milliseconds
next_decision_at : i32
}
:== hunter_tally =
{
potions : i32
hits_taken : i32
damage_taken : hit_points
retreats : i32
cornered : i32
}
:== hunter =
{
at : geometry::position
health : hit_points
next_step_at : i32
target : i32
clock : hunter_clock
tally : hunter_tally
waypoint : i32
call_sign : i32
}
:== pack_score =
{
kills : i32
experience : i32
escapes : i32
deaths : i32
gold : units::amount quantity::gold_coins
}
:== pack = { hunter : hunter; ally : hunter; ally_in_world : bool; foes : fixed_views::band foe; score : pack_score }
:= with_hunter (h : hunter) (p : pack) : pack = { p ?| hunter = h }
:= with_ally (h : hunter) (p : pack) : pack = { p ?| ally = h }
:= with_clock (c : hunter_clock) (h : hunter) : hunter = { h ?| clock = c }
:= with_timers (held : cooldowns::timers) (h : hunter) : hunter = h |> with_clock { (h::clock) ?| timers = held }
:= with_tally (t : hunter_tally) (h : hunter) : hunter = { h ?| tally = t }
:= with_score (s : pack_score) (p : pack) : pack = { p ?| score = s }
:= now_in_world (p : pack) : pack = { p ?| ally_in_world = !+ }
:= turned (p : pack) : pack = { p ?| hunter = p::ally; ally = p::hunter }
:= party_potions (p : pack) : i32 = p::hunter::tally::potions + p::ally::tally::potions
:| brain =
| Rules : policy::adaptive
| Network : network::weights
| Temperate : policy::temperate
| RandomControl
:| footing_rule =
| StepAway
| StepAwayLikeTheLiveProbe
| ScoredStep : terrain::footing
:== patrol_route = { points : fixed_views::band geometry::position; count : i32 }
:| patrol =
| Stays
| Patrols : patrol_route
| SweepsSpawns
| SweepsTerritory : i32
:== tactic = { brain : brain; order : policy::targeting; footing : footing_rule; patrol : patrol }
:== offense = { level : i32; weapon_attack : i32; weapon_skill : i32; step_ms : i32; heal_low : i32; heal_high : i32 }
:= veteran_offense () : offense =
{
level = 30
weapon_attack = 21
weapon_skill = 60
step_ms = knight_sample_step_ms ()
heal_low = 125
heal_high = 175
}
:= novice_offense () : offense =
{
level = 8
weapon_attack = 20
weapon_skill = 30
step_ms = knight_sample_step_ms ()
heal_low = 30
heal_high = 60
}
:= offense_against (kind : creatures::arena_creature) : offense =
:< creatures
?= kind ?|
| Cyclops => veteran_offense ()
| Hunter => veteran_offense ()
| _ => novice_offense ()
:= never () : i32 = 2147483647
:= fresh_chase () : foe_chase = { met_target = !-; nearby = 0; acquired_at = never () }
:= fresh_respawn () : foe_respawn = { returns_at = never (); check_until = 0 }
:= fresh_aim () : foe_aim =
{
aims_at = -1
entrant = 0
in_view = 0
damage_first = 0
damage_second = 0
retarget_at = 0
last_x = -1
last_y = -1
}
:= foe_at (at : geometry::position) (life : living) : foe =
{
at = at
spawn = at
life = life
strikes_at = 0
next_step_at = 0
chase = fresh_chase ()
respawn = fresh_respawn ()
aim = fresh_aim ()
}
:= fresh_foe (kind : creatures::arena_creature) (spawn : geometry::position) : foe =
foe_at spawn (vitality::spawn (creatures::stats_of kind)::max_health)
:= buried_foe (kind : creatures::arena_creature) : foe =
:= grave = fresh_foe kind (geometry::position 0 0 15)
{ grave ?| strikes_at = never (); next_step_at = never () }
:| respawn_rule =
| Respawning
| Clearing
| RespawningAfter : i32
:| swing_after_step =
| SwingKeepsItsTime
| SwingWaitsForThinkOrMove
:| idle_behavior =
| StandsStill
| WandersLikeCanary
:| knight_placement =
| AtLayoutStart
| StandsAt : geometry::position
:| melee_timing =
| MeleeWhenReady
| MeleeWhenReadyEvery : i32
| MeleeEveryOnceTargeted : i32
| MeleeOnCanaryRounds : i32
:| follow_timing =
| FollowsAtOnce
| FollowsOnThink
:| knight_pace =
| SamplePace
| PacedLikeCanary
:| dance_rule =
| NeverDances
| DancesLikeCanary
:| knight_view =
| SeesThisTick
| SeesLastTick
| SeesAtItsPoll
:| ground_timing =
| ScaledFromGrass
| SteppedByCanaryFormula
:| acquisition_rule =
| ChasesAtOnce
| ChasesAThinkAfterAcquiring
:| approach_rule =
| ApproachesAtOnce
| ApproachesAfterAFreshWalkDelay
:| sight_rule =
| SightStopsAtUnpassableTiles
| SightStopsAtProjectileBlockers
:| chase_pace =
| ChasesAtTheServerPace
| ChasesAtTheProbePace
:| retreat_pace =
| RetreatsAtTheServerPace
| RetreatsAtTheProbePace
:| ally_entry =
| EntersWithTheKnight
| EntersAfterMeasuredDelay
:== camp_timing =
{
swing : swing_after_step
melee : melee_timing
idle : idle_behavior
follow : follow_timing
pace : knight_pace
dance : dance_rule
view : knight_view
}
:== camp_motion =
{
ground : ground_timing
acquisition : acquisition_rule
approach : approach_rule
sight : sight_rule
}
:== camp_knight = { retreat_pace : retreat_pace; chase_pace : chase_pace; placement : knight_placement }
:== foe_kind = creatures::arena_creature
:== camp_foes = { kinds : fixed_views::band foe_kind; size : i32; ranged : bool; rule : respawn_rule; wakes_at : i32 }
:== camp_crew =
{
party : i32
ally_enters : ally_entry
party_targeting : bool
potion_budget : i32
hunter_level : i32
}
:== camp =
{
foes : camp_foes
crew : camp_crew
layout : terrain::layout
timing : camp_timing
motion : camp_motion
knight : camp_knight
}
:= unlimited_potions () : i32 = 1000000
:= harder (a : foe_kind) (b : foe_kind) : foe_kind = ? (offense_against b)::level > (offense_against a)::level ?> b |: a
:= hardest_kind (kinds : fixed_views::band foe_kind) (size : i32) : foe_kind =
:~ ^ pick (i : i32) (best : foe_kind) : foe_kind =
? i >= size ?> best |: pick (i + 1) (harder best (fixed_views::band_at i kinds))
pick 1 (fixed_views::band_at 0 kinds)
:= default_hunter_level (kinds : fixed_views::band creatures::arena_creature) (size : i32) : i32 =
(offense_against (hardest_kind kinds size))::level
:= crew_of (foes : camp_foes) : camp_crew =
{
party = 1
ally_enters = EntersWithTheKnight
party_targeting = !-
potion_budget = unlimited_potions ()
hunter_level = default_hunter_level foes::kinds foes::size
}
:= sample_timing () : camp_timing =
{
swing = SwingKeepsItsTime
melee = MeleeWhenReady
idle = StandsStill
follow = FollowsAtOnce
pace = SamplePace
dance = NeverDances
view = SeesThisTick
}
:= sample_motion () : camp_motion =
{
ground = ScaledFromGrass
acquisition = ChasesAtOnce
approach = ApproachesAtOnce
sight = SightStopsAtUnpassableTiles
}
:= sample_knight () : camp_knight =
{
retreat_pace = RetreatsAtTheServerPace
chase_pace = ChasesAtTheServerPace
placement = AtLayoutStart
}
:= sample_camp (foes : camp_foes) (layout : terrain::layout) : camp =
{
foes = foes
crew = crew_of foes
layout = layout
timing = sample_timing ()
motion = sample_motion ()
knight = sample_knight ()
}
:= foes_of (kinds : fixed_views::band foe_kind) (size : i32) (rule : respawn_rule) : camp_foes =
{
kinds = kinds
size = size
ranged = fixed_views::counted creatures::is_ranged kinds > 0
rule = rule
wakes_at = 0
}
:= uniform_camp (kind : creatures::arena_creature) : camp =
sample_camp (foes_of (fixed_views::band_of kind) 3 Respawning) (:< terrain ;; OpenField)
:= with_crew (crew : camp_crew) (c : camp) : camp = { c ?| crew = crew }
:= with_foes (foes : camp_foes) (c : camp) : camp = { c ?| foes = foes }
:= with_hunter_level (level : i32) (c : camp) : camp = c |> with_crew { (c::crew) ?| hunter_level = level }
:= with_potion_budget (count : i32) (c : camp) : camp = c |> with_crew { (c::crew) ?| potion_budget = count }
:= with_party (knights : i32) (c : camp) : camp = c |> with_crew { (c::crew) ?| party = knights }
:= with_delayed_ally_entry (c : camp) : camp = c |> with_crew { (c::crew) ?| ally_enters = EntersAfterMeasuredDelay }
:= ally_enters_with_the_knight (c : camp) : bool =
?= c::crew::ally_enters ?|
| EntersWithTheKnight => !+
| EntersAfterMeasuredDelay => !-
:= ally_starts_in_world (c : camp) : bool = c::crew::party > 1 && ally_enters_with_the_knight c
:= targeting_like_canary (c : camp) : camp = c |> with_crew { (c::crew) ?| party_targeting = !+ }
:= duo_default (c : camp) : camp = c |> with_party 2 |> targeting_like_canary |> with_delayed_ally_entry
the_duo_default_is_candidate_2_with_measured_entry (test)
test the_duo_default_is_candidate_2_with_measured_entry: passed
///- --test --symbol
///> c
:= ~c = duo_default (uniform_camp (:< creatures ;; Troll))
(c::crew::party, c::crew::party_targeting, ally_enters_with_the_knight c) |> _assert_eq (2, !+, !-)
__assert_eq { expected = (2, true, false) }
:= waking_at (ms : i32) (c : camp) : camp = c |> with_foes { (c::foes) ?| wakes_at = ms }
:= live_wake_ms () : i32 = 10000
:= swinging_like_canary (c : camp) : camp = { c ?| timing = { (c::timing) ?| swing = SwingWaitsForThinkOrMove } }
:= wandering_while_idle (c : camp) : camp = { c ?| timing = { (c::timing) ?| idle = WandersLikeCanary } }
:= knight_standing_at (at : geometry::position) (c : camp) : camp =
{ c ?| knight = { (c::knight) ?| placement = StandsAt at } }
:= live_creature_strike_ms () : i32 = 2166
:= meleeing_every (ms : i32) (c : camp) : camp = { c ?| timing = { (c::timing) ?| melee = MeleeWhenReadyEvery ms } }
:= meleeing_every_once_targeted (ms : i32) (c : camp) : camp =
{ c ?| timing = { (c::timing) ?| melee = MeleeEveryOnceTargeted ms } }
:= mirroring_the_live_arena (c : camp) : camp =
c
|> waking_at (live_wake_ms ())
|> wandering_while_idle
|> meleeing_every_once_targeted (live_creature_strike_ms ())
|> swinging_like_canary
:= following_on_think (c : camp) : camp = { c ?| timing = { (c::timing) ?| follow = FollowsOnThink } }
:= meleeing_on_canary_rounds (ms : i32) (c : camp) : camp =
{ c ?| timing = { (c::timing) ?| melee = MeleeOnCanaryRounds ms } }
:= paced_like_canary (c : camp) : camp = { c ?| timing = { (c::timing) ?| pace = PacedLikeCanary } }
:= dancing_like_canary (c : camp) : camp = { c ?| timing = { (c::timing) ?| dance = DancesLikeCanary } }
:= seeing_a_tick_late (c : camp) : camp = { c ?| timing = { (c::timing) ?| view = SeesLastTick } }
:= seeing_at_its_poll (c : camp) : camp = { c ?| timing = { (c::timing) ?| view = SeesAtItsPoll } }
:= stepping_by_canary_formula (c : camp) : camp = { c ?| motion = { (c::motion) ?| ground = SteppedByCanaryFormula } }
:= acquiring_like_canary (c : camp) : camp =
{ c ?| motion = { (c::motion) ?| acquisition = ChasesAThinkAfterAcquiring } }
:= following_like_canary (c : camp) : camp =
{ c ?| motion = { (c::motion) ?| approach = ApproachesAfterAFreshWalkDelay } }
:= sighting_like_canary (c : camp) : camp = { c ?| motion = { (c::motion) ?| sight = SightStopsAtProjectileBlockers } }
:= live_probe_step_ms () : i32 = 650
:= live_chase_step_ms () : i32 = 558
:= chasing_at_the_live_probe_pace (c : camp) : camp =
{ c ?| knight = { (c::knight) ?| chase_pace = ChasesAtTheProbePace } }
:= pacing_retreats_like_the_live_probe (c : camp) : camp =
{ c ?| knight = { (c::knight) ?| retreat_pace = RetreatsAtTheProbePace } }
:= blocks_sight (camp : camp) (p : geometry::position) : bool =
?= camp::motion::sight ?|
| SightStopsAtUnpassableTiles => terrain::walls camp::layout p
| SightStopsAtProjectileBlockers => terrain::sight_blocks camp::layout p
:= canary_timing (c : camp) : camp =
c
|> mirroring_the_live_arena
|> waking_at 0
|> following_on_think
|> meleeing_on_canary_rounds (live_creature_strike_ms ())
|> paced_like_canary
|> dancing_like_canary
:= mirroring_the_current_arena (c : camp) : camp =
canary_timing c
|> seeing_at_its_poll
|> stepping_by_canary_formula
|> acquiring_like_canary
|> following_like_canary
|> sighting_like_canary
|> pacing_retreats_like_the_live_probe
:= canary_spawn_check_ms () : i32 = 30000
:= canary_unblockable_spawn_delay_ms () : i32 = 3 * 1400
:~ ^ greatest_common_divisor (a : i32) (b : i32) : i32 = ? b = 0 ?> a |: greatest_common_divisor b (a % b)
:= canary_respawn_ms (spawntime_ms : i32) : i32 =
:= check = greatest_common_divisor (canary_spawn_check_ms ()) spawntime_ms
(1 + (spawntime_ms + check - 1) / check) * check + canary_unblockable_spawn_delay_ms ()
:= respawning_like_canary (spawntime_ms : i32) (c : camp) : camp =
c |> with_foes { (c::foes) ?| rule = RespawningAfter (canary_respawn_ms spawntime_ms) }
:= player_think_ms () : i32 = 1000
:= live_think_period_ms () : i32 = live_creature_strike_ms () / 2
:= next_think_at (ms : i32) : i32 =
(ms + live_think_period_ms () - 1) / live_think_period_ms () * live_think_period_ms ()
:= swing_is_waiting (held : cooldowns::timers) : bool = held::strike_ready_at > held::attack_ready_at
:= swing_resumes_at (now : i32) (held : cooldowns::timers) : cooldowns::timers =
? swing_is_waiting held && now >= held::attack_ready_at ?> { held ?| strike_ready_at = now } |: held
:= swing_deferred (held : cooldowns::timers) (due : i32) : cooldowns::timers =
{ held ?| attack_ready_at = due; strike_ready_at = next_think_at due }
:= swing_waits (now : i32) (held : cooldowns::timers) : cooldowns::timers =
:= due = cooldowns::ready_at (cooldowns::strike_spell ()) held
? swing_is_waiting held ?> swing_resumes_at now held
|? due <= now ?> held
|: swing_deferred held due
:= swing_after_step (camp : camp) (now : i32) (held : cooldowns::timers) : cooldowns::timers =
?= camp::timing::swing ?|
| SwingKeepsItsTime => held
| SwingWaitsForThinkOrMove => swing_waits now held
:= swing_after_target_moves (camp : camp) (now : i32) (held : cooldowns::timers) : cooldowns::timers =
?= camp::timing::swing ?|
| SwingKeepsItsTime => held
| SwingWaitsForThinkOrMove => swing_resumes_at now held
:= clearing (c : camp) : camp = { c ?| foes = { (c::foes) ?| rule = Clearing } }
:= on_layout (l : terrain::layout) (c : camp) : camp = { c ?| layout = l }
:= spawns_of (c : camp) : geometry::position * geometry::position * geometry::position =
:= at = geometry::position
?= c::foes::rule, c::layout ?|
| Clearing, OpenField => at 100 102 7, at 99 103 7, at 99 104 7
| _ => terrain::spawn_points c::layout
:== entry_trio = catalog::entry * catalog::entry * catalog::entry
:= kinds_of_entries (entries : entry_trio) : fixed_views::band foe_kind =
:= a, b, c = entries
:= first = catalog::entry_creature a
fixed_views::band (first, catalog::entry_creature b, catalog::entry_creature c, first, first, first)
:= camp_of_entries (entries : entry_trio) : camp =
sample_camp (foes_of (kinds_of_entries entries) 3 Respawning) (:< terrain ;; OpenField)
:= hunter_step_pace (c : camp) (base : offense) : i32 =
?= c::timing::pace ?|
| SamplePace => base::step_ms
| PacedLikeCanary => creatures::player_step_ms c::crew::hunter_level
:= hunter_of (c : camp) : offense =
:= base = offense_against (hardest_kind c::foes::kinds c::foes::size)
{ base ?| level = c::crew::hunter_level; step_ms = hunter_step_pace c base }
:= canary_step_ms (camp : camp) (at : geometry::position) (speed : i32) : i32 =
creatures::step_ms_from_canary_formula speed (terrain::ground_speed_at camp::layout at)
:= step_ms_at (camp : camp) (at : geometry::position) (speed : i32) (grass_step_ms : i32) : i32 =
?= camp::motion::ground ?|
| ScaledFromGrass => terrain::on_ground_at camp::layout at grass_step_ms
| SteppedByCanaryFormula => canary_step_ms camp at speed
:= hunter_step_ms_at (camp : camp) (at : geometry::position) : i32 =
step_ms_at camp at (creatures::knight_speed camp::crew::hunter_level) (hunter_of camp)::step_ms
:= no_faster_than_probe (server : i32) : i32 = ? server > live_probe_step_ms () ?> server |: live_probe_step_ms ()
:= retreat_step_ms_at (camp : camp) (at : geometry::position) : i32 =
:= server = hunter_step_ms_at camp at
?= camp::knight::retreat_pace ?|
| RetreatsAtTheServerPace => server
| RetreatsAtTheProbePace => no_faster_than_probe server
:= player_max_health (c : camp) : units::amount quantity::hitpoints =
quantity::hp (knight_max_health (hunter_of c)::level)
:= kind_of (index : i32) (c : camp) : creatures::arena_creature =
fixed_views::band_at index c::foes::kinds
:= knight_start (camp : camp) (sign : i32) : geometry::position =
:= first = ?= camp::knight::placement ?| AtLayoutStart => terrain::player_start camp::layout | StandsAt at => at
? sign = 0 ?> first |: geometry::position (first::x + 1) first::y first::z
:= idle_clock () : hunter_clock =
{
timers = cooldowns::idle_timers ()
block_charges = combat::max_block_charges ()
next_charge_at = combat::block_charge_ms ()
next_action_at = quantity::ms 0
next_decision_at = 0
}
:= zero_tally () : hunter_tally =
{ potions = 0; hits_taken = 0; damage_taken = quantity::hp 0; retreats = 0; cornered = 0 }
:= idle_knight (sign : i32) : hunter =
{
at = terrain::grave ()
health = quantity::hp 0
next_step_at = 0
target = -1
clock = idle_clock ()
tally = zero_tally ()
waypoint = 0
call_sign = sign
}
:= knight_of (camp : camp) (sign : i32) : hunter =
{ (idle_knight sign) ?| at = knight_start camp sign; health = player_max_health camp }
:= ally_at_start (camp : camp) : hunter =
:= ally = knight_of camp 1
? ally_starts_in_world camp ?> ally |: { ally ?| at = terrain::grave () }
:= placed_foe (camp : camp) (i : i32) (at : geometry::position) : foe =
? i < camp::foes::size ?> fresh_foe (kind_of i camp) at |: buried_foe (kind_of i camp)
:= placed_foes (camp : camp) : fixed_views::band foe =
:= a, b, c = spawns_of camp
:= d, e, g = terrain::more_spawn_points camp::layout
:= placed = placed_foe camp
fixed_views::band (placed 0 a, placed 1 b, placed 2 c, placed 3 d, placed 4 e, placed 5 g)
:= zero_score () : pack_score = { kills = 0; experience = 0; escapes = 0; deaths = 0; gold = quantity::gp 0 }
:~ start (camp : camp) : pack =
{
hunter = knight_of camp 0
ally = ally_at_start camp
ally_in_world = ally_starts_in_world camp
foes = placed_foes camp
score = zero_score ()
}
:= slot (index : i32) (p : pack) : foe =
fixed_views::band_at index p::foes
:= with_slot (index : i32) (f : foe) (p : pack) : pack =
{ p ?| foes = fixed_views::band_with index f p::foes }
:~ ^ other_on (index : i32) (p : pack) (q : geometry::position) (i : i32) : bool =
? i >= band_size () ?> !- |? i <> index && geometry::same_tile q (slot i p)::at ?> !+ |: other_on index p q (i + 1)
:= taken_by_others (index : i32) (p : pack) (q : geometry::position) : bool =
geometry::same_tile q p::hunter::at || geometry::same_tile q p::ally::at || other_on index p q 0
:= taken_by_foes (p : pack) (q : geometry::position) : bool =
fixed_views::counted (\ (f : foe) => geometry::same_tile q f::at) p::foes > 0 || geometry::same_tile q p::ally::at
:= key (s : u32) (t : i32) (e : i32) (purpose : pack_roll) : keyed_rng::roll_key pack_roll =
keyed_rng::roll_key (keyed_rng::seed s) (keyed_rng::tick (conv t)) (keyed_rng::entity (conv e)) purpose
:= distance_to (f : foe) (p : pack) : i32 = geometry::chebyshev_distance p::hunter::at f::at
:= tallied_hurt (hurt : i32) (t : hunter_tally) : hunter_tally =
:= hits = ? hurt > 0 ?> t::hits_taken + 1 |: t::hits_taken
{ t ?| hits_taken = hits; damage_taken = units::plus t::damage_taken (quantity::hp hurt) }
:= hurt_by (hurt : i32) (h : hunter) : hunter =
{ h ?| health = units::minus h::health (quantity::hp hurt); tally = tallied_hurt hurt h::tally }
:= melee_hurt (c : camp) (seed : u32) (tick : i32) (index : i32) (shield : bool) : i32 =
:= roll = key seed tick index PackCreatureHit
combat::creature_hit (creatures::stats_of (kind_of index c))::melee_max (combat::knight_sample_gear ()) shield roll
:= spent_charge (shielded : bool) (h : hunter) : hunter =
:= c = h::clock
h |> with_clock { c ?| block_charges = ? shielded ?> c::block_charges - 1 |: c::block_charges }
:= melee_struck (camp : camp) (seed : u32) (tick : i32) (index : i32) (h : hunter) : hunter =
:= shielded = h::clock::block_charges > 0
hurt_by (melee_hurt camp seed tick index shielded) (spent_charge shielded h)
:= creature_hits_hunter (camp : camp) (seed : u32) (tick : i32) (index : i32) (struck : foe) (p : pack) : pack =
with_slot index struck (p |> with_hunter (melee_struck camp seed tick index p::hunter))
:= arrow_hurt (arrow : combat::ranged_attack) (seed : u32) (tick : i32) (index : i32) (charges : i32) : i32 * i32 =
:= roll = key seed tick index PackCreatureShot
combat::ranged_hit_spending_a_charge arrow (combat::knight_sample_gear ()) charges roll
:= arrow_struck (a : combat::ranged_attack) (seed : u32) (tick : i32) (index : i32) (h : hunter) : hunter =
:= c = h::clock
:= hurt, charges = arrow_hurt a seed tick index c::block_charges
hurt_by hurt (h |> with_clock { c ?| block_charges = charges })
:= hunter_takes_arrow (arrow : combat::ranged_attack) (seed : u32) (tick : i32) (index : i32) (p : pack) : pack =
p |> with_hunter (arrow_struck arrow seed tick index p::hunter)
:== turn = { camp : camp; seed : u32; tick : i32; now : i32; index : i32 }
:= arrow_misses (t : turn) (arrow : combat::ranged_attack) (g : foe) (p : pack) (distance : i32) : bool =
g::strikes_at > t::now
|| combat::ranged_in_reach arrow distance (geometry::sight_line_clear (blocks_sight t::camp) g::at p::hunter::at)
= !-
:= arrow_meleed (t : turn) (timed : foe) (p : pack) (distance : i32) : pack =
? distance <= 1 ?> creature_hits_hunter t::camp t::seed t::tick t::index timed p |: with_slot t::index timed p
:= arrow_volley (t : turn) (arrow : combat::ranged_attack) (meleed : pack) : pack =
? combat::ranged_fires arrow (key t::seed t::tick (t::index + 100) PackCreatureShot)
?> hunter_takes_arrow arrow t::seed t::tick t::index meleed
|: meleed
:= arrow_lands (t : turn) (arrow : combat::ranged_attack) (g : foe) (p : pack) (distance : i32) : pack =
arrow_volley t arrow (arrow_meleed t { g ?| strikes_at = t::now + arrow::every_ms } p distance)
:= creature_shoots (t : turn) (arrow : combat::ranged_attack) (g : foe) (p : pack) : pack =
:= distance = distance_to g p
? arrow_misses t arrow g p distance ?> p |: arrow_lands t arrow g p distance
:= creature_think_phase_ms (seed : u32) (index : i32) : i32 = conv ((seed + conv index * 7u32) % 10u32) * tick_ms ()
:= thinks_now (seed : u32) (index : i32) (now : i32) : bool =
now % player_think_ms () = creature_think_phase_ms seed index
:= cardinal_step (k : i32) (from : geometry::position) : geometry::position =
? k = 0
?> terrain::shifted 0 -1 from
|? k = 1
?> terrain::shifted -1 0 from
|? k = 2
?> terrain::shifted 1 0 from
|: terrain::shifted 0 1 from
:= open_cardinal_count (free_at : i32 -> bool) : i32 =
:~ ^ count (k : i32) (acc : i32) : i32 =
? k >= 4
?> acc
|? free_at k
?> count (k + 1) (acc + 1)
|: count (k + 1) acc
count 0 0
:= nth_open_cardinal (free_at : i32 -> bool) (pick : i32) : i32 =
:~ ^ nth (k : i32) (left : i32) : i32 =
? free_at k
?>
? left = 1
?> k
|: nth (k + 1) (left - 1)
|: nth (k + 1) left
nth 0 pick
:= open_step (t : turn) (f : foe) (p : pack) (k : i32) : bool =
terrain::free t::camp::layout (taken_by_others t::index p) (cardinal_step k f::at)
:= wander_idle (t : turn) (f : foe) (count_open : i32) : bool =
f::next_step_at >= never () || thinks_now t::seed t::index t::now = !- || t::now < f::next_step_at || count_open = 0
:= wander_pick (t : turn) (count_open : i32) : i32 =
conv (keyed_rng::roll_between 1 (conv count_open) (key t::seed t::tick t::index PackWander))
:== foe_stats = creatures::creature_stats
:= foe_step_ms (t : turn) (s : foe_stats) (f : foe) : i32 = step_ms_at t::camp f::at s::speed s::step_ms
:= walked_foe (t : turn) (f : foe) (toward : geometry::position) : foe =
:= s = creatures::stats_of (kind_of t::index t::camp)
{ f ?| at = toward; next_step_at = t::now + geometry::step_cost (foe_step_ms t s f) f::at toward }
:= wandered_to (t : turn) (f : foe) (p : pack) (count_open : i32) : pack =
:= toward = cardinal_step (nth_open_cardinal (open_step t f p) (wander_pick t count_open)) f::at
with_slot t::index (walked_foe t f toward) p
:= wander_turn (t : turn) (p : pack) : pack =
:= f = slot t::index p
:= count_open = open_cardinal_count (open_step t f p)
? wander_idle t f count_open ?> p |: wandered_to t f p count_open
:= turn_at (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) : turn =
{ camp = camp; seed = seed; tick = tick; now = now; index = index }
:= wanders (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) (p : pack) : pack =
wander_turn (turn_at camp seed tick now index) p
:== reach_view = { centre : geometry::position; dx : i32; dy : i32; reach : i32 }
:= reach_view_of (f : foe) (p : pack) : reach_view =
:= centre = p::hunter::at
:= reach = geometry::chebyshev_distance f::at centre
{ centre = centre; dx = f::at::x - centre::x; dy = f::at::y - centre::y; reach = reach }
:= reach_side (v : reach_view) (k : i32) : bool =
? k = 0 ?> v::dy >= 0 |? k = 1 ?> v::dx >= 0 |? k = 2 ?> v::dx <= 0 |: v::dy <= 0
:= reach_step_clear (t : turn) (v : reach_view) (p : pack) (q : geometry::position) : bool =
terrain::free t::camp::layout (taken_by_others t::index p) q && (v::reach <= 1 || geometry::sees q v::centre)
:= keeps_reach (t : turn) (v : reach_view) (f : foe) (p : pack) (k : i32) : bool =
:= q = cardinal_step k f::at
reach_side v k && geometry::chebyshev_distance q v::centre = v::reach && reach_step_clear t v p q
:= dance_pick (t : turn) (count_open : i32) : i32 =
conv (keyed_rng::roll_between 1 (conv count_open) (key t::seed t::tick (t::index + 200) PackDance))
:= dance_toward (t : turn) (v : reach_view) (f : foe) (p : pack) (count_open : i32) : geometry::position =
cardinal_step (nth_open_cardinal (keeps_reach t v f p) (dance_pick t count_open)) f::at
:= dance_cost (t : turn) (f : foe) (toward : geometry::position) : i32 =
:= s = creatures::stats_of (kind_of t::index t::camp)
geometry::step_cost (foe_step_ms t s f) f::at toward * target_nearby_step_factor ()
:= retimed_target (t : turn) (p : pack) : pack =
:= h = p::hunter
? h::target = t::index
?> p |> with_hunter (h |> with_timers (swing_after_target_moves t::camp t::now h::clock::timers))
|: p
:= danced_to (t : turn) (v : reach_view) (f : foe) (p : pack) (count_open : i32) : pack =
:= toward = dance_toward t v f p count_open
retimed_target t (with_slot t::index { f ?| at = toward; next_step_at = t::now + dance_cost t f toward } p)
:= dance_step (t : turn) (p : pack) : pack =
:= f = slot t::index p
:= v = reach_view_of f p
:= count_open = open_cardinal_count (keeps_reach t v f p)
? count_open = 0 ?> p |: danced_to t v f p count_open
:= dance_rolls (t : turn) : bool =
thinks_now t::seed t::index t::now
&& keyed_rng::roll_between 1 100 (key t::seed t::tick t::index PackDance) > conv (canary_static_attack_chance ())
:= dances (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) (p : pack) : pack =
:= t = turn_at camp seed tick now index
?= camp::timing::dance ?|
| NeverDances => p
| DancesLikeCanary => ? dance_rolls t ?> dance_step t p |: p
:= respawn_check_until (f : foe) : i32 =
f::respawn::returns_at - canary_unblockable_spawn_delay_ms () + canary_spawn_check_ms ()
:= foe_respawns (t : turn) (k : foe_kind) (f : foe) (p : pack) : pack =
:= fresh = fresh_foe k f::spawn
with_slot t::index { fresh ?| respawn = { (fresh::respawn) ?| check_until = respawn_check_until f } } p
:= stepped_away (t : turn) (f : foe) (p : pack) : geometry::position =
terrain::step_away t::camp::layout (taken_by_others t::index p) p::hunter::at f::at
:= fled_foe (t : turn) (s : foe_stats) (f : foe) (p : pack) : foe =
:= away = stepped_away t f p
{ f ?| at = away; next_step_at = t::now + geometry::step_cost (foe_step_ms t s f) f::at away }
:= escaped_score (p : pack) : pack_score = { (p::score) ?| escapes = p::score::escapes + 1 }
:= foe_escapes (t : turn) (k : foe_kind) (f : foe) (p : pack) : pack =
:= reset = with_slot t::index (fresh_foe k f::spawn) p
:= h = p::hunter
{ reset ?| score = escaped_score p; hunter = { h ?| target = ? h::target = t::index ?> -1 |: h::target } }
:= foe_flees (t : turn) (k : foe_kind) (s : foe_stats) (f : foe) (p : pack) : pack =
:= fled = fled_foe t s f p
? geometry::chebyshev_distance p::hunter::at fled::at >= escape_distance () ?> foe_escapes t k f p
|: with_slot t::index fled p
:= keeps_range (t : turn) (s : foe_stats) (f : foe) (p : pack) : bool =
t::camp::foes::ranged && s::target_distance > 1
&& distance_to f p <= s::target_distance && geometry::sees f::at p::hunter::at
:= nearby_count (f : foe) (p : pack) : i32 =
? geometry::chebyshev_distance f::at p::hunter::at <= 1 ?> (? f::chase::nearby < 2 ?> f::chase::nearby + 1 |: 2)
|? f::chase::nearby > 0 ?> f::chase::nearby - 1
|: 0
:= slowed_by_target (f : foe) (away : geometry::position) (counted : i32) (cost : i32) : i32 =
? geometry::moved_diagonally f::at away = !- && counted >= 1 ?> cost * target_nearby_step_factor () |: cost
:= backing_step_at (t : turn) (s : foe_stats) (f : foe) (away : geometry::position) (counted : i32) : i32 =
t::now + slowed_by_target f away counted (geometry::step_cost (foe_step_ms t s f) f::at away)
:= backed_off (f : foe) (away : geometry::position) (counted : i32) (step_at : i32) : foe =
{ f ?| at = away; chase = { (f::chase) ?| met_target = !+; nearby = counted }; next_step_at = step_at }
:= foe_backs_off (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
:= counted = nearby_count f p
:= away = stepped_away t f p
with_slot t::index (backed_off f away counted (backing_step_at t s f away counted)) p
:= met_target_foe (f : foe) : foe = { f ?| chase = { (f::chase) ?| met_target = !+ } }
:= foe_dances_at_target (t : turn) (f : foe) (p : pack) : pack =
dances t::camp t::seed t::tick t::now t::index (with_slot t::index (met_target_foe f) p)
:= foe_keeps_range (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
? distance_to f p < s::target_distance ?> foe_backs_off t s f p |: foe_dances_at_target t f p
:= foe_loses_sight (t : turn) (f : foe) (p : pack) : pack =
? f::chase::acquired_at >= never () ?> p
|: with_slot t::index { f ?| chase = { (f::chase) ?| acquired_at = never () } } p
:= waits_to_acquire (t : turn) (f : foe) : bool =
(?= t::camp::motion::acquisition ?|
| ChasesAThinkAfterAcquiring => !+
| ChasesAtOnce => !-)
&& f::chase::acquired_at >= never ()
:= acquired_foe (t : turn) (s : foe_stats) (f : foe) : foe =
{ f ?|
chase = { (f::chase) ?| acquired_at = t::now }
next_step_at = t::now + player_think_ms () + foe_step_ms t s f
}
:= foe_acquires (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
? thinks_now t::seed t::index t::now ?> with_slot t::index (acquired_foe t s f) p |: p
:= follows_later (t : turn) (f : foe) : bool =
(?= t::camp::timing::follow ?|
| FollowsOnThink => !+
| FollowsAtOnce => !-)
&& t::now - f::next_step_at >= tick_ms ()
&& thinks_now t::seed t::index t::now = !-
:= chase_step_ms (t : turn) (s : foe_stats) (f : foe) : i32 =
:= base = foe_step_ms t s f
? f::chase::met_target ?> base * chase_step_factor () |: base
:= chased_foe (t : turn) (s : foe_stats) (f : foe) (toward : geometry::position) : foe =
{ f ?| at = toward; next_step_at = t::now + geometry::step_cost (chase_step_ms t s f) f::at toward }
:= foe_chases (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
:= toward = terrain::step_toward t::camp::layout (taken_by_others t::index p) p::hunter::at f::at
retimed_target t (with_slot t::index (chased_foe t s f toward) p)
:= foe_follows (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
? t::camp::foes::ranged && s::target_distance > 1 && thinks_now t::seed t::index t::now = !- ?> p
|? follows_later t f ?> p
|: foe_chases t s f p
:= foe_tracks (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
? geometry::sees f::at p::hunter::at = !- ?> foe_loses_sight t f p
|? waits_to_acquire t f ?> foe_acquires t s f p
|: foe_follows t s f p
:= foe_moves_steady (t : turn) (s : foe_stats) (f : foe) (p : pack) : pack =
? keeps_range t s f p ?> foe_keeps_range t s f p
|? distance_to f p <= 1 ?> foe_dances_at_target t f p
|: foe_tracks t s f p
:= foe_moves_live (t : turn) (k : foe_kind) (s : foe_stats) (f : foe) (p : pack) : pack =
? t::now < f::next_step_at ?> p
|? vitality::health f::life <= s::run_health ?> foe_flees t k s f p
|: foe_moves_steady t s f p
:= foe_moves (t : turn) (k : foe_kind) (s : foe_stats) (f : foe) (p : pack) : pack =
? f::respawn::returns_at <= t::now ?> foe_respawns t k f p
|: foe_moves_live t k s f p
:= paced_strike (t : turn) (g : foe) (period : i32) : i32 =
(? t::now - g::strikes_at < tick_ms () ?> g::strikes_at |: t::now) + period
:= melee_hits (t : turn) (g : foe) (strikes_at : i32) (p : pack) : pack =
creature_hits_hunter t::camp t::seed t::tick t::index { g ?| strikes_at = strikes_at } p
:= adjacent_and_ready (t : turn) (g : foe) (p : pack) : bool = distance_to g p <= 1 && g::strikes_at <= t::now
:= next_canary_round (t : turn) (g : foe) (period : i32) : i32 =
g::strikes_at + (t::now - g::strikes_at + period - 1) / period * period
:= on_canary_round (t : turn) (g : foe) (period : i32) (p : pack) : pack =
? t::now - g::strikes_at < tick_ms () ?> melee_hits t g (g::strikes_at + period) p
|: with_slot t::index { g ?| strikes_at = next_canary_round t g period } p
:= hit_ready (t : turn) (g : foe) (p : pack) : pack =
? adjacent_and_ready t g p ?> melee_hits t g (t::now + creature_strike_ms ()) p |: p
:= hit_once (t : turn) (g : foe) (period : i32) (p : pack) : pack =
? adjacent_and_ready t g p && t::now >= t::camp::foes::wakes_at + period
?> melee_hits t g (paced_strike t g period) p
|: p
:= hit_every (t : turn) (g : foe) (period : i32) (p : pack) : pack =
? adjacent_and_ready t g p ?> melee_hits t g (paced_strike t g period) p |: p
:= hit_round (t : turn) (g : foe) (period : i32) (p : pack) : pack =
? adjacent_and_ready t g p && t::now >= t::camp::foes::wakes_at + period ?> on_canary_round t g period p |: p
:= foe_melee (t : turn) (g : foe) (p : pack) =
?= t::camp::timing::melee ?|
| MeleeWhenReady => hit_ready t g p
| MeleeEveryOnceTargeted n => hit_once t g n p
| MeleeWhenReadyEvery n => hit_every t g n p
| MeleeOnCanaryRounds n => hit_round t g n p
:= foe_shoots (t : turn) (k : foe_kind) (g : foe) (p : pack) : pack =
creature_shoots t (creatures::ranged_attack_of k) g p
:= foe_strikes (t : turn) (k : foe_kind) (s : foe_stats) (q : pack) : pack =
:= g = slot t::index q
? vitality::health g::life <= s::run_health ?> q
|? t::camp::foes::ranged && creatures::is_ranged k ?> foe_shoots t k g q
|: foe_melee t g q
:= foe_turn (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) (p : pack) : pack =
:= t = turn_at camp seed tick now index
:= k = kind_of index camp
:= s = creatures::stats_of k
foe_strikes t k s (foe_moves t k s (slot index p) p)
:= change_target_percent (kind : creatures::arena_creature) : i32 =
?= kind ?|
| Cyclops => 10
| Hunter => 10
| _ => 0
:= damage_strategy_percent (kind : creatures::arena_creature) : i32 =
?= kind ?|
| Cyclops => 30
| _ => 0
:= sees_bits (f : foe) (p : pack) : i32 * i32 =
(? geometry::sees f::at p::hunter::at ?> 1 |: 0), (? geometry::sees f::at p::ally::at ?> 2 |: 0)
:= seen_of (bits : i32 * i32) : i32 =
:= first, second = bits
first + second
:= entered_bits (f : foe) (bits : i32 * i32) : bool * bool =
:= first, second = bits
first = 1 && (f::aim::in_view &&& 1) = 0, second = 2 && (f::aim::in_view &&& 2) = 0
:= sector_rank (at : geometry::position) : i32 = (at::y / 16) * 65536 + at::x / 16
:= aim_newcomer (f : foe) (newcomer : i32) : i32 = ? f::aim::in_view = 0 ?> newcomer |: f::aim::entrant
:= foe_moved_since_aim (f : foe) (appeared : bool) : bool =
appeared = !- && (f::at::x <> f::aim::last_x || f::at::y <> f::aim::last_y)
:= entrant_by_order (t : turn) (appeared : bool) (p : pack) : i32 =
? appeared && t::now < 1000 ?> 1
|? sector_rank p::hunter::at <= sector_rank p::ally::at ?> 0
|: 1
:= entrant_after_entry (f : foe) (moved : bool) (newcomer : i32) : i32 =
? f::aim::last_x < 0 || moved ?> aim_newcomer f newcomer |: newcomer
:= entrant_by_entry (f : foe) (first : bool) (second : bool) : i32 =
:= moved = foe_moved_since_aim f (f::aim::last_x < 0)
? second ?> entrant_after_entry f moved 1
|? first ?> entrant_after_entry f moved 0
|: f::aim::entrant
:= aimed_entrant (t : turn) (f : foe) (p : pack) (bits : i32 * i32) : i32 =
:= one, two = entered_bits f bits
? one && two ?> entrant_by_order t (f::aim::last_x < 0) p
|: entrant_by_entry f one two
:= aim_rolls (t : turn) : i32 * i32 =
conv (keyed_rng::roll_between 1 100 (key t::seed t::tick t::index PackChangeTarget)),
conv (keyed_rng::roll_between 1 100 (key t::seed t::tick t::index PackSearchTarget))
:= is_changing (t : turn) (k : foe_kind) (f : foe) (rolled : i32) : bool =
(f::aim::aims_at < 0) = !- && t::now >= f::aim::retarget_at && rolled <= change_target_percent k
:= aim_kept (f : foe) (bits : i32 * i32) (changing : bool) : bool =
seen_of bits = 0 || (f::aim::aims_at < 0) = !- && changing = !-
:= distance_from (p : pack) (who : i32) (f : foe) : i32 =
geometry::chebyshev_distance (? who = 0 ?> p::hunter::at |: p::ally::at) f::at
:= nearest_of (p : pack) (f : foe) (entrant : i32) : i32 =
:= other = 1 - entrant
? distance_from p other f < distance_from p entrant f ?> other |: entrant
:= other_dealt (f : foe) (other : i32) : i32 = ? other = 0 ?> f::aim::damage_first |: f::aim::damage_second
:= by_damage (k : foe_kind) (f : foe) (entrant : i32) (strategy : i32) (nearest : i32) : i32 =
:= other = 1 - entrant
? strategy > 100 - damage_strategy_percent k ?> (? other_dealt f other > 0 ?> other |: entrant) |: nearest
:= changed_aim (k : foe_kind) (entrant : i32) (strategy : i32) (nearest : i32) : i32 =
? (creatures::stats_of k)::target_distance <= 1 ?> (? strategy <= 50 ?> entrant |: 1 - entrant) |: nearest
:= chosen_aim (k : foe_kind) (f : foe) (p : pack) (entrant : i32) (changing : bool) (strategy : i32) : i32 =
:= nearest = nearest_of p f entrant
? changing ?> changed_aim k entrant strategy nearest |: by_damage k f entrant strategy nearest
:= aim_after_roll
(k : foe_kind) (f : foe) (p : pack) (bits : i32 * i32) (entrant : i32) (changing : bool) (strategy : i32) : i32
=
? aim_kept f bits changing ?> f::aim::aims_at |: chosen_aim k f p entrant changing strategy
:= next_aim (t : turn) (f : foe) (p : pack) (bits : i32 * i32) (entrant : i32) : i32 =
:= k = kind_of t::index t::camp
:= rolled, strategy = aim_rolls t
aim_after_roll k f p bits entrant (is_changing t k f rolled) strategy
:= retarget_due (t : turn) (f : foe) : i32 = ? t::now >= f::aim::retarget_at ?> t::now + 4000 |: f::aim::retarget_at
:= aim_seen (t : turn) (f : foe) (aims_at : i32) (entrant : i32) (seen : i32) =
{ (f::aim) ?|
aims_at = aims_at
entrant = entrant
in_view = seen
retarget_at = retarget_due t f
last_x = f::at::x
last_y = f::at::y
}
:= aimed_foe (t : turn) (f : foe) (p : pack) (bits : i32 * i32) : foe =
:= entrant = aimed_entrant t f p bits
{ f ?| aim = aim_seen t f (next_aim t f p bits entrant) entrant (seen_of bits) }
:= aimed_by_canary (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) (p : pack) : foe =
:= t = turn_at camp seed tick now index
:= f = slot index p
aimed_foe t f p (sees_bits f p)
:= ally_is_nearer (f : foe) (p : pack) : bool =
geometry::chebyshev_distance p::ally::at f::at < geometry::chebyshev_distance p::hunter::at f::at
:= foe_turn_at (t : turn) (p : pack) : pack = foe_turn t::camp t::seed t::tick t::now t::index p
:= idle_turn (t : turn) (p : pack) : pack =
?= t::camp::timing::idle ?|
| StandsStill => p
| WandersLikeCanary => wanders t::camp t::seed t::tick t::now t::index p
:= aimed_view (t : turn) (p : pack) : bool * pack =
:= aimed = aimed_by_canary t::camp t::seed t::tick t::now t::index p
:= q = with_slot t::index aimed p
? aimed::aim::aims_at = 1 ?> !+, turned q |: !-, q
:= ally_view (t : turn) (p : pack) : bool * pack =
? t::camp::crew::party > 1 && ally_is_nearer (slot t::index p) p ?> !+, turned p |: !-, p
:= foe_view (t : turn) (p : pack) : bool * pack =
? t::camp::crew::party > 1 && t::camp::crew::party_targeting ?> aimed_view t p |: ally_view t p
:= viewed_turn (t : turn) (p : pack) : pack =
:= flipped, q = foe_view t p
:= moved = foe_turn_at t q
? flipped ?> turned moved |: moved
:= foe_acts_at (t : turn) (p : pack) : pack =
? t::now < t::camp::foes::wakes_at ?> idle_turn t p |: viewed_turn t p
:= foe_acts (camp : camp) (seed : u32) (tick : i32) (now : i32) (index : i32) (p : pack) : pack =
foe_acts_at (turn_at camp seed tick now index) p
:= picked_in (mask : u32) (i : i32) : bool = ((mask >>> i) &&& 1u32) = 1u32
:== beat = { camp : camp; seed : u32; tick : i32; now : i32 }
:= foes_act_where (b : beat) (mask : u32) (p : pack) : pack =
:~ ^ each (i : i32) (q : pack) : pack =
? i >= band_size () ?> q
|? picked_in mask i ?> each (i + 1) (foe_acts b::camp b::seed b::tick b::now i q)
|: each (i + 1) q
each 0 p
:= foes_act (camp : camp) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
:~ ^ each (i : i32) (q : pack) : pack =
? i >= band_size ()
?> q
|: each (i + 1) (foe_acts camp seed tick now i q)
each 0 p
:= health_percent_of (camp : camp) (index : i32) (f : foe) : i32 =
vitality::health f::life * 100 / (creatures::stats_of (kind_of index camp))::max_health
:= candidate_of (index : i32) (p : pack) (camp : camp) : policy::candidate =
:= f = slot index p
{
is_current = p::hunter::target = index
distance = distance_to f p
health_percent = health_percent_of camp index f
}
:= better_target (camp : camp) (order : policy::targeting) (p : pack) (a : i32) (b : i32) : i32 =
? a < 0 ?> b
|? b < 0 ?> a
|? policy::prefers_by order (candidate_of a p camp) (candidate_of b p camp) ?> a
|: b
:= visible_pick (p : pack) (index : i32) : i32 = ? distance_to (slot index p) p <= sight_range () ?> index |: -1
:= choose_target (camp : camp) (order : policy::targeting) (p : pack) : i32 =
:~ ^ fold (i : i32) (best : i32) : i32 =
? i >= band_size () ?> best |: fold (i + 1) (better_target camp order p best (visible_pick p i))
fold 1 (visible_pick p 0)
:= aimed_by (camp : camp) (order : policy::targeting) (p : pack) : pack =
:= h = p::hunter
p |> with_hunter { h ?| target = choose_target camp order p }
:= heal_cooldown () = vocations::as_cooldown (vocations::knight_heal ())
:= heal_castable (now : i32) (h : hunter) : bool = cooldowns::can_cast now (heal_cooldown ()) h::clock::timers
:= strike_castable (now : i32) (h : hunter) : bool =
cooldowns::can_cast now (cooldowns::strike_spell ()) h::clock::timers
:= reach_of (p : pack) =
:< policy
:= h = p::hunter
? h::target < 0 ?> NoTarget |? distance_to (slot h::target p) p <= 1 ?> InReach |: OutOfReach
:= observe (now : i32) (p : pack) : policy::observation =
:= h = p::hunter
{
health = units::value_of h::health
heal_ready = heal_castable now h
strike_ready = strike_castable now h
target = reach_of p
}
:== action_proof = readiness::ready readiness::action_slot
:= action_gap_after (slot : action_proof) : units::amount quantity::milliseconds =
units::plus (readiness::since slot) (quantity::ms (parity_action_gap_ms ()))
:= heal_amount (seed : u32) (tick : i32) (o : offense) (h : hunter) : i32 =
conv (
keyed_rng::roll_between (conv o::heal_low) (conv o::heal_high)
(key seed tick (99 + 100 * h::call_sign) PackPlayerHeal)
)
:= heal_timers (slot : action_proof) (h : hunter) : cooldowns::timers =
:= now = units::value_of (readiness::since slot)
cooldowns::after_cast now (vocations::as_cooldown (vocations::knight_heal ())) h::clock::timers
:= healed_clock (slot : action_proof) (h : hunter) : hunter_clock =
{ (h::clock) ?| timers = heal_timers slot h; next_action_at = action_gap_after slot }
:= healed_health (camp : camp) (amount : i32) (h : hunter) : hit_points =
units::capped (player_max_health camp) (units::plus h::health (quantity::hp amount))
:= healed_hunter (camp : camp) (slot : action_proof) (amount : i32) (h : hunter) : hunter =
{ h ?|
health = healed_health camp amount h
clock = healed_clock slot h
tally = { (h::tally) ?| potions = h::tally::potions + 1 }
}
:= player_heals (camp : camp) (seed : u32) (tick : i32) (slot : action_proof) (p : pack) : pack =
:= h = p::hunter
p |> with_hunter (healed_hunter camp slot (heal_amount seed tick (hunter_of camp) h) h)
:= hunter_attacker (o : offense) =
{
mode =
:< damage
Balanced
level = o::level
attack_value = o::weapon_attack
attack_skill = o::weapon_skill
}
:= player_hit (t : turn) (h : hunter) (o : offense) (s : foe_stats) : i32 =
combat::melee_hit (hunter_attacker o) { armor = s::armor; defense = s::defense }
(key t::seed t::tick (t::index + 100 * h::call_sign) PackPlayerHit)
:= strike_timed (t : turn) (h : hunter) : hunter =
h |> with_timers (cooldowns::after_cast t::now (cooldowns::strike_spell ()) h::clock::timers)
:= dealt_aim (h : hunter) (f : foe) (dealt : i32) : foe_aim =
? h::call_sign = 0 ?> { (f::aim) ?| damage_first = f::aim::damage_first + dealt }
|: { (f::aim) ?| damage_second = f::aim::damage_second + dealt }
:= strike_survived (t : turn) (h : hunter) (f : foe) (wounded : living) (timed : pack) : pack =
:= dealt = vitality::health f::life - vitality::health wounded
with_slot t::index { f ?| life = wounded; aim = dealt_aim h f dealt } timed
:= respawn_check (now : i32) (f : foe) (delay : i32) : i32 =
? now < f::respawn::check_until ?> f::respawn::check_until + delay - canary_spawn_check_ms () |: now + delay
:= returning_foe (awaiting : foe) (f : foe) (returns : i32) : foe =
{ awaiting ?| spawn = f::spawn; respawn = { (awaiting::respawn) ?| returns_at = returns } }
:= slain_foe (t : turn) (k : foe_kind) (f : foe) (awaiting : foe) : foe =
?= t::camp::foes::rule ?|
| Respawning => fresh_foe k f::spawn
| Clearing => buried_foe k
| RespawningAfter delay => returning_foe awaiting f (respawn_check t::now f delay)
:= loot_lucky (t : turn) (drop : creatures::gold_drop) : bool =
conv (keyed_rng::roll_between 1 100000 (key t::seed t::tick t::index PackLoot)) <= drop::chance_per_100000
:= loot_coins (t : turn) (k : foe_kind) : i32 =
:= drop = creatures::gold_drop_of k
? loot_lucky t drop
?> conv (keyed_rng::roll_between 1 (conv drop::max_count) (key t::seed (t::tick + 1) t::index PackLoot))
|: 0
:= slain_score (q : pack) (s : foe_stats) (coins : i32) : pack_score =
{ (q::score) ?|
kills = q::score::kills + 1
experience = q::score::experience + s::experience
gold = units::plus q::score::gold (quantity::gp coins)
}
:= strike_slain (t : turn) (k : foe_kind) (s : foe_stats) (f : foe) (h : hunter) (timed : pack) =
:= reset = with_slot t::index (slain_foe t k f (buried_foe k)) timed
{ reset ?| score = slain_score timed s (loot_coins t k); hunter = { h ?| target = -1 } }
:= strike_outcome (t : turn) (k : foe_kind) (s : foe_stats) (h : hunter) (q : pack) (hit : i32) =
:< vitality
:= f = slot t::index q
?= strike hit f::life ?|
| Survived wounded => strike_survived t h f wounded q
| Slain _ => strike_slain t k s f h q
:= strikes_target (t : turn) (h : hunter) (p : pack) : pack =
:= k = kind_of t::index t::camp
:= s = creatures::stats_of k
:= timed = strike_timed t h
strike_outcome t k s timed (p |> with_hunter timed) (player_hit t h (hunter_of t::camp) s)
:= player_strikes
(camp : camp) (seed : u32) (tick : i32) (proof : readiness::ready readiness::attack_group) (p : pack)
: pack
=
:= h = p::hunter
strikes_target (turn_at camp seed tick (units::value_of (readiness::since proof)) h::target) h p
:= stepped_hunter (c : camp) (now : i32) (to : geometry::position) (h : hunter) : hunter =
:= q = { h ?| at = to; next_step_at = now + geometry::step_cost (hunter_step_ms_at c h::at) h::at to }
q |> with_timers (swing_after_step c now h::clock::timers)
:= hunter_steps (camp : camp) (now : i32) (toward : geometry::position) (p : pack) : pack =
p |> with_hunter (stepped_hunter camp now toward p::hunter)
:= no_faster_than_live_chase (server : i32) : i32 = ? server > live_chase_step_ms () ?> server |: live_chase_step_ms ()
:= chase_step_ms_at (camp : camp) (at : geometry::position) : i32 =
:= server = hunter_step_ms_at camp at
?= camp::knight::chase_pace ?|
| ChasesAtTheServerPace => server
| ChasesAtTheProbePace => no_faster_than_live_chase server
:= fresh_walk_delays (camp : camp) : bool =
?= camp::motion::approach ?|
| ApproachesAfterAFreshWalkDelay => !+
| ApproachesAtOnce => !-
:= approach_delayed (camp : camp) (now : i32) (h : hunter) (toward : geometry::position) : bool =
fresh_walk_delays camp && now - h::next_step_at >= tick_ms () && geometry::same_tile toward h::at = !-
:= approach_step_at (camp : camp) (now : i32) (h : hunter) (toward : geometry::position) : i32 =
now + geometry::step_cost (chase_step_ms_at camp h::at) h::at toward
:= approach_waits (camp : camp) (now : i32) (h : hunter) (toward : geometry::position) (p : pack) : pack =
p |> with_hunter { h ?| next_step_at = approach_step_at camp now h toward }
:= approach_walks (camp : camp) (now : i32) (h : hunter) (toward : geometry::position) (p : pack) : pack =
:= q = hunter_steps camp now toward p
:= k = q::hunter
q |> with_hunter { k ?| next_step_at = approach_step_at camp now h toward }
:= approach_from (camp : camp) (now : i32) (h : hunter) (toward : geometry::position) (p : pack) : pack =
? now < h::next_step_at ?> p
|? approach_delayed camp now h toward ?> approach_waits camp now h toward p
|: approach_walks camp now h toward p
:= player_approaches (camp : camp) (now : i32) (p : pack) : pack =
:= h = p::hunter
approach_from camp now h (terrain::step_toward camp::layout (taken_by_foes p) (slot h::target p)::at h::at) p
:= threat_rank (f : foe) : i32 = f::at::x * 100000 + f::at::y
:= earlier_threat (p : pack) (a : i32) (b : i32) : i32 =
? a < 0 ?> b |? b < 0 ?> a |? threat_rank (slot b p) < threat_rank (slot a p) ?> b |: a
:= adjacent_pick (p : pack) (index : i32) : i32 = ? distance_to (slot index p) p <= 1 ?> index |: -1
:= nearest_attacker_index (p : pack) : i32 =
:~ ^ fold (i : i32) (best : i32) : i32 =
? i >= band_size () ?> best |: fold (i + 1) (earlier_threat p best (adjacent_pick p i))
fold 1 (adjacent_pick p 0)
:= slot_of_nearest_attacker (p : pack) : foe =
:= chosen = nearest_attacker_index p
slot (? chosen < 0 ?> 0 |: chosen) p
:== retreat_beat = { camp : camp; now : i32; slot : action_proof }
:= foes_near (p : pack) (q : geometry::position) : i32 =
fixed_views::counted (\ (f : foe) => geometry::chebyshev_distance f::at q <= 1) p::foes
:= plain_step_away (camp : camp) (threat : foe) (p : pack) : geometry::position =
terrain::step_away camp::layout (taken_by_foes p) threat::at p::hunter::at
:= probe_step_away (camp : camp) (threat : foe) (p : pack) : geometry::position =
terrain::step_away_like_the_live_probe camp::layout (taken_by_foes p) threat::at p::hunter::at
:= scored_step_away (camp : camp) (w : terrain::footing) (threat : foe) (p : pack) : geometry::position =
terrain::scored_step camp::layout (taken_by_foes p) (foes_near p) w threat::at p::hunter::at
:= retreat_target (camp : camp) (rule : footing_rule) (threat : foe) (p : pack) =
?= rule ?|
| StepAway => plain_step_away camp threat p
| StepAwayLikeTheLiveProbe => probe_step_away camp threat p
| ScoredStep w => scored_step_away camp w threat p
:= retreat_step_at (camp : camp) (now : i32) (h : hunter) (away : geometry::position) : i32 =
now + geometry::step_cost (retreat_step_ms_at camp h::at) h::at away
:= retreat_clock (r : retreat_beat) (stuck : bool) (h : hunter) : hunter_clock =
{ (h::clock) ?|
next_action_at = action_gap_after r::slot
timers = ? stuck ?> h::clock::timers |: swing_after_step r::camp r::now h::clock::timers
}
:= retreat_tally (stuck : bool) (h : hunter) : hunter_tally =
{ (h::tally) ?|
retreats = ? stuck ?> h::tally::retreats |: h::tally::retreats + 1
cornered = ? stuck ?> h::tally::cornered + 1 |: h::tally::cornered
}
:= retreated_hunter (r : retreat_beat) (stuck : bool) (to : geometry::position) (h : hunter) : hunter =
:= moved = { h ?| at = to; next_step_at = retreat_step_at r::camp r::now h to }
{ moved ?| clock = retreat_clock r stuck h; tally = retreat_tally stuck h }
:= retreat_with (r : retreat_beat) (away : geometry::position) (p : pack) : pack =
p |> with_hunter (retreated_hunter r (geometry::same_tile away p::hunter::at) away p::hunter)
:= player_retreats (camp : camp) (rule : footing_rule) (slot : action_proof) (p : pack) : pack =
:= r = { camp = camp; now = units::value_of (readiness::since slot); slot = slot }
retreat_with r (retreat_target camp rule (slot_of_nearest_attacker p) p) p
:= health_fraction (camp : camp) (p : pack) : i32 =
units::value_of p::hunter::health * network::one () / units::value_of (player_max_health camp)
:= target_health (f : foe) (s : foe_stats) : i32 = vitality::health f::life * network::one () / s::max_health
:= observed_inputs (hp : i32) (attackers : i32) (f : foe) (s : foe_stats) (o : policy::observation) (p : pack) =
network::inputs_of hp attackers (target_health f s) (distance_to f p) o::heal_ready o::strike_ready
(vitality::health f::life <= s::run_health)
:= target_inputs (camp : camp) (now : i32) (p : pack) (hp : i32) (attackers : i32) : network::inputs =
:= f = slot p::hunter::target p
observed_inputs hp attackers f (creatures::stats_of (kind_of p::hunter::target camp)) (observe now p) p
:= features (camp : camp) (attackers : i32) (now : i32) (p : pack) : network::inputs =
:= hp = health_fraction camp p
? p::hunter::target < 0 ?> network::inputs_of hp attackers 0 0 !- !- !- |: target_inputs camp now p hp attackers
:= temperament_of (kind : creatures::arena_creature) : policy::temperament =
:< policy
? (creatures::stats_of kind)::run_health > 0 ?> Flees |: Stands
:== temper_count = policy::by_temperament i32
:= temper_joined (camp : camp) (index : i32) (crowd : temper_count) : temper_count =
:< policy
?= temperament_of (kind_of index camp) ?|
| Stands => { crowd ?| standing = crowd::standing + 1 }
| Flees => { crowd ?| fleeing = crowd::fleeing + 1 }
:= joined_crowd (camp : camp) (p : pack) (index : i32) (crowd : temper_count) : temper_count =
? distance_to (slot index p) p > 1 ?> crowd |: temper_joined camp index crowd
:= crowd_of (camp : camp) (p : pack) : temper_count =
:~ ^ fold (i : i32) (crowd : temper_count) : temper_count =
? i >= band_size () ?> crowd |: fold (i + 1) (joined_crowd camp p i crowd)
fold 0 { standing = 0; fleeing = 0 }
:= rules_heals (a : policy::adaptive) (attackers : i32) (now : i32) (p : pack) : bool =
policy::heals (policy::thresholds_of a attackers) (observe now p)
:= network_heals (b : beat) (w : network::weights) (attackers : i32) (p : pack) : bool =
(observe b::now p)::heal_ready && (network::decide w (features b::camp attackers b::now p))::heal
:= temperate_heals (b : beat) (r : policy::temperate) (p : pack) : bool =
policy::heals (policy::temperate_thresholds r (crowd_of b::camp p)) (observe b::now p)
:= wants_heal (b : beat) (d : brain) (attackers : i32) (p : pack) : bool =
?= d ?|
| Rules a => rules_heals a attackers b::now p
| Network w => network_heals b w attackers p
| Temperate r => temperate_heals b r p
| RandomControl => !-
:= network_engagement (o : policy::observation) (engage : bool) : policy::engagement =
:< policy
? engage ?>
?= o::target ?|
| InReach => ? o::strike_ready ?> Strike |: Hold
| OutOfReach => Approach
| NoTarget => Hold
|: Hold
:== plan = bool * policy::engagement
:= rules_plan (a : policy::adaptive) (attackers : i32) (now : i32) (p : pack) : plan =
:< policy
retreats a attackers, engages (thresholds_of a attackers) (observe now p)
:= network_plan (b : beat) (w : network::weights) (attackers : i32) (p : pack) : plan =
:= o = observe b::now p
:= d = network::decide w (features b::camp attackers b::now p)
d::retreat, network_engagement o d::engage
:= temperate_plan (b : beat) (r : policy::temperate) (p : pack) : plan =
:< policy
:= crowd = crowd_of b::camp p
temperate_retreats r crowd, engages (temperate_thresholds r crowd) (observe b::now p)
:= random_plan () : plan =
:< policy
!-, Hold
:= after_heal_plan (b : beat) (d : brain) (attackers : i32) (p : pack) : plan =
?= d ?|
| Rules a => rules_plan a attackers b::now p
| Network w => network_plan b w attackers p
| Temperate r => temperate_plan b r p
| RandomControl => random_plan ()
:= next_waypoint (route : patrol_route) (p : pack) : pack =
:= h = p::hunter
p |> with_hunter { h ?| waypoint = ? h::waypoint + 1 >= route::count ?> 0 |: h::waypoint + 1 }
:= walk_or_advance (camp : camp) (route : patrol_route) (now : i32) (toward : geometry::position) (p : pack) =
? geometry::same_tile toward p::hunter::at ?> next_waypoint route p |: hunter_steps camp now toward p
:= patrol_walk (camp : camp) (route : patrol_route) (now : i32) (goal : geometry::position) (p : pack) : pack =
walk_or_advance camp route now (terrain::step_toward camp::layout (taken_by_foes p) goal p::hunter::at) p
:= patrol_goal (c : camp) (route : patrol_route) (now : i32) (p : pack) : pack =
:= goal = fixed_views::band_at p::hunter::waypoint route::points
? geometry::chebyshev_distance goal p::hunter::at <= 1 ?> next_waypoint route p |: patrol_walk c route now goal p
:= patrol_step (camp : camp) (route : patrol_route) (now : i32) (p : pack) : pack =
? now < p::hunter::next_step_at ?> p |: patrol_goal camp route now p
:= awaiting_return (f : foe) : bool = f::respawn::returns_at < never ()
:= east_of (border : i32) (f : foe) : bool = f::spawn::x >= border
:= in_territory (border : i32) (index : i32) (p : pack) : bool =
east_of border (slot index p) = (p::hunter::call_sign = 0)
:= travel_ms (camp : camp) (f : foe) (p : pack) : i32 =
geometry::chebyshev_distance f::spawn p::hunter::at * terrain::on_ground camp::layout (hunter_of camp)::step_ms
:= return_wait (now : i32) (f : foe) (travel : i32) : i32 =
? f::respawn::returns_at - now > travel ?> f::respawn::returns_at - now |: travel
:= spawn_wait (now : i32) (f : foe) (p : pack) (travel : i32) : i32 =
? awaiting_return f ?> return_wait now f travel
|? geometry::chebyshev_distance f::spawn p::hunter::at <= 1 ?> never ()
|: travel
:= expected_wait_ms (camp : camp) (now : i32) (index : i32) (p : pack) : i32 =
:= f = slot index p
? index >= camp::foes::size ?> never () |: spawn_wait now f p (travel_ms camp f p)
:= claimed_wait_ms (camp : camp) (claims : i32 -> bool) (now : i32) (index : i32) (p : pack) : i32 =
? claims index
?> expected_wait_ms camp now index p
|: never ()
:= slot_wait (camp : camp) (claims : i32 -> bool) (now : i32) (p : pack) (i : i32) : i32 * i32 =
claimed_wait_ms camp claims now i p, i
:= sooner_wait (best : i32 * i32) (next : i32 * i32) : i32 * i32 =
:= best_wait, _ = best
:= next_wait, _ = next
? next_wait < best_wait ?> next |: best
:= soonest_claimed_spawn (camp : camp) (claims : i32 -> bool) (now : i32) (p : pack) : i32 =
:= w = slot_wait camp claims now p
:= _, best = sooner_wait (sooner_wait (sooner_wait (sooner_wait (sooner_wait (w 0) (w 1)) (w 2)) (w 3)) (w 4)) (w 5)
best
:= soonest_spawn (camp : camp) (now : i32) (p : pack) : i32 = soonest_claimed_spawn camp (\ _ => !+) now p
:= step_to_spawn (camp : camp) (now : i32) (index : i32) (p : pack) : pack =
:= toward = terrain::step_toward camp::layout (taken_by_foes p) (slot index p)::spawn p::hunter::at
? geometry::same_tile toward p::hunter::at ?> p |: hunter_steps camp now toward p
:= claims_of (border : i32) (p : pack) (index : i32) : bool = in_territory border index p
:= territory_claim (camp : camp) (border : i32) (now : i32) (p : pack) : pack =
:= index = soonest_claimed_spawn camp (claims_of border p) now p
? claimed_wait_ms camp (claims_of border p) now index p >= never () ?> p |: step_to_spawn camp now index p
:= territory_step (camp : camp) (border : i32) (now : i32) (p : pack) : pack =
? now < p::hunter::next_step_at ?> p |: territory_claim camp border now p
:= sweep_pick (camp : camp) (now : i32) (p : pack) : pack =
:= index = soonest_spawn camp now p
? expected_wait_ms camp now index p >= never () ?> p |: step_to_spawn camp now index p
:= sweep_step (camp : camp) (now : i32) (p : pack) : pack =
? now < p::hunter::next_step_at ?> p |: sweep_pick camp now p
:| random_move =
| DrinkPotion
| EngageTarget
| StepSomewhere
| StandStill
:= random_move_of (seed : u32) (tick : i32) (sign : i32) : random_move =
:= pick = keyed_rng::roll_between 0 3 (key seed tick (98 + 100 * sign) PackRandomControl)
? pick = 0 ?> DrinkPotion |? pick = 1 ?> EngageTarget |? pick = 2 ?> StepSomewhere |: StandStill
:= action_gate_at (now : i32) (h : hunter) : readiness::gate readiness::action_slot =
readiness::action_gate (quantity::ms now) h::clock::next_action_at
:= can_drink (b : beat) (p : pack) : bool =
p::hunter::tally::potions < b::camp::crew::potion_budget
&& cooldowns::can_cast b::now (vocations::as_cooldown (vocations::knight_heal ())) p::hunter::clock::timers
:= drinks (b : beat) (p : pack) : pack =
?= action_gate_at b::now p::hunter ?|
| Open slot => ? can_drink b p ?> player_heals b::camp b::seed b::tick slot p |: p
| ClosedUntil _ => p
:= strike_if_ready (b : beat) (p : pack) : pack =
?= readiness::attack_gate (quantity::ms b::now) p::hunter::clock::timers ?|
| Open proof => player_strikes b::camp b::seed b::tick proof p
| ClosedUntil _ => p
:= engages_target (b : beat) (p : pack) : pack =
? p::hunter::target < 0 ?> p
|? distance_to (slot p::hunter::target p) p <= 1 ?> strike_if_ready b p
|: player_approaches b::camp b::now p
:= random_step_index (b : beat) (p : pack) : i32 =
conv (keyed_rng::roll_between 0 7 (key b::seed b::tick (97 + 100 * p::hunter::call_sign) PackRandomControl))
:= steps_somewhere (b : beat) (p : pack) : pack =
:= next = terrain::neighbor (random_step_index b p) p::hunter::at
? b::now < p::hunter::next_step_at ?> p
|? terrain::free b::camp::layout (taken_by_foes p) next ?> hunter_steps b::camp b::now next p
|: p
:= random_moved (b : beat) (p : pack) : pack =
?= random_move_of b::seed b::tick p::hunter::call_sign ?|
| DrinkPotion => drinks b p
| EngageTarget => engages_target b p
| StepSomewhere => steps_somewhere b p
| StandStill => p
:= beat_at (camp : camp) (seed : u32) (tick : i32) (now : i32) : beat =
{ camp = camp; seed = seed; tick = tick; now = now }
:= random_pick (camp : camp) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
random_moved (beat_at camp seed tick now) p
:= auto_attack (camp : camp) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
? p::hunter::target < 0 ?> p
|? distance_to (slot p::hunter::target p) p <= 1 ?> strike_if_ready (beat_at camp seed tick now) p
|: p
:= random_acts (camp : camp) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
random_pick camp seed tick now p |> auto_attack camp seed tick now
:= attackers_of (p : pack) : i32 = fixed_views::counted (\ (f : foe) => distance_to f p <= 1) p::foes
:= heals_now (b : beat) (t : tactic) (attackers : i32) (q : pack) : bool =
q::hunter::tally::potions < b::camp::crew::potion_budget && wants_heal b t::brain attackers q
:= heal_gate (b : beat) (heals : bool) (q : pack) : pack =
?= readiness::action_gate (quantity::ms b::now) q::hunter::clock::next_action_at ?|
| Open slot => ? heals ?> player_heals b::camp b::seed b::tick slot q |: q
| ClosedUntil _ => q
:= patrol_turn (b : beat) (t : tactic) (q : pack) : pack =
?= t::patrol ?|
| Stays => q
| Patrols r => patrol_step b::camp r b::now q
| SweepsSpawns => sweep_step b::camp b::now q
| SweepsTerritory border => territory_step b::camp border b::now q
:= strike_gate (b : beat) (q : pack) : pack =
?= readiness::attack_gate (quantity::ms b::now) q::hunter::clock::timers ?|
| Open proof => player_strikes b::camp b::seed b::tick proof q
| ClosedUntil _ => q
:= engaged (b : beat) (plan : policy::engagement) (q : pack) : pack =
:< policy
?= plan ?|
| Strike => strike_gate b q
| Approach => player_approaches b::camp b::now q
| Hold => q
:= retreat_gate (b : beat) (t : tactic) (p : policy::engagement) (q : pack) : pack =
?= readiness::action_gate (quantity::ms b::now) q::hunter::clock::next_action_at ?|
| Open slot => player_retreats b::camp t::footing slot q
| ClosedUntil _ => engaged b p q
:= retreat_or_engage (b : beat) (t : tactic) (retreat : bool) (plan : policy::engagement) (q : pack) : pack =
? retreat && b::now >= q::hunter::next_step_at ?> retreat_gate b t plan q |: engaged b plan q
:= target_turn (b : beat) (t : tactic) (attackers : i32) (q : pack) : pack =
?= after_heal_plan b t::brain attackers q ?|
| retreat, plan => retreat_or_engage b t retreat plan q
:= acts_after_aim (b : beat) (t : tactic) (aimed : pack) : pack =
:= attackers = attackers_of aimed
:= healed = heal_gate b (heals_now b t attackers aimed) aimed
? healed::hunter::target < 0 ?> patrol_turn b t healed |: target_turn b t attackers healed
:= ruled_acts (camp : camp) (t : tactic) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
acts_after_aim { camp = camp; seed = seed; tick = tick; now = now } t (aimed_by camp t::order p)
:= player_acts (camp : camp) (t : tactic) (seed : u32) (tick : i32) (now : i32) (p : pack) : pack =
?= t::brain ?|
| RandomControl => random_acts camp seed tick now (aimed_by camp t::order p)
| _ => ruled_acts camp t seed tick now p
:= respawn_player (camp : camp) (p : pack) : pack =
:= h = p::hunter
{ p ?|
hunter = { h ?| at = knight_start camp h::call_sign; health = player_max_health camp; target = -1 }
score = { (p::score) ?| deaths = p::score::deaths + 1 }
}
:= one_more_charge (charges : i32) : i32 = ? charges < combat::max_block_charges () ?> charges + 1 |: charges
:= charged_up (now : i32) (h : hunter) : hunter =
:= c = h::clock
:= charges = one_more_charge c::block_charges
h |> with_clock { c ?| block_charges = charges; next_charge_at = now + combat::block_charge_ms () }
:= recharged (now : i32) (p : pack) : pack =
? now < p::hunter::clock::next_charge_at ?> p |: p |> with_hunter (charged_up now p::hunter)
:= decided_turn (b : beat) (t : tactic) (p : pack) : pack =
:= h = p::hunter
:= decided = h |> with_clock { (h::clock) ?| next_decision_at = b::now + client_decision_ms () }
player_acts b::camp t b::seed b::tick b::now (p |> with_hunter decided)
:= knight_turn_inline (b : beat) (t : tactic) (p : pack) : pack =
:= h = p::hunter
? units::at_least (quantity::hp 0) h::health ?> respawn_player b::camp p
|? b::now < h::clock::next_decision_at ?> p
|: decided_turn b t p
:~ ^ knight_turn_shared (b : beat) (t : tactic) (p : pack) : pack = knight_turn_inline b t p
:= knight_turn (b : beat) (t : tactic) (p : pack) : pack =
!!!!BackendSwitch({
C = \ () => knight_turn_shared b t p : pack
Rust = \ () => knight_turn_shared b t p : pack
Wgsl = \ () => knight_turn_inline b t p : pack
})
:= late_entry_ms (index : i32) : i32 =
? index = 5 ?> 1193 |? index = 6 ?> 1318 |? index = 7 ?> 2346 |? index = 8 ?> 1847 |: 1520
:= measured_entry_ms (index : i32) : i32 =
? index = 0 ?> 746
|? index = 1 ?> 745
|? index = 2 ?> 618
|? index = 3 ?> 934
|? index = 4 ?> 1116
|: late_entry_ms index
:= ally_entry_ms (seed : u32) : i32 = measured_entry_ms (conv (keyed_rng::roll_between 0 9 (key seed 0 0 PackEntry)))
:= entered_ally (camp : camp) (now : i32) (p : pack) : pack =
:= waiting = p::ally
:= c = waiting::clock
now_in_world (with_ally ({ waiting ?| at = knight_start camp 1 } |> with_clock { c ?| next_decision_at = now }) p)
:= ally_entered (camp : camp) (seed : u32) (now : i32) (p : pack) : pack =
? camp::crew::party > 1 && p::ally_in_world = !- && now >= ally_entry_ms seed ?> entered_ally camp now p |: p
:= charged_both (b : beat) (p : pack) : pack =
:= charged = p |> ally_entered b::camp b::seed b::now |> recharged b::now
? b::camp::crew::party > 1 ?> turned (recharged b::now (turned charged)) |: charged
:= knights_act (b : beat) (t : tactic) (q : pack) : pack =
:= after_lead = knight_turn b t q
? b::camp::crew::party > 1 && q::ally_in_world ?> turned (knight_turn b t (turned after_lead))
|: after_lead
:= poll_landing (b : beat) : i32 =
:= phase : i32 = conv (keyed_rng::roll_between 0 99 (key b::seed b::tick 0 PackPoll))
b::now - tick_ms () + phase + client_lag_ms ()
:= early_at (p : pack) (landing : i32) (i : i32) : bool = (slot i p)::next_step_at <= landing
:= early_mask (p : pack) (landing : i32) : u32 =
:~ ^ fold (i : i32) (mask : u32) : u32 =
? i >= band_size () ?> mask |: fold (i + 1) (? early_at p landing i ?> mask ||| (1u32 <<< i) |: mask)
fold 0 0u32
:= acts_at_poll (b : beat) (t : tactic) (p : pack) : pack =
:= early = early_mask p (poll_landing b)
p |> foes_act_where b early |> knights_act b t |> foes_act_where b (early ^^^ 4294967295u32)
:= this_tick_acts (b : beat) (t : tactic) (q : pack) : pack =
q |> foes_act b::camp b::seed b::tick b::now |> knights_act b t
:= last_tick_acts (b : beat) (t : tactic) (q : pack) : pack =
q |> knights_act b t |> foes_act b::camp b::seed b::tick b::now
:= advanced (b : beat) (t : tactic) (p : pack) : pack =
:= both = charged_both b p
?= b::camp::timing::view ?|
| SeesThisTick => this_tick_acts b t both
| SeesLastTick => last_tick_acts b t both
| SeesAtItsPoll => acts_at_poll b t both
:= advance (camp : camp) (t : tactic) (seed : u32) (tick : i32) (p : pack) : pack =
advanced { camp = camp; seed = seed; tick = tick; now = tick * tick_ms () } t p
:= simulate (camp : camp) (t : tactic) (seed : u32) (ticks : i32) : pack =
:~ ^ loop (tick : i32) (p : pack) : pack =
? tick >= ticks ?> p |: loop (tick + 1) (advance camp t seed tick p)
loop 0 (start camp)
:= clear_done (c : camp) (limit : i32) (tick : i32) (p : pack) : bool =
tick >= limit || p::score::kills >= c::foes::size
:= clear_run_on (c : camp) (t : tactic) (seed : u32) (limit : i32) : i32 * pack =
:~ ^ loop (tick : i32) (p : pack) : i32 * pack =
? clear_done c limit tick p ?> tick * tick_ms (), p |: loop (tick + 1) (advance c t seed tick p)
loop 0 (start c)
:= clear_run (camp : camp) (t : tactic) (seed : u32) (max_ticks : i32) : i32 * pack =
clear_run_on (clearing camp) t seed max_ticks
:== seed_run = { camp : camp; tactic : tactic; first : u32; count : i32 }
:= kill_score_of (p : pack) : i32 = p::score::kills - death_penalty () * p::score::deaths
:= hunt_at (r : seed_run) (index : i32) : pack = simulate r::camp r::tactic (r::first + conv index) (scenario_ticks ())
:~ ^ score_from (r : seed_run) (index : i32) (acc : i32) : i32 =
? index >= r::count ?> acc |: score_from r (index + 1) (acc + kill_score_of (hunt_at r index))
:= seed_run_of (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : seed_run =
{ camp = camp; tactic = t; first = first_seed; count = count }
:= score (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : i32 =
score_from (seed_run_of camp t first_seed count) 0 0
:= experience_of (p : pack) : i32 = p::score::experience - p::score::deaths * death_experience_penalty ()
:~ ^ experience_from (r : seed_run) (index : i32) (acc : i32) : i32 =
? index >= r::count ?> acc |: experience_from r (index + 1) (acc + experience_of (hunt_at r index))
:= hunt_experience (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : i32 =
experience_from (seed_run_of camp t first_seed count) 0 0
:= highest_tuned_heal () : i32 = 400
:= potion_price () : units::amount quantity::gold_coins = quantity::gp 50
:= all_blessings_price_at_level_30 () : units::amount quantity::gold_coins = quantity::gp 14000
:= hunt_profit (p : pack) : units::amount quantity::gold_coins =
units::minus
(units::minus p::score::gold (units::scaled (party_potions p) (potion_price ())))
(units::scaled p::score::deaths (all_blessings_price_at_level_30 ()))
:== gold_amount = units::amount quantity::gold_coins
:~ ^ profit_from (r : seed_run) (index : i32) (acc : gold_amount) : gold_amount =
? index >= r::count ?> acc |: profit_from r (index + 1) (units::plus acc (hunt_profit (hunt_at r index)))
:= profit (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : gold_amount =
profit_from (seed_run_of camp t first_seed count) 0 (quantity::gp 0)
:= tallied (acc : i32 * i32 * i32) (p : pack) : i32 * i32 * i32 =
:= kills, escapes, deaths = acc
kills + p::score::kills, escapes + p::score::escapes, deaths + p::score::deaths
:~ ^ totals_from (r : seed_run) (index : i32) (acc : i32 * i32 * i32) : i32 * i32 * i32 =
? index >= r::count ?> acc |: totals_from r (index + 1) (tallied acc (hunt_at r index))
:= totals (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : i32 * i32 * i32 =
totals_from (seed_run_of camp t first_seed count) 0 (0, 0, 0)
:= cavebot (order : policy::targeting) : tactic =
{
brain = Rules (policy::fixed { heal_below = 120; strike_above = 0 })
order = order
footing = StepAway
patrol = Stays
}
:= cavebot_with_heal (heal : i32) : tactic =
{
brain = Rules (policy::fixed { heal_below = heal; strike_above = 0 })
order =
:< policy
CurrentFirst
footing = StepAway
patrol = Stays
}
:== ledger = { hunt_kills : i32; hunt_deaths : i32; hunt_profit : units::amount quantity::gold_coins }
:= ledgered (acc : ledger) (p : pack) : ledger =
{
hunt_kills = acc::hunt_kills + p::score::kills
hunt_deaths = acc::hunt_deaths + p::score::deaths
hunt_profit = units::plus acc::hunt_profit (hunt_profit p)
}
:~ ^ ledger_from (r : seed_run) (index : i32) (acc : ledger) : ledger =
? index >= r::count ?> acc |: ledger_from r (index + 1) (ledgered acc (hunt_at r index))
:= ledger_of (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : ledger =
ledger_from (seed_run_of camp t first_seed count) 0 {
hunt_kills = 0
hunt_deaths = 0
hunt_profit = quantity::gp 0
}
:== clear_ledger = { clear_potions : i32; clear_deaths : i32; clear_ms : i32 }
:= ledger_after (acc : clear_ledger) (elapsed : i32) (p : pack) : clear_ledger =
{
clear_potions = acc::clear_potions + party_potions p
clear_deaths = acc::clear_deaths + p::score::deaths
clear_ms = acc::clear_ms + elapsed
}
:= clear_step (camp : camp) (t : tactic) (first : u32) (index : i32) (acc : clear_ledger) : clear_ledger =
:= elapsed, p = clear_run camp t (first + conv index) 6000
ledger_after acc elapsed p
:= zero_clear_ledger () : clear_ledger = { clear_potions = 0; clear_deaths = 0; clear_ms = 0 }
:= clear_ledger_of (camp : camp) (t : tactic) (first : u32) (count : i32) =
:~ ^ loop (index : i32) (acc : clear_ledger) : clear_ledger =
? index >= count ?> acc |: loop (index + 1) (clear_step camp t first index acc)
loop 0 (zero_clear_ledger ())
:= clear_cost (l : clear_ledger) : units::amount quantity::gold_coins =
units::plus
(units::scaled l::clear_potions (potion_price ()))
(units::scaled l::clear_deaths (all_blessings_price_at_level_30 ()))
:= clear_loss (l : clear_ledger) : gold_amount = units::minus (quantity::gp 0) (clear_cost l)
:= below_the_kill_floor () : i32 = -1000000000
:! kill_score = ()
:! experience_score = ()
:== coin_unit = quantity::gold_coins
:| objective unit =
| Kills :: objective kill_score
| Experience :: objective experience_score
| Profit :: objective coin_unit
| ProfitKeepingKills :: i32 -> objective coin_unit
| CheapClearWithin :: i32 -> objective coin_unit
:= run_kills (r : seed_run) : units::amount kill_score = units::amount_of (score_from r 0 0)
:= run_experience (r : seed_run) : units::amount experience_score = units::amount_of (experience_from r 0 0)
:= run_profit (r : seed_run) : gold_amount = profit_from r 0 (quantity::gp 0)
:= floor_profit (r : seed_run) (floor : i32) : gold_amount =
:= l = ledger_of r::camp r::tactic r::first r::count
? l::hunt_kills >= floor ?> l::hunt_profit |: quantity::gp (below_the_kill_floor () + l::hunt_kills * 1000)
:= clear_profit (r : seed_run) (limit : i32) : gold_amount =
:= l = clear_ledger_of r::camp r::tactic r::first r::count
? l::clear_ms <= limit * r::count ?> clear_loss l
|: quantity::gp (below_the_kill_floor () - l::clear_ms / r::count)
:= amount_on \* u. (o : objective u) (r : seed_run) : units::amount u =
?= o ?|
| Kills => run_kills r
| Experience => run_experience r
| Profit => run_profit r
| ProfitKeepingKills f => floor_profit r f
| CheapClearWithin m => clear_profit r m
:= measured \* unit.
(o : objective unit) (camp : camp) (t : tactic) (first_seed : u32) (count : i32)
: units::amount unit
=
amount_on o (seed_run_of camp t first_seed count)
:= worth \* unit. (o : objective unit) (camp : camp) (t : tactic) (first_seed : u32) (count : i32) : i32 =
units::value_of (measured o camp t first_seed count)
:= kept_best (heal : i32) (s : i32) (best : i32 * i32) : i32 * i32 =
:= best_heal, best_score = best
? s > best_score ?> (heal, s) |: best
:= best_tuned_heal (at : i32 -> i32) : i32 =
:~ ^ best_from (heal : i32) (best : i32 * i32) : i32 * i32 =
? heal > highest_tuned_heal () ?> best |: best_from (heal + 10) (kept_best heal (at heal) best)
:= tuned, _ = best_from 50 (40, at 40)
tuned
:= shaped_heal_tuned_for \* unit.
(o : objective unit) (shape : tactic -> tactic) (camp : camp) (first_seed : u32) (count : i32)
: i32
=
best_tuned_heal (\ heal => worth o camp (cavebot_with_heal heal |> shape) first_seed count)
:= tuned_cavebot_heal_for \* unit. (o : objective unit) (camp : camp) (first_seed : u32) (count : i32) : i32 =
shaped_heal_tuned_for o (\ t => t) camp first_seed count
:= tuned_cavebot_heal (camp : camp) (first_seed : u32) (count : i32) : i32 =
tuned_cavebot_heal_for Kills camp first_seed count
:= heal_fraction_below (camp : camp) (heal : i32) : i32 =
network::fraction_below heal (units::value_of (player_max_health camp))
:= network_tactic (w : network::weights) : tactic =
{ brain = Network w; order = :< policy ;; CurrentFirst; footing = StepAway; patrol = Stays }
:= cavebot_network (camp : camp) (heal : i32) : network::weights =
network::threshold_healer (heal_fraction_below camp heal)
:= genome_brain (g : search::genome) : brain =
Rules { heal_base = g::first; heal_per_attacker = g::second; strike_above = 0; retreat_at = g::third }
:= tactic_with_order (order : policy::targeting) (g : search::genome) : tactic =
{ brain = genome_brain g; order = order; footing = StepAway; patrol = Stays }
:= live_retreat_tactic (heal : i32) (retreat_at : i32) : tactic =
:= evolved_shape : tactic =
tactic_with_order (:< policy ;; ReachFirst) { first = heal; second = 0; third = retreat_at }
{ evolved_shape ?| footing = StepAwayLikeTheLiveProbe }
:== three = i32 * i32 * i32
:= with_three (at : i32) (values : three) (v : search::vector) : search::vector =
:= a, b, c = values
v |> search::with_gene at a |> search::with_gene (at + 1) b |> search::with_gene (at + 2) c
:= genes_of (first : three) (second : three) (third : three) (fourth : three) : search::vector =
search::vector_of 12 0 |> with_three 0 first |> with_three 3 second |> with_three 6 third |> with_three 9 fourth
:= scored_low () : search::vector = genes_of (40, 0, 1) (-200, -200, -200) (-200, -50, -50) (-50, -10, -50)
:= scored_high () : search::vector = genes_of (400, 80, 4) (200, 200, 200) (200, 50, 50) (50, 10, 50)
:= scored_start () : search::vector = genes_of (143, 34, 1) (50, 100, -10) (-1, 0, 0) (0, 0, 0)
:= scored_brain (g : search::vector) : brain =
:= at i = search::gene g i
Rules { heal_base = at 0; heal_per_attacker = at 1; strike_above = 0; retreat_at = at 2 }
:= scored_order (g : search::vector) : policy::targeting =
:= at i = search::gene g i
policy::Scored { current = at 3; reach = at 4; health = at 5; distance = at 6 }
:= scored_footing (g : search::vector) : footing_rule =
:= at i = search::gene g i
ScoredStep { away = at 7; room = at 8; crowd = at 9; align = at 10; diagonal = at 11 }
:= scored_tactic (g : search::vector) : tactic =
{ brain = scored_brain g; order = scored_order g; footing = scored_footing g; patrol = Stays }
:= tactic_of (g : search::genome) : tactic = tactic_with_order (:< policy ;; CurrentFirst) g
:= temperate_genes (heal : i32) (stand : i32) (flee : i32) (retreat_stand : i32) (retreat_flee : i32) : search::vector =
search::vector_of 5 0 |> with_three 0 (heal, stand, flee)
|> search::with_gene 3 retreat_stand |> search::with_gene 4 retreat_flee
:= temperate_low () : search::vector = temperate_genes 0 0 0 1 1
:= temperate_high () : search::vector = temperate_genes 400 200 200 4 4
:= temperate_brain (g : search::vector) : brain =
:= at i = search::gene g i
Temperate {
heal_base = at 0
heal_per = { standing = at 1; fleeing = at 2 }
strike_above = 0
retreat_at = { standing = at 3; fleeing = at 4 }
}
:= temperate_tactic (order : policy::targeting) (g : search::vector) : tactic =
{ brain = temperate_brain g; order = order; footing = StepAway; patrol = Stays }
:== seed_range = { first : u32; count : i32 }
:== trial unit = { o : objective unit; camp : camp; seeds : seed_range }
:== search_plan = { seed : u32; generations : i32; offspring : i32 }
:= trial_worth \* unit. (x : trial unit) (t : tactic) : i32 = worth x::o x::camp t x::seeds::first x::seeds::count
:= temperate_search (p : search_plan) : search::vector_search =
:= low, high = temperate_low (), temperate_high ()
{ low = low; high = high; seed = p::seed; pairs = p::offspring; generations = p::generations }
:= evolve_temperate_for \* unit.
(x : trial unit) (order : policy::targeting) (start : search::vector) (p : search_plan)
: search::vector
=
search::evolve_vector (\ g => trial_worth x (temperate_tactic order g)) (temperate_search p) start
:= evolved_on_the_cyclops_spot () : search::genome = { first = 143; second = 34; third = 1 }
:= against_standing_only (g : search::genome) : search::vector =
temperate_genes g::first g::second 0 g::third (policy::never_retreat ())
:= arena_hunt () : camp =
:< creatures
:= kinds = fixed_views::band (Cyclops, Cyclops, Troll, Troll, Troll, Troll)
sample_camp (foes_of kinds 6 (RespawningAfter 60000)) (:< terrain ;; ArenaHunt)
:= cyclops_hunting_ground () : camp =
:= ground = arena_hunt ()
ground |> with_foes { (ground::foes) ?| kinds = fixed_views::band_of (:< creatures ;; Cyclops) }
:= plaza_and_fields_route () : patrol_route =
:= at x y = geometry::position x y 7
{ points = fixed_views::band (at 104 86, at 125 86, at 117 88, at 124 96, at 119 104, at 117 92); count = 6 }
:= patrolling (route : patrol_route) (t : tactic) : tactic = { t ?| patrol = Patrols route }
:= sweeping_spawns (t : tactic) : tactic = { t ?| patrol = SweepsSpawns }
:= sweeping_territory (border : i32) (t : tactic) : tactic = { t ?| patrol = SweepsTerritory border }
:= random_control () : tactic =
{ brain = RandomControl; order = :< policy ;; CurrentFirst; footing = StepAway; patrol = Stays }
:= cyclops_and_two_trolls_on_the_arena_spot () : camp =
:< catalog
:= entry kind = pack_entry (spawn_of kind 1 2)
camp_of_entries (entry CyclopsKind, entry TrollKind, entry TrollKind) |> on_layout (:< terrain ;; ArenaSpot)
:= tactic_bounds () : search::genome * search::genome =
{ first = 0; second = 0; third = 1 }, { first = 400; second = 60; third = 4 }
:= tactic_search (p : search_plan) : search::genome_search =
:= low, high = tactic_bounds ()
{ low = low; high = high; seed = p::seed; offspring = p::offspring; generations = p::generations; spread = 40 }
:= evolve_rules_for \* unit.
(x : trial unit) (order : policy::targeting) (start : search::genome) (p : search_plan)
: search::genome
=
search::evolve (\ g => trial_worth x (tactic_with_order order g)) (tactic_search p) start
:= evolve_tactic_from_for \* unit. (x : trial unit) (start : search::genome) (p : search_plan) : search::genome =
search::evolve (\ g => trial_worth x (tactic_of g)) (tactic_search p) start
:! training = ()
:! held_out = ()
:| private seed_block role =
| TrainingBlock :: seed_range -> seed_block training
| HeldOutBlock :: seed_range -> seed_block held_out
:= range_of \* role. (b : seed_block role) : seed_range =
?= b ?|
| TrainingBlock r => r
| HeldOutBlock r => r
:= training_block () : seed_block training = TrainingBlock { first = training_first (); count = training_count () }
:= held_out_block () : seed_block held_out = HeldOutBlock { first = held_out_first (); count = held_out_count () }
:= held_out_slice (count : i32) : seed_block held_out =
HeldOutBlock { first = held_out_first (); count = ? count < held_out_count () ?> count |: held_out_count () }
:= shaped_cavebot_heal_tuned_on \* unit.
(o : objective unit) (shape : tactic -> tactic) (camp : camp) (b : seed_block training)
: i32
=
:= r = range_of b
shaped_heal_tuned_for o shape camp r::first r::count
:= cavebot_heal_tuned_on \* unit. (o : objective unit) (camp : camp) (b : seed_block training) : i32 =
:= r = range_of b
tuned_cavebot_heal_for o camp r::first r::count
:= trial_on \* unit. (o : objective unit) (camp : camp) (b : seed_block training) : trial unit =
{ o = o; camp = camp; seeds = range_of b }
:= rules_evolved_on \* unit.
(o : objective unit) (order : policy::targeting) (start : search::genome) (camp : camp) (b : seed_block training)
(p : search_plan)
: search::genome
=
evolve_rules_for (trial_on o camp b) order start p
:= judged_on \* unit.
(o : objective unit) (camp : camp) (t : tactic) (b : seed_block held_out)
: units::amount unit
=
:= r = range_of b
measured o camp t r::first r::count
:= ledger_on \* role. (camp : camp) (t : tactic) (b : seed_block role) : ledger =
:= r = range_of b
ledger_of camp t r::first r::count
:= worth_on_training \* unit. (o : objective unit) (camp : camp) (t : tactic) (b : seed_block training) : i32 =
:= r = range_of b
worth o camp t r::first r::count
:= tactic_evolved_on \* unit.
(o : objective unit) (start : search::genome) (camp : camp) (b : seed_block training) (p : search_plan)
: search::genome
=
evolve_tactic_from_for (trial_on o camp b) start p
:= clears_judged_on (camp : camp) (t : tactic) (b : seed_block held_out) : clear_ledger =
:= r = range_of b
clear_ledger_of camp t r::first r::count
:= route_of_genes (v : search::vector) : patrol_route =
:= at i = geometry::position (search::gene v (2 * i)) (search::gene v (2 * i + 1)) 7
{ points = fixed_views::band (at 0, at 1, at 2, at 3, at 4, at 5); count = search::gene v 12 }
:= with_point (i : i32) (point : geometry::position) (v : search::vector) : search::vector =
v |> search::with_gene (2 * i) point::x |> search::with_gene (2 * i + 1) point::y
:~ ^ route_genes_from (r : patrol_route) (i : i32) (v : search::vector) : search::vector =
? i >= band_size () ?> v |: route_genes_from r (i + 1) (with_point i (fixed_views::band_at i r::points) v)
:= route_genes (r : patrol_route) : search::vector =
route_genes_from r 0 (search::vector_of 13 0) |> search::with_gene 12 r::count
:= hunt_route_low () : search::vector =
route_genes { points = fixed_views::band_of (geometry::position 100 82 7); count = 2 }
:= hunt_route_high () : search::vector =
route_genes { points = fixed_views::band_of (geometry::position 128 108 7); count = 6 }
:= evolved_route (fitness : search::vector -> i32) (s : search::vector_search) (start : patrol_route) : patrol_route =
route_of_genes (search::evolve_vector fitness s (route_genes start))
:= route_experience (camp : camp) (heal : i32) (r : seed_range) (v : search::vector) : i32 =
hunt_experience camp (cavebot_with_heal heal |> patrolling (route_of_genes v)) r::first r::count
:= route_search (seed : u32) (generations : i32) (pairs : i32) : search::vector_search =
{ low = hunt_route_low (); high = hunt_route_high (); seed = seed; pairs = pairs; generations = generations }
:= route_evolved_on
(camp : camp) (start : patrol_route) (heal : i32) (seed : u32) (b : seed_block training) (gens : i32) (pairs : i32)
: patrol_route
=
evolved_route (route_experience camp heal (range_of b)) (route_search seed gens pairs) start
:= central_shuttle_route () : patrol_route =
:= at x y = geometry::position x y 7
{ points = fixed_views::band (at 117 92, at 116 96, at 118 88, at 124 92, at 109 93, at 120 92); count = 2 }
:= spawn_side_loop_route () : patrol_route =
:= at x y = geometry::position x y 7
{ points = fixed_views::band (at 117 88, at 124 96, at 117 92, at 117 92, at 117 92, at 117 92); count = 3 }
:= sized (size : i32) (c : camp) : camp = c |> with_foes { (c::foes) ?| size = size }
:= all_killed (camp : camp) (p : pack) : bool = p::score::kills >= camp::foes::size
the_soonest_spawn_fold_covers_a_band_of_six (test)
test the_soonest_spawn_fold_covers_a_band_of_six: passed
///- --test --symbol
///> c
:= ~size = band_size ()
size |> _assert_eq 6
__assert_eq { expected = 6 }