By adamlubek
This recipe demonstrates RxJS implementation of Mine Sweeper Game.
( StackBlitz )
// RxJS v6+
import { fromEvent, of } from 'rxjs';
import {
map,
tap,
filter,
pluck,
switchMap,
takeWhile,
finalize
} from 'rxjs/operators';
import { renderMinefield, renderScore, renderGameOver } from './html-renderer';
import { size, mine } from './constants';
import { addMines, addMarks } from './mines';
const mines$ = of(
Array(size)
.fill(0)
.map(e => Array(size).fill(0))
).pipe(map(addMines), map(addMarks), tap(renderMinefield));
const click$ = mines =>
fromEvent(document, 'click').pipe(
map(({ clientX, clientY }: MouseEvent) =>
document.elementFromPoint(clientX, clientY)
),
filter(elem => elem.id !== ''),
tap(elem =>
(val => (
renderScore(val === mine || elem.innerHTML !== '_' ? 0 : val),
(elem.innerHTML = val)
))(mines[elem.id[0]][elem.id[1]])
),
pluck('id'),
takeWhile(([x, y]) => mines[x][y] !== mine),
finalize(renderGameOver)
);
mines$.pipe(switchMap(click$)).subscribe();
import { size, mine } from './constants';
const randomNumber = () => Math.floor(Math.random() * Math.floor(size));
export const addMines = arr => {
for (let i = 0; i < size / 2; i++) {
arr[randomNumber()][randomNumber()] = mine;
}
return arr;
};
const mark = (arr, x, y) =>
arr[x] !== undefined && arr[x][y] !== undefined
? (arr[x][y] += arr[x][y] === mine ? 0 : 1)
: () => {};
export const addMarks = arr => {
for (let ri = 0; ri < size; ri++) {
for (let ci = 0; ci < size; ci++) {
if (arr[ri][ci] === mine) {
mark(arr, ri - 1, ci + 1);
mark(arr, ri - 1, ci);
mark(arr, ri - 1, ci - 1);
mark(arr, ri, ci + 1);
mark(arr, ri, ci - 1);
mark(arr, ri + 1, ci + 1);
mark(arr, ri + 1, ci);
mark(arr, ri + 1, ci - 1);
}
}
}
return arr;
};
export const mine = 9;
export const size = 10;
export const renderMinefield = arr =>
arr.forEach((r, ri) =>
(elem =>
r.forEach(
(c, ci) =>
(col => (
(col.innerText = '_'),
(col.id = `${ri}${ci}`),
elem.appendChild(document.createTextNode('\u00A0\u00A0')),
elem.appendChild(col)
))(document.createElement('span')),
document.body.appendChild(elem)
))(document.createElement('div'))
);
export const renderScore = val =>
(scoreElem => (scoreElem.innerText = parseInt(scoreElem.innerText) + val))(
document.getElementById('score')
);
export const renderGameOver = () =>
(document.body.innerHTML += '<br/>GAME OVER');
const addElem = decorator =>
(elem => (decorator(elem), document.body.appendChild(elem)))(
document.createElement('span')
);
addElem(elem => (elem.innerText = 'Score: '));
addElem(elem => ((elem.id = 'score'), (elem.innerText = '0')));