Fandom

Grobots Wiki

Big Bertha

54pages on
this wiki
Add New Page
Talk0 Share
Big Bertha
Big-bertha1
Blaster Bertha finishes the Borg Cube with one of its massive shots.

Author

Warren

Strategy

Giant seed spawns Active

Last modified

12 August 2012

Big Bertha copies The Lunacy's strategy of a massive seed with a long-range weapon that spawns an ordinary side. The Lunacy uses Massive Beast for the giant seed, and builds Revenge Killer 4; Big Bertha uses a new giant type, also named Big Bertha, and builds Active 9.

Big Bertha is named after a 43-tonne German WWI howitzer that fired 830 kg shells 12 km.

The Big Bertha type is eater-powered and has a large long-range grenade launcher and a small long-range blaster. Every other time the blaster is shot together with the grenade at the target; the other times the blaster is aimed a bit behind the enemy. This is designed primarily to defeat active dodgers, which will often dodge the grenade and walk right into the blast.

Blaster Bertha is a variant with a long-range 158-damage blaster and no grenades. This enormous blaster kills most civilian cells in one shot and most fighter cells in two shots. Many sides do poorly against kill-shots. In particular, many sides do not start dodging until shots are seen or damage is taken, but with a kill shot that may come too late. Also dead cells can't call for help.

Note that Blaster Bertha was originally called Big Bertha, but the grenade-using version has a much better claim to the name of a howitzer, so I renamed it. Blaster Bertha is a lot better than Grenade Bertha against current top-9 opponents, but Big Bertha does much better against active dodging so in the future it may have the upper hand.

A third variant is RK Bertha. As its name suggests it uses RK 5 instead of Active. It is intermediate between the other two in the grenade/blaster tradeoff: it spends the same amount on grenade and blaster hardware. It doesn't have the every-other-shot trick as that proved to be counter-productive.

Source - RK BerthaEdit

Version 1Edit

#side RK Bertha
#author Warren
#color e83
#seed 1 3 3 2 3 2

Like Big Bertha but more blaster and less grenades and more importantly RK 5 instead of Active.

;################ SHARED #################
#code

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


#const HOME_CORNER 972 ;;a 0/1 vector

#const CORNER_CLEARED 978 ;;0/1 indicator

;;;;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 GATHERER_TYPE 3
#const FIGHTER_TYPE 2

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

;copied from Walled City 2 via Cyclops
;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 100 + ;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+300
	food-hash write
	1 return

reclaim-food: ;;updates time-stamp of food we've claimed already.
	time 100 + food-hash write
return

clamp-pos: ;x y -> x y limited to in-range
0 max world-height min swap
0 max world-width min swap
return



#type Big Bertha
#color ff0
#hardware
  processor 40
  armor 650
  repair-rate 0.25
  engine 0.4
  energy 1000 200
  
  blaster 83.7 26 50 ;50
  grenades 103.7 26 50 ;136
  
  robot-sensor 27.5 2
  shot-sensor 7
  food-sensor 13 5
  eater 4
  constructor 1.7
 
  
#code


#const WAKE_ENERGY 180
#const SHOOT_ENERGY 200
#const START_CONSTRUCT_ENERGY 600
#const CONSTRUCT_ENERGY 450
#const REPAIR_ENERGY 330

#var temp

#vector target-food
#vector current-waypoint
#var waypoint-left ;bool, true if waypoint is left side vs bottom
#var waypoint-second ;bool, true if waypoint is second on side vs first.
#var waypoint-radius ;scalar
#const waypoint-gutter 10
#var world-width-minus-gutters
#var world-height-minus-gutters

#var eating ;bool
#var danger-time 0


#var corner-left-right
#var corner-bottom-top
#vector corner-position

map-*: ;x y a b -- x*a y*b
rot * rrot * swap ;; How unintuitive
return

;convert real coords to home-centric ones
real-to-my-posn: ; x y - x y
corner-position v- corner-left-right corner-bottom-top map-*
return

;inverse
my-posn-to-real: ; x y - x y
corner-left-right corner-bottom-top map-* corner-position v+
return

;convert a relative vector in our coords to real or back again
;(the same because -1*-1=1)
my-relative-to-real:
real-to-my-relative:
corner-left-right corner-bottom-top map-*
return

set-corner-stuff: ;; a b -- where a,b are 0/1 indicators of which corner we have
;stack: nearest corner in 0/1 for each
2dup world-height * swap world-width * swap corner-position!
-2 vs* 1 1 v+ corner-bottom-top! corner-left-right!
return

adjust-engine:
eating if
 time danger-time < if
  target-food position radius 3 * in-range if
  ;passive dodge circling while touching food
  radius 4 /
  position target-food v- angle 1 + polar-to-rect target-food v+ position v- engine-velocity!
  else
  ;head to food with wiggle
  target-food position v- 0.05 vs* rect-to-polar time 40 mod 20 > 0.5 -0.5 ifev + polar-to-rect engine-velocity!
  engine-max-power engine-power! 
  then
 else
  ;straight to food
  target-food position v- 0.05 vs* engine-velocity!
  target-food position velocity 5 vs* v+ radius 2 / in-range engine-max-power * engine-power! 
 then

  engine-max-power engine-power!
else
  0.08 current-waypoint position v- angle time danger-time < if time 40 mod 20 > 0.5 -0.5 ifev + then
  polar-to-rect engine-velocity!
  engine-max-power engine-power!
then
return

set-current-waypoint:
  waypoint-left if
    waypoint-radius world-height-minus-gutters < if
      waypoint-gutter waypoint-gutter waypoint-radius +  
    else
      waypoint-radius world-height-minus-gutters - waypoint-gutter + world-height waypoint-gutter - 
    then
  else
    waypoint-radius world-width-minus-gutters < if
      waypoint-gutter waypoint-radius + waypoint-gutter   
    else
      world-width waypoint-gutter - waypoint-radius world-width-minus-gutters - waypoint-gutter +  
    then
  then
  my-posn-to-real^ current-waypoint!
return

#var shoot-straight 1 ;;boolean
#start
 world-width waypoint-gutter 2 * - world-width-minus-gutters!
 world-height waypoint-gutter 2 * - world-height-minus-gutters!

 time 20 < if
	position world-height / round swap world-width / round swap 2dup HOME_CORNER vwrite
 else
	HOME_CORNER vread
 then set-corner-stuff^
	1 waypoint-left!
        0 waypoint-second!
        0 waypoint-radius!
        set-current-waypoint^

time 20 > if
do ;;hack to avoid friendly fire on birth
   current-waypoint seek-location
energy WAKE_ENERGY < position current-waypoint 10 in-range or until-loop
then

do

 eating eaten not and target-food position radius in-range and if
  0 eating!
 then

 eating if reclaim-food^ then

 eating not time food-sensor-time - 30 > and if
  
  robot-found if
   ;run away
   position robot-position v- angle food-sensor-focus-direction!
   robot-sensor-range 5 - robot-distance - food-sensor-focus-distance!
  else
   current-waypoint position v- angle food-sensor-focus-direction!
   5 food-sensor-focus-distance! ;was 4
  then

  fire-food-sensor sync  
   
	food-found if
		Food-check-loop:
                food-energy 80 > if
		claim-food^ and-if
   food-position target-food!
   1 eating!
		else
			next-food Food-check-loop& ifg
		then
	then ;food-found
 then

 energy SHOOT_ENERGY > grenades-cooldown not and if
 time robot-sensor-time 30 + > and-if
		fire-robot-sensor sync
		robot-found if	
  ;accelerating while firing can mess up aiming of long range shots a bit, on the order of a distance of 0.02*30=0.6. This
  ; isn't enough to miss most targets, but together with other errors might cause a miss, so maintain steady velocity while firing.
  ;From the GB source:
  ; GBWorld::SimOneFrame order: think (includes lead-blaster firing direction computation), move (includes drag), act, collide
  ; HardwareState::Act order: engine, blaster
  ;with engine-velocity set to velocity the drag and engine effect cancel and thinking and blaster-acting velocities should match.
		velocity engine-velocity! engine-max-power engine-power! ;helps aim

		shoot-straight if
			robot-position robot-velocity lead-grenade
			robot-position robot-velocity lead-blaster
		else
		;blaster aimed around 2 units behind the enemy, grenade a tiny bit ahead. The reason for the tiny bit ahead is convincing stationary actives to dodge the wrong way.
		; Grenade is slightly faster so enemy probably dodges it, right into the blaster.
			robot-distance 10 min 0.1 * square robot-position position v- angle polar-to-rect robot-position position v- robot-velocity cross 0 > if swap negate else negate swap then 2dup 
			
			-0.02 vs* robot-position v+ robot-velocity lead-grenade
			energy 150 > if	
			robot-position v+ robot-velocity lead-blaster
			else 2drop then
	then
	;shoot-straight not shoot-straight!
   robot-sensor-time 100 + danger-time max danger-time!
   sync ;let the lead-blaster happen
   adjust-engine^ ;puts engine back in an appropriate state
 then
 then


 20 periodic-shot-sensor if
   shot-found if
      time 200 + danger-time max danger-time!
   then
 then

constructor-type not energy START_CONSTRUCT_ENERGY > and if
   GATHERER_TYPE constructor-type!
 then


 constructor-progress 300 > CONSTRUCT_ENERGY START_CONSTRUCT_ENERGY ifev energy < constructor-max-rate * constructor-rate!


 energy REPAIR_ENERGY > max-repair-rate * repair-rate!


 current-waypoint position 10 in-range if
   1 CORNER_CLEARED write ;;is redundant except for first time but that's harmless
   waypoint-second if
    waypoint-left not waypoint-left!
    0 waypoint-second!
   else
    1 waypoint-second!
    world-width world-height + waypoint-gutter 4 * - dup temp! waypoint-radius = if
	0 waypoint-radius!
    else
	    temp 4 / waypoint-radius + temp min waypoint-radius! ;temp/4 was 25
    then
    
   then
   set-current-waypoint^
 then

 adjust-engine^

forever


#type Arm's Length
;#decoration 00f cross
#color f00
#hardware
  processor 35
  armor 200
  engine 0.11
  energy 600 200
  radio send receive
  
  blaster 18 10.5 12
  enemy-syphon 0.01 11 ;primarily for distracting enemy active dodgers
  
  robot-sensor 10.5 3
  shot-sensor 9 2
  food-sensor 6 3
  eater 0.4
  
#code


#const FIGHT_DIST 9.3

#vector desired-velocity
#const NO_DODGE_DIST 2.5 ;minimum miss distance (center to center) that we don't dodge
#var miss-dist ;temp for dodging
;Here's a simple dodge and move routine. It takes no arguments on the stack and returns nothing. Its inputs are the vector variable desired-velocity and the hardware variables shot-sensor-focus-*. It sets engine-velocity and engine-power appropriately.
;The user should set desired-velocity to the velocity they would prefer absent any dodging. For example set desired-velocity whenever you would have set engine-velocity (or called seek-location).
;If user knows what direction shots will come from set shot-sensor-focus-direction to that direction and shot-sensor-focus-distance to 5ish. Otherwise set shot-sensor-focus-distance to 0.
dodge-and-move:
fire-shot-sensor sync

shot-found No-danger& nifg
;;following is specialized to shot-sensor returning <=2 results
shot-velocity norm dup 1 < and
shot-velocity unitize shot-position position v- dot -2.5 < ;quick and dirty test to see if it's far enough away to dodge.
and have-shot& ifg ;ignore stationary or hyper-fast shots
next-shot drop ;if only one shot this does nothing but that's fine
shot-velocity norm dup 1 < and no-danger& nifg
have-shot: ;30 instr up to here
#vector dodged-shot ;debug out
shot-position dodged-shot!

;new coordinates: shot-velocity minus half our velocity is along new x axis.
;compute our position in a shifted and rotated coordinated system (axes) where
;the shot is at the origin and the shot is moving along the (new) x axis.
;The new y' component is the miss distance (positive or negative) if we sit still. The new x' coordinate is how far we're in front of the shot (if positive) or behind it (if negative).
	position shot-position v- rect-to-polar shot-velocity velocity 0.5 vs* v- angle - polar-to-rect
	miss-dist! ;leave other coordinate on stack for next line
;Despite the name miss-dist is a component and can be negative.

;use how far shot has to go before passing us from stack
1.8 < ; no time to dodge or shot already past us; ignore it.
miss-dist abs NO_DODGE_DIST > or no-danger& ifg ;only dodge shots that are close to hitting us
;try to dodge at speed 2 with appropriate sign.
	 miss-dist 0 < -2 2 ifev
;leave cross velocity on stack

;now convert velocity back to ordinary coordinates to set the engine.
	pi/2 shot-velocity angle + polar-to-rect 2dup engine-velocity! ;;preliminary to get moving, adjust in a sec
	engine-max-power engine-power! ;69 instr to here
	desired-velocity rect-to-polar swap 0.1 min 6 * swap polar-to-rect v+ ;mix in a little desired-velocity, up to 0.6 vs. 2 dodge
	engine-velocity!
	return
no-danger:
	desired-velocity engine-velocity!
	engine-max-power engine-power!
return ;end of dodge-and-move


#var current-seperation
#var enemys-closing-speed
#vector current-enemy-position

#vector target-delta
#vector expected-shot-velocity

#vector enemy-position
#vector enemy-velocity
#var enemy-time
#var enemy-processed

#vector rumor-position
#vector rumor-velocity
#var rumor-time
#var have-rumor

#var anger
#var rumor-xmit-time
#var safe-xmit-time
#var hungry-xmit-time

#var received-time
#var received-anger
#vector received-position

#var max-crusade-distance

#var have-food 0

#const EDGE_SHY 10
#vector circle-center ;wandering for food

non-shielded-robot-found: ;returns 1 if a non-shielded robot is found, 0 otherwise. Leaves robot cursor on non-shielded target.
robot-found if
  do
    robot-shield-fraction 0.25 > if
      1 return
    then
  next-robot while-loop
then
0 return

out-of-bounds: ;x y -> bool
2dup 0 < swap 0 < or if
  2drop 1 return
then
world-height > swap world-width > or return

#start
5 shot-sensor-focus-distance! ;stays unchanged forever
30 60 random max-crusade-distance!

Begin-waiting:
    0 robot-sensor-focus-distance!
0 anger!
0 enemy-syphon-rate!
0 have-rumor!
0 have-food!
position EDGE_SHY max world-height EDGE_SHY - min swap EDGE_SHY max world-width EDGE_SHY - min swap circle-center!
do
  energy max-energy / 0.2 > if
  time robot-sensor-time - 20 > and-if
	    fire-robot-sensor sync
	    non-shielded-robot-found^ if
	      Begin-chasing-enemy& jump
	    then
  then

have-food nif
30 periodic-food-sensor and-if
	food-found if
		Food-check-loop:
		food-velocity norm nif
		claim-food^ and-if
			1 have-food!
		else
			next-food Food-check-loop& ifg
		then
	then ;food-found
then
  
;food code copied from cyclops
  have-food if
    food-position position v- 0.08 vs* desired-velocity!
	reclaim-food^
	food-position position radius in-range
	eaten not and if
		0 have-food!
	then
  else
    population 10 > if
    circle-center position v- 0.001 vs* 0.08 time 50 / polar-to-rect v+ desired-velocity!
    else
     circle-center position v- 0.05 vs* desired-velocity!
    then
  then

  energy 30 > if
    time shot-sensor-time - 8 > if
        dodge-and-move^
    shot-found and-if
      energy max-energy / 0.2 > if
 	      set-shot-chase^
	      Begin-chasing-rumor& jump
      else
 	18 periodic-robot-sensor if ;not enough energy to chase, but shoot while eating
	robot-found and-if
		robot-position robot-velocity lead-blaster
	then
      then ;energy
    then ;shot-sense-time and shot found
  else
     desired-velocity engine-velocity! engine-max-power engine-power!
  then


  do
    help-channel receive
  while
    received-time! received-position!
    received-position position dist max-crusade-distance < 
    time received-time - 40 < and
      have-rumor if received-position position dist rumor-position position dist < and then ;closer
    if
      received-position rumor-position!
      received-time rumor-time!
      0 0 rumor-velocity!
      0 anger!
      1 have-rumor!
    then
  loop
  
  have-rumor if
  time rumor-xmit-time - 20 > and-if
  energy max-energy / 0.3 > and-if
    time rumor-xmit-time!
    rumor-position rumor-time 1.5 4 kill-channel send
  then
  
  time safe-xmit-time - 50 > if
  energy max-energy / 0.5 > and-if
    time safe-xmit-time!
    position time 3 safe-channel send
  then
  
  do
    kill-channel receive
  while
    received-anger! received-time! received-position!
    received-position position dist max-crusade-distance < if
    time received-time - 40 < and-if
	    have-rumor nif
	      received-position rumor-position!
	      received-time rumor-time!
	      1 have-rumor!
	    then
	    
	    received-position rumor-position dist 5 < if
	      anger received-anger + anger!
	    then
    then
  loop
  
  time rumor-time - 70 > if
    0 have-rumor!
  then
  
  have-rumor if
  anger 1 type-population sqrt > 0.1 random-bool or and-if
  energy max-energy / 0.25 > and-if
    Begin-chasing-rumor& jump
  then
  
  anger 0.93 * anger!
  
  energy max-energy / 0.6 < if
  time hungry-xmit-time - 100 > and-if
  velocity norm not and-if
   time hungry-xmit-time!
   position time 3 hungry-channel send
  then

forever

Begin-chasing-enemy: ;entered when robot-* is enemy
      robot-sensor-time enemy-time!
      robot-velocity enemy-velocity!
      robot-position enemy-position!
      robot-distance current-seperation!
      0 enemy-processed!
	0 enemy-syphon-rate!
do
  energy max-energy / 0.05 < if
    Begin-waiting& jump
  then
  time robot-sensor-time - 8 >= if
    do
      time robot-sensor-time - 9 >=
    until  ;wait for sensing time
      sync 
    loop
    0 robot-sensor-focus-distance!
    fire-robot-sensor sync
    non-shielded-robot-found^ if
      robot-sensor-time enemy-time!
      robot-velocity enemy-velocity!
      robot-position enemy-position!
      robot-distance current-seperation!
      0 enemy-processed!
    else
      Begin-waiting& jump
    then
  then
  
  enemy-processed nif
      do
        blaster-cooldown
      while
        sync
      loop
      enemy-position enemy-velocity lead-blaster

      1 enemy-processed!
      enemy-position enemy-time 15 4 kill-channel send
	enemy-position position v- angle shot-sensor-focus-direction!
  then ;enemy processed

    time shot-sensor-time - 5 >= dodge-and-move& ifc

  enemy-position
      enemy-velocity time enemy-time - vs* v+
      current-enemy-position!
    current-enemy-position position dist current-seperation!
    enemy-velocity position current-enemy-position v- unitize dot enemys-closing-speed!
  
    ;adjust perpendicular component of velocity to 90% of current, and parallel to maintain good seperation

    current-seperation FIGHT_DIST - 0.03 * ;stack: desired closing speed
    enemys-closing-speed - current-enemy-position position v- angle polar-to-rect
    ;stack: closing velocity
    
    2dup 100 vs* position v+ out-of-bounds^ if
      0.07 current-enemy-position position v- angle Pi/2 + polar-to-rect
    else
        enemy-velocity 0.5 vs* velocity 0.4 vs* v+ current-enemy-position position v- swap negate project
    then
    ;stack: closing-velocity, orbit-velocity
    v+ desired-velocity!
    
    time shot-sensor-time - 5 >= dodge-and-move& ifc
    current-enemy-position position v- rect-to-polar enemy-syphon-direction! enemy-syphon-distance!
    enemy-syphon-max-rate enemy-syphon-rate!
forever

set-shot-chase:
	shot-direction shot-sensor-focus-direction!
      0 0 rumor-velocity!
      shot-position shot-velocity unitize shot-power 40 > -15 -10 ifev vs* v+ clamp-pos^ rumor-position!
      shot-sensor-time rumor-time!
return

Begin-chasing-rumor:
  rumor-position rumor-time 10 4 kill-channel send
do
  energy max-energy / 0.1 < if
    Begin-waiting& jump
  then

  rumor-position position v- unitize desired-velocity!

  time shot-sensor-time - 5 >= if
  dodge-and-move^
    shot-found if
      set-shot-chase^
    then
  then
  
  time robot-sensor-time - 10 > if
    1 robot-sensor-sees-enemies!
    0 robot-sensor-sees-friends!
    fire-robot-sensor sync
    non-shielded-robot-found^ if
      Begin-chasing-enemy& jump
    then
  then
  
  rumor-position position dist 5 < if
    Begin-waiting& jump
  then
forever

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

(Devon 20030721) Fixed for syphon changes.

#hardware
processor 17
energy 400 30
armor 120
engine 0.08
radio send receive

constructor 1.4

food-sensor 10 4
shot-sensor 5
robot-sensor 4
eater 2

syphon 0.3 18

#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

#vector flee-position
#var flee-time -1000

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


new-wander-position:
 0.2 random-bool if
    birth-place
 else
  0.5 random-bool if
    random-edge-position^
  else
    position 20 random-angle polar-to-rect v+ 2dup out-of-bounds nif
      ;return it
    else
      2drop random-edge-position^
    then
 then then return

#var last-armor
Defend:
  time robot-sensor-time - 20 > if
    fire-robot-sensor fire-shot-sensor sync
    last-armor armor > robot-found 1 > or 
    shot-found or
    if
      Call-for-help^
    then
     last-armor armor dup last-armor! > shot-found or if
      rdrop Begin-fleeing& jump
    then
  then
return

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

#start
position birth-place!
armor last-armor!

Begin-seeking-food:
  0 syphon-rate!
  new-wander-position^ wander-position!
do
  constructor-progress energy max-energy / 0.3 > and constructor-max-rate 0 ifev constructor-rate!

  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 energy 50 > time flee-time - 500 > and 0.1 0.05 ifev vs* engine-velocity!
  engine-max-power engine-power!



;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
  next-meal-position seek-location
  reclaim-food^

  do
    food-channel receive
  while
   received-food-time! received-food-amount! received-food-position!
   update-stats-food-msg^
  loop

  constructor-progress if
    energy max-energy / 0.2 > constructor-max-rate 0 ifev constructor-rate!
  else
    energy max-energy / 0.8 > if
      FIGHTER_TYPE type-population population / 0.16666 - 2 * 
	population 8 < if 1 min 0 max else 0.2 max 0.8 min then random-bool GATHERER_TYPE FIGHTER_TYPE ifev constructor-type! 
      constructor-max-rate constructor-rate!
    else
      0 constructor-rate!
    then
  then
  
  energy max-energy / 0.12 > if
    defend^
  then
  #vector sink-position
  energy max-energy / 0.2 > if
      hungry-channel receive if
      time swap - 30 < if
        2dup position dist syphon-range radius + < if
          sink-position!
        else
          2drop
        then
      else
        2drop
      then
      then
      sink-position or if
        sink-position position v- rect-to-polar syphon-direction! syphon-distance!
        syphon-max-rate negate syphon-rate!
      else
        0 syphon-rate!
      then
  else
    0 syphon-rate!
  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
  
forever

Begin-fleeing:
  0 syphon-rate!
  shot-found if
    15 shot-velocity angle 0.5 random-bool 0.4 -0.4 ifev + position v+ clamp-pos^ flee-position!
  else
    robot-found time robot-sensor-time - 20 < if
      robot-position position v- unitize -20 vs* position v+ clamp-pos^ 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
  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

#end

Source - Big BerthaEdit

To avoid confusion from the name change Big Bertha starts at version 2.

Version 2Edit

#side Big Bertha 2
#author Warren
#color ff2f41
#seed 1 2 3 3 3 4 3

Inspired by The Lunacy. The giant seed type "Big Bertha" is named after a 43-tonne German WWI howitzer that fired 830 kg shells 12 km.
The giant seed uses a large long-range grenade launcher and a smaller long-range blaster. Every other shot the blaster is shot a bit behind the target.
This is designed principly so that active dodgers will dodge the grenade and dodge into the blaster.

The other three types are Active 9 with light modifications only.

;################ SHARED #################
#code

;;;;types
#const GIANT_TYPE 1
#const CTOR_TYPE 2
#const GATHERER_TYPE 3
#const FIGHTER_TYPE 4

;;;;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 HOME_CORNER 972 ;;a 0/1 vector

#const COLONY_POSITION 975

#const CORNER_CLEARED 978 ;;0/1 indicator

;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 Big Bertha
#color ff0
#hardware
  processor 40
  armor 650
  repair-rate 0.25
  engine 0.4
  energy 1000 200
  
  blaster 28 25.5 50
  grenades 163 26 50
  
  robot-sensor 27.5 2
  shot-sensor 7
  food-sensor 13 5
  eater 4
  constructor 1.7
 
  
#code


#const WAKE_ENERGY 180
#const SHOOT_ENERGY 200
#const START_CONSTRUCT_ENERGY 600
#const CONSTRUCT_ENERGY 450
#const REPAIR_ENERGY 330

#var temp

#vector target-food
#vector current-waypoint
#var waypoint-left ;bool, true if waypoint is left side vs bottom
#var waypoint-second ;bool, true if waypoint is second on side vs first.
#var waypoint-radius ;scalar
#const waypoint-gutter 10
#var world-width-minus-gutters
#var world-height-minus-gutters

#var eating ;bool
#var danger-time 0


#var corner-left-right
#var corner-bottom-top
#vector corner-position

map-*: ;x y a b -- x*a y*b
rot * rrot * swap ;; How unintuitive
return

;convert real coords to home-centric ones
real-to-my-posn: ; x y - x y
corner-position v- corner-left-right corner-bottom-top map-*
return

;inverse
my-posn-to-real: ; x y - x y
corner-left-right corner-bottom-top map-* corner-position v+
return

;convert a relative vector in our coords to real or back again
;(the same because -1*-1=1)
my-relative-to-real:
real-to-my-relative:
corner-left-right corner-bottom-top map-*
return

set-corner-stuff: ;; a b -- where a,b are 0/1 indicators of which corner we have
;stack: nearest corner in 0/1 for each
2dup world-height * swap world-width * swap corner-position!
-2 vs* 1 1 v+ corner-bottom-top! corner-left-right!
return

adjust-engine:
eating if
 time danger-time < if
  target-food position radius 3 * in-range if
  ;passive dodge circling while touching food
  radius 4 /
  position target-food v- angle 1 + polar-to-rect target-food v+ position v- engine-velocity!
  else
  ;head to food with wiggle
  target-food position v- 0.05 vs* rect-to-polar time 40 mod 20 > 0.5 -0.5 ifev + polar-to-rect engine-velocity!
  engine-max-power engine-power! 
  then
 else
  ;straight to food
  target-food position v- 0.05 vs* engine-velocity!
  target-food position velocity 5 vs* v+ radius 2 / in-range engine-max-power * engine-power! 
 then

  engine-max-power engine-power!
else
  0.08 current-waypoint position v- angle time danger-time < if time 40 mod 20 > 0.5 -0.5 ifev + then
  polar-to-rect engine-velocity!
  engine-max-power engine-power!
then
return

set-current-waypoint:
  waypoint-left if
    waypoint-radius world-height-minus-gutters < if
      waypoint-gutter waypoint-gutter waypoint-radius +  
    else
      waypoint-radius world-height-minus-gutters - waypoint-gutter + world-height waypoint-gutter - 
    then
  else
    waypoint-radius world-width-minus-gutters < if
      waypoint-gutter waypoint-radius + waypoint-gutter   
    else
      world-width waypoint-gutter - waypoint-radius world-width-minus-gutters - waypoint-gutter +  
    then
  then
  my-posn-to-real^ current-waypoint!
return

#var shoot-straight 0 ;;boolean
#start
 world-width waypoint-gutter 2 * - world-width-minus-gutters!
 world-height waypoint-gutter 2 * - world-height-minus-gutters!

 time 20 < if
	position world-height / round swap world-width / round swap 2dup HOME_CORNER vwrite
 else
	HOME_CORNER vread
 then set-corner-stuff^
	1 waypoint-left!
        0 waypoint-second!
        0 waypoint-radius!
        set-current-waypoint^

time 20 > if
do ;;hack to avoid friendly fire on birth
   current-waypoint seek-location
energy WAKE_ENERGY < position current-waypoint 10 in-range or until-loop
then

do

 eating eaten not and target-food position radius in-range and if
  0 eating!
 then


 eating not time food-sensor-time - 30 > and if
  
  robot-found if
   ;run away
   position robot-position v- angle food-sensor-focus-direction!
   robot-sensor-range 5 - robot-distance - food-sensor-focus-distance!
  else
   current-waypoint position v- angle food-sensor-focus-direction!
   5 food-sensor-focus-distance! ;was 4
  then

  fire-food-sensor sync  
   
	food-found if
		Food-check-loop:
                food-energy 80 > if
		claim-food^ and-if
   food-position target-food!
   1 eating!
		else
			next-food Food-check-loop& ifg
		then
	then ;food-found
 then

 energy SHOOT_ENERGY > grenades-cooldown not and if
 time robot-sensor-time 30 + > and-if
		fire-robot-sensor sync
		robot-found if	
  ;accelerating while firing can mess up aiming of long range shots a bit, on the order of a distance of 0.02*30=0.6. This
  ; isn't enough to miss most targets, but together with other errors might cause a miss, so maintain steady velocity while firing.
  ;From the GB source:
  ; GBWorld::SimOneFrame order: think (includes lead-blaster firing direction computation), move (includes drag), act, collide
  ; HardwareState::Act order: engine, blaster
  ;with engine-velocity set to velocity the drag and engine effect cancel and thinking and blaster-acting velocities should match.
		velocity engine-velocity! engine-max-power engine-power! ;helps aim

		shoot-straight if
			robot-position robot-velocity lead-grenade
			robot-position robot-velocity lead-blaster
		else
		;blaster aimed around 2 units behind the enemy, grenade a tiny bit ahead. The reason for the tiny bit ahead is convincing stationary actives to dodge the wrong way.
		; Grenade is slightly faster so enemy probably dodges it, right into the blaster.
			robot-distance 10 min 0.14 * square robot-position position v- angle polar-to-rect robot-position position v- robot-velocity cross 0 > if swap negate else negate swap then 2dup 
			
			-0.02 vs* robot-position v+ robot-velocity lead-grenade
			energy 150 > if	
			robot-position v+ robot-velocity lead-blaster
			else 2drop then
	then
	shoot-straight not shoot-straight!
   robot-sensor-time 100 + danger-time max danger-time!
   sync ;let the lead-blaster happen
   adjust-engine^ ;puts engine back in an appropriate state
 then
 then


 20 periodic-shot-sensor if
   shot-found if
      time 200 + danger-time max danger-time!
   then
 then

constructor-type not energy START_CONSTRUCT_ENERGY > and if
   CTOR_TYPE type-population not if CTOR_TYPE else GATHERER_TYPE then constructor-type!
 then


 constructor-progress 300 > CONSTRUCT_ENERGY START_CONSTRUCT_ENERGY ifev energy < constructor-max-rate * constructor-rate!


 energy REPAIR_ENERGY > max-repair-rate * repair-rate!

 current-waypoint position 10 in-range if
   1 CORNER_CLEARED write ;;is redundant except for first time but that's harmless
   waypoint-second if
    waypoint-left not waypoint-left!
    0 waypoint-second!
   else
    1 waypoint-second!
    world-width world-height + waypoint-gutter 4 * - dup temp! waypoint-radius = if
	0 waypoint-radius!
    else
	    temp 4 / waypoint-radius + temp min waypoint-radius! ;temp/4 was 25
    then
    
   then
   set-current-waypoint^
 then

 adjust-engine^

forever

;;#################################################
#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 GIANT_TYPE type-population 50 * + sqrt 2 * gutter!
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!

type type-population 1 > if
	set-gutter^
	do
		position 2 population sqrt 4 * + random-angle polar-to-rect v+ home!
	home edge-dist^ gutter > until-loop
else
;;;If giant has gotten to corner that's our home, otherwise current position is the best we can do.
	CORNER_CLEARED read if 
         HOME_CORNER vread 2dup world-height * swap world-width * swap 
        else
         position
        then 
	set-gutter^
        keep-away-edges^ home!
        home COLONY_POSITION vwrite
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 6 <= 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 6 <= 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

Source - Blaster BerthaEdit

Version 1Edit

#side Blaster Bertha
#author Warren
#color ff2f41
#seed 1 2 3 3 3 4 3

Like the Big Bertha side but instead of Big Bertha's grenade/blaster combo it uses a single large blaster.
One of theses blasts kills most civilians in one shot and most fighters in two.

Blaster Bertha is currently quite a bit better than Big Bertha, but if run against 8 Active 9's then Blaster Bertha gets scores of a few percent whereas Big Bertha does fine.

(This side was originally called Big Bertha for the first week, but I decided the Big Bertha name was much better suited to the grenade-using variant
so I renamed this one.)

;################ SHARED #################
#code

;;;;types
#const GIANT_TYPE 1
#const CTOR_TYPE 2
#const GATHERER_TYPE 3
#const FIGHTER_TYPE 4

;;;;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 HOME_CORNER 972 ;;a 0/1 vector

#const COLONY_POSITION 975

#const CORNER_CLEARED 978 ;;0/1 indicator

;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 Blaster Bertha
#color ff0
#hardware
  processor 35
  armor 650
  repair-rate 0.25
  engine 0.4
  energy 1000 200
  
  blaster 158 26 50
  
  robot-sensor 29 6
  shot-sensor 7
  food-sensor 13 5
  eater 4
  constructor 1.7
 
  
#code


#const WAKE_ENERGY 180
#const SHOOT_ENERGY 200
#const START_CONSTRUCT_ENERGY 600
#const CONSTRUCT_ENERGY 450
#const REPAIR_ENERGY 330

#var temp

#vector target-food
#vector current-waypoint
#var waypoint-left ;bool, true if waypoint is left side vs bottom
#var waypoint-second ;bool, true if waypoint is second on side vs first.
#var waypoint-radius ;scalar
#const waypoint-gutter 10
#var world-width-minus-gutters
#var world-height-minus-gutters

#var eating ;bool
#var danger-time 0


#var corner-left-right
#var corner-bottom-top
#vector corner-position

map-*: ;x y a b -- x*a y*b
rot * rrot * swap ;; How unintuitive
return

;convert real coords to home-centric ones
real-to-my-posn: ; x y - x y
corner-position v- corner-left-right corner-bottom-top map-*
return

;inverse
my-posn-to-real: ; x y - x y
corner-left-right corner-bottom-top map-* corner-position v+
return

;convert a relative vector in our coords to real or back again
;(the same because -1*-1=1)
my-relative-to-real:
real-to-my-relative:
corner-left-right corner-bottom-top map-*
return

set-corner-stuff: ;; a b -- where a,b are 0/1 indicators of which corner we have
;stack: nearest corner in 0/1 for each
2dup world-height * swap world-width * swap corner-position!
-2 vs* 1 1 v+ corner-bottom-top! corner-left-right!
return

adjust-engine:
eating if
 time danger-time < if
  target-food position radius 3 * in-range if
  ;passive dodge circling while touching food
  radius 4 /
  position target-food v- angle 1 + polar-to-rect target-food v+ position v- engine-velocity!
  else
  ;head to food with wiggle
  target-food position v- 0.05 vs* rect-to-polar time 40 mod 20 > 0.5 -0.5 ifev + polar-to-rect engine-velocity!
  engine-max-power engine-power! 
  then
 else
  ;straight to food
  target-food position v- 0.05 vs* engine-velocity!
  target-food position velocity 5 vs* v+ radius 2 / in-range engine-max-power * engine-power! 
 then

  engine-max-power engine-power!
else
  0.08 current-waypoint position v- angle time danger-time < if time 40 mod 20 > 0.5 -0.5 ifev + then
  polar-to-rect engine-velocity!
  engine-max-power engine-power!
then
return

set-current-waypoint:
  waypoint-left if
    waypoint-radius world-height-minus-gutters < if
      waypoint-gutter waypoint-gutter waypoint-radius +  
    else
      waypoint-radius world-height-minus-gutters - waypoint-gutter + world-height waypoint-gutter - 
    then
  else
    waypoint-radius world-width-minus-gutters < if
      waypoint-gutter waypoint-radius + waypoint-gutter   
    else
      world-width waypoint-gutter - waypoint-radius world-width-minus-gutters - waypoint-gutter +  
    then
  then
  my-posn-to-real^ current-waypoint!
return


#start
 world-width waypoint-gutter 2 * - world-width-minus-gutters!
 world-height waypoint-gutter 2 * - world-height-minus-gutters!

 time 20 < if
	position world-height / round swap world-width / round swap 2dup HOME_CORNER vwrite
 else
	HOME_CORNER vread
 then set-corner-stuff^
	1 waypoint-left!
        0 waypoint-second!
        0 waypoint-radius!
        set-current-waypoint^

time 20 > if
do ;;hack to avoid friendly fire on birth
   current-waypoint seek-location
energy WAKE_ENERGY < position current-waypoint 10 in-range or until-loop
then

do

 eating eaten not and target-food position radius in-range and if
  0 eating!
 then


 eating not time food-sensor-time - 30 > and if
  
  robot-found if
   ;run away
   position robot-position v- angle food-sensor-focus-direction!
   robot-sensor-range 5 - robot-distance - food-sensor-focus-distance!
  else
   current-waypoint position v- angle food-sensor-focus-direction!
   5 food-sensor-focus-distance! ;was 4
  then

  fire-food-sensor sync  
   
	food-found if
		Food-check-loop:
                food-energy 80 > if
		claim-food^ and-if
   food-position target-food!
   1 eating!
		else
			next-food Food-check-loop& ifg
		then
	then ;food-found
 then

 energy SHOOT_ENERGY > blaster-cooldown not and if
 time robot-sensor-time 30 + > and-if
  fire-robot-sensor sync
  robot-found if
  ;accelerating while firing can mess up aiming of long range shots a bit, on the order of a distance of 0.02*30=0.6. This
  ; isn't enough to miss most targets, but together with other errors might cause a miss, so maintain steady velocity while firing.
  ;From the GB source:
  ; GBWorld::SimOneFrame order: think (includes lead-blaster firing direction computation), move (includes drag), act, collide
  ; HardwareState::Act order: engine, blaster
  ;with engine-velocity set to velocity the drag and engine effect cancel and thinking and blaster-acting velocities should match.
   velocity engine-velocity! engine-max-power engine-power!
   robot-position robot-velocity lead-blaster
   robot-sensor-time 100 + danger-time max danger-time!
   sync ;let the lead-blaster happen
   adjust-engine^ ;puts engine back in an appropriate state
  then
 then


 20 periodic-shot-sensor if
   shot-found if
      time 200 + danger-time max danger-time!
   then
 then

constructor-type not energy START_CONSTRUCT_ENERGY > and if
   CTOR_TYPE type-population not if CTOR_TYPE else GATHERER_TYPE then constructor-type!
 then


 constructor-progress 300 > CONSTRUCT_ENERGY START_CONSTRUCT_ENERGY ifev energy < constructor-max-rate * constructor-rate!


 energy REPAIR_ENERGY > max-repair-rate * repair-rate!

 current-waypoint position 10 in-range if
   1 CORNER_CLEARED write ;;is redundant except for first time but that's harmless
   waypoint-second if
    waypoint-left not waypoint-left!
    0 waypoint-second!
   else
    1 waypoint-second!
    world-width world-height + waypoint-gutter 4 * - dup temp! waypoint-radius = if
	0 waypoint-radius!
    else
	    temp 4 / waypoint-radius + temp min waypoint-radius! ;temp/4 was 25
    then
    
   then
   set-current-waypoint^
 then

 adjust-engine^

forever

;;#################################################
#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 GIANT_TYPE type-population 50 * + sqrt 2 * gutter!
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!

type type-population 1 > if
	set-gutter^
	do
		position 2 population sqrt 4 * + random-angle polar-to-rect v+ home!
	home edge-dist^ gutter > until-loop
else
;;;If giant has gotten to corner that's our home, otherwise current position is the best we can do.
	CORNER_CLEARED read if 
         HOME_CORNER vread 2dup world-height * swap world-width * swap 
        else
         position
        then 
	set-gutter^
        keep-away-edges^ home!
        home COLONY_POSITION vwrite
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 6 <= 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 6 <= 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

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.