CodePen

HTML

            
              <canvas width="400" height="400" id="show"></canvas>
<textarea id="input" cols="40" rows="20">
4 5
0 0 5 udlr
4 0 5 ud
4 2 2 lr
2 3 3 udlr
</textarea>
<button onclick="go()">Go</button>

            
          
!

CSS

            
              
            
          
!
? ?
? ?
Must be a valid URL.
+ add another resource
via CSS Lint

JS

            
              function P(x, y) {
    if (!(this instanceof P)) {
        return new P(x, y);
    } else {
        this.x = x;
        this.y = y;
        return this;
    }
}

P.prototype.plus = function(p) {
    return P(this.x + p.x, this.y + p.y);
};

P.prototype.within = function(n) {
    return this.x >= 0 && this.x < n && this.y >= 0 && this.y < n;
};

P.prototype.toString = function() {
    return '[P ' + this.x + ' ' + this.y + ']';
};


function Wave(p, v, timeout) {
    this.p = p;
    this.v = v;
    this.timeout = timeout;
}

Wave.prototype.step = function() {
    this.p = this.p.plus(this.v);
    this.timeout--;
    return this;
};


function Cell(radius, dirs) {
    this.radius = radius || 0;
    this.dirs = (dirs || '').toLowerCase().split('').sort().join('');
    this.active = false;
}

Cell.prototype.toString = function() {
    return '[Cell ' + this.radius + (this.dirs ? ' ' + this.dirs : '') + ']';
};

Cell.prototype.activate = function() {
    if (this.active) {
        return [];
    } else {
        this.active = true;
        return this.dirs.split('').map(function(dir) {
            return { u: P(0, -1), d: P(0, 1), l: P(-1, 0), r: P(1, 0) }[dir];
        });
    }
};


function Grid(size) {
    this.size = size;
    this.grid = {};
    this.waves = [];
}

Grid.prototype.get = function(p) {
    var element = this.grid[p];
    return element != null ? element : this.grid[p] = new Cell();
};

Grid.prototype.set = function(p, cell) {
    this.grid[p] = cell;
};

Grid.prototype.activate = function(p) {
    var cell = this.get(p);
    return cell.activate().map(function(v) {
        return new Wave(p, v, cell.radius);
    });
};

Grid.prototype.init = function(p) {
    this.waves = this.activate(p);
};

Grid.prototype.step = function() {
    var added = [];
    this.waves = this.waves.filter(function(wave) {
        if (wave.timeout > 0 && wave.p.within(this.size)) {
            added.push(this.activate(wave.step().p));
            return true;
        } else {
            return false;
        }
    }.bind(this));
    added.forEach(function(waves) {
        this.waves.push.apply(this.waves, waves);
    }.bind(this));
};

Grid.trim = function(str) {
    return str.replace(/^[ \n\t\r]+|[ \n\t\r]+$/g, '');
};

Grid.parse = function(str) {
    var lines = Grid.trim(str).split(/\n/);
    var size = lines[0].split(/ +/).map(parseFloat)[1];
    var grid = new Grid(size);
    var cells = [];
    lines.slice(1).forEach(function(line) {
        var split = Grid.trim(line).split(/ +/);
        var nums = split.slice(0, 3).map(parseFloat);
        var p = P(nums[0], nums[1]);
        var cell = new Cell(nums[2], split[3]);
        cells.push(p);
        grid.set(p, cell);
    });
    grid.init(cells[0]);
    return grid;
};


function Show(id, grid) {
    this.ctx = document.getElementById(id).getContext('2d');
    this.grid = grid;
    this.timer = null;
}

Show.prototype.update = function() {
    var ctx = this.ctx;
    var w = this.ctx.canvas.width;
    var h = this.ctx.canvas.height;
    var size = this.grid.size;
    var xsize = w / size;
    var ysize = h / size;
    for (var y = 0; y < size; y++) {
        for (var x = 0; x < size; x++) {
            var cell = this.grid.get(P(x, y));
            var type;
            if (cell.radius === 0) {
                type = '#eee';
            } else {
                type = cell.active ? '#0f0' : '#f00';
            }
            ctx.fillStyle = type;
            ctx.fillRect(x * xsize, y * ysize, xsize, ysize);
        }
    }
    ctx.fillStyle = '#000';
    this.grid.waves.forEach(function(wave) {
        ctx.fillRect(wave.p.x * xsize + xsize / 4,
                     wave.p.y * ysize + ysize / 4,
                     xsize / 2, xsize / 2);
    });
};


Show.prototype.stop = function() {
    window.clearInterval(this.timer);
    return this;
};

Show.prototype.animate = function() {
    var show = this;
    show.stop();
    show.timer = window.setInterval(function() {
        show.grid.step();
        show.update();
        if (show.grid.waves.length === 0) {
            show.stop();
        }
    }, 250);
    return this;
};


function go() {
    var input = document.getElementById('input').value;
    new Show('show', Grid.parse(input)).animate();
}

            
          
!
Must be a valid URL.
+ add another resource
via JS Hint
Loading ..................