#side Active 7 #author Warren #color f08 #seed 1 2 3 2 3 2 1 2 3 2 Active 7 is a *major* upgrade to Active designed specifically to defeat the giants. The blaster range has been substantially increased. With long-range blasters quite popular recently I wonder if they might be overpowered. Key features: * Every cell publishs location, velocity, and energy needs in shared memory under their id. Robots announce this periodically. Types: * Constructor: 2.3 constructor, engine, passive dodging. Upon birth it wanders ~10 units away from home, then sits still unless attacked. * Gatherer: 2.1 eater, range 30 rate 1.3 syphon. * Fighter: range 17 rate 25/25 blasters powered in part by range 15 rate 0.5 enemy-syphon. Active dodges. ;################ SHARED ################# #code ;;;;types #const CTOR_TYPE 1 #const GATHERER_TYPE 2 #const FIGHTER_TYPE 3 ;;;;message channels ;;#const help-channel 1 ;position time #const kill-channel 2 #const safe-channel 3 ;#const hungry-channel 5 #const food-channel 10 ; position amount time #const hungry-channel 4 ; id type ;;;;;;;;;;;;;;;;shared memory and related;;;;;;;;;;;;;;;;;;;;; ;001-400 are ally stats. When ALLY_SIZE=10, 10I-9 to 10I stores info for id I, e.g. 001-010 is id 1. ;format: x y vx vy t energy-level ;these offsets include the base implicitly, so e.g. velocity of I is at ROBOT_STAT_SIZE * I + ALLY_VEL_OFFSET #const ALLY_POS_OFFSET -5 #const ALLY_VEL_OFFSET -3 #const ALLY_TIME_OFFSET -1 #const ALLY_HUNGER_OFFSET -0 ;#const NUM_ROBOTS 100 #const ALLY_SIZE 6 ;;do 10 for easy debugging ;501-800 are food claims ;format: claim-time #const FOOD_CLAIM_BASE 701 #const NUM_FOOD_CLAIMS 250 ;981-990 are side records. Currently only one item is stored, namely the time at which we have/will make peace with each side #const SIDE_BASE 980 #const SIDE_SIZE 1 ;################# SHARED VARS ###################### #var temp #vector vtemp #var hunger ;;;;;;;;;;; real shared code ;;;;;;;;;;;; ;Streamlined version of equivalent from Walled City 2. ;Looks like it should work on any CPU 7 or greater. #var food-hash claim-food: food-position drop world-width / ;stack: between 0 and 1 NUM_FOOD_CLAIMS * floor ;stack: presumably between 0 inclusive and NUM_FOOD_CLAIMS exclusive FOOD_CLAIM_BASE + food-hash! ;staack empty time 400 + ;put on stack for later food-hash sync read time < ClaimExpired& ifg ;valid claim already not ;;drops and then pushes "0" since time+400 != 0. return ClaimExpired: ;stack: time+400 food-hash write 1 return ;; adress -- quality ;; calculate-sink-quality: temp! temp ALLY_POS_OFFSET + vread position syphon-range in-range ;stack: in-range or not temp ALLY_HUNGER_OFFSET + read * ;stack: quality return #var last-id-announce -1000 #var energy-sink-adr 0 ; 0 for none. #var energy-sink-quality #var received-adr #var received-type ;;This function writes our data to shared memory, announces our existence, and listen to other announcements ;; -- communicate: ;;first write our own stuff id ALLY_SIZE * temp! position temp ALLY_POS_OFFSET + velocity temp ALLY_VEL_OFFSET + time temp ALLY_TIME_OFFSET + hunger temp ALLY_HUNGER_OFFSET + ;;stack: stuff to write sync write write vwrite vwrite ;;second announce if hungry time last-id-announce 100 + > if hunger 0.05 > and-if id 1 hungry-channel send time last-id-announce! then ;;third listen to other announcements energy-sink-adr if energy-sink-adr ALLY_TIME_OFFSET + read time 30 - > and-if ;we have an up to date sink energy-sink-adr calculate-sink-quality^ energy-sink-quality! else 0 energy-sink-adr! then do #var skipped 0 population 10 / floor random-int dup skipped! hungry-channel skip-messages hungry-channel receive while ALLY_SIZE * received-adr! syphon-range 2 > if update-energy-sink^ then ; process-announcement^ loop return update-energy-sink: received-adr id ALLY_SIZE * = ifr energy-sink-adr replace-sink& nifg received-adr calculate-sink-quality^ temp! temp energy-sink-quality <= ifr replace-sink: received-adr energy-sink-adr! temp energy-sink-quality! return ;;This function transfers energy to neediest friend in range ;; -- transfer-energy: energy-sink-adr energy-sink-quality and if energy max-energy / 0.2 > and-if hunger energy-sink-adr ALLY_HUNGER_OFFSET + read < and-if syphon-max-rate negate syphon-rate! energy-sink-adr ALLY_POS_OFFSET + vread position v- energy-sink-adr ALLY_VEL_OFFSET + vread time energy-sink-adr ALLY_TIME_OFFSET + read - 5 + vs* v+ velocity 5 vs* v- rect-to-polar syphon-direction! syphon-distance! else 0 syphon-rate! then return ;;################################################# #type Ctor ;#decoration 0f0 cross #color 00f #hardware processor 18 energy 700 10 armor 200 engine 0.08 solar-cells 0.02 shot-sensor 5 robot-sensor 4 constructor 2.3 #code ; -- ; This procedure responds to a received type/id pair, passed in received-* variables, and responds as needed. ; For Ctor, we keep track of process-announcement: return out-of-bounds: ;x y -> bool 2dup 0 < swap 0 < or if 2drop 1 return then world-height > swap world-width > or return #var gutter keep-away-edges: ; x y -> x y population sqrt 3 * gutter! gutter max world-height gutter - min swap gutter max world-width gutter - min swap return ;edge-dist: ;x y -> d ; 2dup min swap world-height swap - min swap world-width swap - min ;return #vector home #var last-birth-time 0 #var baby-cost #start constructor-type if ;;seeded with fetus time last-birth-time! constructor-remaining baby-cost! ;;;hack then max-repair-rate repair-rate! time 10 > if position 10 population sqrt + random-angle polar-to-rect v+ home! else position home! then #var loop-time do friendly-collision position home 3 in-range and if position 1 random-angle polar-to-rect v+ home! then ;;fix home here home keep-away-edges^ home! time loop-time! constructor-type nif last-birth-time if ;base type on how long it's taken us to reproduce baby-cost time last-birth-time - / ;stack: average power consumption #var power-consumption dup power-consumption! constructor-max-rate 0.85 * > and-if CTOR_TYPE constructor-type! else ;either we're newly born ourselves or else reproduction was slow last time. FIGHTER_TYPE type-population population / 0.5 / 1 min random-bool GATHERER_TYPE FIGHTER_TYPE ifev constructor-type! then constructor-remaining constructor-type CTOR_TYPE = if 0.5 * then baby-cost! ;contains hack so we don't build CTOR twice in a row time last-birth-time! then max-energy 0.06 * energy < constructor-max-rate 0 ifev constructor-rate! #var last-danger-time -555 30 periodic-shot-sensor if shot-found if shot-sensor-time last-danger-time! time 300 + shot-side SIDE_BASE + write position time 3 kill-channel send shot-velocity unitize 2 vs* home v+ home! then then 30 periodic-robot-sensor if robot-found if robot-sensor-time last-danger-time! then then time last-danger-time 200 + < if home position v- 0.05 vs* 0.2 time 9 / polar-to-rect v+ engine-velocity! ;;period is 2pi * 7 engine-max-power engine-power! else home seek-location then 1 energy max-energy / - 1.5 / 0.8 energy exponent + hunger! communicate^ forever ;;################################################# #type Gatherer ;#decoration 0f0 cross #color 0f0 #hardware processor 23 energy 400 15 armor 150 engine 0.06 food-sensor 9 3 shot-sensor 5 robot-sensor 4 eater 2.1 syphon 1.3 30 #code ;shared variables #var typical-food-amount ;message received variables #vector received-food-position #var received-food-amount #var received-food-time #vector next-meal-position #vector wander-position #var begin-food-chase-time #vector birth-place Update-stats-food-msg: time received-food-time - 50 < if received-food-position position dist 30 < and-if received-food-amount 0.1 * typical-food-amount 0.9 * + typical-food-amount! then return #const edge-space 4 ;this subreutine copied from eventually 12 random-edge-position: 0 1 random-int if 0 1 random-int edge-space world-width edge-space - ifev edge-space world-height edge-space - random else edge-space world-width edge-space - random 0 1 random-int edge-space world-height edge-space - ifev then return out-of-bounds: ;x y -> bool 2dup 0 < swap 0 < or if 2drop 1 return then world-height > swap world-width > or return #const gutter 5 keep-away-edges: ; x y -> x y gutter max world-height gutter - min swap gutter max world-width gutter - min swap return new-wander-position: population 30 > if random-edge-position^ else birth-place 20 population + random-angle polar-to-rect v+ keep-away-edges^ then return #var last-armor Defend: time robot-sensor-time - 30 > if fire-robot-sensor fire-shot-sensor sync shot-found if Call-for-help^ time 300 + shot-side SIDE_BASE + write else last-armor armor > robot-found 1 > or Call-for-help& ifc then last-armor armor dup last-armor! > shot-found or if rdrop Begin-fleeing& jump then then return call-for-help: position time 3 kill-channel send return common: 0.7 energy exponent hunger! communicate^ transfer-energy^ return #start position birth-place! armor last-armor! Begin-seeking-food: new-wander-position^ wander-position! do common^ energy max-energy / 0.15 > if defend^ then wander-position position dist 5 < if new-wander-position^ wander-position! then wander-position position v- unitize 0.08 vs* engine-velocity! engine-max-power engine-power! birth-place position v- rect-to-polar food-sensor-focus-direction! 4 / food-sensor-focus-distance! ;food code copied from cyclops 30 periodic-food-sensor if food-found if Food-check-loop: food-velocity norm nif claim-food^ and-if food-position next-meal-position! Begin-eating-food& jump else next-food Food-check-loop& ifg then then ;food-found then do food-channel receive while received-food-time! received-food-amount! received-food-position! update-stats-food-msg^ received-food-amount 10 / received-food-position position dist > if ;ooooooo ffoooooodd received-food-position next-meal-position! Begin-eating-food& jump then loop forever Begin-eating-food: time begin-food-chase-time! do common^ next-meal-position seek-location do food-channel receive while received-food-time! received-food-amount! received-food-position! update-stats-food-msg^ loop energy max-energy / 0.12 > if defend^ then eaten not if time begin-food-chase-time - 200 300 random > ;long time position next-meal-position radius in-range or ;on food and-if ; Begin-seeking-food& jump then energy max-energy / 0.99 > birth-place position 25 in-range not and Begin-homing& ifg forever #vector flee-position #var flee-time Begin-fleeing: shot-found if shot-velocity unitize 15 vs* position v+ flee-position! else robot-found time robot-sensor-time - 20 < if robot-position position v- unitize -20 vs* position v+ flee-position! else do safe-channel receive while time swap - 100 < if 2dup position dist 50 < if flee-position! Got-safe-place& jump else 2drop then else 2drop then loop random-edge-position^ flee-position! Got-safe-place: then ;robot-found then ;shot-found time flee-time! do common^ flee-position seek-location time flee-time - 75 > flee-position position dist 5 < or energy max-energy / 0.05 < or if Begin-seeking-food& jump then forever Begin-homing: do common^ birth-place seek-location birth-place position 20 in-range energy max-energy / 0.3 < or if Begin-seeking-food& jump then forever ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Fighter #type Fighter #decoration fcc cross #color f00 #hardware energy 400 100 armor 220 processor 35 ;to dodge in 2 frames engine 0.15 robot-sensor 17 3 shot-sensor 7 4 blaster 25 17.5 25 enemy-syphon 0.5 15 #code ;communication with Dodge-and-move: #vector desired-velocity 0 0 #var speed-slop 0.01 ;communication with Eat-and-move: #vector desired-position random-position: 0 world-width random 0 world-height random return ;returns distance to nearest wall wall-distance: ; -> dist position min world-size position v- min min return restrict-location: ;x y -> x y world-height min 0 max swap world-width min 0 max swap return ;in: desired-position, ;;;position-slop ;out: desired-velocity, speed-slop #const MAX_SPEED 0.15 eat-and-move: desired-position position v- rect-to-polar swap 0.05 * MAX_SPEED min swap polar-to-rect desired-velocity! radius 0.05 * speed-slop! return ;end of eat-and-move #const FIGHT_DISTANCE 14 ;was 15 #var CRUSADE_DIST #const MIN_CRUSADE_DIST 10 #var have-crusade 0 #var crusade-time #vector crusade-position #vector birth-place #var suck 0 ;;got enemy syphon target? #var wait-far-from-home-factor ;;between 0 and 8 #start 15 50 random-int CRUSADE_DIST! 0 10 random wait-far-from-home-factor! position birth-place! position desired-position! Begin-normaling: do energy max-energy / 0.2 > No-target& nifg robot-sensor-time robot-found have-crusade or blaster-reload-time 60 ifev + time < if fire-robot-sensor sync robot-found if NextRobot: robot-position robot-velocity 50 vs* v+ position 10 in-range if time 300 + robot-side SIDE_BASE + write then robot-side SIDE_BASE + read FIGHTER_TYPE type-population 8 > if 5000 + then time < TryNextTarget& ifg ;;not hostile armor max-armor < if robot-position robot-sensor-time 3 kill-channel send then blaster-cooldown nif robot-position robot-velocity time robot-sensor-time - vs* v+ ;stack: current position of enemy robot-velocity lead-blaster then FIGHT_DISTANCE birth-place robot-position dist 5 - 5 max min ;;FIGHT_DISTANCE, but closer if close to home position robot-position v- angle ;;time 5 / id + reorient 30 / + polar-to-rect robot-position v+ desired-position! 1 suck! Done-targeting& jump TryNextTarget: next-robot NextRobot& ifg then 0 suck! else Done-targeting& jump then ;time to fire No-target: have-crusade if crusade-position position 5 in-range time crusade-time - 200 > or and-if 0 have-crusade! then have-crusade nif shot-found and-if shot-velocity norm and-if shot-position shot-velocity -100 vs* v+ crusade-position! shot-sensor-time crusade-time! 1 have-crusade! then have-crusade nif kill-channel messages 2 - 0 max 0 random-int kill-channel skip-messages do kill-channel receive while ;stack: x y t crusade-time! crusade-position! time crusade-time - 100 < if ;newish crusade-position position dist CRUSADE_DIST < and-if crusade-position position dist MIN_CRUSADE_DIST > and-if 1 have-crusade! done-chatting& jump then ;acceptable crusade loop Done-chatting: then ;have-crusade not have-crusade if energy max-energy / 0.3 > and-if crusade-position desired-position! else position birth-place population sqrt wait-far-from-home-factor * in-range if position else birth-place then desired-position! then ;have crusade Done-targeting: ;;jump here if we have a target suck if robot-position position v- robot-velocity time robot-sensor-time - 5 + vs* v+ velocity 5 vs* v- rect-to-polar enemy-syphon-direction! enemy-syphon-distance! enemy-syphon-max-rate enemy-syphon-rate! else 0 enemy-syphon-rate! then eat-and-move^ Dodge-and-move^ 1 energy max-energy / - hunger! communicate^ Dodge-and-move^ forever #const Flee-angle 1.5 ;angle between shot velocity and fleeing. #const MIN_MISS_DIST 2.5 #const flee-speed 0.1 #const ASSUMED_ROBOT_SLOWDOWN_FACTOR 0.4 #const DODGE_OVERKILL_FACTOR 1.2 #var miss #vector dv #vector assumed-robot-velocity #vector go-dir #var last-danger-time -500 ;Dodge has inputs: ;-desired-velocity, the desired medium-term engine velocity ;-speed-slop, the amount the velocity can differ before engine is used ;Dodge then sets engine-velocity and power to avoid shots. Dodge-and-move: time last-danger-time 100 + > if shot-sensor-time 30 + time > robot-found not and and-if Obey-user& jump then do shot-sensor-time time 4 - < until sync loop ;do as much computation as possible before firing shot sensor to get up-to-date info energy max-energy / 2 * ; armor max-armor = 1 2 ifev * time shot-sensor-time - 20 / + energy max-energy / 6 * min armor max-armor / < Obey-user& ifg velocity ASSUMED_ROBOT_SLOWDOWN_FACTOR vs* 2dup desired-velocity dist 0.05 <= if 2drop desired-velocity else 2dup desired-velocity v- unitize -0.05 vs* v+ ;expected velocity then assumed-robot-velocity! assumed-robot-velocity rect-to-polar shot-sensor-focus-direction! 3 * shot-sensor-focus-distance! fire-shot-sensor sync shot-found Obey-User& nifg time last-danger-time! ;;;;;;;;; shot-process-loop: shot-velocity norm 2 < if ;don't dodge super-fast shots such as force fields shot-velocity assumed-robot-velocity v- dv! dv shot-position position v- dot 0 < and-if else next-shot shot-process-loop& obey-user& ifeg then dv unitize shot-position position v- cross miss! ;positive means the shot will miss to our right (when facing the incoming shot) dv angle miss 0 > Flee-angle Flee-angle negate ifev - ;;stack: angle to dodge dup 1 swap polar-to-rect go-dir! ;-- dodge-angle MIN_MISS_DIST miss abs - 0 max ;dist to move shot-distance 3 max / dv norm * DODGE_OVERKILL_FACTOR * ;this line: flee speed swap polar-to-rect ;-- flee-velocity assumed-robot-velocity v+ 2dup ;2 copies of engine-vel on stack desired-velocity v- go-dir dot ;negative if desired deviation from calculated is ok 0 < if 2drop Obey-User& jump then engine-velocity! engine-max-power engine-power! CheckNewHostiles& jump Obey-User: desired-velocity engine-velocity! velocity desired-velocity v- norm speed-slop < 0 engine-max-power ifev engine-power! CheckNewHostiles: shot-found if do time 500 + shot-side SIDE_BASE + write next-shot while-loop then return ;end of Dodge subroutine #end