Solve the no connection puzzle: Difference between revisions
Content added Content deleted
m (→{{header|Haskell}}: Tidying, and pruned out one import.) |
(→JS ES6: Updated primitives, tidied.) |
||
Line 1,776: | Line 1,776: | ||
'use strict'; |
'use strict'; |
||
// |
// -------------- NO CONNECTION PUZZLE --------------- |
||
// solvedPuzzle :: () -> [Int] |
|||
const solvedPuzzle = () => { |
|||
// universe :: [[Int]] |
|||
const universe = permutations(enumFromTo(1)(8)); |
|||
// isSolution :: [Int] -> Bool |
|||
const isSolution = ([a, b, c, d, e, f, g, h]) => |
|||
all(x => abs(x) > 1)([ |
|||
a - d, c - d, g - d, e - d, a - c, c - g, |
|||
g - e, e - a, b - e, d - e, h - e, f - e, |
|||
b - d, d - h, h - f, f - b |
|||
]); |
|||
return universe[ |
|||
until(i => isSolution(universe[i]))( |
|||
succ |
|||
)(0) |
|||
]; |
|||
} |
|||
// ---------------------- TEST ----------------------- |
|||
const main = () => { |
|||
const |
|||
firstSolution = solvedPuzzle(), |
|||
[a, b, c, d, e, f, g, h] = firstSolution; |
|||
return unlines( |
|||
zipWith( |
|||
a => n => a + ' = ' + n.toString() |
|||
)(enumFromTo('A')('H'))(firstSolution) |
|||
.concat([ |
|||
[], |
|||
[a, b], |
|||
[c, d, e, f], |
|||
[g, h] |
|||
].map( |
|||
xs => unwords(xs.map(show)) |
|||
.padStart(5, ' ') |
|||
)) |
|||
); |
|||
} |
|||
// ---------------- GENERIC FUNCTIONS ---------------- |
|||
// abs :: Num -> Num |
|||
const abs = |
|||
// Absolute value of a given number - without the sign. |
|||
Math.abs; |
|||
// abs :: Num a => a -> a |
|||
const abs = Math.abs; |
|||
// all :: (a -> Bool) -> [a] -> Bool |
// all :: (a -> Bool) -> [a] -> Bool |
||
const all = |
const all = p => |
||
// True if p(x) holds for every x in xs. |
|||
xs => [...xs].every(p); |
|||
// concatMap :: (a -> [b]) -> [a] -> [b] |
|||
const concatMap = (f, xs) => [].concat.apply([], xs.map(f)); |
|||
// |
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c |
||
const |
const compose = (...fs) => |
||
// A function defined by the right-to-left |
|||
// composition of all the functions in fs. |
|||
fs.reduce( |
|||
(f, g) => x => f(g(x)), |
|||
x => x |
|||
); |
|||
// deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a] |
|||
const deleteBy = (f, x, xs) => |
|||
xs.length > 0 ? ( |
|||
f(x, xs[0]) ? ( |
|||
xs.slice(1) |
|||
) : [xs[0]].concat(deleteBy(f, x, xs.slice(1))) |
|||
) : []; |
|||
// enumFromTo :: Enum a => a -> a -> [a] |
// enumFromTo :: Enum a => a -> a -> [a] |
||
const enumFromTo = |
const enumFromTo = m => n => { |
||
const [ |
const [x, y] = [m, n].map(fromEnum), |
||
b = x + (isNaN(m) ? 0 : m - x); |
|||
return Array.from({ |
|||
length: 1 + (y - x) |
|||
}, (_, i) => toEnum(m)(b + i)); |
|||
return Array.from({ |
|||
length: Math.floor(end - base) + 1 |
|||
}, (_, i) => blnS ? String.fromCodePoint(base + i) : m + i); |
|||
})(); |
|||
}; |
}; |
||
// id :: a -> a |
|||
const id = x => x; |
|||
// |
// fromEnum :: Enum a => a -> Int |
||
const |
const fromEnum = x => |
||
typeof x !== 'string' ? ( |
|||
x.constructor === Object ? ( |
|||
. |
x.value |
||
) : |
) : parseInt(Number(x)) |
||
) : x.codePointAt(0); |
|||
// justifyRight :: Int -> Char -> String -> String |
|||
const justifyRight = n => |
|||
// The string s, preceded by enough padding (with |
|||
// the character c) to reach the string length n. |
|||
c => s => n > s.length ? ( |
|||
s.padStart(n, c) |
|||
) : s; |
|||
// length :: [a] -> Int |
|||
const length = xs => |
|||
// Returns Infinity over objects without finite |
|||
// length. This enables zip and zipWith to choose |
|||
// the shorter argument when one is non-finite, |
|||
// like cycle, repeat etc |
|||
'GeneratorFunction' !== xs.constructor |
|||
.constructor.name ? ( |
|||
xs.length |
|||
) : Infinity; |
|||
// list :: StringOrArrayLike b => b -> [a] |
|||
const list = xs => |
|||
// xs itself, if it is an Array, |
|||
// or an Array derived from xs. |
|||
Array.isArray(xs) ? ( |
|||
xs |
|||
) : Array.from(xs || []); |
|||
// permutations :: [a] -> [[a]] |
// permutations :: [a] -> [[a]] |
||
const permutations = xs => |
const permutations = xs => ( |
||
ys => ys.reduceRight( |
|||
(a, y) => a.flatMap( |
|||
ys => Array.from({ |
|||
length: 1 + ys.length |
|||
}, (_, i) => i) |
|||
.map(n => ys.slice(0, n) |
|||
]; |
|||
.concat(y) |
|||
.concat(ys.slice(n)) |
|||
) |
|||
), [ |
|||
[] |
|||
] |
|||
) |
|||
)(list(xs)); |
|||
// show :: a -> String |
// show :: a -> String |
||
const show = x => |
const show = x => |
||
JSON.stringify(x); |
|||
// unlines :: [String] -> String |
|||
const unlines = xs => xs.join('\n'); |
|||
// |
// succ :: Enum a => a -> a |
||
const |
const succ = x => |
||
1 + x; |
|||
while (!p(v)) v = f(v); |
|||
return v; |
|||
}; |
|||
// unwords :: [String] -> String |
|||
const unwords = xs => xs.join(' '); |
|||
// |
// take :: Int -> [a] -> [a] |
||
// take :: Int -> String -> String |
|||
const take = n => |
|||
// The first n elements of a list, |
|||
// string of characters, or stream. |
|||
xs => 'GeneratorFunction' !== xs |
|||
}; |
|||
.constructor.constructor.name ? ( |
|||
xs.slice(0, n) |
|||
) : [].concat.apply([], Array.from({ |
|||
length: n |
|||
}, () => { |
|||
const x = xs.next(); |
|||
return x.done ? [] : [x.value]; |
|||
})); |
|||
// toEnum :: a -> Int -> a |
|||
// CONNECTION PUZZLE ------------------------------------------------------ |
|||
const toEnum = e => |
|||
// The first argument is a sample of the type |
|||
// allowing the function to make the right mapping |
|||
x => ({ |
|||
'number': Number, |
|||
'string': String.fromCodePoint, |
|||
'boolean': Boolean, |
|||
'object': v => e.min + v |
|||
} [typeof e])(x); |
|||
// universe :: [[Int]] |
|||
const universe = permutations(enumFromTo(1, 8)); |
|||
// |
// unlines :: [String] -> String |
||
const |
const unlines = xs => |
||
// A single string formed by the intercalation |
|||
all(x => abs(x) > 1, [a - d, c - d, g - d, e - d, a - c, c - g, g - e, |
|||
// of a list of strings with the newline character. |
|||
e - a, b - e, d - e, h - e, f - e, b - d, d - h, h - f, f - b |
|||
xs.join('\n'); |
|||
// firstSolution :: [Int] |
|||
const firstSolution = universe[until( |
|||
i => isSolution(universe[i]), |
|||
i => i + 1, |
|||
0 |
|||
)]; |
|||
// until :: (a -> Bool) -> (a -> a) -> a -> a |
|||
// TEST ------------------------------------------------------------------- |
|||
const until = p => |
|||
f => x => { |
|||
let v = x; |
|||
while (!p(v)) v = f(v); |
|||
return v; |
|||
}; |
|||
// [Int] |
|||
const [a, b, c, d, e, f, g, h] = firstSolution; |
|||
// unwords :: [String] -> String |
|||
return unlines( |
|||
const unwords = xs => |
|||
// A space-separated string derived |
|||
(a, n) => a + ' = ' + n.toString(), |
|||
// from a list of words. |
|||
xs.join(' '); |
|||
) |
|||
.concat( |
|||
[ |
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] |
||
const zipWith = f => |
|||
// Use of `take` and `length` here allows |
|||
// zipping with non-finite lists |
|||
// i.e. generators like cycle, repeat, iterate. |
|||
xs => ys => { |
|||
const n = Math.min(length(xs), length(ys)); |
|||
) |
|||
return Infinity > n ? ( |
|||
); |
|||
(([as, bs]) => Array.from({ |
|||
length: n |
|||
}, (_, i) => f(as[i])( |
|||
bs[i] |
|||
)))([xs, ys].map( |
|||
compose(take(n), list) |
|||
)) |
|||
) : zipWithGen(f)(xs)(ys); |
|||
}; |
|||
return main(); |
|||
})();</lang> |
})();</lang> |
||
{{Out}} |
{{Out}} |