Grobots Wiki
Advertisement
Active
Active-9
An Active 9 colony. Its three types (fighters, gatherers and constructors) are all visible.

Author

Warren

Strategy

Gatherer with active dodging

Last modified

21 April 2012

As its name suggests, Active was the first side with active dodging. The implementation is extremely complex, but the result is impressive. It is nearly immune to long-range blasters, it ignores shots that won't hit, it makes its enemies appear to narrowly miss by accident, and it wins tournaments.

Its complexity makes it difficult to learn from, but don't be intimidated; Active uses a complex multilayered form of dodging. It can be done in much simpler ways.

Strategy[]

Early versions that had fighters with eaters could eat while fighting and dodging. For example, it would sometimes dodge by moving between two nearby foods. This prevented it from running out of energy in combat. Since version 7(?) the fighters no longer have eaters so it doesn't do this anymore. The gatherers do fine with passive dodging and running away, so Warren hasn't tried giving the gatherers this active dodging while eating feature.

Development[]

Old versions were weakened by rule changes that made movement harder, but versions 7 and 8 separated combat from economy and retweaked dodging, so Active 8 was the best side as of July 2012. It has since been dethroned by its variant Big Bertha.

Variants[]

  • Active Large is a variant with larger cells to exploit their combat advantages.
  • Big Bertha is a variant with a giant one-cell seed that later creates an Active colony.

Source[]

Version 9[]

#side Active 9
#author Warren
#color f08
#seed 1 2 3 2 3 2 2 3

Changes in version 9 include:
* Gatherers passive dodge when see danger, even when eating (circle food).
* Keeps track of center of mass of the constructor cells (see COLONY_POSITION). This is not used much yet, but is quite helpful in the derived Big Bertha side.

Changes in version 8 include:
* Redone active dodging.
* Gatherers run away better.
* Partial but effective friendly fire avoidance.
* Removed enemy-syphon from fighters. Increased blaster size to compensate.
My impression is that the friendly fire protection is the primary reason for the improved performance compared to version 7.

;################ 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 noff-channel 6 ; SM_id position
#const food-channel 10  ;  position amount time

#const hungry-channel 4 ; id type

;;;;;;;;;;;;;;;;shared memory and related;;;;;;;;;;;;;;;;;;;;;



;001-660 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 110
#const ALLY_SIZE 6 ;;do 10 for easy debugging
#const UPPER_ROBOT_AREA 660 ;; NUM_ROBOTS * ALLY_SIZE, the highest address in the ALLY shared memory

;701-950 are food claims
;format: claim-time
#const FOOD_CLAIM_BASE 701
#const NUM_FOOD_CLAIMS 250

#const DANGER_TIME 970

#const COLONY_POSITION 975

;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

#var my-id ;;usually id, but if id>NUM_ROBOTS we assign manually


;;;;;;;;;;; real shared code ;;;;;;;;;;;;


call-for-help:
  position time 3 kill-channel send
return

;; called at initialization to set my-id to an unused slot
assign-id: ;; --
	id NUM_ROBOTS <= if
		id my-id!
	else
		;; take a dead 'bots ID
		ALLY_SIZE ALLY_TIME_OFFSET + temp!
		do
			temp read time 1000 - < if
			sync temp read time 1000 - < and-if ;;sync and check again to avoid two robots claiming same id
				time temp write ;;claim it
				temp ALLY_TIME_OFFSET - ALLY_SIZE / my-id!
				return			 
			then
			temp ALLY_SIZE + temp!
		temp UPPER_ROBOT_AREA > until-loop
		stop ;;population over NUM_ROBOTS; panic!
	then
return


;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


write-our-posn:
my-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
return


announce-if-hungry:
time last-id-announce 100 + > if
hunger 0.05 > and-if
	my-id 1 hungry-channel send
	time last-id-announce!
then
return

listen-to-hungry:
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    ;; i.e. if this robot is a type with a syphon
   	update-energy-sink^
   then
;   process-announcement^
  loop

return

;;This function writes our data to shared memory, announces our existence, and listen to other announcements
;; -- 
communicate-unused:

;;first write our own stuff
write-our-posn^

;;second announce if hungry
announce-if-hungry^

;;third listen to other announcements
listen-to-hungry^
return

update-energy-sink:
   received-adr my-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 600 3
armor 120
engine 0.09
solar-cells 0.02

shot-sensor 5
robot-sensor 4

constructor 2.0 ;;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

set-gutter: ;--
	population sqrt 2 * gutter! ;;was 3
return

keep-away-edges: ; x y -> x y
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-armor
#var last-birth-time 0
#var baby-cost
#var num-children 0

#start
	max-armor last-armor!
	assign-id^
	constructor-type if
	;;seeded with fetus
		time last-birth-time!
		constructor-remaining baby-cost! ;;;hack
	then

	max-repair-rate repair-rate!

time 10 > if
	set-gutter^
	do
		position 2 population sqrt 4 * + random-angle polar-to-rect v+ home!
	home edge-dist^ gutter > until-loop
else
	position COLONY_POSITION vwrite

	;set home, snugging against nearby walls

	set-gutter^

#const max-snug-dist 28
#const snug-edge-dist 10
#var snugged 0
	position drop world-width 2 / - abs world-width 2 / max-snug-dist - > if 	
		position drop world-width 2 / > world-width snug-edge-dist - snug-edge-dist ifev
		1 snugged!
	else
		position drop
        then
	;stack: home x
	position nip world-height 2 / - abs world-height 2 / max-snug-dist - > if 	
		position nip world-height 2 / > world-height snug-edge-dist - snug-edge-dist ifev
		1 snugged!
	else
		position nip
        then
	;stack: home x, y
	snugged nif 5 position world-width world-height 2 vs/ v- angle polar-to-rect v+ then ;;move towards wall
	keep-away-edges^ 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
	set-gutter^
	home keep-away-edges^ home!


	time loop-time!

	position sync COLONY_POSITION vread 100 vs* v+ 101 vs/ COLONY_POSITION vwrite ;;move colony position towards us

	constructor-type nif
		last-birth-time if
#var power-consumption
			baby-cost time last-birth-time - / power-consumption!
			;base type on how long it's taken us to reproduce
		constructor-max-rate 1.0 num-children 0.1 * - 0.7 max * power-consumption < 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.45 / 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!
		num-children 1 + num-children!
	then
	constructor-progress 200 > 0.04 0.2 ifev max-energy * energy < constructor-max-rate 0 ifev constructor-rate!

#var last-danger-time -555
	14 periodic-shot-sensor if
		shot-found if
			shot-sensor-time last-danger-time!	
			time 300 + shot-side SIDE_BASE + write 
  			call-for-help^
			home position 5 in-range nif
				position shot-velocity unitize 15 vs* v+ home!
			else
				shot-velocity unitize 3 population sqrt + vs* home v+ home! 
			then
		then
	then

	30 periodic-robot-sensor if
		robot-found if
			robot-sensor-time last-danger-time!			
		then
	then

	armor dup last-armor < if
		time last-danger-time!
	then
	last-armor!

	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!
      write-our-posn^
      announce-if-hungry^
forever

;;#################################################
#type Gatherer
;#decoration 0f0 cross
#color 0f0

#hardware
processor 23
energy 400 15
armor 150
engine 0.05

food-sensor 9 3
shot-sensor 5
robot-sensor 4
eater 2.0

syphon 1.4 30 

#code

assert-stack-empty:
	stack nifr
	pause
	return

;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
#var scary-shot-found 0

#var flee-type
#vector flee-position
#var flee-time

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 25 population + random-angle polar-to-rect v+ keep-away-edges^
 then 
return

#var last-armor
Defend:
  energy max-energy / 0.1 > if
  time robot-sensor-time - time flee-time 300 + > 15 8 ifev > if
    fire-robot-sensor fire-shot-sensor sync
    shot-found shot-side side <> shot-type 3 <> or and scary-shot-found!  ;;shot-type 3 is friendly-syphon
    scary-shot-found if ;;ignore friendly-syphons.
	Call-for-help^
;;Same-side recording is harmless
;	shot-side side <> if
		time 300 + shot-side SIDE_BASE + write
;	then
    else
    	last-armor armor > robot-found if robot-found 2 >= robot-side SIDE_BASE + read time > or or then Call-for-help& ifc
    then

  then ;time to fire sensor
  else
   ;no energy to sense
   0 scary-shot-found!
  then
  last-armor armor dup last-armor! > scary-shot-found or energy max-energy / 0.05 > and if
     rdrop Begin-fleeing& jump
  then
return


common:
	CTOR_TYPE type-population 3 <= if
		COLONY_POSITION vread birth-place!
	then
      0.7 energy exponent hunger!
      write-our-posn^
      listen-to-hungry^
      announce-if-hungry^
      transfer-energy^
return

;;;;;;;;;;; START
#start
1 shot-sensor-sees-friendly!
assign-id^
position birth-place!

Begin-seeking-food:
  armor last-armor!  
  new-wander-position^ wander-position!
do
 common^

;  energy max-energy / 0.08 > if
    defend^
 ; then

  wander-position position dist 5 < if
    new-wander-position^ wander-position!
  then
  
  wander-position position v- unitize 0.2 0.09 energy max-energy / 0.02 > ifev 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  
  
	transfer-energy^ ;;also called in common^
  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

adjust-engine-eating:
 time flee-time 500 + < if
 next-meal-position position radius 2 * in-range and-if
  ;passive dodge circling while touching food
  0.05 radius 3 / position next-meal-position v- angle 1 + polar-to-rect next-meal-position v+ position v- angle polar-to-rect engine-velocity!
  engine-max-power engine-power!
 else
  ;straight to food
  next-meal-position position v- 0.07 vs* engine-velocity!
  next-meal-position position velocity 5 vs* v+ radius 2 / in-range not engine-max-power * engine-power! 
 then
return


Begin-eating-food:
  time begin-food-chase-time!
do

  adjust-engine-eating^

 common^

  adjust-engine-eating^

  do
    food-channel receive
  while
   received-food-time! received-food-amount! received-food-position!
   update-stats-food-msg^
  loop
  
;  energy max-energy / 0.05 > if
    defend^
;  then  

  adjust-engine-eating^

  transfer-energy^
  

  adjust-engine-eating^

  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 energy-sink-adr energy-sink-quality and and not and Begin-homing& ifg
forever

Begin-fleeing:
  scary-shot-found if
    ;;run away from both shot position and shot velocity.
    ;;this calculation ensures we never flee towards either.
    position shot-position v- rect-to-polar swap 1.5 > swap polar-to-rect ;;unit vector if norm >1, zero otherwise. This means relative position of close shots ignored.
    shot-velocity unitize shot-side side = if vnegate then
    v+ unitize 15 vs* position v+ flee-position!
	1 flee-type!
;    shot-side side <> if
;	   15 shot-velocity angle polar-to-rect position v+ flee-position!
 ;   else
;	;;it's our team's shot. Run away from the shot itself (a combat zone)
 ;           position shot-position v- unitize 15 vs* position v+ flee-position!
  ;  then
  else
    robot-found time robot-sensor-time - 20 < and if
      robot-position position v- unitize -20 vs* position v+ flee-position!
      2 flee-type!
    else
#comment
		  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:
#code
;	velocity norm if
;		13 velocity angle pi + polar-to-rect position v+ flee-position!	3 flee-type!
;        else
		13 birth-place position v- angle polar-to-rect position v+ flee-position! 4 flee-type!
;	then
	  then ;robot-found
  then ;shot-found
  time flee-time!

;  armor last-armor!
do
 common^
  0.3 flee-position position v- angle time flee-time - 80 mod 40 >= 0.6 -0.6 ifev + polar-to-rect engine-velocity!
  engine-max-power engine-power!
  time flee-time - 85 >
  flee-position position dist 4 < or
  energy max-energy / 0.02 < or if
    Begin-seeking-food& jump
  then

  energy max-energy / 0.05 >
  time flee-time - 100 > and if
  time shot-sensor-time - 15 > and-if
    fire-shot-sensor sync
    shot-found shot-side side <> shot-type 3 <> or and scary-shot-found!  ;;shot-type 3 is friendly-syphon
    scary-shot-found if ;;ignore friendly-syphons.
	Call-for-help^
	time 300 + shot-side SIDE_BASE + write
    ;;run away from both shot position and shot velocity.
    ;;this calculation ensures we never flee towards either.
    position shot-position v- rect-to-polar swap 1.5 > swap polar-to-rect ;;unit vector if norm >1, zero otherwise. This means relative position of close shots ignored.
    shot-velocity unitize shot-side side = if vnegate then
    v+ unitize 15 vs* position v+ flee-position! 5 flee-type!
    then

  else
    0 scary-shot-found! ;;is this needed?
  then ;time to fire sensor

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 700 80 ;;includes extra for syphon resistance
armor 180
repair-rate 0.025
processor 50 ;to dodge in a few frames
engine 0.15

robot-sensor 15 3
shot-sensor 9 4

blaster 40.4 14.5 26 ; large blaster

enemy-syphon 0.0 13.5 ;negligible

#code

#var temp2
#const pseudo-max-energy 400
#const RUN_HOME_ENERGY 100

;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

#const WALL_OFFSET 5
restrict-location: ;x y -> x y
world-height WALL_OFFSET - min WALL_OFFSET max swap
world-width WALL_OFFSET - min WALL_OFFSET max swap return



#const FIGHT_DISTANCE 12 ;14
#var CRUSADE_DIST
#const MIN_CRUSADE_DIST 10

#var have-crusade 0
#var crusade-time
#vector crusade-position
#vector birth-place
#var wander
#var wait-far-from-home-factor ;;between 0 and 8

buddy-quality: ;; id -> quality
	ALLY_SIZE * temp!
	temp ALLY_TIME_OFFSET + read time 50 - < if
		0 ;;out of date record
	else
		1000 temp ALLY_POS_OFFSET + vread position dist -
	then
return

#var buddy-one-id 70
#var buddy-two-id 75
#var buddy-one-quality
#var buddy-two-quality
#var buddy-rec-id
#var buddy-rec-quality
#vector buddy-one-pos
#vector buddy-two-pos
#vector buddy-one-cur-pos
#vector buddy-two-cur-pos
#vector buddy-one-vel
#vector buddy-two-vel
#var buddy-one-time
#var buddy-two-time
#var have-target ;;bool

#vector wander-loc -99 -99

#const gutter 11
keep-away-edges: ; x y -> x y
gutter max world-height gutter - min swap
gutter max world-width gutter - min swap
return

#start
;shot-sensor-firing-cost print
0.3 random-bool FIGHTER_TYPE type-population 4 >= and wander!
assign-id^
22 45 random-int CRUSADE_DIST!
5 20 random wait-far-from-home-factor!
position birth-place!
position desired-position!

Begin-normaling:
do

       CTOR_TYPE type-population 3 <= if
		COLONY_POSITION vread birth-place!
	then
	birth-place keep-away-edges^ birth-place!


;;;;keep track of two nearest buddies (as defined in buddy-quality proc)
#var buddy-announce-time -999
	time buddy-announce-time - 50 > if
		my-id 1 noff-channel send
		time buddy-announce-time!
	then


	buddy-one-id buddy-quality^ buddy-one-quality!
	buddy-two-id buddy-quality^ buddy-two-quality!

	dodge-and-move^

	do noff-channel receive
	while
		; id
		buddy-rec-id!
		buddy-rec-id my-id <> buddy-rec-id buddy-one-id <> buddy-rec-id buddy-two-id <> and and if
		buddy-rec-id buddy-quality^ buddy-rec-quality!
		buddy-rec-quality buddy-one-quality buddy-two-quality min > and-if
			;;replace one of them
			buddy-one-quality buddy-two-quality < if
				buddy-rec-id buddy-one-id!
				buddy-rec-quality buddy-one-quality!
			else
				buddy-rec-id buddy-two-id!
				buddy-rec-quality buddy-two-quality!
			then

		then
	loop

	dodge-and-move^


	energy pseudo-max-energy / shot-found 0.09 0.2 ifev > No-target& nifg

Wait: ;;unused label
	do
		blaster-cooldown
	while
		blaster-cooldown 2 >= if sync dodge-and-move^ else sync then
	loop

Scan: ;;unused label
	robot-sensor-time robot-found have-crusade or blaster-reload-time 60 ifev +
	time <= if
				sync
				buddy-one-id ALLY_SIZE * temp!
				temp ALLY_POS_OFFSET + vread buddy-one-pos!
				temp ALLY_VEL_OFFSET + vread buddy-one-vel!
				temp ALLY_TIME_OFFSET + read buddy-one-time!
				buddy-one-pos buddy-one-vel time buddy-one-time - vs* v+ buddy-one-cur-pos!

				buddy-two-id ALLY_SIZE * temp!
				temp ALLY_POS_OFFSET + vread buddy-two-pos!
				temp ALLY_VEL_OFFSET + vread buddy-two-vel!
				temp ALLY_TIME_OFFSET + read buddy-two-time!
				buddy-two-pos buddy-two-vel time buddy-two-time - vs* v+ buddy-two-cur-pos!

		fire-robot-sensor sync
		robot-found if
			NextRobot:
				robot-position robot-velocity 50 vs* v+ position 7 population sqrt + 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
			
;;				blaster-cooldown NoShoot& ifg ;;unnecessary conditioning?
				robot-position 
				robot-velocity velocity v- robot-distance blaster-speed / vs* v+ position v- unitize blaster-speed vs* velocity v+ vtemp!
				;vtemp is velocity of blaster after firing.

				buddy-one-cur-pos position v- vtemp dot 0 > buddy-one-quality and if
				buddy-one-cur-pos position v- buddy-one-vel vtemp v- unitize cross abs 1.5 < NoShoot& ifg
				then

				buddy-two-cur-pos position v- vtemp dot 0 > buddy-two-quality and if
				buddy-two-cur-pos position v- buddy-two-vel vtemp v- unitize cross abs 1.5 < NoShoot& ifg
				then

				robot-position robot-velocity lead-blaster

				NoShoot:

				dodge-and-move^

				1 have-crusade!
				time crusade-time!
				robot-position crusade-position!

				;call for help if they're closing us, they're reloading, or we see a shot
				robot-velocity robot-position position v- unitize dot -0.1 < robot-reloading or shot-found or if
					robot-position robot-sensor-time 3 kill-channel send
				then


				robot-position robot-velocity 10 vs* v+ vtemp!
				FIGHT_DISTANCE birth-place vtemp dist 5 - 5 max min ;;FIGHT_DISTANCE, but closer if close to home
				position vtemp v- angle ;;time 5 / my-id + reorient 30 / +
				polar-to-rect
				vtemp v+
				;;stack: tentative desired position
				;;Now add correction to stay away from buddies
				buddy-one-quality if
					position buddy-one-cur-pos v- rect-to-polar
					;stack: tent-des-pos ally-dist ally-angle
					swap 1 max reciprocal square swap polar-to-rect v+ 
				then
				buddy-two-quality if
					position buddy-two-cur-pos v- rect-to-polar
					;stack: tent-des-pos ally-dist ally-angle
					swap 1 max reciprocal square swap polar-to-rect v+ 
				then
				desired-position!
				;;stack empty
				1 have-target!
				Done-targeting& jump
TryNextTarget:
			next-robot NextRobot& ifg
		then
	else
		Done-targeting& jump
	then ;time to fire
	
No-target:
	0 have-target!

	have-crusade if
	crusade-position position 5 in-range 
	time crusade-time - 200 > or
	and-if
			0 have-crusade!
	then

        dodge-and-move^


		
	have-crusade nif
	energy pseudo-max-energy / 0.3 > and-if
		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

	dodge-and-move^

	have-crusade if
	energy pseudo-max-energy / 0.15 > and-if	
		energy pseudo-max-energy / 0.2 > if
			crusade-position
		else
			position
		then
		desired-position!
	else
                population 10 < if
			population temp!
		else
			population sqrt wait-far-from-home-factor * energy RUN_HOME_ENERGY < if 0.5 * then temp!
                then
		position birth-place temp in-range nif
			birth-place
		else
			energy pseudo-max-energy / 0.5 > if
			wander and-if
				wander-loc birth-place temp in-range not wander-loc position 5 in-range or if
					birth-place temp random-angle polar-to-rect v+ restrict-location^ wander-loc!
				then
				wander-loc
			else
				position
			then
		then
		desired-position!
	then ;have crusade
	
Done-targeting: ;;jump here if we have a target
	eat-and-move^
	dodge-and-move^

	energy pseudo-max-energy / 0.2 > max-repair-rate 0 ifev repair-rate!

	write-our-posn^
	1 energy pseudo-max-energy / - hunger!
        announce-if-hungry^

	Dodge-and-move^
forever

#const MAX_SPEED 0.19


;in: desired-position
;out: desired-velocity, speed-slop
;As might be guessed from the name this procedure used to be responsible for eating too.
;Now that this fighter no longer eats it might be cleaner to have the main code set desired-velocity directly and eliminate this procedure.
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


#var shots-angle 0   ;;;the angle which the shots we're dodging are coming from (for sensor-focus aiming)
#var last-danger-time -500 ;;;shot-sensor is fired more frequently after danger
#vector assumed-self-velocity ;;;an initial guess at our velocity in the near future
#vector dv  ;;;;shot-velocity assumed-self-velocity v-
#vector perp_hat ;;;;a unit vector perpendicular to dv (rotated 90 degrees CCW)
#var assumed_miss ;;;miss distance if we travel at asssumed-self-velocity
#const MIN_MISS_DIST 2.1 ;;;minimum acceptable distance between shot center and our center
;#const MIN_GRENADE_MISS_DIST 3 ;;grenades have shot-type 2. Treating grenades differently is not worth the extra 5ish instructions probably.
#vector dodged-shot-posn ;;for debugging, the position of the shot we dodged
#var shot-sense-time ;;for debug
;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 while staying as close to desired-velocity as possible.
Dodge-and-move:
	time shot-sensor-time - time last-danger-time 100 + > 8 5 ifev < ifr
	write-our-posn^ ;;not really dodge and move related but it wants to be done a lot
	energy pseudo-max-energy / 0.05 < Obey-user& ifg

	;do as much computation as possible before firing shot sensor to get up-to-date info		
	desired-velocity rect-to-polar swap 0.05 min swap polar-to-rect vtemp! ;;vtemp is desired-vel limited to speed 0.1
	velocity vtemp dist 0.05 <= if
		vtemp
	else
		velocity vtemp v- unitize -0.05 vs* velocity v+
	then
	assumed-self-velocity!

	;stack empty

	6 shot-sensor-focus-distance! shots-angle shot-sensor-focus-direction!

;;;From here until engine-velocity is set instructions have been squeezed out.

;;;; Here through the commented-out HaveShot label is structured weird to save a few precious instructions.
;;;; No unnecessary jumps but hard to read.
	fire-shot-sensor sync
        shot-sensor-time shot-sense-time! ;;for debug
	shot-found Obey-User& nifg
shot-process-loop:
	shot-velocity norm 2 < CheckPos& ifg
NextShot:
	next-shot shot-process-loop& ifg
Obey-User: ;This ObeyUser stuff doesn't belong here, but putting it here saves an instruction (pushing address for ifeg).
	desired-velocity
	shot-found 2 > if 0.1 time 7 / polar-to-rect v+ then   ;;;passive dodge
	engine-velocity!
	velocity desired-velocity v- norm speed-slop < 0 engine-max-power ifev engine-power!
	CheckNewHostiles& jump			
CheckPos:
	;don't dodge super-fast shots such as force fields
	shot-velocity assumed-self-velocity v- dv!
	dv unitize shot-position position v- dot
	-2.0 < NextShot& nifg ;;don't dodge shots too late to dodge
	dv norm 0.05 < NextShot& ifg ;;prevents divide by zero errors from dv norm and dv project.

;HaveShot:
#var dodging-needed
	dv unitize negate swap perp_hat! ;;perp_hat is a unit vector perpendicular to dv (rotated pi/2 CCW)
;;one of the key ideas of this active dodging routine is to express vectors in a basis of dv and perp_hat.
	perp_hat shot-position position v- dot assumed_miss!
	;negative "assumed_miss" means shot miss to left, dodge right (facing shot's origin) if we go assumed_vel
	;positive means    miss right dodge left
	
perp_hat ;;put on stack for use in a while
;The following line has a bug if assumed_miss==0, but who cares as that's unlikely and using signum saves a few instructions.
shot-position position v- perp_hat MIN_MISS_DIST assumed_miss signum * vs* v-
;stack: perp_hat, our change in position before intercept if shot is to barely miss
shot-distance dv norm / ;this line is time to intercept.
vs/ shot-velocity v+ ;stack: perp_hat, velocity so that we barely miss
2 vs* velocity v- ;;adjust velocity by twice to account for slow acceleration
perp_hat dot ;;target velocity to barely miss, perp_hat direction
0.25 min -0.25 max ;;limit attempted speed to attainable
desired-velocity perp_hat dot
;;now the stack has the old perp_hat, perp_hat component velocity to barely miss, and perp_hat component velocity if we
;; don't dodge and simply go at desired. Now we see if desired velocity would allow us tomiss.
	assumed_miss 0 > if
		2dup < dodging-needed! min
	else	
		2dup > dodging-needed! max
	then
;stack: perp_hat, desired perp_hat component of engine_velocity
	vs* ;use the perp_hat pushed on stack many lines ago
	dodging-needed nif desired-velocity else assumed-self-velocity then dv project v+
	engine-velocity!
;stack empty now

	engine-max-power engine-power!

	shot-velocity angle pi + shots-angle!
	shot-position dodged-shot-posn! ;;for debugging

CheckNewHostiles:
	shot-found if

		;have-crusade nif
		shot-velocity norm have-target not and if
			shot-position shot-velocity -100 vs* v+ crusade-position!
			shot-sensor-time crusade-time!
			1 have-crusade!
		then

		time last-danger-time!
		do
			time 500 + shot-side SIDE_BASE + write 
		next-shot while-loop
	then
	return
;end of Dodge subroutine

#end

Version 8[]

Version 8

Advertisement