Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
|
|
@ -0,0 +1,411 @@
|
|||
(() => {
|
||||
'use strict';
|
||||
|
||||
// problems :: [[String]]
|
||||
const problems = [
|
||||
[
|
||||
" 000 " //
|
||||
, " 0 00 " //
|
||||
, " 0000000" //
|
||||
, "000 0 0" //
|
||||
, "0 0 000" //
|
||||
, "1000000 " //
|
||||
, " 00 0 " //
|
||||
, " 000 " //
|
||||
],
|
||||
[
|
||||
"-----1-0-----" //
|
||||
, "-----0-0-----" //
|
||||
, "----00000----" //
|
||||
, "-----000-----" //
|
||||
, "--0--0-0--0--" //
|
||||
, "00000---00000" //
|
||||
, "--00-----00--" //
|
||||
, "00000---00000" //
|
||||
, "--0--0-0--0--" //
|
||||
, "-----000-----" //
|
||||
, "----00000----" //
|
||||
, "-----0-0-----" //
|
||||
, "-----0-0-----" //
|
||||
]
|
||||
];
|
||||
|
||||
// GENERIC FUNCTIONS ------------------------------------------------------
|
||||
|
||||
// comparing :: (a -> b) -> (a -> a -> Ordering)
|
||||
const comparing = f =>
|
||||
(x, y) => {
|
||||
const
|
||||
a = f(x),
|
||||
b = f(y);
|
||||
return a < b ? -1 : a > b ? 1 : 0
|
||||
};
|
||||
|
||||
// concat :: [[a]] -> [a] | [String] -> String
|
||||
const concat = xs =>
|
||||
xs.length > 0 ? (() => {
|
||||
const unit = typeof xs[0] === 'string' ? '' : [];
|
||||
return unit.concat.apply(unit, xs);
|
||||
})() : [];
|
||||
|
||||
// charColRow :: Char -> [String] -> Maybe (Int, Int)
|
||||
const charColRow = (c, rows) =>
|
||||
foldr((a, xs, iRow) =>
|
||||
a.nothing ? (() => {
|
||||
const mbiCol = elemIndex(c, xs);
|
||||
return mbiCol.nothing ? mbiCol : {
|
||||
just: [mbiCol.just, iRow],
|
||||
nothing: false
|
||||
};
|
||||
})() : a, {
|
||||
nothing: true
|
||||
}, rows);
|
||||
|
||||
// 2 or more arguments
|
||||
// curry :: Function -> Function
|
||||
const curry = (f, ...args) => {
|
||||
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
|
||||
function () {
|
||||
return go(xs.concat(Array.from(arguments)));
|
||||
};
|
||||
return go([].slice.call(args, 1));
|
||||
};
|
||||
|
||||
// elem :: Eq a => a -> [a] -> Bool
|
||||
const elem = (x, xs) => xs.indexOf(x) !== -1;
|
||||
|
||||
// elemIndex :: Eq a => a -> [a] -> Maybe Int
|
||||
const elemIndex = (x, xs) => {
|
||||
const i = xs.indexOf(x);
|
||||
return {
|
||||
nothing: i === -1,
|
||||
just: i
|
||||
};
|
||||
};
|
||||
|
||||
// enumFromTo :: Int -> Int -> [Int]
|
||||
const enumFromTo = (m, n) =>
|
||||
Array.from({
|
||||
length: Math.floor(n - m) + 1
|
||||
}, (_, i) => m + i);
|
||||
|
||||
// filter :: (a -> Bool) -> [a] -> [a]
|
||||
const filter = (f, xs) => xs.filter(f);
|
||||
|
||||
// findIndex :: (a -> Bool) -> [a] -> Maybe Int
|
||||
const findIndex = (f, xs) => {
|
||||
for (var i = 0, lng = xs.length; i < lng; i++) {
|
||||
if (f(xs[i])) return {
|
||||
nothing: false,
|
||||
just: i
|
||||
};
|
||||
}
|
||||
return {
|
||||
nothing: true
|
||||
};
|
||||
};
|
||||
|
||||
// foldl :: (b -> a -> b) -> b -> [a] -> b
|
||||
const foldl = (f, a, xs) => xs.reduce(f, a);
|
||||
|
||||
// foldr (a -> b -> b) -> b -> [a] -> b
|
||||
const foldr = (f, a, xs) => xs.reduceRight(f, a);
|
||||
|
||||
// groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
|
||||
const groupBy = (f, xs) => {
|
||||
const dct = xs.slice(1)
|
||||
.reduce((a, x) => {
|
||||
const
|
||||
h = a.active.length > 0 ? a.active[0] : undefined,
|
||||
blnGroup = h !== undefined && f(h, x);
|
||||
return {
|
||||
active: blnGroup ? a.active.concat([x]) : [x],
|
||||
sofar: blnGroup ? a.sofar : a.sofar.concat([a.active])
|
||||
};
|
||||
}, {
|
||||
active: xs.length > 0 ? [xs[0]] : [],
|
||||
sofar: []
|
||||
});
|
||||
return dct.sofar.concat(dct.active.length > 0 ? [dct.active] : []);
|
||||
};
|
||||
|
||||
// intercalate :: String -> [a] -> String
|
||||
const intercalate = (s, xs) => xs.join(s);
|
||||
|
||||
// intersectBy::(a - > a - > Bool) - > [a] - > [a] - > [a]
|
||||
const intersectBy = (eq, xs, ys) =>
|
||||
(xs.length > 0 && ys.length > 0) ?
|
||||
xs.filter(x => ys.some(curry(eq)(x))) : [];
|
||||
|
||||
// justifyRight :: Int -> Char -> Text -> Text
|
||||
const justifyRight = (n, cFiller, strText) =>
|
||||
n > strText.length ? (
|
||||
(cFiller.repeat(n) + strText)
|
||||
.slice(-n)
|
||||
) : strText;
|
||||
|
||||
// length :: [a] -> Int
|
||||
const length = xs => xs.length;
|
||||
|
||||
// map :: (a -> b) -> [a] -> [b]
|
||||
const map = (f, xs) => xs.map(f);
|
||||
|
||||
// mappendComparing :: [(a -> b)] -> (a -> a -> Ordering)
|
||||
const mappendComparing = fs => (x, y) =>
|
||||
fs.reduce((ord, f) => {
|
||||
if (ord !== 0) return ord;
|
||||
const
|
||||
a = f(x),
|
||||
b = f(y);
|
||||
return a < b ? -1 : a > b ? 1 : 0
|
||||
}, 0);
|
||||
|
||||
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
|
||||
const maximumBy = (f, xs) =>
|
||||
xs.reduce((a, x) => a === undefined ? x : (
|
||||
f(x, a) > 0 ? x : a
|
||||
), undefined);
|
||||
|
||||
// min :: Ord a => a -> a -> a
|
||||
const min = (a, b) => b < a ? b : a;
|
||||
|
||||
// replicate :: Int -> a -> [a]
|
||||
const replicate = (n, a) => {
|
||||
let v = [a],
|
||||
o = [];
|
||||
if (n < 1) return o;
|
||||
while (n > 1) {
|
||||
if (n & 1) o = o.concat(v);
|
||||
n >>= 1;
|
||||
v = v.concat(v);
|
||||
}
|
||||
return o.concat(v);
|
||||
};
|
||||
|
||||
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
|
||||
const sortBy = (f, xs) => xs.slice()
|
||||
.sort(f);
|
||||
|
||||
// splitOn :: String -> String -> [String]
|
||||
const splitOn = (s, xs) => xs.split(s);
|
||||
|
||||
// take :: Int -> [a] -> [a]
|
||||
const take = (n, xs) => xs.slice(0, n);
|
||||
|
||||
// unlines :: [String] -> String
|
||||
const unlines = xs => xs.join('\n');
|
||||
|
||||
// until :: (a -> Bool) -> (a -> a) -> a -> a
|
||||
const until = (p, f, x) => {
|
||||
let v = x;
|
||||
while (!p(v)) v = f(v);
|
||||
return v;
|
||||
};
|
||||
|
||||
// zip :: [a] -> [b] -> [(a,b)]
|
||||
const zip = (xs, ys) =>
|
||||
xs.slice(0, Math.min(xs.length, ys.length))
|
||||
.map((x, i) => [x, ys[i]]);
|
||||
|
||||
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
|
||||
const zipWith = (f, xs, ys) =>
|
||||
Array.from({
|
||||
length: min(xs.length, ys.length)
|
||||
}, (_, i) => f(xs[i], ys[i]));
|
||||
|
||||
// HOLY KNIGHT's TOUR FUNCTIONS -------------------------------------------
|
||||
|
||||
// kmoves :: (Int, Int) -> [(Int, Int)]
|
||||
const kmoves = ([x, y]) => map(
|
||||
([a, b]) => [a + x, b + y], [
|
||||
[1, 2],
|
||||
[1, -2],
|
||||
[-1, 2],
|
||||
[-1, -2],
|
||||
[2, 1],
|
||||
[2, -1],
|
||||
[-2, 1],
|
||||
[-2, -1]
|
||||
]);
|
||||
|
||||
// rowPosns :: Int -> String -> [(Int, Int)]
|
||||
const rowPosns = (iRow, s) => {
|
||||
return foldl((a, x, i) => (elem(x, ['0', '1']) ? (
|
||||
a.concat([
|
||||
[i, iRow]
|
||||
])
|
||||
) : a), [], splitOn('', s));
|
||||
};
|
||||
|
||||
// hash :: (Int, Int) -> String
|
||||
const hash = ([col, row]) => col.toString() + '.' + row.toString();
|
||||
|
||||
// Start node, and degree-sorted cache of moves from each node
|
||||
// All node references are hash strings (for this cache)
|
||||
|
||||
// problemModel :: [[String]] -> {cache: {nodeKey: [nodeKey], start:String}}
|
||||
const problemModel = boardLines => {
|
||||
const
|
||||
steps = foldl((a, xs, i) =>
|
||||
a.concat(rowPosns(i, xs)), [], boardLines),
|
||||
courseMoves = (xs, [x, y]) => intersectBy(
|
||||
([a, b], [c, d]) => a === c && b === d, kmoves([x, y]), xs
|
||||
),
|
||||
maybeStart = charColRow('1', boardLines);
|
||||
return {
|
||||
start: maybeStart.nothing ? '' : hash(maybeStart.just),
|
||||
boardWidth: boardLines.length > 0 ? boardLines[0].length : 0,
|
||||
stepCount: steps.length,
|
||||
cache: (() => {
|
||||
const moveCache = foldl((a, xy) => (
|
||||
a[hash(xy)] = map(hash, courseMoves(steps, xy)),
|
||||
a
|
||||
), {}, steps),
|
||||
lstMoves = Object.keys(moveCache),
|
||||
dctDegree = foldl((a, k) =>
|
||||
(a[k] = moveCache[k].length,
|
||||
a), {}, lstMoves);
|
||||
|
||||
return foldl((a, k) => (
|
||||
a[k] = sortBy(comparing(x => dctDegree[x]), moveCache[k]),
|
||||
a
|
||||
), {}, lstMoves);
|
||||
})()
|
||||
};
|
||||
};
|
||||
|
||||
// firstSolution :: {nodeKey: [nodeKey]} -> Int ->
|
||||
// nodeKey -> nodeKey -> [nodeKey] ->
|
||||
// -> {path::[nodeKey], pathLen::Int, found::Bool}
|
||||
const firstSolution = (dctMoves, intTarget, strStart, strNodeKey, path) => {
|
||||
const
|
||||
intPath = path.length,
|
||||
moves = dctMoves[strNodeKey];
|
||||
|
||||
if ((intTarget - intPath) < 2 && elem(strStart, moves)) {
|
||||
return {
|
||||
nothing: false,
|
||||
just: [strStart, strNodeKey].concat(path),
|
||||
pathLen: intTarget
|
||||
};
|
||||
}
|
||||
|
||||
const
|
||||
nexts = filter(k => !elem(k, path), moves),
|
||||
intNexts = nexts.length,
|
||||
lstFullPath = [strNodeKey].concat(path);
|
||||
|
||||
// Until we find a full path back to start
|
||||
return until(
|
||||
x => (x.nothing === false || x.i >= intNexts),
|
||||
x => {
|
||||
const
|
||||
idx = x.i,
|
||||
dctSoln = firstSolution(
|
||||
dctMoves, intTarget, strStart, nexts[idx], lstFullPath
|
||||
);
|
||||
return {
|
||||
i: idx + 1,
|
||||
nothing: dctSoln.nothing,
|
||||
just: dctSoln.just,
|
||||
pathLen: dctSoln.pathLen
|
||||
};
|
||||
}, {
|
||||
nothing: true,
|
||||
just: [],
|
||||
i: 0
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// maybeTour :: [String] -> {
|
||||
// nothing::Bool, Just::[nodeHash], i::Int: pathLen::Int }
|
||||
const maybeTour = trackLines => {
|
||||
const
|
||||
dctModel = problemModel(trackLines),
|
||||
strStart = dctModel.start;
|
||||
return strStart !== '' ? firstSolution(
|
||||
dctModel.cache, dctModel.stepCount, strStart, strStart, []
|
||||
) : {
|
||||
nothing: true
|
||||
};
|
||||
};
|
||||
|
||||
// showLine :: Int -> Int -> String -> Maybe (Int, Int) ->
|
||||
// [(Int, Int, String)] -> String
|
||||
const showLine = curry((intCell, strFiller, maybeStart, xs) => {
|
||||
const
|
||||
blnSoln = maybeStart.nothing,
|
||||
[startCol, startRow] = blnSoln ? [0, 0] : maybeStart.just;
|
||||
return foldl((a, [iCol, iRow, sVal], i, xs) => ({
|
||||
col: iCol + 1,
|
||||
txt: a.txt +
|
||||
concat(replicate((iCol - a.col) * intCell, strFiller)) +
|
||||
justifyRight(
|
||||
intCell, strFiller,
|
||||
(blnSoln ? sVal : (
|
||||
iRow === startRow &&
|
||||
iCol === startCol ? '1' : '0')
|
||||
)
|
||||
)
|
||||
}), {
|
||||
col: 0,
|
||||
txt: ''
|
||||
},
|
||||
xs
|
||||
)
|
||||
.txt
|
||||
});
|
||||
|
||||
// solutionString :: [String] -> Int -> String
|
||||
const solutionString = (boardLines, iProblem) => {
|
||||
const
|
||||
dtePre = Date.now(),
|
||||
intCols = boardLines.length > 0 ? boardLines[0].length : 0,
|
||||
soln = maybeTour(boardLines),
|
||||
intMSeconds = Date.now() - dtePre;
|
||||
|
||||
if (soln.nothing) return 'No solution found …';
|
||||
|
||||
const
|
||||
kCol = 0,
|
||||
kRow = 1,
|
||||
kSeq = 2,
|
||||
steps = soln.just,
|
||||
lstTriples = zipWith((h, n) => {
|
||||
const [col, row] = map(
|
||||
x => parseInt(x, 10), splitOn('.', h)
|
||||
);
|
||||
return [col, row, n.toString()];
|
||||
},
|
||||
steps,
|
||||
enumFromTo(1, soln.pathLen)),
|
||||
cellWidth = length(maximumBy(
|
||||
comparing(x => length(x[kSeq])), lstTriples
|
||||
)[kSeq]) + 1,
|
||||
lstGroups = groupBy(
|
||||
(a, b) => a[kRow] === b[kRow],
|
||||
sortBy(
|
||||
mappendComparing([x => x[kRow], x => x[kCol]]),
|
||||
lstTriples
|
||||
)),
|
||||
startXY = take(2, lstTriples[0]),
|
||||
strMap = 'PROBLEM ' + (parseInt(iProblem, 10) + 1) + '.\n\n' +
|
||||
unlines(map(showLine(cellWidth, ' ', {
|
||||
nothing: false,
|
||||
just: startXY
|
||||
}), lstGroups)),
|
||||
strSoln = 'First solution found in c. ' +
|
||||
intMSeconds + ' milliseconds:\n\n' +
|
||||
unlines(map(showLine(cellWidth, ' ', {
|
||||
nothing: true,
|
||||
just: startXY
|
||||
}), lstGroups)) + '\n\n';
|
||||
|
||||
console.log(strSoln);
|
||||
return strMap + '\n\n' + strSoln;
|
||||
};
|
||||
|
||||
// TEST -------------------------------------------------------------------
|
||||
return unlines(map(solutionString, problems));
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue