about summary refs log blame commit diff stats
path: root/hest-life.mu
blob: cb7325808286b559c9090345b33a5203476b4511 (plain) (tree)
1
2
3
4
5
6
7
8





                                                         
                                               
                                           




                                                                              
 


                                                                                  
                            
                    
   
                      







                                                   




                  
                                        
                                                                                         
           
                
                                                             

 




               


                                                            




                                            
   



                        
   



                                                                                                     



                                                             
               
                                                                                
                                                                                
                                                                                

                                                                                







                                                                              
                             

                                                                            
                                                           


                                                                            
                                                             
                                                                            
            
                                                                                 
                                                  
                                                                                              
                                                 







                                                                                                   
               

                                                                                  
                                                                                              
                                         
                                                                     
                           




                                                                        
                         



                                                                        
                                    
                                                                                                      






                                                                                                      
                                            
                                              
                                       








                                          
                                                



                                                                                                           
                                                                                                           
                                                                                                           













                                                                                                           

                                                                                          

                                                                                                   



                                          







                                                                                                           

                                                                                                           


                                                                                                           






                                                                                                                                     

 






                                                                                                                   


                                                              
                     
   
                           










                                                    






                          

















                                                










                                                

 

                                                





                                              




                        







                                   








                                              

 
                                                     
                                                
                                            
                     

                                                  
                                                                          
                         
                                                                 



                          
                                                              



                           





                               

 



































                                                                          
                                                                   
                                                

                                                                          
                           

                                                           
                           



                                                



                                                                        
                           



























                                                                          


             











                                                                        





























                                                                          









































                                                                   











































































































                                                                               
 
# Conway's Game of Life in a Hestified way
#   https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
#   https://ivanish.ca/hest-podcast
#
# To build:
#   $ ./translate life.mu
# I run it on my 2.5GHz Linux laptop like this:
#   $ qemu-system-i386 -enable-kvm code.img
#
# If things seem too fast or too slow on your computer, adjust the loop bounds
# in the function `pause` at the bottom. Its value will depend on how you
# accelerate Qemu. Mu will eventually get a clock to obviate the need for this
# tuning.

fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
  var env-storage: environment
  var env/esi: (addr environment) <- address env-storage
  initialize-environment env
  render screen, env
  {
    edit keyboard, env
    var play?/eax: (addr boolean) <- get env, play?
    compare *play?, 0/false
    {
      break-if-=
      step env
      render screen, env
    }
    pause env
    loop
  }
}

type environment {
  data: (handle array handle array cell)
  zoom: int  # 0 = 1024 px per cell; 5 = 4px per cell; each step adjusts by a factor of 4
  tick: int
  play?: boolean
  loop: int  # if non-zero, return tick to 0 after this point
}

type cell {
  curr: boolean
  next: boolean
}

fn render screen: (addr screen), _self: (addr environment) {
  clear-screen screen
  var self/esi: (addr environment) <- copy _self
  var zoom/eax: (addr int) <- get self, zoom
  compare *zoom, 0
  {
    break-if-!=
    render0 screen, self
  }
  compare *zoom, 4
  {
    break-if-!=
    render4 screen, self
  }
  # clock
  var tick-a/eax: (addr int) <- get self, tick
  set-cursor-position screen, 0x78/x, 0/y
  draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, *tick-a, 7/fg 0/bg
}

fn render0 screen: (addr screen), _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  # cell border
  draw-vertical-line   screen,  0xc0/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
  draw-vertical-line   screen, 0x340/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
  draw-horizontal-line screen,  0x40/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
  draw-horizontal-line screen, 0x2c0/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
  # neighboring inputs, corners
  var color/eax: int <- state-color self, 0x7f/cur-topleftx, 0x5f/cur-toplefty
  draw-rect screen,  0x90/xmin   0x10/ymin,    0xb0/xmax   0x30/ymax,  color
  color <- state-color self, 0x81/cur-toprightx, 0x5f/cur-toprighty
  draw-rect screen, 0x350/xmin   0x10/ymin,   0x370/xmax   0x30/ymax,  color
  color <- state-color self, 0x7f/cur-botleftx, 0x61/cur-botlefty
  draw-rect screen,  0x90/xmin  0x2d0/ymin,    0xb0/xmax  0x2f0/ymax,  color
  color <- state-color self, 0x81/cur-botrightx, 0x61/cur-botrighty
  draw-rect screen, 0x350/xmin  0x2d0/ymin,   0x370/xmax  0x2f0/ymax,  color
  # neighboring inputs, edges
  color <- state-color self, 0x80/cur-topx, 0x5f/cur-topy
  draw-rect screen, 0x1f0/xmin   0x10/ymin,   0x210/xmax   0x30/ymax,  color
  color <- state-color self, 0x7f/cur-leftx, 0x60/cur-lefty
  draw-rect screen,  0x90/xmin  0x170/ymin,    0xb0/xmax  0x190/ymax,  color
  color <- state-color self, 0x80/cur-botx, 0x61/cur-boty
  draw-rect screen, 0x1f0/xmin  0x2d0/ymin,   0x210/xmax  0x2f0/ymax,  color
  color <- state-color self, 0x81/cur-rightx, 0x60/cur-righty
  draw-rect screen, 0x350/xmin  0x170/ymin,   0x370/xmax  0x190/ymax,  color
  # sum node
  draw-rect screen, 0x170/xsmin 0x140/ysmin,  0x190/xsmax 0x160/ysmax, 0x40/color
  set-cursor-position screen, 0x2d/scol, 0x13/srow
  draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "+", 0xf/color, 0/bg
  # conveyors from neighboring inputs to sum node
  draw-monotonic-bezier screen,  0xa0/x0  0x20/y0,  0x100/x1 0x150/ys,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen,  0xa0/x0 0x180/y0,   0xc0/x1 0x150/ys,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen,  0xa0/x0 0x2e0/y0,  0x100/x1 0x150/ys,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen, 0x200/x0  0x20/y0,  0x180/x1  0x90/y1,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen, 0x200/x0 0x2e0/y0,  0x180/x1 0x200/y1,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen, 0x360/x0  0x20/y0,  0x180/x1  0xc0/y1,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen, 0x360/x0 0x180/y0,  0x35c/x1 0x150/ys,  0x180/x2 0x150/ys,  4/color
  draw-monotonic-bezier screen, 0x360/x0 0x2e0/y0,  0x180/x1 0x200/y1,  0x180/x2 0x150/ys,  4/color
  # filter node
  draw-rect screen, 0x200/xfmin, 0x1c0/yfmin, 0x220/xfmax, 0x1e0/yfmax, 0x31/color
  set-cursor-position screen, 0x40/fcol, 0x1b/frow
  draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "?", 0xf/color, 0/bg
  # conveyor from sum node to filter node
  draw-line screen 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 0xa2/color
  # cell outputs at corners
  var color/eax: int <- state-color self, 0x80/curx, 0x60/cury
  draw-rect screen,  0xd0/xmin  0x50/ymin,   0xf0/xmax  0x70/ymax, color
  draw-rect screen, 0x310/xmin  0x50/ymin,  0x330/xmax  0x70/ymax, color
  draw-rect screen,  0xd0/xmin 0x290/ymin,   0xf0/xmax 0x2b0/ymax, color
  draw-rect screen, 0x310/xmin 0x290/ymin,  0x330/xmax 0x2b0/ymax, color
  # cell outputs at edges
  draw-rect screen, 0x1f0/xmin  0x50/ymin, 0x210/xmax,  0x70/ymax, color
  draw-rect screen,  0xd0/xmin 0x170/ymin,  0xf0/xmax, 0x190/ymax, color
  draw-rect screen, 0x1f0/xmin 0x290/ymin, 0x210/xmax, 0x2b0/ymax, color
  draw-rect screen, 0x310/xmin 0x170/ymin, 0x330/xmax, 0x190/ymax, color
  # conveyors from filter to outputs
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x1c0/x1  0x60/y1,  0xe0/x2   0x60/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,   0xe0/x1 0x1c0/y1,  0xe0/x2  0x180/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x1c0/x1 0x2a0/y1,  0xe0/x2  0x2a0/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x210/x1  0x60/y1, 0x200/x2   0x60/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x210/x1 0x230/y1, 0x200/x2  0x2a0/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x120/y1, 0x320/x2   0x60/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x1c0/y1  0x320/x2  0x180/y2,  0x2a/color
  draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x230/y1, 0x320/x2  0x2a0/y2,  0x2a/color
  # time-variant portion: 16 repeating steps
  var tick-a/eax: (addr int) <- get self, tick
  var progress/eax: int <- copy *tick-a
  progress <- and 0xf
  # 7 time steps for getting inputs to sum
  {
    compare progress, 7
    break-if->=
    var u/xmm7: float <- convert progress
    var six/eax: int <- copy 6
    var six-f/xmm0: float <- convert six
    u <- divide six-f
    # points on conveyors from neighboring cells
    draw-bezier-point screen, u,  0xa0/x0  0x20/y0, 0x100/x1 0x150/ys, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u,  0xa0/x0 0x180/y0,  0xc0/x1 0x150/ys, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u,  0xa0/x0 0x2e0/y0, 0x100/x1 0x150/ys, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u, 0x200/x0  0x20/y0, 0x180/x1  0x90/y1, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u, 0x200/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u, 0x360/x0  0x20/y0, 0x180/x1  0xc0/y1, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u, 0x360/x0 0x180/y0, 0x35c/x1 0x150/ys, 0x180/x2 0x150/ys, 7/color, 4/radius
    draw-bezier-point screen, u, 0x360/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/x2 0x150/ys, 7/color, 4/radius
    return
  }
  # two time steps for getting count to filter
  progress <- subtract 7
  {
    compare progress, 2
    break-if->=
    progress <- increment  # (0, 1) => (1, 2)
    var u/xmm7: float <- convert progress
    var three/eax: int <- copy 3
    var three-f/xmm0: float <- convert three
    u <- divide three-f
    draw-linear-point screen, u, 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 7/color, 4/radius
    set-cursor-position screen, 0x3a/scol, 0x18/srow
    var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x60/cury
    draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
    return
  }
  # final 7 time steps for updating output
  progress <- subtract 2
  # points on conveyors to outputs
  var u/xmm7: float <- convert progress
  var six/eax: int <- copy 6
  var six-f/xmm0: float <- convert six
  u <- divide six-f
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x1c0/x1  0x60/y1,  0xe0/x2   0x60/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,   0xe0/x1 0x1c0/y1,  0xe0/x2  0x180/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x1c0/x1 0x2a0/y1,  0xe0/x2  0x2a0/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x210/xf  0x60/y1, 0x200/x2   0x60/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x210/xf 0x230/y1, 0x200/x2  0x2a0/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x120/y1, 0x320/x2   0x60/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x1c0/y1  0x320/x2  0x180/y2, 7/color, 4/radius
  draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x230/y1, 0x320/x2  0x2a0/y2, 7/color, 4/radius
}

fn draw-bezier-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int, radius: int {
  var _cy/eax: int <- bezier-point u, y0, y1, y2
  var cy/ecx: int <- copy _cy
  var cx/eax: int <- bezier-point u, x0, x1, x2
  draw-disc screen, cx, cy, radius, color, 0xf/border-color=white
}

fn draw-linear-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, color: int, radius: int {
  var _cy/eax: int <- line-point u, y0, y1
  var cy/ecx: int <- copy _cy
  var cx/eax: int <- line-point u, x0, x1
  draw-disc screen, cx, cy, radius, color, 0xf/border-color=white
}

fn edit keyboard: (addr keyboard), _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var key/eax: byte <- read-key keyboard
  # space: play/pause
  {
    compare key, 0x20/space
    break-if-!=
    var play?/eax: (addr boolean) <- get self, play?
    compare *play?, 0/false
    {
      break-if-=
      copy-to *play?, 0/false
      return
    }
    copy-to *play?, 1/true
    return
  }
  # 0: back to start
  {
    compare key, 0x30/0
    break-if-!=
    clear-environment self
    return
  }
  # l: loop from here to start
  {
    compare key, 0x6c/l
    break-if-!=
    var tick-a/eax: (addr int) <- get self, tick
    var tick/eax: int <- copy *tick-a
    var loop/ecx: (addr int) <- get self, loop
    copy-to *loop, tick
    return
  }
  # L: reset loop
  {
    compare key, 0x4c/L
    break-if-!=
    var loop/eax: (addr int) <- get self, loop
    copy-to *loop, 0
    return
  }
}

fn pause _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var i/ecx: int <- copy 0
  {
    compare i, 0x10000000
    break-if->=
    i <- increment
    loop
  }
}

fn step _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var tick-a/ecx: (addr int) <- get self, tick
  var zoom/edx: (addr int) <- get self, zoom
  compare *zoom, 0
  {
    break-if-!=
    increment *tick-a
  }
  compare *zoom, 4
  {
    break-if-!=
    add-to *tick-a, 0x10
  }
  var tick/eax: int <- copy *tick-a
  tick <- and 0xf
  compare tick, 0
  {
    break-if-!=
    step4 self
  }
  var loop-a/eax: (addr int) <- get self, loop
  compare *loop-a, 0
  {
    break-if-=
    var loop/eax: int <- copy *loop-a
    compare *tick-a, loop
    break-if-<
    clear-environment self
  }
}

fn initialize-environment _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var zoom/eax: (addr int) <- get self, zoom
#?   copy-to *zoom, 4
  var play?/eax: (addr boolean) <- get self, play?
  copy-to *play?, 1/true
  var data-ah/eax: (addr handle array handle array cell) <- get self, data
  populate data-ah, 0x100
  var data/eax: (addr array handle array cell) <- lookup *data-ah
  var y/ecx: int <- copy 0
  {
    compare y, 0xc0
    break-if->=
    var dest-ah/eax: (addr handle array cell) <- index data, y
    populate dest-ah, 0x100
    y <- increment
    loop
  }
  set self, 0x80, 0x5f, 1/alive
  set self, 0x81, 0x5f, 1/alive
  set self, 0x7f, 0x60, 1/alive
  set self, 0x80, 0x60, 1/alive
  set self, 0x80, 0x61, 1/alive
  flush self
}

fn clear-environment _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var tick/eax: (addr int) <- get self, tick
  copy-to *tick, 0
  var zoom/eax: (addr int) <- get self, zoom
#?   copy-to *zoom, 4
  var play?/eax: (addr boolean) <- get self, play?
  copy-to *play?, 1/true
  var data-ah/eax: (addr handle array handle array cell) <- get self, data
  var data/eax: (addr array handle array cell) <- lookup *data-ah
  var y/ecx: int <- copy 0
  {
    compare y, 0xc0
    break-if->=
    var row-ah/eax: (addr handle array cell) <- index data, y
    var row/eax: (addr array cell) <- lookup *row-ah
    var x/edx: int <- copy 0
    {
      compare x, 0x100
      break-if->=
      var dest/eax: (addr cell) <- index row, x
      clear-object dest
      x <- increment
      loop
    }
    y <- increment
    loop
  }
  set self, 0x80, 0x5f, 1/alive
  set self, 0x81, 0x5f, 1/alive
  set self, 0x7f, 0x60, 1/alive
  set self, 0x80, 0x60, 1/alive
  set self, 0x80, 0x61, 1/alive
  flush self
}

fn set _self: (addr environment), _x: int, _y: int, _val: boolean {
  var self/esi: (addr environment) <- copy _self
  var data-ah/eax: (addr handle array handle array cell) <- get self, data
  var data/eax: (addr array handle array cell) <- lookup *data-ah
  var y/ecx: int <- copy _y
  var row-ah/eax: (addr handle array cell) <- index data, y
  var row/eax: (addr array cell) <- lookup *row-ah
  var x/ecx: int <- copy _x
  var cell/eax: (addr cell) <- index row, x
  var dest/eax: (addr boolean) <- get cell, next
  var val/ecx: boolean <- copy _val
  copy-to *dest, val
}

fn state _self: (addr environment), _x: int, _y: int -> _/eax: boolean {
  var self/esi: (addr environment) <- copy _self
  var x/ecx: int <- copy _x
  var y/edx: int <- copy _y
  # clip at the edge
  compare x, 0
  {
    break-if->=
    return 0/false
  }
  compare y, 0
  {
    break-if->=
    return 0/false
  }
  compare x, 0x100/width
  {
    break-if-<
    return 0/false
  }
  compare y, 0xc0/height
  {
    break-if-<
    return 0/false
  }
  var data-ah/eax: (addr handle array handle array cell) <- get self, data
  var data/eax: (addr array handle array cell) <- lookup *data-ah
  var row-ah/eax: (addr handle array cell) <- index data, y
  var row/eax: (addr array cell) <- lookup *row-ah
  var cell/eax: (addr cell) <- index row, x
  var src/eax: (addr boolean) <- get cell, curr
  return *src
}

fn state-color _self: (addr environment), x: int, y: int -> _/eax: int {
  var self/esi: (addr environment) <- copy _self
  var color/ecx: int <- copy 0x1a/dead
  {
    var state/eax: boolean <- state self, x, y
    compare state, 0/dead
    break-if-=
    color <- copy 0xf/alive
  }
  return color
}

fn flush  _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var data-ah/eax: (addr handle array handle array cell) <- get self, data
  var _data/eax: (addr array handle array cell) <- lookup *data-ah
  var data/esi: (addr array handle array cell) <- copy _data
  var y/ecx: int <- copy 0
  {
    compare y, 0xc0/height
    break-if->=
    var row-ah/eax: (addr handle array cell) <- index data, y
    var _row/eax: (addr array cell) <- lookup *row-ah
    var row/ebx: (addr array cell) <- copy _row
    var x/edx: int <- copy 0
    {
      compare x, 0x100/width
      break-if->=
      var cell-a/eax: (addr cell) <- index row, x
      var curr-a/edi: (addr boolean) <- get cell-a, curr
      var next-a/esi: (addr boolean) <- get cell-a, next
      var val/eax: boolean <- copy *next-a
      copy-to *curr-a, val
      copy-to *next-a, 0/dead
      x <- increment
      loop
    }
    y <- increment
    loop
  }
}

fn render4 screen: (addr screen), _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var y/ecx: int <- copy 0
  {
    compare y, 0xc0/height
    break-if->=
    var x/edx: int <- copy 0
    {
      compare x, 0x100/width
      break-if->=
      var state/eax: boolean <- state self, x, y
      compare state, 0/false
      {
        break-if-=
        render4-cell screen, x, y, 0xf/alive
      }
      compare state, 0/false
      {
        break-if-!=
        render4-cell screen, x, y, 0x1a/dead
      }
      x <- increment
      loop
    }
    y <- increment
    loop
  }
}

fn render4-cell screen: (addr screen), x: int, y: int, color: int {
  var xmin/eax: int <- copy x
  xmin <- shift-left 2
  var xmax/ecx: int <- copy xmin
  xmax <- add 4
  var ymin/edx: int <- copy y
  ymin <- shift-left 2
  var ymax/ebx: int <- copy ymin
  ymax <- add 4
  draw-rect screen, xmin ymin, xmax ymax, color
}

fn step4 _self: (addr environment) {
  var self/esi: (addr environment) <- copy _self
  var y/ecx: int <- copy 0
  {
    compare y, 0xc0/height
    break-if->=
    var x/edx: int <- copy 0
    {
      compare x, 0x100/width
      break-if->=
      var n/eax: int <- num-live-neighbors self, x, y
      # if neighbors < 2, die of loneliness
      {
        compare n, 2
        break-if->=
        set self, x, y, 0/dead
      }
      # if neighbors > 3, die of overcrowding
      {
        compare n, 3
        break-if-<=
        set self, x, y, 0/dead
      }
      # if neighbors = 2, preserve state
      {
        compare n, 2
        break-if-!=
        var old-state/eax: boolean <- state self, x, y
        set self, x, y, old-state
      }
      # if neighbors = 3, cell quickens to life
      {
        compare n, 3
        break-if-!=
        set self, x, y, 1/live
      }
      x <- increment
      loop
    }
    y <- increment
    loop
  }
  flush self
}

fn num-live-neighbors _self: (addr environment), x: int, y: int -> _/eax: int {
  var self/esi: (addr environment) <- copy _self
  var result/edi: int <- copy 0
  # row above: zig
  decrement y
  decrement x
  var s/eax: boolean <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  increment x
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  increment x
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  # curr row: zag
  increment y
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  subtract-from x, 2
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  # row below: zig
  increment y
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  increment x
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  increment x
  s <- state self, x, y
  {
    compare s, 0/false
    break-if-=
    result <- increment
  }
  return result
}