#side Active 5
#author Warren
#color f08

(20021231) Improved eat-and-move copied from haystack.
(Devon 20030104) Only make missiles when populous.
(20030502) Shorter range, and more energy storage.
           More cautious about crusades, and more prone to running away.
           More position slop.
					 Added decorations.
(Devon 20030627) Switched to shieldless missiles.

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
#decoration fcc circle
#color fff
#hardware
energy 500 100
armor 120
processor 35 ;to dodge in 2 frames
radio send receive
eater 1.8
engine 0.15

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

constructor 0.9
repair-rate 0.1

grenades 35 9 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.5 > 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 7
#const MIN_FIGHT_POSITION_SLOP 3
#const FIGHT_DISTANCE 7
#const CRUSADE_DIST 25
#const MIN_CRUSADE_DIST 12

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

Begin-normaling:
do
	energy max-energy / 0.2 > 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.6 > and-if
	constructor-type not constructor-remaining 200 > or 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.45 < 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.2 < 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
#decoration fcc circle

(Devon 20030602) No more shields.

#hardware
processor 15
energy 300 300
armor 150

engine 0.15

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:
	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
charge& choose-next-state& jump

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

#end
