Software Artisan's Blog

~ onLife >> onMusic >> onDesign >> onPatterns >> onRefactoring >> onAgile

Tag Archives: reactjs

Game of Life in RxJS (and ReactJS) and RxGroovy

15 Sunday Nov 2015

Posted by Dhaval Dalal in Functional Programming, Functional Reactive, Groovy, JavaScript

≈ Leave a comment

Tags

Functional Programming, reactjs, rxjs


Last week at the Global Day of CodeRetreat in Mumbai, I wrote the Game Of Life using RxJS. Though I used the Node.js and text console to render the game during the retreat, below is the HTML version for rendering an Oscillating Pulsar. Hit Run to see it oscillate.

Code uses all pure functions and ES6 arrows. Below is the excerpt, Check out the code on GitHub if it interests you.

var cellAt = function(r, c, world) {
  var row = world[r];
  if (row == undefined || row[c] == undefined) {
    return 0;
  }
  return world[r][c];
}

var neighbours = function(r, c, world) {
  return [
    cellAt(r-1, c-1, world), cellAt(r-1, c  , world), cellAt(r-1, c+1, world), 
    cellAt(r,   c-1, world),                          cellAt(r,   c+1, world), 
    cellAt(r+1, c-1, world), cellAt(r+1, c  , world), cellAt(r+1, c+1, world)  
  ];
}

var isAlive = cell => cell != 0;
var isUnderPopulated = liveNeighbours => liveNeighbours < 2;
var canSurvive = liveNeighbours => liveNeighbours == 2 || liveNeighbours == 3;
var isOverCrowded = liveNeighbours => liveNeighbours > 3;
var canReproduce = liveNeighbours => liveNeighbours == 3;

var newCell = function(cell, neighbours) {
  var liveNeighbours = neighbours.reduce((a, b) => a + b);
  if (isAlive(cell) && (isUnderPopulated(liveNeighbours) || isOverCrowded(liveNeighbours))) {
      return 0;
  }
  if (isAlive(cell) && canSurvive(liveNeighbours)) {
      return 1;
  }
  if(!isAlive(cell) && canReproduce(liveNeighbours)) {
      return 1;
  }
  return 0;
}

var newWorld = 
  oldWorld => oldWorld.map((row, r) => 
    row.map((oldCell, c) => 
      newCell(oldCell, neighbours(r, c, oldWorld))));

and here is the HTML. In the inline script below, I use Rx.Observable‘s generateWithRelativeTime to produce a new world from seed world. It starts with the seed world pattern and ticks every 1 sec producing a new world from the previous world based on the rules of the game encoded above. The subscribe function’s success callback uses the render function display the newly generated world by mapping the 2D world-array to HTML divs. I take only first 30 generations as this pattern continues infinitely.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/gameOfLife.css">
    <title>Rx Game Of Life</title>
</head>
<body>
    <div id="output"></div>
    
    <script src="lib/rx/rx.all.js"></script>
    <script src="src/gameOfLife.js"></script>
    <script src="src/gameOfLifePatterns.js"></script>
    <script>
    function toDiv(cell) {
      var cellStyle = (cell == 0) ? "dead" : "live";
      return "<div class='cell cell-" + cellStyle + "'>&nbsp;</div>";
    }
    
    function render(world)  {
      var html = world.map(r => r.map(toDiv))
                    .join("<br>")
                    .replace(/,/g, "");
      document.getElementById("output").innerHTML = html;
    }
    
    var seedWorld = oscillatingPulsar();

    Rx.Observable.generateWithRelativeTime(seedWorld,
      w => true, // don't terminate
      newWorld,  // next function
      w => w,    // return value
      () => 1000 // tick every
    )
    .take(30)
    .subscribe(
      render,
      e  => console.info(e.message),
      () => console.info("*** Complete ***")
    );
    </script>
</body>
</html>

You can replace line 30 with calls to other patterns to see them in action.

Below is the game using ReactJS. As ReactJS helps render the View in MVC, only view related functions: toDiv and render have changed to accommodate it. Additionally, I’ve passed row and column ids as rid and cid respectively, so that ReactJS does not give a warning:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the React.render call using <div>.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/gameOfLife.css">
    <title>Rx Game Of Life Using ReactJS</title>
</head>
<body>
  <div id="output"></div>

  <script src="lib/rx/rx.all.js"></script>
  <script src="lib/reactjs/JSXTransformer.js"></script>
  <script src="lib/reactjs/react.js"></script>
  <script src="src/gameOfLife.js"></script>
  <script src="src/gameOfLifePatterns.js"></script>

  <script type="text/jsx">
    function toDiv(cell, rid, cid) {
      var cellStyle = (cell == 0) ? "dead" : "live";
      return (<div key={rid + "" + cid} className={`cell cell-${cellStyle}`}>&nbsp;</div>);
    }

    function render(world)  {
      var grid = world.map((r, rid) => {
        return (<div key={rid}>
                  {r.map((cell, cid) =>
                     toDiv(cell, rid, cid)
                  )}
               </div>);
      });
      React.render(<div>{grid}</div>,
                   document.getElementById("output"));
    }
    
    var seedWorld = oscillatingPulsar();

    Rx.Observable.generateWithRelativeTime(seedWorld,
      w => true, // don't terminate
      newWorld,  // next function
      w => w,    // return value
      () => 1000 // tick every
    )
    .take(30)
    .subscribe(
      render,
      e  => console.info(e.message),
      () => console.info("*** Complete ***")
    );
  </script>
</body>
</html>

See it in action below 🙂

Below is the game using RxGroovy.

import rx.Observable
import java.util.concurrent.*

List.metaClass.collectWithIndex = { body ->
  def idx = 0
  delegate.collect { body(it, idx++) }
}

def cellAt(rowIdx, colIdx, world) {
  def r = rowIdx >= 0 ? rowIdx : Integer.MAX_VALUE
  def c =  colIdx >= 0 ? colIdx : Integer.MAX_VALUE

  def row = world[r]
  if (row == null || row[c] == null)
        return 0
  return world[r][c]
}

def neighbours(r, c, world) {
  [
    cellAt(r-1, c-1, world), cellAt(r-1, c  , world), cellAt(r-1, c+1, world),
    cellAt(r,   c-1, world),                          cellAt(r,   c+1, world),
    cellAt(r+1, c-1, world), cellAt(r+1, c  , world), cellAt(r+1, c+1, world)
  ]
}

def isAlive(cell) { cell != 0 }
def isUnderPopulated(liveNeighbours) { liveNeighbours < 2 }
def canSurvive(liveNeighbours) { liveNeighbours == 2 || liveNeighbours == 3 }
def isOverCrowded(liveNeighbours) { liveNeighbours > 3 }
def canReproduce(liveNeighbours) { liveNeighbours == 3 }

def newCell(cell, neighbours) {
  def liveNeighbours = neighbours.inject(0) {a,e -> a + e}
  if (isAlive(cell) && (isUnderPopulated(liveNeighbours) || isOverCrowded(liveNeighbours)))
    return 0

  if (isAlive(cell) && canSurvive(liveNeighbours))
    return 1

  if (!isAlive(cell) && canReproduce(liveNeighbours))
    return 1

  return 0
}

def newWorld = { oldWorld ->
  oldWorld.collectWithIndex { row, rIdx ->
    row.collectWithIndex { oldCell, cIdx ->
      newCell(oldCell, neighbours(rIdx, cIdx, oldWorld))
    }
  }
}

def blinker = [
  [0, 1, 0],
  [0, 1, 0],
  [0, 1, 0]
]

Observable.interval(1, TimeUnit.SECONDS)
.scan(blinker, { old, tick -> newWorld(old)})
.take(10) // 10 generations
.subscribe (
    { println("$it") },
    { e -> println("onError() => $e") },
    { println('onComplete() => *** Complete ***') }
)

Advertisements

Share this:

  • Twitter
  • LinkedIn
  • Google
  • Reddit
  • Email
  • Facebook
  • Print

Like this:

Like Loading...

Dhaval Dalal

Profile

Dhaval Dalal

Slides

View DhavalDalal's profile on slideshare

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 290 other followers

Ideas

  • Code Jugalbandi

Open Source Projects

  • Java8 Exception Monad
  • Midas
  • Tayra

Recent Posts

  • Exploring Concurrency CodeJugalbandi February 6, 2019
  • Destructuring and Pattern Matching in Functional Programming December 29, 2018
  • Help yourself by making iTerm2 vivid May 30, 2018
  • How to disable failed discrete-GPU (NVIDIA GeForce GT 650M) for 15″ MacBook-Pro 10,1 (mid-2012) on High Sierra 10.13.4 May 29, 2018
  • Improvised Tasks.tmbundle for TextMate April 15, 2018

Calendar

February 2019
M T W T F S S
« Dec    
 123
45678910
11121314151617
18192021222324
25262728  

Categories

  • Agile (13)
  • APL (2)
  • Array-Oriented (1)
  • C# (14)
  • Clojure (2)
  • Code Jugalbandi (5)
  • Code Retreat (2)
  • Computer (2)
  • Education (1)
  • Erlang (3)
  • Functional Programming (10)
  • Functional Reactive (3)
  • General (34)
  • Groovy (15)
  • GWT (6)
  • Haskell (2)
  • Java (28)
  • JavaScript (10)
  • Life (2)
  • Microservices (1)
  • MongoDB (2)
  • Music (2)
  • Neo4J (1)
  • Sanskrit (4)
  • Scala (8)
  • Thoughts-Philosophy (9)
  • Yoga (2)

Archives

Follow me on Twitter

My Tweets

Blog Stats

  • 26,703 hits

Tags

Agile Code Jugalbandi Erlang Functional Programming General Groovy Homeopathy iTerm2 java Java8 Life Microservices reactjs rxjs Sanskrit Scala Textmate2 Thoughts-Philosophy

RSS

  • RSS - Posts
  • RSS - Comments
Advertisements

Blog at WordPress.com.

loading Cancel
Post was not sent - check your email addresses!
Email check failed, please try again
Sorry, your blog cannot share posts by email.
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy
%d bloggers like this: