about summary refs log tree commit diff stats
path: root/js/life.combinators.js
blob: be2657e3098001cf19aee007a315f50c2e231ce0 (plain) (blame)
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
identity        I   a → a
constant        K   a → b → a
apply           A   (a → b) → a → b
thrush          T   a → (a → b) → b
duplication     W   (a → a → b) → a → b
flip            C   (a → b → c) → b → a → c
compose         B   (b → c) → (a → b) → a → c
substitution    S   (a → b → c) → (a → b) → a → c
chain           S_³ (a → b → c) → (b → a) → b → c
converge        S2³ (b → c → d) → (a → b) → (a → c) → a → d
psi             P   (b → b → c) → (a → b) → a → a → c
fix-point4      Y   (a → a) → a

ref:
https://www.willtaylor.blog/combinators-and-church-encoding-in-javscript/
*/

const I  = x => x
const K  = x => y => x
const A  = f => x => f (x)
const T  = x => f => f (x)
const W  = f => x => f (x) (x)
const C  = f => y => x => f (x) (y)
const B  = f => g => x => f (g (x))
const S  = f => g => x => f (x) (g (x))
const S_ = f => g => x => f (g (x)) (x)
const S2 = f => g => h => x => f (g (x)) (h (x))
const P  = f => g => x => y => f (g (x)) (g (y))
const Y  = f => (g => g (g)) (g => f (x => g (g) (x)))

// validate combinators
// I(1) // 1
// K(1)(2) // 1
// A(I)(1) // 1
// T(1)(I) // 1
// W(I)(1) // 1
// C(I)(1)(2) // 1
// B(I)(I)(1) // 1
// S(I)(I)(1) // 1
// S_(I)(I)(1) // 1
// S2(I)(I)(I)(1) // 1
// P(I)(I)(1)(2) // 1
// Y(I)(1) // 1

(function() {
    console.log('validating combinators');
    console.assert(I(1) === 1, 'I failed');
    console.assert(K(1)(2) === 1, 'K failed');
    console.assert(A(I)(1) === 1, 'A failed');
    console.assert(T(1)(I) === 1, 'T failed');
    console.assert(W(I)(1) === 1, 'W failed'); // FIXME: Does this really work?
    console.assert(C(I)(1)(2) === 1, 'C failed');
    console.assert(B(I)(I)(1) === 1, 'B failed');
    console.assert(S(I)(I)(1) === 1, 'S failed');
    console.assert(S_(I)(I)(1) === 1, 'S_ failed');
    console.assert(S2(I)(I)(I)(1) === 1, 'S2 failed');
    console.assert(P(I)(I)(1)(2) === 1, 'P failed');
    console.assert(Y(I)(1) === 1, 'Y failed');
}());


// Count the number of live neighbors of a cell
const countLiveNeighbors = B (A (B (A (B (A (K (A (I)))))))) (A (B (A (K (A (I))))))
const isAlive = cell => count => (cell && (count === 2 || count === 3)) || (!cell && count === 3)
const rules = B (A (B (A (K (A (I)))))) (A (B (A (K (A (I))))))
const nextState = B (A (B (A (K (A (I)))))) (A (B (A (K (A (I))))))
const nextBoardState = B (A (B (A (K (A (I)))))) (A (B (A (K (A (I))))))


// validate countLiveNeighbors rules
(function() {
    // FIXME: I think I messed up these test values, maybe?
    // FIXME: I also don't think this'll work given that the combinators will only grok 1 arg that is another combinator...right?
    console.log('validating countLiveNeighbors');
    console.assert(countLiveNeighbors([[true, false, true], [false, true, false], [true, false, true]], 1, 1) === 4, 'countLiveNeighbors 1 failed');
    console.assert(countLiveNeighbors([[true, false, true], [false, true, false], [true, false, true]], 0, 0) === 2, 'countLiveNeighbors 2 failed');
    console.assert(countLiveNeighbors([[true, false, true], [false, true, false], [true, false, true]], 2, 2) === 4, 'countLiveNeighbors 3 failed');
    console.assert(countLiveNeighbors([[true, false, true], [false, true, false], [true, false, true]], 0, 2) === 2, 'countLiveNeighbors 4 failed');
    console.assert(countLiveNeighbors([[true, false, true], [false, true, false], [true, false, true]], 2, 0) === 2, 'countLiveNeighbors 5 failed');
}());

// validate isAlive rules
(function() {
    console.log('validating isAlive');
    console.assert(isAlive(true)(2) === true, 'isAlive 1 failed');
    console.assert(isAlive(true)(3) === true, 'isAlive 2 failed');
    console.assert(isAlive(true)(4) === false, 'isAlive 3 failed');
    console.assert(isAlive(false)(3) === true, 'isAlive 4 failed');
    console.assert(isAlive(false)(2) === false, 'isAlive 5 failed');
}());

// validate rules
(function() {
    console.log('validating rules');
    console.assert(rules(true)(2) === true, 'rules 1 failed');
    // console.assert(rules(true)(3) === true, 'rules 2 failed');
    // console.assert(rules(true)(4) === false, 'rules 3 failed');
    // console.assert(rules(false)(3) === true, 'rules 4 failed');
    // console.assert(rules(false)(2) === false, 'rules 5 failed');
}());

// validate nextState rules
(function() {
    // FIXME: something is up with Bluebird I think...
    console.log('validating nextState');
    console.assert(nextState(true)(2) === true, 'nextState 1 failed');
    console.assert(nextState(true)(3) === true, 'nextState 2 failed');
    console.assert(nextState(true)(4) === false, 'nextState 3 failed');
    console.assert(nextState(false)(3) === true, 'nextState 4 failed');
    console.assert(nextState(false)(2) === false, 'nextState 5 failed');
}());

// validate nextBoardState rules
(function() {
    console.log('validating nextBoardState');
    const board = [
        [false, false, false],
        [true, true, true],
        [false, false, false]
    ];
    const nextBoard = [
        [false, true, false],
        [false, true, false],
        [false, true, false]
    ];
    console.assert(JSON.stringify(nextBoardState(board)) === JSON.stringify(nextBoard), 'nextBoardState 1 failed');
}());