By adamlubek​
This recipe demonstrates RxJs implementation of Flappy Bird like game.
​​​​
( StackBlitz )
// RxJS v6+import { interval, merge, combineLatest, fromEvent } from 'rxjs';import { tap, scan, takeWhile } from 'rxjs/operators';import { paint } from './html-renderer';​const gamePipe = (x, y) => ({ x, y, checked: false });const gameSize = 10;const createPipes = y =>(random =>Array.from(Array(gameSize).keys()).map(e => gamePipe(e, y)).filter(e => e.x < random || e.x > random + 2))(Math.floor(Math.random() * Math.floor(gameSize)));​const gamePipes$ = interval(500).pipe(scan < any,any >(acc =>(acc.length < 2 ? [...acc, createPipes(gameSize)] : acc).filter(c => c.some(e => e.y > 0)).map(cols => cols.map(e => gamePipe(e.x, e.y - 1))),[createPipes(gameSize / 2), createPipes(gameSize)]));​const fly = xPos => (xPos > 0 ? (xPos -= 1) : xPos);const fall = xPos => (xPos < gameSize - 1 ? (xPos += 1) : gameSize - 1);const bird$ = merge(interval(300), fromEvent(document, 'keydown')).pipe(scan < any,any >((xPos, curr) => (curr instanceof KeyboardEvent ? fly(xPos) : fall(xPos)),gameSize - 1));​const updateGame = (bird, pipes) =>(game => (pipes.forEach(col => col.forEach(v => (game[v.x][v.y] = 2))),(game[bird][0] = 1),game))(Array(gameSize).fill(0).map(e => Array(gameSize).fill(0)));​const valueOnCollisionFor = pipes => ({when: predicate =>!pipes[0][0].checked && predicate ? ((pipes[0][0].checked = true), 1) : 0});​combineLatest(bird$, gamePipes$).pipe(scan < any,any >((state, [bird, pipes]) => ({bird: bird,pipes: pipes,lives:state.lives -valueOnCollisionFor(pipes).when(pipes.some(c => c.some(c => c.y === 0 && c.x === bird))),score:state.score + valueOnCollisionFor(pipes).when(pipes[0][0].y === 0)}),{ lives: 3, score: 0, bird: 0, pipes: [] }),tap(state =>paint(updateGame(state.bird, state.pipes), state.lives, state.score)),takeWhile(state => state.lives > 0)).subscribe();
const createElem = col => {const elem = document.createElement('div');elem.classList.add('board');elem.style.display = 'inline-block';elem.style.marginLeft = '10px';elem.style.height = '6px';elem.style.width = '6px';elem.style['background-color'] =col === 0? 'white': col === 1? 'cornflowerblue': col === 2? 'gray': 'silver';elem.style['border-radius'] = '90%';return elem;};​export const paint = (game: number[][], lives, score) => {document.body.innerHTML = `Lives: ${lives}, Score: ${score}`;​game.forEach(row => {const rowContainer = document.createElement('div');row.forEach(col => rowContainer.appendChild(createElem(col)));document.body.appendChild(rowContainer);});};