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

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-600 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

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

#const DANGER_TIME 970


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


;; 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 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!
	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 1.0 num-children 0.1 * - 0.7 max * > 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!
		num-children 1 + num-children!
	then
	max-energy 0.04 * 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
			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.06

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

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:
  energy max-energy / 0.1 > if
  time robot-sensor-time - 30 > 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 1 > or 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

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

common:
	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

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.05 > if
    defend^
;  then  

  next-meal-position seek-location

  transfer-energy^
  
  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

#var flee-type
#vector flee-position
#var flee-time
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 - 20 > 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 8 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 suck 0 ;;got enemy syphon target?
#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


#vector wander-loc -99 -99

#start
0.5 random-bool FIGHTER_TYPE type-population 7 >= and wander!
blaster-firing-cost print
assign-id^
15 50 random-int CRUSADE_DIST!
5 20 random wait-far-from-home-factor!
position birth-place!
position desired-position!

Begin-normaling:
do

;;;;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 > 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 > 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:

				;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

				dodge-and-move^

				robot-position robot-velocity 15 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 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

	dodge-and-move^
	
	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 pseudo-max-energy / 0.3 > and-if
		crusade-position desired-position!
	else
		population sqrt wait-far-from-home-factor * energy RUN_HOME_ENERGY < if 0.5 * then temp!
		position birth-place temp in-range nif
			birth-place
		else
			energy pseudo-max-energy / 0.7 > 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^

	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

	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

;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 - 6 < ifr

	time last-danger-time 100 + >
	time shot-sensor-time - 15 > robot-found not and and
	energy pseudo-max-energy / 0.05 < or
		Obey-user& ifg
	
	do
		time shot-sensor-time - 8 >	
	until
		write-our-posn^ ;;; waste time for a few frames
	loop

	;do as much computation as possible before firing shot sensor to get up-to-date info		
	desired-velocity rect-to-polar swap 0.12 min swap polar-to-rect vtemp! ;;vtemp is desired-vel limited to speed 0.1
	velocity vtemp dist 0.08 <= if
		vtemp
	else
		velocity vtemp v- unitize -0.08 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
	write-our-posn^ ;;contains a "sync".
	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 if 0.1 time 10 / 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.2 < 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
		time last-danger-time!
		do
			time 500 + shot-side SIDE_BASE + write 
		next-shot while-loop
	then
	return
;end of Dodge subroutine

#end
