# 5025703a71387c428b8e69edf7aed5e11b5e4a076d7c26038d52a3d13724fe56 Elan 2.0.0-alpha4 valid

# Hodgepodge, after M.Gerhardt, H.Schuster, J.Tyson (1990) 
# A Cellular Automaton Model of Excitable Media
# 
# gW, gH : grid width, height
constant gW set to 40

constant gH set to 30

# iR : infection rate (1..20)
constant iR set to 1

# w1, w2 : weighting factors (low integers)
constant w1 set to 4

constant w2 set to 1

# colours : cellColour.s in descending order of RGB value
# state : of a cell is colours.indexOf(cellColour) in [0..colours.length() - 1]
# colours[0] = healthy; colours[colours.length() - 1] = ill, otherwise infected
constant healthy set to white

constant ill set to black

function getColours() returns List<of Int>
  return [healthy, 0xffe6ff, 0xffccff, 0xffb3ff, 0xff99ff, 0xff80ff, 0xff66ff, 0xff4dff, 0xff33ff, 0xff1aff, 0xff00ff, 0xe600e6, 0xcc00cc, 0xb300b3, 0x990099, 0x800080, 0x660066, 0x4d004d, 0x330033, 0x1a001a, ill]
end function

# vN : neighbourhood: von Neumann (4) true, Moore (8) false
constant vN set to false

main
  # colour grids: hodge for display, podge for working
  variable podge set to createBlockGraphics(healthy)
  variable hodge set to new AsRef<of List<of List<of Int>>>(createBlockGraphics(healthy))
  variable blank set to createBlockGraphics(healthy)
  # initial colours of grid
  call updateGrid(hodge, podge, true)
  while not uniform(hodge.value())
    # successive updates to grid in blank podge
    set podge to blank
    call updateGrid(hodge, podge, false)
  end while
end main

procedure updateGrid(hodge as AsRef<of List<of List<of Int>>>, podge as List<of List<of Int>>, initial as Boolean)
  variable colours set to getColours()
  for j in range(0, gH)
    for i in range(0, gW)
      if initial then
        set podge[i][j] to colours[randint(0, (colours.length()) - 1)]
        [ghosted] set podge[1][1] to 0x1a001a
      else
        set podge[i][j] to newColour(getNeighbourColours(hodge.value(), i, j), hodge.value()[i][j])
      end if
    end for
  end for
  variable a set to 0
  call hodge.set(podge)
  call displayBlocks(hodge.value())
  call sleep_ms(50)
end procedure

function uniform(grid as List<of List<of Int>>) returns Boolean
  variable uniformGrid set to createBlockGraphics(grid[0][0])
  return if(grid.equals(uniformGrid), true, false)
end function

function getNeighbourColours(grid as List<of List<of Int>>, i as Int, j as Int) returns List<of Int>
  # grid wraps around: all cells have the same number of neighbours
  # H and V neighbours(von Neumann)
  variable sL set to grid[(i - 1 + gW) mod gW][j]
  variable sR set to grid[(i + 1 + gW) mod gW][j]
  variable sA set to grid[i][(j - 1 + gH) mod gH]
  variable sB set to grid[i][(j + 1 + gH) mod gH]
  variable neighbourColours set to [sL, sR, sA, sB]
  if vN is false then
    # add diagonal neighbours (Moore)
    variable sLA set to grid[(i - 1 + gW) mod gW][(j - 1 + gH) mod gH]
    variable sRA set to grid[(i + 1 + gW) mod gW][(j - 1 + gH) mod gH]
    variable sLB set to grid[(i - 1 + gW) mod gW][(j + 1 + gH) mod gH]
    variable sRB set to grid[(i + 1 + gW) mod gW][(j + 1 + gH) mod gH]
    set neighbourColours to [sL, sR, sA, sB, sLA, sRA, sLB, sRB]
  end if
  return neighbourColours
end function

function newColour(neighbourColours as List<of Int>, nowColour as Int) returns Int
  variable colours set to getColours()
  variable nInfected set to 0
  variable nIll set to 0
  variable sumStates set to colours.indexOf(nowColour)
  for colour in neighbourColours
    set sumStates to sumStates + colours.indexOf(colour)
    if colour < healthy then
      set nInfected to nInfected + 1
      if colour is ill then
        set nIll to nIll + 1
      end if
    end if
  end for
  return updateColour(nowColour, sumStates, nInfected, nIll)
end function

function updateColour(nowColour as Int, sumStates as Int, nInfected as Int, nIll as Int) returns Int
  variable colours set to getColours()
  variable state set to 0
  if nowColour is healthy then
    set state to divAsInt(nInfected, w1) + divAsInt(nIll, w2)
  elif nowColour isnt ill then
    set state to divAsInt(sumStates, (nInfected + 1)) + iR
  end if
  return if(state > (colours.length() - 1), ill, colours[state])
end function
