162 lines
4.7 KiB
JavaScript
162 lines
4.7 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// main :: IO ()
|
|
const main = () => {
|
|
|
|
// clockWorkTick :: Dict -> (Dict, Char)
|
|
const clockWorkTick = wheelMap => {
|
|
// The new configuration of the wheels, tupled with
|
|
// a digit found by recursive descent from a single
|
|
// click of the first wheel.
|
|
const click = wheels => wheelName => {
|
|
const
|
|
wheel = wheels[wheelName] || ['?'],
|
|
v = wheel[0];
|
|
return bool(click)(Tuple)(isDigit(v) || '?' === v)(
|
|
insertDict(wheelName)(
|
|
leftRotate(wheel)
|
|
)(wheels)
|
|
)(v);
|
|
};
|
|
return click(wheelMap)('A');
|
|
};
|
|
|
|
// leftRotate ::[a] -> [a]
|
|
const leftRotate = xs =>
|
|
// The head of the list appended
|
|
// to the tail of of the list.
|
|
0 < xs.length ? (
|
|
xs.slice(1).concat(xs[0])
|
|
) : [];
|
|
|
|
|
|
// TEST -------------------------------------------
|
|
// State of each wheel-set after 20 clicks,
|
|
// paired with the resulting series of characters.
|
|
|
|
const tuple = uncurry(Tuple);
|
|
const wheelLists = [
|
|
[tuple('A', '123')],
|
|
[tuple('A', '1B2'), tuple('B', '34')],
|
|
[tuple('A', '1DD'), tuple('D', '678')],
|
|
[tuple('A', '1BC'), tuple('B', '34'), tuple('C', '5B')]
|
|
];
|
|
|
|
console.log([
|
|
'Series and state of each wheel-set after 20 clicks:\n',
|
|
unlines(
|
|
map(tuples => showWheels(
|
|
mapAccumL(
|
|
compose(constant, clockWorkTick)
|
|
)(dictFromList(tuples))(replicate(20)(''))
|
|
))(wheelLists)
|
|
),
|
|
'\nInitial state of each wheel-set:\n',
|
|
map(map(compose(
|
|
JSON.stringify,
|
|
dictFromList,
|
|
x => [Array.from(x)]
|
|
)))(wheelLists).join('\n')
|
|
].join('\n'));
|
|
};
|
|
|
|
// DISPLAY FORMATTING ---------------------------------
|
|
|
|
// showWheels :: (Dict, [Char]) -> String
|
|
const showWheels = tpl =>
|
|
JSON.stringify(
|
|
Array.from(secondArrow(concat)(tpl))
|
|
);
|
|
|
|
// GENERIC FUNCTIONS ----------------------------------
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = a => b => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// bool :: a -> a -> Bool -> a
|
|
const bool = f => t => p =>
|
|
p ? t : f;
|
|
|
|
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
|
const compose = (...fs) =>
|
|
x => fs.reduceRight((a, f) => f(a), x);
|
|
|
|
// concat :: [[a]] -> [a]
|
|
// concat :: [String] -> String
|
|
const concat = xs =>
|
|
0 < xs.length ? (() => {
|
|
const unit = 'string' !== typeof xs[0] ? (
|
|
[]
|
|
) : '';
|
|
return unit.concat.apply(unit, xs);
|
|
})() : [];
|
|
|
|
// constant :: a -> b -> a
|
|
const constant = k => _ => k;
|
|
|
|
// dictFromList :: [(k, v)] -> Dict
|
|
const dictFromList = kvs =>
|
|
Object.fromEntries(kvs);
|
|
|
|
// secondArrow :: (a -> b) -> ((c, a) -> (c, b))
|
|
const secondArrow = f => xy =>
|
|
// A function over a simple value lifted
|
|
// to a function over a tuple.
|
|
// f (a, b) -> (a, f(b))
|
|
Tuple(xy[0])(
|
|
f(xy[1])
|
|
);
|
|
|
|
// insertDict :: String -> a -> Dict -> Dict
|
|
const insertDict = k => v => dct =>
|
|
Object.assign({}, dct, {
|
|
[k]: v
|
|
});
|
|
|
|
// isDigit :: Char -> Bool
|
|
const isDigit = c => {
|
|
const n = c.codePointAt(0);
|
|
return 48 <= n && 57 >= n;
|
|
};
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = f => xs =>
|
|
(Array.isArray(xs) ? (
|
|
xs
|
|
) : xs.split('')).map(f);
|
|
|
|
// Map-accumulation is a combination of map and a catamorphism;
|
|
// it applies a function to each element of a list, passing an
|
|
// accumulating parameter from left to right, and returning a final
|
|
// value of this accumulator together with the new list.
|
|
|
|
// mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
|
|
const mapAccumL = f => acc => xs =>
|
|
xs.reduce((a, x) => {
|
|
const pair = f(a[0])(x);
|
|
return Tuple(pair[0])(a[1].concat(pair[1]));
|
|
}, Tuple(acc)([]));
|
|
|
|
// replicate :: Int -> a -> [a]
|
|
const replicate = n => x =>
|
|
Array.from({
|
|
length: n
|
|
}, () => x);
|
|
|
|
// uncurry :: (a -> b -> c) -> ((a, b) -> c)
|
|
const uncurry = f =>
|
|
(x, y) => f(x)(y);
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs => xs.join('\n');
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|