-
Notifications
You must be signed in to change notification settings - Fork 0
/
sokoban.js
92 lines (76 loc) · 2.48 KB
/
sokoban.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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import _ from "./libs/atomic_/core.js";
import * as l from "./levels.js";
export {tile} from "./levels.js";
export const init = _.get(l.levels, _);
export function contents(fixtures){
return _.flatten(_.mapIndexed(function(y, row){
return _.mapIndexed(function(x, what){
const coords = [x, y];
return {what, coords};
}, row);
}, fixtures));
}
const ordered = _.sort(_.asc(_.first), _.asc(_.second), _);
export const dests = _.pipe(_.get(_, "fixtures"), contents, _.filter(({what}) => l.isDest(what), _), _.mapa(_.get(_, "coords"), _), ordered);
export function solved({crates, dests}){
return _.eq(ordered(crates), dests);
}
function solvable({crates, dests}){
return _.count(crates) === _.count(dests);
}
export function verify(state){
if (!solvable(state)) {
throw new Error("This map has a destinations to crates mismatch.");
}
return state;
}
export function add(stuff){
return _.reducekv(function(state, key, f){
return _.assoc(state, key, f(state));
}, _, stuff);
}
export function locate(fixtures, coords){
const [col, row] = coords;
return _.getIn(fixtures, [row, col]);
}
function at(f){
return function(offset, pos, {fixtures, crates}){
const coords = f(pos, offset);
const what = _.detect(_.eq(_, coords), crates) ? "crate" : locate(fixtures, coords);
return {what, coords};
}
}
const upward = at(function([x, y], offset){
return [x, y - offset]
});
const downward = at(function([x, y], offset){
return [x, y + offset]
});
const leftward = at(function([x, y], offset){
return [x - offset, y]
});
const rightward = at(function([x, y], offset){
return [x + offset, y]
});
const clear = _.pipe(_.get(_, "what"), l.empty);
function move(beyond) {
return function(state){
const {worker} = state;
const beyond1 = beyond(1, worker, state);
const beyond2 = beyond(2, worker, state);
const may = (clear(beyond1) || clear(beyond2)) && !l.blocked(beyond1.what);
const pushed = may && beyond1.what === "crate";
return _.chain(state,
may ? _.assoc(_, "worker", beyond1.coords) : _.identity,
pushed ? _.update(_, "crates", function(crates){
const idx = _.detectIndex(function(coords){
return _.eq(coords, beyond1.coords);
}, crates);
return idx !== null ? _.assoc(crates, idx, beyond2.coords) : crates;
}) : _.identity);
}
}
export const up = move(upward);
export const down = move(downward);
export const left = move(leftward);
export const right = move(rightward);