generated from SpencerAung/blue-water
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
122 lines (106 loc) · 2.81 KB
/
index.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
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
const _ = require('Ramda')
/**
* Flip the adjacent zeros of input[cur] to 1, mark input[cur] as empty by inserting '.'
* @example
* // returns [1, '.', 1, 0]
* flip(1, [0,1,0,0])
*/
const flip = _.curry((cur, input) => input.map((v, i) => {
if (i === cur - 1 && v === 0) return 1
else if (i === cur + 1 && v === 0) return 1
else if (i === cur) return '.'
return v
}))
/**
* Returns empty string if input is not a string
* @example
* // returns ''
* safeInput(null)
* @exmaple
* // returns '0101'
* safeInput('0101')
*/
const safeInput = (input) => {
if (!input) {
return ''
}
if (typeof input === 'string') {
return input
}
return ''
}
/**
* Prepare array for _solitaire. Convert string 1s and 0s to number type.
* Keep '.' as is. Remove other characters.
* @example
* // returns [1,0,1,1]
* parseInput('1011')
* @example
* // returns []
* parseInput('something')
* @example
* // returns [1,0,1,'.',1]
* parseInput('101.1')
*/
const parseInput = input => input.split('').reduce((acc, v) => {
if (v === '1') return acc.concat([1])
else if (v === '0') return acc.concat([0])
else if (v === '.') return acc.concat(['.'])
return acc
}, [])
/**
* Prepares output for solitaire.
* if result array is empty, it means there is no solution
* otherwise, pretty print solution steps
* @example
* // returns 'no solution'
* parseOutput([])
* @example
* // returns '1 0 2 3'
* parseOutput([1,0,2,3])
*/
const parseOutput = result => result.length ? result.join(' ') : 'no solution'
/**
* Check if there is any possible solutions by looking into presentance of 1s
*/
const hasSolution = input => input.indexOf(1) !== -1
/**
* Remove 1s and flip adjacents 0s into 1s. Repeats utils every item is removed.
* If there are no more 1s to remove and 0 still remains, it has no solution.
* In this of no solution, returns an empty array
* If there is a solution, returns the index of 1s removed in their removed order.
* @example
* // returns []
* _solitaire([0,0,'.'])
* @example
* // returns [1,0,2,3]
* _solitaire([0,1,0,1])
*/
function _solitaire (input, cur = 0, steps = []) {
if (!hasSolution(input)) {
return []
}
// if every item is 1, we can just return the items' indexes as solution
if (input.every(v => v === 1)) {
return input.map((_, i) => i)
}
if (input[cur] === 0) {
return _solitaire(input, input.indexOf(1), steps)
}
if (input[cur] === 1) {
const flipped = flip(cur, input)
const newSteps = steps.concat([cur])
if (flipped.every(v => v === '.')) {
return newSteps
}
if (!hasSolution(flipped)) {
return []
}
return _solitaire(flipped, flipped.indexOf(1), newSteps)
}
}
/**
* Solitaire, a card flipping game
*/
const solitaire = _.compose(parseOutput, _solitaire, parseInput, safeInput)
module.exports = solitaire