Learn RxJS
Search…
Alphabet Invasion Game
By adamlubek
This recipe demonstrates RxJS implementation of Alphabet Invasion Game.

Example Code

Alphabet Invasion

index.ts

1
// RxJS v6+
2
import { interval, fromEvent, combineLatest, BehaviorSubject } from 'rxjs';
3
import { scan, startWith, map, takeWhile, switchMap } from 'rxjs/operators';
4
import { State, Letter, Letters } from './interfaces';
5
6
const randomLetter = () =>
7
String.fromCharCode(
8
Math.random() * ('z'.charCodeAt(0) - 'a'.charCodeAt(0)) + 'a'.charCodeAt(0)
9
);
10
const levelChangeThreshold = 20;
11
const speedAdjust = 50;
12
const endThreshold = 15;
13
const gameWidth = 30;
14
15
const intervalSubject = new BehaviorSubject(600);
16
17
const letters$ = intervalSubject.pipe(
18
switchMap(i =>
19
interval(i).pipe(
20
scan<number, Letters>
21
(letters => ({
22
intrvl: i,
23
ltrs: [
24
{
25
letter: randomLetter(),
26
yPos: Math.floor(Math.random() * gameWidth)
27
},
28
...letters.ltrs
29
]
30
}),
31
{ ltrs: [], intrvl: 0 })
32
)
33
)
34
);
35
36
const keys$ = fromEvent(document, 'keydown').pipe(
37
startWith({ key: '' }),
38
map((e: KeyboardEvent) => e.key)
39
);
40
41
const renderGame = (state: State) => (
42
(document.body.innerHTML = `Score: ${state.score}, Level: ${state.level} <br/>`),
43
state.letters.forEach(
44
l =>
45
(document.body.innerHTML += '&nbsp'.repeat(l.yPos) + l.letter + '<br/>')
46
),
47
(document.body.innerHTML +=
48
'<br/>'.repeat(endThreshold - state.letters.length - 1) +
49
'-'.repeat(gameWidth))
50
);
51
const renderGameOver = () => (document.body.innerHTML += '<br/>GAME OVER!');
52
const noop = () => {};
53
54
const game$ = combineLatest(keys$, letters$).pipe(
55
scan<[string, Letters], State>
56
((state, [key, letters]) => (
57
letters.ltrs[letters.ltrs.length - 1] &&
58
letters.ltrs[letters.ltrs.length - 1].letter === key
59
? ((state.score = state.score + 1), letters.ltrs.pop())
60
: noop,
61
state.score > 0 && state.score % levelChangeThreshold === 0
62
? ((letters.ltrs = []),
63
(state.level = state.level + 1),
64
(state.score = state.score + 1),
65
intervalSubject.next(letters.intrvl - speedAdjust))
66
: noop,
67
{ score: state.score, letters: letters.ltrs, level: state.level }
68
),
69
{ score: 0, letters: [], level: 1 }),
70
takeWhile(state => state.letters.length < endThreshold)
71
);
72
73
game$.subscribe(renderGame, noop, renderGameOver);
Copied!

interfaces.ts

1
export interface Letter {
2
letter: String;
3
yPos: number;
4
}
5
export interface Letters {
6
ltrs: Letter[];
7
intrvl: number;
8
}
9
export interface State {
10
score: number;
11
letters: Letter[];
12
level: number;
13
}
Copied!

Operators Used

Last modified 6mo ago