"A naive demonstration of Conway's Game of Life, using terminal graphics." load: 'src/lib/random.slate'. load: 'src/plugins/smart-console/init.slate'. lobby define: #LifeGame &parents: {Cloneable} &slots: {#screen. #height. #width. #cells -> {}. "Array of size H * W." #newCells -> {}. "Array of size H * W." #randomStream -> RandomStream clone}. g@(LifeGame traits) random: max [ g randomStream next mod: max ]. g@(LifeGame traits) init "Load up #cells to be randomized." [ g cells doWithIndex: [| :each :index | g cells at: index put: 0]. g cells size / 4 timesRepeat: [g cells at: (g random: g cells size) put: 1]. g ]. g@(LifeGame traits) new "Target stdout and set up the game for it." [| scr | (Console is: SmartConsole) ifFalse: [error: 'The Life demo only runs on a SmartConsole. If the plugin is loaded and available then entering "repl." should activate it.'. ^ Nil]. g clone `>> [| :newG | screen: Console. height: Console height. width: Console width. cells: (g cells new &capacity: newG height * newG width). newCells: newG cells copy. randomStream: (g randomStream newSeed: 4357). init. ] ]. g@(LifeGame traits) at: array [ g cells at: array first + (array second * g width) ]. g@(LifeGame traits) at: x at: y [ g cells at: x + (y * g width) ]. g@(LifeGame traits) neighborsOf: array [| index n atTop atBottom atLeft atRight | n: 0. index: array first + (array second * g width). atTop: array second isZero. atBottom: array second = (g height - 1). atLeft: array first isZero. atRight: array first = (g width - 1). atTop ifFalse: [ n: n + (g cells at: index - g width). atLeft ifFalse: [n: n + (g cells at: index - g width - 1)]. atRight ifFalse: [n: n + (g cells at: index - g width + 1)]]. atLeft ifFalse: [n: n + (g cells at: index - 1)]. atRight ifFalse: [n: n + (g cells at: index + 1)]. atBottom ifFalse: [ n: n + (g cells at: index + g width). atLeft ifFalse: [n: n + (g cells at: index + g width - 1)]. atRight ifFalse: [n: n + (g cells at: index + g width + 1)]]. n ]. g@(LifeGame traits) calcNewState "The core life rules." [| n index | 0 below: g height do: [| :y | 0 below: g width do: [| :x | n: (g neighborsOf: {x. y}). index: x + (y * g width). (n > 3 or: [n < 2]) ifTrue: [g newCells at: index put: 0] ifFalse: [n = 3 ifTrue: [g newCells at: index put: 1] ifFalse: [g newCells at: index put: (g at: x at: y)]]]]. g ]. g@(LifeGame traits) step "Iterate: swap new with old arrays, and display the new one." [| temp | g calcNewState. temp: g newCells. g newCells: g cells. g cells: temp. g redraw ]. g@(LifeGame traits) redraw [| skipLast | 0 below: g height do: [| :y width | g screen moveCursorTo: {0. y}. (y >= (g height - 1)) /\ [g screen autoScrollsAtBottom] ifTrue: [width: g width - 1] ifFalse: [width: g width]. 0 below: width do: [| :x | (g at: x at: y) isZero ifTrue: [g screen backgroundColor: g screen colors Blue] ifFalse: [g screen backgroundColor: g screen colors Green]. g screen writer nextPut: $\s]]. g screen flush. ]. g@(LifeGame) run [ [g screen hideCursor. [g step] loop] ensure: [g screen showCursor] ]. inform: 'Type "SmartREPL start." to start a REPL on a SmartConsole and then "LifeGame new run." to see Life unfold.'.