about summary refs log blame commit diff stats
path: root/505colors.mu
blob: 45b13ab57f6e2e995ddbff28448022bceb52d183 (plain) (tree)
275
276
277
278
279
280
281pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# Chessboard program: you type in moves in algebraic notation, and it'll
# display the position after each move.

def 
fn nearest-color-euclidean r: int, g: int, b: int -> _/eax: int {
  var result/edi: int <- copy 0x100/invalid
  var max-distance/esi: int <- copy 0x30000/max  # 3 * 0x100*0x100
  var r2/ecx: int <- copy 0
  var g2/edx: int <- copy 0
  var b2/ebx: int <- copy 0
  var color/eax: int <- copy 0
  {
    compare color, 0x100
    break-if->=
    $nearest-color-euclidean:body: {
      r2, g2, b2 <- color-rgb color
      {
        var curr-distance/eax: int <- euclidean-distance-squared r, g, b, r2, g2, b2
        compare curr-distance, max-distance
        break-if->= $nearest-color-euclidean:body
        max-distance <- copy curr-distance
      }
      result <- copy color
    }
    color <- increment
    loop
  }
  return result
}

fn euclidean-distance-squared r1: int, g1: int, b1: int, r2: int, g2: int, b2: int -> _/eax: int {
  var result/edi: int <- copy 0
  # red
  var tmp/eax: int <- copy r1
  tmp <- subtract r2
  tmp <- multiply tmp
  result <- add tmp
  # green
  tmp <- copy g1
  tmp <- subtract g2
  tmp <- multiply tmp
  result <- add tmp
  # blue
  tmp <- copy b1
  tmp <- subtract b2
  tmp <- multiply tmp
  result <- add tmp
  return result
}

# Hue/saturation/luminance for an rgb triple.
# rgb are in [0, 256)
# hsl are also returned in [0, 256)
# from https://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl
fn hsl r: int, g: int, b: int -> _/ecx: int, _/edx: int, _/ebx: int {
  var _max/eax: int <- maximum r, g
  _max <- maximum _max, b
  var max/ecx: int <- copy _max
  var _min/eax: int <- minimum r, g
  _min <- minimum _min, b
  var min/edx: int <- copy _min
  var luminance/ebx: int <- copy min
  luminance <- add max
  luminance <- shift-right 1  # TODO: round up instead of down
  # if rgb are all equal, it's a shade of grey
  compare min, max
  {
    break-if-!=
    return 0, 0, luminance
  }
  # saturation =
  #   luminance < 128 | 255*(max-min)/         (max+min)
  #   otherwise       | 255*(max-min)/(2*255 - (max+min))
  var nr/esi: int <- copy max
  nr <- subtract min
  var dr/eax: int <- copy 0
  compare luminance, 0x80
  {
    break-if->=
    dr <- copy max
    dr <- add min
  }
  {
    break-if-<
    dr <- copy 0xff
    dr <- shift-left 1
    dr <- subtract max
    dr <- subtract min
  }
  var q/xmm0: float <- convert nr
  var tmp/xmm1: float <- convert dr
  q <- divide tmp
  var int-255/eax: int <- copy 0xff
  tmp <- convert int-255
  q <- multiply tmp
  var saturation/esi: int <- convert q
  # hue = 
  #   red is max   | 256.0/6*       (g-b)/(max-min)
  #   green is max | 256.0/6*(2.0 + (b-r)/(max-min))
  #   blue is max  | 256.0/6*(4.0 + (r-g)/(max-min))
  var zero/eax: int <- copy 0
  var hue-f/xmm0: float <- convert zero
  var dr/eax: int <- copy max
  dr <- subtract min
  var dr-f/xmm1: float <- convert dr
  $hsl:compute-hue-normalized: {
    compare r, max
    {
      break-if-!=
      var nr/eax: int <- copy g
      nr <- subtract b
      hue-f <- convert nr
      hue-f <- divide dr-f
      break $hsl:compute-hue-normalized
    }
    compare g, max
    {
      break-if-!=
      var nr/eax: int <- copy b
      nr <- subtract r
      var f/xmm2: float <- convert nr
      f <- divide dr-f
      var two/ecx: int <- copy 2
      hue-f <- convert two
      hue-f <- add f
      break $hsl:compute-hue-normalized
    }
    compare b, max
    {
      break-if-!=
      var nr/eax: int <- copy r
      nr <- subtract g
      var f/xmm2: float <- convert nr
      f <- divide dr-f
      var two/ecx: int <- copy 4
      hue-f <- convert two
      hue-f <- add f
      break $hsl:compute-hue-normalized
    }
  }
  var int-256/eax: int <- copy 0x100
  var scaling-factor/xmm1: float <- convert int-256
  var int-6/eax: int <- copy 6
  var six-f/xmm2: float <- convert int-6
  scaling-factor <- divide six-f
  hue-f <- multiply scaling-factor
  var hue/eax: int <- convert hue-f
  # if hue < 0, hue = 256 - hue
  compare hue, 0
  {
    break-if->=
    var tmp/ecx: int <- copy 0x100
    tmp <- subtract hue
    hue <- copy tmp
  }
  return hue, saturation, luminance
}

fn test-hsl-black {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0, 0, 0
  check-ints-equal h, 0, "F - test-hsl-black/hue"
  check-ints-equal s, 0, "F - test-hsl-black/saturation"
  check-ints-equal l, 0, "F - test-hsl-black/luminance"
}

fn test-hsl-white {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0xff, 0xff, 0xff
  check-ints-equal h, 0, "F - test-hsl-white/hue"
  check-ints-equal s, 0, "F - test-hsl-white/saturation"
  check-ints-equal l, 0xff, "F - test-hsl-white/luminance"
}

fn test-hsl-grey {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0x30, 0x30, 0x30
  check-ints-equal h, 0, "F - test-hsl-grey/hue"
  check-ints-equal s, 0, "F - test-hsl-grey/saturation"
  check-ints-equal l, 0x30, "F - test-hsl-grey/luminance"
}

# red hues: 0-0x54
fn test-hsl-slightly-red {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0xff, 0xfe, 0xfe
  check-ints-equal h, 0, "F - test-hsl-slightly-red/hue"
  check-ints-equal s, 0xff, "F - test-hsl-slightly-red/saturation"
  check-ints-equal l, 0xfe, "F - test-hsl-slightly-red/luminance"  # TODO: should round up
}

fn test-hsl-extremely-red {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0xff, 0, 0
  check-ints-equal h, 0, "F - test-hsl-extremely-red/hue"
  check-ints-equal s, 0xff, "F - test-hsl-extremely-red/saturation"
  check-ints-equal l, 0x7f, "F - test-hsl-extremely-red/luminance"  # TODO: should round up
}

# green hues: 0x55-0xaa
fn test-hsl-slightly-green {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0xfe, 0xff, 0xfe
  check-ints-equal h, 0x55, "F - test-hsl-slightly-green/hue"
  check-ints-equal s, 0xff, "F - test-hsl-slightly-green/saturation"
  check-ints-equal l, 0xfe, "F - test-hsl-slightly-green/luminance"  # TODO: should round up
}

fn test-hsl-extremely-green {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0, 0xff, 0
  check-ints-equal h, 0x55, "F - test-hsl-extremely-green/hue"
  check-ints-equal s, 0xff, "F - test-hsl-extremely-green/saturation"
  check-ints-equal l, 0x7f, "F - test-hsl-extremely-green/luminance"  # TODO: should round up
}

# blue hues: 0xab-0xff
fn test-hsl-slightly-blue {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0xfe, 0xfe, 0xff
  check-ints-equal h, 0xab, "F - test-hsl-slightly-blue/hue"
  check-ints-equal s, 0xff, "F - test-hsl-slightly-blue/saturation"
  check-ints-equal l, 0xfe, "F - test-hsl-slightly-blue/luminance"  # TODO: should round up
}

fn test-hsl-extremely-blue {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0, 0, 0xff
  check-ints-equal h, 0xab, "F - test-hsl-extremely-blue/hue"
  check-ints-equal s, 0xff, "F - test-hsl-extremely-blue/saturation"
  check-ints-equal l, 0x7f, "F - test-hsl-extremely-blue/luminance"  # TODO: should round up
}

# cyan: 0x7f

fn test-hsl-cyan {
  var h/ecx: int <- copy 0
  var s/edx: int <- copy 0
  var l/ebx: int <- copy 0
  h, s, l <- hsl 0, 0xff, 0xff
  check-ints-equal h, 0x80, "F - test-hsl-cyan/hue"
  check-ints-equal s, 0xff, "F - test-hsl-cyan/saturation"
  check-ints-equal l, 0x7f, "F - test-hsl-cyan/luminance"  # TODO: should round up
}

fn nearest-color-euclidean-hsl h: int, s: int, l: int -> _/eax: int {
  var result/edi: int <- copy 0x100/invalid
  var max-distance/esi: int <- copy 0x30000/max  # 3 * 0x100*0x100
  var a/ecx: int <- copy 0
  var b/edx: int <- copy 0
  var c/ebx: int <- copy 0
  var color/eax: int <- copy 0
  {
    compare color, 0x100
    break-if->=
    $nearest-color-euclidean-hsl:body: {
      a, b, c <- color-rgb color
      a, b, c <- hsl a, b, c
      {
        var curr-distance/eax: int <- euclidean-hsl-squared a, b, c, h, s, l
        compare curr-distance, max-distance
        break-if->= $nearest-color-euclidean-hsl:body
        max-distance <- copy curr-distance
      }
      result <- copy color
    }
    color <- increment
    loop
  }
  return result
}

fn test-nearest-color-euclidean-hsl {
  # red from lightest to darkest
  var red/eax: int <- nearest-color-euclidean-hsl 0, 0xff, 0xff
  check-ints-equal red, 0x58/88, "F - test-nearest-color-euclidean-hsl/full-red1"
  red <- nearest-color-euclidean-hsl 0, 0xff, 0xc0
  check-ints-equal red, 0x40/64, "F - test-nearest-color-euclidean-hsl/full-red2"
  red <- nearest-color-euclidean-hsl 0, 0xff, 0x80
  check-ints-equal red, 0x28/40, "F - test-nearest-color-euclidean-hsl/full-red3"
  red <- nearest-color-euclidean-hsl 0, 0xff, 0x40
  check-ints-equal red, 0x28/40, "F - test-nearest-color-euclidean-hsl/full-red4"
  red <- nearest-color-euclidean-hsl 0, 0xff, 0
  check-ints-equal red, 0x28/40, "F - test-nearest-color-euclidean-hsl/full-red5"
  # try a number really close to red but on the other side of the cylinder
  red <- nearest-color-euclidean-hsl 0xff, 0xff, 0xff
#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, red, 7/fg 0/bg
  check-ints-equal red, 0x57/87, "F - test-nearest-color-euclidean-hsl/other-end-of-red"  # still looks red
  # half-saturation red from lightest to darkest
  red <- nearest-color-euclidean-hsl 0, 0x80, 0xff
  check-ints-equal red, 0xf/15, "F - test-nearest-color-euclidean-hsl/half-red1"   # ?? grey ??
  red <- nearest-color-euclidean-hsl 0, 0x80, 0xc0
  check-ints-equal red, 4, "F - test-nearest-color-euclidean-hsl/half-red2"
  red <- nearest-color-euclidean-hsl 0, 0x80, 0x80
  check-ints-equal red, 4, "F - test-nearest-color-euclidean-hsl/half-red3"
  red <- nearest-color-euclidean-hsl 0, 0x80, 0x40
  check-ints-equal red, 4, "F - test-nearest-color-euclidean-hsl/half-red4"
  red <- nearest-color-euclidean-hsl 0, 0x80, 0
  check-ints-equal red, 0x70/112, "F - test-nearest-color-euclidean-hsl/half-red5"
}

fn euclidean-hsl-squared h1: int, s1: int, l1: int, h2: int, s2: int, l2: int -> _/eax: int {
  var result/edi: int <- copy 0
  # hue
  var tmp/eax: int <- copy h1
  tmp <- subtract h2
  tmp <- multiply tmp
  # TODO: should we do something to reflect that hue is a cylindrical space?
  # I can't come up with a failing test.
  result <- add tmp
  # saturation
  tmp <- copy s1
  tmp <- subtract s2
  tmp <- multiply tmp
  result <- add tmp
  # luminance
  tmp <- copy l1
  tmp <- subtract l2
  tmp <- multiply tmp
  result <- add tmp
  return result
}

###

fn maximum a: int, b: int -> _/eax: int {
  var a2/eax: int <- copy a
  compare a2, b
  {
    break-if-<
    return a
  }
  return b
}

fn minimum a: int, b: int -> _/eax: int {
  var a2/eax: int <- copy a
  compare a2, b
  {
    break-if->
    return a
  }
  return b
}
o">-equal rank, 0 break-if above-min print screen, [rank too low: ] print screen, c return 0/dummy, 0/quit, 1/error } { below-max:boolean <- lesser-or-equal rank, 7 break-if below-max print screen, [rank too high: ] print screen, c return 0/dummy, 0/quit, 1/error } return rank, 0/quit, 0/error ] # read a character from the given channel and check that it's what we expect # return true on error def expect-from-channel stdin:address:source:character, expected:character, screen:address:screen -> result:boolean, stdin:address:source:character, screen:address:screen [ local-scope load-ingredients c:character, eof?:boolean, stdin <- read stdin return-if eof? 1/true { match?:boolean <- equal c, expected break-if match? print screen, [expected character not found] } result <- not match? ] scenario read-move-blocking [ assume-screen 20/width, 2/height run [ 1:address:source:character, 2:address:sink:character <- new-channel 2/capacity 3:number/routine <- start-running read-move, 1:address:source:character, screen:address:screen # 'read-move' is waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-blocking: routine failed to pause after coming up (before any keys were pressed)] # press 'a' 2:address:sink:character <- write 2:address:sink:character, 97/a restart 3:number/routine # 'read-move' still waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-blocking: routine failed to pause after rank 'a'] # press '2' 2:address:sink:character <- write 2:address:sink:character, 50/'2' restart 3:number/routine # 'read-move' still waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-blocking: routine failed to pause after file 'a2'] # press '-' 2:address:sink:character <- write 2:address:sink:character, 45/'-' restart 3:number/routine # 'read-move' still waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?/routine-state, [ F read-move-blocking: routine failed to pause after hyphen 'a2-'] # press 'a' 2:address:sink:character <- write 2:address:sink:character, 97/a restart 3:number/routine # 'read-move' still waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?/routine-state, [ F read-move-blocking: routine failed to pause after rank 'a2-a'] # press '4' 2:address:sink:character <- write 2:address:sink:character, 52/'4' restart 3:number/routine # 'read-move' still waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-blocking: routine failed to pause after file 'a2-a4'] # press 'newline' 2:address:sink:character <- write 2:address:sink:character, 10 # newline restart 3:number/routine # 'read-move' now completes wait-for-routine 3:number 4:number <- routine-state 3:number 5:boolean/completed? <- equal 4:number/routine-state, 1/completed assert 5:boolean/completed?, [ F read-move-blocking: routine failed to terminate on newline] trace 1, [test], [reached end] ] trace-should-contain [ test: reached end ] ] scenario read-move-quit [ assume-screen 20/width, 2/height run [ 1:address:source:character, 2:address:sink:character <- new-channel 2/capacity 3:number/routine <- start-running read-move, 1:address:channel:character, screen:address:screen # 'read-move' is waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-quit: routine failed to pause after coming up (before any keys were pressed)] # press 'q' 2:address:sink:character <- write 2:address:sink:character, 113/q restart 3:number/routine # 'read-move' completes wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/completed? <- equal 4:number/routine-state, 1/completed assert 5:boolean/completed?, [ F read-move-quit: routine failed to terminate on 'q'] trace 1, [test], [reached end] ] trace-should-contain [ test: reached end ] ] scenario read-move-illegal-file [ assume-screen 20/width, 2/height run [ 1:address:source:character, 2:address:sink:character <- new-channel 2/capacity 3:number/routine <- start-running read-move, 1:address:source:character, screen:address:screen # 'read-move' is waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-file: routine failed to pause after coming up (before any keys were pressed)] 1:address:sink:character <- write 1:address:sink:character, 50/'2' restart 3:number/routine wait-for-routine 3:number ] screen-should-contain [ .file too low: 2 . . . ] ] scenario read-move-illegal-rank [ assume-screen 20/width, 2/height run [ 1:address:source:character, 2:address:sink:character <- new-channel 2/capacity 3:number/routine <- start-running read-move, 1:address:source:character, screen:address:screen # 'read-move' is waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-file: routine failed to pause after coming up (before any keys were pressed)] 1:address:sink:character <- write 1:address:sink:character, 97/a 1:address:sink:character <- write 1:address:sink:character, 97/a restart 3:number/routine wait-for-routine 3:number ] screen-should-contain [ .rank too high: a . . . ] ] scenario read-move-empty [ assume-screen 20/width, 2/height run [ 1:address:source:character, 2:address:sink:character <- new-channel 2/capacity 3:number/routine <- start-running read-move, 1:address:source:character, screen:address:screen # 'read-move' is waiting for input wait-for-routine 3:number 4:number <- routine-state 3:number/id 5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting assert 5:boolean/waiting?, [ F read-move-file: routine failed to pause after coming up (before any keys were pressed)] 1:address:sink:character <- write 1:address:sink:character, 10/newline 1:address:sink:character <- write 1:address:sink:character, 97/a restart 3:number/routine wait-for-routine 3:number ] screen-should-contain [ .that's not enough . . . ] ] def make-move board:address:array:address:array:character, m:address:move -> board:address:array:address:array:character [ local-scope load-ingredients from-file:number <- get *m, from-file:offset from-rank:number <- get *m, from-rank:offset to-file:number <- get *m, to-file:offset to-rank:number <- get *m, to-rank:offset from-f:address:array:character <- index *board, from-file to-f:address:array:character <- index *board, to-file src:character/square <- index *from-f, from-rank *to-f <- put-index *to-f, to-rank, src *from-f <- put-index *from-f, from-rank, 32/space ] scenario making-a-move [ assume-screen 30/width, 12/height run [ 2:address:array:address:array:character/board <- initial-position 3:address:move <- new move:type *3:address:move <- merge 6/g, 1/'2', 6/g, 3/'4' 2:address:array:address:array:character/board <- make-move 2:address:array:address:array:character/board, 3:address:move screen:address:screen <- print-board screen:address:screen, 2:address:array:address:array:character/board ] screen-should-contain [ # 012345678901234567890123456789 .8 | r n b q k b n r . .7 | p p p p p p p p . .6 | . .5 | . .4 | P . .3 | . .2 | P P P P P P P . .1 | R N B Q K B N R . . +---------------- . . a b c d e f g h . . . ] ]