/
main.js
75 lines (62 loc) · 2.16 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
const flyd = require('flyd');
const {stream} = flyd;
const kb = require('flyd-keyboard');
const forwardTo = require('flyd-forwardto');
const timeInterval = require('flyd-timeinterval');
const inLast = require('flyd-inlast');
const {stringify} = require('../utils');
const Type = require('union-type');
const {curry, map, liftN, prop, invoker, forEach, __, pipe, length, equals, compose, filter, partial, always, flip, isEmpty, tap} = require('ramda');
const RESET_AFTER = 2000;
const MAX_INTERVAL = 1000;
const KONAMI = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
const KEYS = { 38: '↑', 40: '↓', 37: '←', 39: '→', 66: 'b', 65: 'a' };
const init = always([]);
const playSound = (function() {
const audio = new Audio('sound.mp3');
return audio.play.bind(audio);
}());
const Action = Type({
// keycode, interval
Keydown: [Number, Number],
Reset: []
});
const keyDowns$ = kb.keyDowns();
const interval$ = timeInterval(keyDowns$);
const keyAndInterval$ = liftN(2, Action.Keydown)(keyDowns$, interval$.map(prop('interval')));
const actions$ = stream();
flyd.on(actions$, keyAndInterval$);
const tryKeyOnSeq = curry((key, interval, seq) => {
const correct = KONAMI[seq.length] === key;
if (seq.length > 0 && interval <= MAX_INTERVAL && correct)
return seq.concat(key);
else if (correct)
return [key];
else
return [];
});
const update = Action.caseOn({
Keydown : tryKeyOnSeq,
Reset : init
});
const model$ = flyd.scan(flip(update), init(), actions$);
const recentKeys$ = inLast(RESET_AFTER, keyAndInterval$);
const isInactive$ = flyd.transduce(filter(isEmpty), recentKeys$);
const isCorrect$ = flyd.transduce(filter(equals(KONAMI)), model$);
isInactive$.map(forwardTo(actions$, Action.Reset));
isCorrect$.map(pipe(
tap(playSound),
forwardTo(actions$, Action.Reset)
));
const container = document.getElementById('code');
const render = (keys) => {
const appendChild = invoker(1, 'appendChild');
const kbd = (key) => {
const elem = document.createElement('kbd')
elem.appendChild(document.createTextNode(KEYS[key]));
return elem;
};
container.innerHTML = '';
forEach(appendChild(__, container), map(kbd, keys));
};
flyd.on(render, model$);