Move general functions into AoCLib
This commit is contained in:
parent
3234596629
commit
a41e1ce80e
|
@ -29,4 +29,4 @@ in pkgs.writeText "answers" ''
|
|||
|
||||
Task2:
|
||||
${answer2}
|
||||
''
|
||||
''
|
||||
|
|
|
@ -10,11 +10,13 @@ let
|
|||
];
|
||||
|
||||
inherentValue = x: { A = 1; B = 2; C = 3; }.${x};
|
||||
|
||||
comparativeValue = { they, you }: if they == you then 3 else
|
||||
if (they == "A" && you == "B")
|
||||
|| (they == "B" && you == "C")
|
||||
|| (they == "C" && you == "A") then 6 else
|
||||
0;
|
||||
|
||||
score = match: comparativeValue match + inherentValue match.you;
|
||||
|
||||
equalizeValueType = x: { X = "A"; Y = "B"; Z = "C"; }.${x};
|
||||
|
@ -31,7 +33,9 @@ let
|
|||
Y = { A = 1; B = 2; C = 3; };
|
||||
Z = { A = 2; B = 3; C = 1; };
|
||||
}.${you}.${they};
|
||||
|
||||
comparativeValue2 = x: { X = 0; Y = 3; Z = 6; }.${x};
|
||||
|
||||
score2 = match: comparativeValue2 match.you + inherentValue2 match;
|
||||
|
||||
answer2 = pipe guide [
|
||||
|
@ -46,4 +50,4 @@ in pkgs.writeText "answers" ''
|
|||
|
||||
Task2:
|
||||
${answer2}
|
||||
''
|
||||
''
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, AoCLib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (AoCLib) transformRange chunksOf;
|
||||
|
||||
compartments = pipe (fileContents ./input.txt) [
|
||||
(splitString "\n")
|
||||
];
|
||||
|
@ -11,15 +13,15 @@ let
|
|||
c1 = substring 0 ((stringLength s) / 2) s;
|
||||
c2 = substring ((stringLength s) / 2) (stringLength s) s;
|
||||
};
|
||||
|
||||
charsToSet = s: foldl (set: c: set // { ${c} = true; }) { } (stringToCharacters s);
|
||||
|
||||
getCommonChar = { c1, c2 }:
|
||||
findSingle
|
||||
(c: c2.${c} or false)
|
||||
"Error: no common chars"
|
||||
"Error: multiple chars"
|
||||
(attrNames c1);
|
||||
mapRange = min: max: f: i: if min <= i && i < max then f i else i;
|
||||
transformRange = min: max: offset: i: mapRange min max (x: x + offset) i;
|
||||
|
||||
charValue = c: pipe c [
|
||||
lib.strings.charToInt
|
||||
|
@ -36,14 +38,12 @@ let
|
|||
toString
|
||||
];
|
||||
|
||||
chunksOf = n: l: if length l <= n
|
||||
then [l]
|
||||
else [(take n l)] ++ (chunksOf n (drop n l));
|
||||
toA123 = l: {
|
||||
a1 = elemAt l 0;
|
||||
a2 = elemAt l 1;
|
||||
a3 = elemAt l 2;
|
||||
};
|
||||
|
||||
getCommonChar2 = { a1, a2, a3 }:
|
||||
findSingle
|
||||
(c: a2.${c} or false && a3.${c} or false)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
|
@ -43,4 +43,4 @@ in pkgs.writeText "answers" ''
|
|||
|
||||
Task2:
|
||||
${answer2}
|
||||
''
|
||||
''
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, AoCLib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (AoCLib) allUnique;
|
||||
|
||||
countWithNUntil = n: pred: list: let
|
||||
inner = list': count:
|
||||
if pred (take n list')
|
||||
|
@ -10,13 +12,10 @@ let
|
|||
else inner (tail list') (count + 1);
|
||||
in inner list 0;
|
||||
|
||||
allItemsAreUnique = l: l == []
|
||||
|| !(elem (head l) (tail l)) && allItemsAreUnique (tail l);
|
||||
|
||||
answerN = n: pipe ./input.txt [
|
||||
fileContents
|
||||
stringToCharacters
|
||||
(countWithNUntil n allItemsAreUnique)
|
||||
(countWithNUntil n allUnique)
|
||||
(add n)
|
||||
toString
|
||||
];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, AoCLib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (AoCLib) transpose countWhile multiply;
|
||||
|
||||
calculateTreeVisibilityForLine = line: let
|
||||
updateState = { currentMax ? (-1), trees ? [] }: tree:
|
||||
if tree > currentMax then { currentMax = tree; trees = trees ++ [true]; }
|
||||
|
@ -11,9 +13,6 @@ let
|
|||
backwards = reverseList (foldr (flip updateState) { } line).trees;
|
||||
in zipListsWith or forwards backwards;
|
||||
|
||||
transpose = grid:
|
||||
genList (n: map ((flip elemAt) n) grid) (length grid);
|
||||
|
||||
combineGridsWith = f: grid1: grid2: let
|
||||
height = length grid1;
|
||||
width = length (elemAt grid1 0);
|
||||
|
@ -88,13 +87,13 @@ let
|
|||
visibleDistanceBackwards
|
||||
reverseList
|
||||
];
|
||||
in zipListsWith (x: y: x * y) forwards backwards;
|
||||
in zipListsWith multiply forwards backwards;
|
||||
|
||||
answer2 = pipe trees [
|
||||
(lines: { horizontal = lines; vertical = transpose lines; })
|
||||
(mapAttrs (_: map visibleDistanceHorizontal))
|
||||
({horizontal, vertical}: { inherit horizontal; vertical = transpose vertical; })
|
||||
({horizontal, vertical}: combineGridsWith (x: y: x * y) horizontal vertical)
|
||||
({horizontal, vertical}: combineGridsWith multiply horizontal vertical)
|
||||
(map (foldr max 0))
|
||||
(foldr max 0)
|
||||
toString
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, AoCLib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (AoCLib) scanl abs repeat cmp;
|
||||
|
||||
mapDirectionStepsToHorizontalVertical = { direction, steps }: {
|
||||
horizontal = if direction == "L" then steps else
|
||||
if direction == "R" then -steps else 0;
|
||||
|
@ -10,11 +12,6 @@ let
|
|||
if direction == "U" then -steps else 0;
|
||||
};
|
||||
|
||||
scanl = f: x1: list: let
|
||||
x2 = head list;
|
||||
x1' = f x1 x2;
|
||||
in if list == [] then [] else [x1'] ++ (scanl f x1' (tail list));
|
||||
|
||||
foldHeadPosition =
|
||||
{ x ? 0, y ? 0 }:
|
||||
{ horizontal, vertical }: {
|
||||
|
@ -31,20 +28,14 @@ let
|
|||
(scanl foldHeadPosition {})
|
||||
];
|
||||
|
||||
abs = x: if x < 0 then -x else x;
|
||||
|
||||
ropePieceLength = headPiece: tailPiece: let
|
||||
deltaX = abs (headPiece.x - tailPiece.x);
|
||||
deltaY = abs (headPiece.y - tailPiece.y);
|
||||
in max deltaX deltaY;
|
||||
|
||||
moveRopePiece = headPiece: tailPiece: {
|
||||
x = if headPiece.x > tailPiece.x then tailPiece.x + 1 else
|
||||
if headPiece.x < tailPiece.x then tailPiece.x - 1 else
|
||||
tailPiece.x;
|
||||
y = if headPiece.y > tailPiece.y then tailPiece.y + 1 else
|
||||
if headPiece.y < tailPiece.y then tailPiece.y - 1 else
|
||||
tailPiece.y;
|
||||
x = tailPiece.x + (cmp headPiece.x tailPiece.x);
|
||||
y = tailPiece.y + (cmp headPiece.y tailPiece.y);
|
||||
};
|
||||
|
||||
moveRope = headToOverlap: rope: let
|
||||
|
@ -69,8 +60,6 @@ let
|
|||
tailPositions = [(last newRope)] ++ nextIteration.tailPositions;
|
||||
};
|
||||
|
||||
repeat = item: times: map (const item) (range 1 times);
|
||||
|
||||
f = n: { rope ? (repeat { x = 0; y = 0; } n), tailPositions ? [{ x = 0; y = 0; }] }: newHeadPosition: let
|
||||
newRope = moveRopeUntilHeadOverlapsAndReportLastPositions newHeadPosition rope;
|
||||
in {
|
||||
|
|
|
@ -1,39 +1,9 @@
|
|||
{ pkgs, lib }:
|
||||
{ pkgs, lib, AoCLib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
# See https://github.com/NixOS/nixpkgs/pull/205457
|
||||
toInt = str:
|
||||
let
|
||||
inherit (builtins) match fromJSON;
|
||||
# RegEx: Match any leading whitespace, possibly a '-', one or more digits,
|
||||
# and finally match any trailing whitespace.
|
||||
strippedInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*" str;
|
||||
|
||||
# RegEx: Match a leading '0' then one or more digits.
|
||||
isLeadingZero = match "0[[:digit:]]+" (head strippedInput) == [];
|
||||
|
||||
# Attempt to parse input
|
||||
parsedInput = fromJSON (head strippedInput);
|
||||
|
||||
generalError = "toInt: Could not convert ${escapeNixString str} to int.";
|
||||
|
||||
octalAmbigError = "toInt: Ambiguity in interpretation of ${escapeNixString str}"
|
||||
+ " between octal and zero padded integer.";
|
||||
|
||||
in
|
||||
# Error on presence of non digit characters.
|
||||
if strippedInput == null
|
||||
then throw generalError
|
||||
# Error on presence of leading zero/octal ambiguity.
|
||||
else if isLeadingZero
|
||||
then throw octalAmbigError
|
||||
# Error if parse function fails.
|
||||
else if !isInt parsedInput
|
||||
then throw generalError
|
||||
# Return result.
|
||||
else parsedInput;
|
||||
inherit (AoCLib) toInt repeat takeWithStride chunksOf;
|
||||
|
||||
lineToInstruction = line:
|
||||
if line == "noop"
|
||||
|
@ -48,17 +18,12 @@ let
|
|||
else { cycles = 2; X = nextX; nextX = nextX + instr.val; };
|
||||
in if instructions == [] then [] else [nextSignalState] ++ (foldToSignalState nextSignalState (tail instructions));
|
||||
|
||||
repeat = item: times: map (const item) (range 1 times);
|
||||
|
||||
expandSignalStates = signalStates: let
|
||||
s = head signalStates;
|
||||
in if signalStates == []
|
||||
then []
|
||||
else (repeat s.X s.cycles) ++ (expandSignalStates (tail signalStates));
|
||||
|
||||
takeWithStride = n: l:
|
||||
if l == [] then [] else [(head l)] ++ takeWithStride n (drop n l);
|
||||
|
||||
signalStates = pipe ./input.txt [
|
||||
fileContents
|
||||
(splitString "\n")
|
||||
|
@ -75,14 +40,10 @@ let
|
|||
toString
|
||||
];
|
||||
|
||||
splitAtInterval = n: list:
|
||||
if list == [] then []
|
||||
else [(take n list)] ++ (splitAtInterval n (drop n list));
|
||||
|
||||
f = i: v: if v <= i && i <= v + 2 then "#" else ".";
|
||||
|
||||
answer2 = pipe signalStates [
|
||||
(splitAtInterval 40)
|
||||
(chunksOf 40)
|
||||
(map (imap1 f))
|
||||
(map (concatStringsSep ""))
|
||||
(concatStringsSep "\n")
|
||||
|
|
21
flake.nix
21
flake.nix
|
@ -5,17 +5,18 @@
|
|||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
AoCLib = pkgs.callPackage ./lib.nix { };
|
||||
packages.${system} = {
|
||||
day01 = pkgs.callPackage ./day01 { };
|
||||
day02 = pkgs.callPackage ./day02 { };
|
||||
day03 = pkgs.callPackage ./day03 { };
|
||||
day04 = pkgs.callPackage ./day04 { };
|
||||
day05 = pkgs.callPackage ./day05 { };
|
||||
day06 = pkgs.callPackage ./day06 { };
|
||||
day07 = pkgs.callPackage ./day07 { };
|
||||
day08 = pkgs.callPackage ./day08 { };
|
||||
day09 = pkgs.callPackage ./day09 { };
|
||||
day10 = pkgs.callPackage ./day10 { };
|
||||
day01 = pkgs.callPackage ./day01 { inherit (self) AoCLib; };
|
||||
day02 = pkgs.callPackage ./day02 { inherit (self) AoCLib; };
|
||||
day03 = pkgs.callPackage ./day03 { inherit (self) AoCLib; };
|
||||
day04 = pkgs.callPackage ./day04 { inherit (self) AoCLib; };
|
||||
day05 = pkgs.callPackage ./day05 { inherit (self) AoCLib; };
|
||||
day06 = pkgs.callPackage ./day06 { inherit (self) AoCLib; };
|
||||
day07 = pkgs.callPackage ./day07 { inherit (self) AoCLib; };
|
||||
day08 = pkgs.callPackage ./day08 { inherit (self) AoCLib; };
|
||||
day09 = pkgs.callPackage ./day09 { inherit (self) AoCLib; };
|
||||
day10 = pkgs.callPackage ./day10 { inherit (self) AoCLib; };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
{ pkgs, lib }: with lib; rec {
|
||||
# Transpose a square grid
|
||||
#
|
||||
# [[a]] -> [[a]]
|
||||
transpose = grid:
|
||||
genList (n: map ((flip elemAt) n) grid) (length grid);
|
||||
|
||||
# Checks if there are any duplicate items in the list,
|
||||
# in which case it returns false.
|
||||
#
|
||||
# [a] -> Bool
|
||||
allUnique = list: list == []
|
||||
|| !(elem (head list) (tail list)) && allUnique (tail list);
|
||||
|
||||
# Takes items until either a predicate fails, or the list is empty.
|
||||
#
|
||||
# (a -> Bool) -> [a] -> Int
|
||||
takeWhile = pred: xs:
|
||||
if xs == [] || !(stopPred (head xs))
|
||||
then []
|
||||
else [(head xs)] + (takeWhile pred (tail xs));
|
||||
|
||||
# Counts items until either a predicate fails, or the list is empty
|
||||
#
|
||||
# (a -> Bool) -> [a] -> Int
|
||||
countWhile = pred: xs: length (takeWhile head xs);
|
||||
|
||||
# Like foldl, but keeps all intermediate values
|
||||
#
|
||||
# (b -> a -> b) -> b -> [a] -> [b]
|
||||
scanl = f: x1: list: let
|
||||
x2 = head list;
|
||||
x1' = f x1 x2;
|
||||
in if list == [] then [] else [x1'] ++ (scanl f x1' (tail list));
|
||||
|
||||
# Like scanl, but uses the first element as its start element.
|
||||
#
|
||||
# (a -> a -> a) -> [a] -> [a]
|
||||
scanl1 = f: list:
|
||||
if list == [] then [] else scanl f (head list) (tail list);
|
||||
|
||||
# See https://github.com/NixOS/nixpkgs/pull/205457
|
||||
toInt = str:
|
||||
let
|
||||
inherit (builtins) match fromJSON;
|
||||
# RegEx: Match any leading whitespace, possibly a '-', one or more digits,
|
||||
# and finally match any trailing whitespace.
|
||||
strippedInput = match "[[:space:]]*(-?[[:digit:]]+)[[:space:]]*" str;
|
||||
|
||||
# RegEx: Match a leading '0' then one or more digits.
|
||||
isLeadingZero = match "0[[:digit:]]+" (head strippedInput) == [];
|
||||
|
||||
# Attempt to parse input
|
||||
parsedInput = fromJSON (head strippedInput);
|
||||
|
||||
generalError = "toInt: Could not convert ${escapeNixString str} to int.";
|
||||
|
||||
octalAmbigError = "toInt: Ambiguity in interpretation of ${escapeNixString str}"
|
||||
+ " between octal and zero padded integer.";
|
||||
|
||||
in
|
||||
# Error on presence of non digit characters.
|
||||
if strippedInput == null
|
||||
then throw generalError
|
||||
# Error on presence of leading zero/octal ambiguity.
|
||||
else if isLeadingZero
|
||||
then throw octalAmbigError
|
||||
# Error if parse function fails.
|
||||
else if !isInt parsedInput
|
||||
then throw generalError
|
||||
# Return result.
|
||||
else parsedInput;
|
||||
|
||||
# Trivial function to wrap around the multiplication operation.
|
||||
#
|
||||
# Number -> Number -> Number
|
||||
multiply = x: y: x * y;
|
||||
|
||||
# Trivial function to take the absolute value of a number
|
||||
#
|
||||
# Number -> Number
|
||||
abs = x: if x < 0 then -x else x;
|
||||
|
||||
# Generate a list by repeating an element n times.
|
||||
#
|
||||
# a -> Int -> [a]
|
||||
repeat = item: times: map (const item) (range 1 times);
|
||||
|
||||
# Compare two items, return either 1, 0, or -1 depending on whether
|
||||
# one is bigger than the other.
|
||||
#
|
||||
# Ord a => a -> a -> Int
|
||||
cmp = x: y: if x > y then 1 else if x < y then -1 else 0;
|
||||
|
||||
# Take 1 item, and skip the n-1 next items continuosly until the list is empty.
|
||||
#
|
||||
# Int -> [a] -> [a]
|
||||
takeWithStride = n: l:
|
||||
if l == [] then [] else [(head l)] ++ takeWithStride n (drop n l);
|
||||
|
||||
# Split a list at every n items.
|
||||
#
|
||||
# Int -> [a] -> [[a]]
|
||||
chunksOf = n: list: if list == []
|
||||
then []
|
||||
else [(take n list)] ++ (chunksOf n (drop n list));
|
||||
|
||||
# Map something orderable through a function f if it is between min and max.
|
||||
#
|
||||
# Ord a => a -> a -> (a -> b) -> a -> Either a b
|
||||
mapRange = min: max: f: o: if min <= o && o < max then f o else o;
|
||||
|
||||
# Shift a number n by an offset if it is between min and max.
|
||||
#
|
||||
# Number -> Number -> Number -> Number -> Number
|
||||
transformRange = min: max: offset: n: mapRange min max (x: x + offset) n;
|
||||
}
|
Loading…
Reference in New Issue