#side Active 3
#author Warren
#color f08

(20021231) Improved eat-and-move copied from haystack.
(Devon 20030104) Only make missiles when populous.

A predator with active dodging.

ToDo:
-identify source of shots and target (cross-product trickery, similar to dodging)
-missile defense
-add a rat type that runs away (experimental)

#type Dodger
#hardware
energy 400 100
armor 120
processor 35 ;to dodge in 2 frames
radio send receive
eater 1.2
engine 0.15

robot-sensor 11
shot-sensor 9 3
food-sensor 8 5

constructor 0.9
repair-rate 0.1

grenades 35 11 25

;blaster 0.1 5 3

#code

#const TARGET_CHANNEL 1

;communication with Dodge-and-move:
#vector desired-velocity 0 0
#var speed-slop 0.01

;communication with Eat-and-move:
#var position-slop 3
#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

food-sense: ;t ->
food-sensor-time food-sensor-time -1000 ifev + time < if
	;too old
	fire-food-sensor sync
	return	
else return ;previous sensing is new enough
then

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
#vector current-food
#var have-current-food 0
#const MAX_SPEED 0.15
#vector wander-loc
#var wander-time
eat-and-move:
	food-sensor-time 30 + time < if
	position-slop 0.1 > and-if ;don't waste sensor firing if pos-slop tiny
		have-current-food if
		current-food desired-position dist position-slop < and-if
			current-food
		else
			desired-position
			position-slop food-sensor-range min 0.5 * random-angle polar-to-rect v+
			;add a little randomness
		then
		position v- rect-to-polar
		food-sensor-focus-direction! food-sensor-focus-distance!
		fire-food-sensor sync
		0 have-current-food!
		food-found if
			Food-check-loop:
			food-position desired-position dist position-slop < if
				food-position current-food!
				1 have-current-food!
			else
				next-food Food-check-loop& ifg
			then
		then ;food-found
	then ;time to fire
	have-current-food if
		current-food
	else
		position-slop food-sensor-range > if ;no point wandering if we can see it already
			desired-position wander-loc dist position-slop >
			wander-loc position 2 in-range
			or if
				position-slop random-angle polar-to-rect
				desired-position v+
				restrict-location^ wander-loc!
			then
			wander-loc	
		else
			desired-position
		then
	then
	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

#start
	random-position wander-loc!

#const MAX_FIGHT_POSITION_SLOP 6
#const MIN_FIGHT_POSITION_SLOP 1
#const FIGHT_DISTANCE 9
#const CRUSADE_DIST 25
#const MIN_CRUSADE_DIST 12

#var have-crusade 0
#var crusade-time
#vector crusade-position

Begin-normaling:
do
	energy 50 > No-target& nifg
	robot-sensor-time grenades-reload-time + time < if fire-robot-sensor sync
		robot-found if
			armor max-armor < if
				robot-position robot-sensor-time 3 TARGET_CHANNEL send
			then
			grenades-cooldown nif
				robot-position position v-
				robot-velocity time robot-sensor-time - vs* v+
				;stack: current relative position of enemy
				
				robot-velocity velocity v-
				robot-distance grenades-speed / vs* v+
				rect-to-polar fire-grenade
			then
			position robot-position v- unitize FIGHT_DISTANCE vs*
			robot-position v+ desired-position!
			MAX_FIGHT_POSITION_SLOP
			energy max-energy / MAX_FIGHT_POSITION_SLOP MIN_FIGHT_POSITION_SLOP - *
			- position-slop!
			Done-targeting& jump
		else ;no robot found
			No-target& jump
		then ;found
	else
		Done-targeting& jump
	then ;time to fire
	
	No-target:
	have-crusade if
	crusade-position position robot-sensor-range 0.8 * 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
		TARGET_CHANNEL messages 2 - 0 max 0 random-int TARGET_CHANNEL skip-messages
		do
			TARGET_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
	armor max-armor / 0.5 > and-if
		crusade-position desired-position!
		5 position-slop!
	else
		infinity position-slop!
		position desired-position!
	then ;have crusade
	
	Done-targeting:
	Dodge-and-move^
	constructor-type nif
		1 type-population 10 > if 1 2 random-int
		else 1 then constructor-type!
	then
	energy 100 > max-repair-rate 0 ifev repair-rate!
	max-energy 0.7 * constructor-remaining min energy <
	constructor-max-rate 0 ifev constructor-rate!
	eat-and-move^
	Dodge-and-move^
	
	shot-found if ;run away
	shot-velocity norm and-if
	energy max-energy / 0.05 > and-if
	energy max-energy / 0.2 <
	armor max-armor / 0.3 < or and-if
		shot-velocity 60 vs* position v+ restrict-location^ desired-position!	
		Begin-running& jump
	then
forever

;desired-position is set before entering running
Begin-running:
do
	energy 100 > max-repair-rate 0 ifev repair-rate!
	
	energy max-energy / 0.1 < nif
	robot-sensor-time grenades-reload-time + time < and-if
	grenades-cooldown not and-if
		fire-robot-sensor sync
	robot-found and-if
		robot-position position v-
		robot-velocity time robot-sensor-time - vs* v+
		;stack: current relative position of enemy
		
		robot-velocity velocity v-
		robot-distance grenades-speed / vs* v+
		rect-to-polar fire-grenade
	then ;shooting

	5 position-slop!
	eat-and-move^
	Dodge-and-move^
	desired-position position dist position-slop < if
		Begin-normaling& jump
	then
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

;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:
	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
	shot-process-loop:
		shot-velocity norm if
			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 -
	dup 1 swap polar-to-rect go-dir!
	MIN_MISS_DIST miss abs - 0 max ;dist to move
	shot-distance 3 max / shot-velocity norm * DODGE_OVERKILL_FACTOR * ;this line: flee speed
	swap polar-to-rect
	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
	;2dup angle fire-blaster
	engine-velocity!
	engine-max-power engine-power!
	return
	Obey-User:
	desired-velocity engine-velocity!
	velocity desired-velocity v- norm speed-slop < 0 engine-max-power ifev engine-power!
	return
;end of Dodge subroutine

;a second version of above, different strategy
Dodge-and-move2:

#comment


#type Missile
#color f00
#hardware
processor 15
energy 300 300
armor 150

engine 0.15

shield 0.4

bomb 1300

robot-sensor 10
shot-sensor 8

solar-cells 0.05

#code
#vector target
#vector target-v
#vector wander-loc

#const shot-chase-distance 10

new-wander-loc:
	-20 world-width 20 + random -20 world-height 20 + random
	restrict-position^ wander-loc! return

restrict-position: ; vector -> vector
world-height min 0 max swap world-width min 0 max swap return

set-shot-chase: ;stack: empty -> empty
			shot-chase-distance
			shot-velocity -100 vs* shot-position position v- v+ angle
			polar-to-rect position v+
			restrict-position^ target! 0 0 target-v!
return

choose-next-state: ; ->
	robot-sensor-time 20 + time < if
		fire-robot-sensor fire-shot-sensor sync
		armor max-armor / 0.5 > if
		shot-found and-if
		shot-velocity norm and-if
			set-shot-chase^
			drop charge& jump
		then
		robot-found 1 > robot-mass 30 > or if
			robot-distance 2 < if die sync then
			robot-position target!
			robot-velocity target-v!
			drop charge& jump
		then
		drop wander& jump
	then ;time to scan
jump
	
#start
new-wander-loc^
begin-wandering:
wander:
	0 shield!
	wander-loc position v- rect-to-polar swap 0.05 * 0.1 min swap polar-to-rect
	target-v v+ engine-velocity!
	engine-max-power engine-power!
	wander-loc position dist 5 < new-wander-loc& ifc
	energy max-energy / 0.1 < begin-nursing& ifg
wander& choose-next-state& jump

begin-charging:
charge:
	target seek-location
	energy 30 > max-shield 0 ifev shield!
charge& choose-next-state& jump

begin-nursing:
	0 engine-power!
	0 shield!
do
	sync sync sync sync sync sync sync sync sync sync
	energy max-energy / 0.4 > begin-wandering& ifg
forever

#end
