Move general functions into AoCLib

This commit is contained in:
Oystein Kristoffer Tveit 2022-12-12 13:57:34 +01:00
parent 3234596629
commit a41e1ce80e
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
12 changed files with 161 additions and 91 deletions

View File

@ -10,11 +10,13 @@ let
]; ];
inherentValue = x: { A = 1; B = 2; C = 3; }.${x}; inherentValue = x: { A = 1; B = 2; C = 3; }.${x};
comparativeValue = { they, you }: if they == you then 3 else comparativeValue = { they, you }: if they == you then 3 else
if (they == "A" && you == "B") if (they == "A" && you == "B")
|| (they == "B" && you == "C") || (they == "B" && you == "C")
|| (they == "C" && you == "A") then 6 else || (they == "C" && you == "A") then 6 else
0; 0;
score = match: comparativeValue match + inherentValue match.you; score = match: comparativeValue match + inherentValue match.you;
equalizeValueType = x: { X = "A"; Y = "B"; Z = "C"; }.${x}; equalizeValueType = x: { X = "A"; Y = "B"; Z = "C"; }.${x};
@ -31,7 +33,9 @@ let
Y = { A = 1; B = 2; C = 3; }; Y = { A = 1; B = 2; C = 3; };
Z = { A = 2; B = 3; C = 1; }; Z = { A = 2; B = 3; C = 1; };
}.${you}.${they}; }.${you}.${they};
comparativeValue2 = x: { X = 0; Y = 3; Z = 6; }.${x}; comparativeValue2 = x: { X = 0; Y = 3; Z = 6; }.${x};
score2 = match: comparativeValue2 match.you + inherentValue2 match; score2 = match: comparativeValue2 match.you + inherentValue2 match;
answer2 = pipe guide [ answer2 = pipe guide [

View File

@ -1,8 +1,10 @@
{ pkgs, lib }: { pkgs, lib, AoCLib, ... }:
with lib; with lib;
let let
inherit (AoCLib) transformRange chunksOf;
compartments = pipe (fileContents ./input.txt) [ compartments = pipe (fileContents ./input.txt) [
(splitString "\n") (splitString "\n")
]; ];
@ -11,15 +13,15 @@ let
c1 = substring 0 ((stringLength s) / 2) s; c1 = substring 0 ((stringLength s) / 2) s;
c2 = substring ((stringLength s) / 2) (stringLength s) s; c2 = substring ((stringLength s) / 2) (stringLength s) s;
}; };
charsToSet = s: foldl (set: c: set // { ${c} = true; }) { } (stringToCharacters s); charsToSet = s: foldl (set: c: set // { ${c} = true; }) { } (stringToCharacters s);
getCommonChar = { c1, c2 }: getCommonChar = { c1, c2 }:
findSingle findSingle
(c: c2.${c} or false) (c: c2.${c} or false)
"Error: no common chars" "Error: no common chars"
"Error: multiple chars" "Error: multiple chars"
(attrNames c1); (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 [ charValue = c: pipe c [
lib.strings.charToInt lib.strings.charToInt
@ -36,14 +38,12 @@ let
toString toString
]; ];
chunksOf = n: l: if length l <= n
then [l]
else [(take n l)] ++ (chunksOf n (drop n l));
toA123 = l: { toA123 = l: {
a1 = elemAt l 0; a1 = elemAt l 0;
a2 = elemAt l 1; a2 = elemAt l 1;
a3 = elemAt l 2; a3 = elemAt l 2;
}; };
getCommonChar2 = { a1, a2, a3 }: getCommonChar2 = { a1, a2, a3 }:
findSingle findSingle
(c: a2.${c} or false && a3.${c} or false) (c: a2.${c} or false && a3.${c} or false)

View File

@ -1,4 +1,4 @@
{ pkgs, lib }: { pkgs, lib, ... }:
with lib; with lib;

View File

@ -1,4 +1,4 @@
{ pkgs, lib }: { pkgs, lib, ... }:
with lib; with lib;

View File

@ -1,8 +1,10 @@
{ pkgs, lib }: { pkgs, lib, AoCLib, ... }:
with lib; with lib;
let let
inherit (AoCLib) allUnique;
countWithNUntil = n: pred: list: let countWithNUntil = n: pred: list: let
inner = list': count: inner = list': count:
if pred (take n list') if pred (take n list')
@ -10,13 +12,10 @@ let
else inner (tail list') (count + 1); else inner (tail list') (count + 1);
in inner list 0; in inner list 0;
allItemsAreUnique = l: l == []
|| !(elem (head l) (tail l)) && allItemsAreUnique (tail l);
answerN = n: pipe ./input.txt [ answerN = n: pipe ./input.txt [
fileContents fileContents
stringToCharacters stringToCharacters
(countWithNUntil n allItemsAreUnique) (countWithNUntil n allUnique)
(add n) (add n)
toString toString
]; ];

View File

@ -1,4 +1,4 @@
{ pkgs, lib }: { pkgs, lib, ... }:
with lib; with lib;

View File

@ -1,8 +1,10 @@
{ pkgs, lib }: { pkgs, lib, AoCLib, ... }:
with lib; with lib;
let let
inherit (AoCLib) transpose countWhile multiply;
calculateTreeVisibilityForLine = line: let calculateTreeVisibilityForLine = line: let
updateState = { currentMax ? (-1), trees ? [] }: tree: updateState = { currentMax ? (-1), trees ? [] }: tree:
if tree > currentMax then { currentMax = tree; trees = trees ++ [true]; } if tree > currentMax then { currentMax = tree; trees = trees ++ [true]; }
@ -11,9 +13,6 @@ let
backwards = reverseList (foldr (flip updateState) { } line).trees; backwards = reverseList (foldr (flip updateState) { } line).trees;
in zipListsWith or forwards backwards; in zipListsWith or forwards backwards;
transpose = grid:
genList (n: map ((flip elemAt) n) grid) (length grid);
combineGridsWith = f: grid1: grid2: let combineGridsWith = f: grid1: grid2: let
height = length grid1; height = length grid1;
width = length (elemAt grid1 0); width = length (elemAt grid1 0);
@ -88,13 +87,13 @@ let
visibleDistanceBackwards visibleDistanceBackwards
reverseList reverseList
]; ];
in zipListsWith (x: y: x * y) forwards backwards; in zipListsWith multiply forwards backwards;
answer2 = pipe trees [ answer2 = pipe trees [
(lines: { horizontal = lines; vertical = transpose lines; }) (lines: { horizontal = lines; vertical = transpose lines; })
(mapAttrs (_: map visibleDistanceHorizontal)) (mapAttrs (_: map visibleDistanceHorizontal))
({horizontal, vertical}: { inherit horizontal; vertical = transpose vertical; }) ({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)) (map (foldr max 0))
(foldr max 0) (foldr max 0)
toString toString

View File

@ -1,8 +1,10 @@
{ pkgs, lib }: { pkgs, lib, AoCLib, ... }:
with lib; with lib;
let let
inherit (AoCLib) scanl abs repeat cmp;
mapDirectionStepsToHorizontalVertical = { direction, steps }: { mapDirectionStepsToHorizontalVertical = { direction, steps }: {
horizontal = if direction == "L" then steps else horizontal = if direction == "L" then steps else
if direction == "R" then -steps else 0; if direction == "R" then -steps else 0;
@ -10,11 +12,6 @@ let
if direction == "U" then -steps else 0; 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 = foldHeadPosition =
{ x ? 0, y ? 0 }: { x ? 0, y ? 0 }:
{ horizontal, vertical }: { { horizontal, vertical }: {
@ -31,20 +28,14 @@ let
(scanl foldHeadPosition {}) (scanl foldHeadPosition {})
]; ];
abs = x: if x < 0 then -x else x;
ropePieceLength = headPiece: tailPiece: let ropePieceLength = headPiece: tailPiece: let
deltaX = abs (headPiece.x - tailPiece.x); deltaX = abs (headPiece.x - tailPiece.x);
deltaY = abs (headPiece.y - tailPiece.y); deltaY = abs (headPiece.y - tailPiece.y);
in max deltaX deltaY; in max deltaX deltaY;
moveRopePiece = headPiece: tailPiece: { moveRopePiece = headPiece: tailPiece: {
x = if headPiece.x > tailPiece.x then tailPiece.x + 1 else x = tailPiece.x + (cmp headPiece.x tailPiece.x);
if headPiece.x < tailPiece.x then tailPiece.x - 1 else y = tailPiece.y + (cmp headPiece.y tailPiece.y);
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;
}; };
moveRope = headToOverlap: rope: let moveRope = headToOverlap: rope: let
@ -69,8 +60,6 @@ let
tailPositions = [(last newRope)] ++ nextIteration.tailPositions; 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 f = n: { rope ? (repeat { x = 0; y = 0; } n), tailPositions ? [{ x = 0; y = 0; }] }: newHeadPosition: let
newRope = moveRopeUntilHeadOverlapsAndReportLastPositions newHeadPosition rope; newRope = moveRopeUntilHeadOverlapsAndReportLastPositions newHeadPosition rope;
in { in {

View File

@ -1,39 +1,9 @@
{ pkgs, lib }: { pkgs, lib, AoCLib, ... }:
with lib; with lib;
let let
# See https://github.com/NixOS/nixpkgs/pull/205457 inherit (AoCLib) toInt repeat takeWithStride chunksOf;
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;
lineToInstruction = line: lineToInstruction = line:
if line == "noop" if line == "noop"
@ -48,17 +18,12 @@ let
else { cycles = 2; X = nextX; nextX = nextX + instr.val; }; else { cycles = 2; X = nextX; nextX = nextX + instr.val; };
in if instructions == [] then [] else [nextSignalState] ++ (foldToSignalState nextSignalState (tail instructions)); in if instructions == [] then [] else [nextSignalState] ++ (foldToSignalState nextSignalState (tail instructions));
repeat = item: times: map (const item) (range 1 times);
expandSignalStates = signalStates: let expandSignalStates = signalStates: let
s = head signalStates; s = head signalStates;
in if signalStates == [] in if signalStates == []
then [] then []
else (repeat s.X s.cycles) ++ (expandSignalStates (tail signalStates)); 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 [ signalStates = pipe ./input.txt [
fileContents fileContents
(splitString "\n") (splitString "\n")
@ -75,14 +40,10 @@ let
toString 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 "."; f = i: v: if v <= i && i <= v + 2 then "#" else ".";
answer2 = pipe signalStates [ answer2 = pipe signalStates [
(splitAtInterval 40) (chunksOf 40)
(map (imap1 f)) (map (imap1 f))
(map (concatStringsSep "")) (map (concatStringsSep ""))
(concatStringsSep "\n") (concatStringsSep "\n")

View File

@ -5,17 +5,18 @@
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in { in {
AoCLib = pkgs.callPackage ./lib.nix { };
packages.${system} = { packages.${system} = {
day01 = pkgs.callPackage ./day01 { }; day01 = pkgs.callPackage ./day01 { inherit (self) AoCLib; };
day02 = pkgs.callPackage ./day02 { }; day02 = pkgs.callPackage ./day02 { inherit (self) AoCLib; };
day03 = pkgs.callPackage ./day03 { }; day03 = pkgs.callPackage ./day03 { inherit (self) AoCLib; };
day04 = pkgs.callPackage ./day04 { }; day04 = pkgs.callPackage ./day04 { inherit (self) AoCLib; };
day05 = pkgs.callPackage ./day05 { }; day05 = pkgs.callPackage ./day05 { inherit (self) AoCLib; };
day06 = pkgs.callPackage ./day06 { }; day06 = pkgs.callPackage ./day06 { inherit (self) AoCLib; };
day07 = pkgs.callPackage ./day07 { }; day07 = pkgs.callPackage ./day07 { inherit (self) AoCLib; };
day08 = pkgs.callPackage ./day08 { }; day08 = pkgs.callPackage ./day08 { inherit (self) AoCLib; };
day09 = pkgs.callPackage ./day09 { }; day09 = pkgs.callPackage ./day09 { inherit (self) AoCLib; };
day10 = pkgs.callPackage ./day10 { }; day10 = pkgs.callPackage ./day10 { inherit (self) AoCLib; };
}; };
}; };
} }

117
lib.nix Normal file
View File

@ -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;
}