Diff
Cargo.toml | 4 ++--
2022/03.clj | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
2022/3.clj | 50 --------------------------------------------------
2023/01.hs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/02.hs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/03.hs | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/04.hs | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/05.rs | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/06.hs | 46 ++++++++++++++++++++++++++++++++++++++++++++++
2023/07.hs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/08.hs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/09.hs | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2023/1.hs | 68 --------------------------------------------------------------------
2023/2.hs | 84 --------------------------------------------------------------------------------
2023/3.hs | 87 --------------------------------------------------------------------------------
2023/4.hs | 81 --------------------------------------------------------------------------------
2023/5.rs | 246 --------------------------------------------------------------------------------
2023/6.hs | 46 ----------------------------------------------
2023/7.hs | 108 --------------------------------------------------------------------------------
2023/8.hs | 83 --------------------------------------------------------------------------------
2023/9.hs | 69 ---------------------------------------------------------------------
2022/01/default.nix | 17 +++++++++++++++++
2022/01/main.f90 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2022/02/default.nix | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
2022/04/4.jsonnet | 18 ++++++++++++++++++
2022/04/default.nix | 14 ++++++++++++++
2022/05/.terraform.lock.hcl | 19 +++++++++++++++++++
2022/05/default.nix | 19 +++++++++++++++++++
2022/05/main.tf.jinja2 | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2022/06/.gitignore | 2 ++
2022/06/apache2.conf | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2022/06/read.sh | 9 +++++++++
2022/1/default.nix | 17 -----------------
2022/1/main.f90 | 90 --------------------------------------------------------------------------------
2022/2/default.nix | 49 -------------------------------------------------
2022/4/4.jsonnet | 18 ------------------
2022/4/default.nix | 14 --------------
2022/5/.terraform.lock.hcl | 19 -------------------
2022/5/default.nix | 19 -------------------
2022/5/main.tf.jinja2 | 54 ------------------------------------------------------
2022/6/.gitignore | 2 --
2022/6/apache2.conf | 156 --------------------------------------------------------------------------------
2022/6/read.sh | 9 ---------
2022/05/executor/main.tf | 24 ++++++++++++++++++++++++
2022/5/executor/main.tf | 24 ------------------------
45 files changed, 1395 insertions(+), 1395 deletions(-)
@@ -23,8 +23,8 @@
path = "2023/12.rs"
[[bin]]
name = "aoc2023-5"
path = "2023/5.rs"
name = "aoc2023-05"
path = "2023/05.rs"
[profile.release]
overflow-checks = true
@@ -1,0 +1,50 @@
(require '[clojure.java.io :as io])
(require '[clojure.set :as set])
(defn char-to-number [char]
(cond
(Character/isLowerCase char) (- (int char) 96)
(Character/isUpperCase char) (- (int char) 38)))
(defn split-half [s]
(let [idx (quot (count s) 2)
[left right] (split-at idx s)]
[(set (apply str left)) (set (apply str right))]))
(defn part1-process-line [line]
(->> (split-half line)
(apply set/intersection)))
(defn part1 [lines]
(->> lines
(map part1-process-line)
(mapcat identity)
(map char-to-number)
(reduce +)))
(defn part2-process-lines [group]
(let [[g1 g2 g3] group]
(->> (set/intersection (set g1) (set g2) (set g3))
(map char-to-number)
(reduce +))))
(defn part2 [lines]
(->> (partition 3 lines)
(map part2-process-lines)
(reduce +)))
(def lines (with-open [rdr (io/reader *in*)] (vec (->> (line-seq rdr)))))
(println (part1 lines))
(println (part2 lines))
@@ -1,50 +1,0 @@
(require '[clojure.java.io :as io])
(require '[clojure.set :as set])
(defn char-to-number [char]
(cond
(Character/isLowerCase char) (- (int char) 96)
(Character/isUpperCase char) (- (int char) 38)))
(defn split-half [s]
(let [idx (quot (count s) 2)
[left right] (split-at idx s)]
[(set (apply str left)) (set (apply str right))]))
(defn part1-process-line [line]
(->> (split-half line)
(apply set/intersection)))
(defn part1 [lines]
(->> lines
(map part1-process-line)
(mapcat identity)
(map char-to-number)
(reduce +)))
(defn part2-process-lines [group]
(let [[g1 g2 g3] group]
(->> (set/intersection (set g1) (set g2) (set g3))
(map char-to-number)
(reduce +))))
(defn part2 [lines]
(->> (partition 3 lines)
(map part2-process-lines)
(reduce +)))
(def lines (with-open [rdr (io/reader *in*)] (vec (->> (line-seq rdr)))))
(println (part1 lines))
(println (part2 lines))
@@ -1,0 +1,68 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.List (find, isInfixOf)
import Data.Maybe (catMaybes)
main = print =<< run 0
run :: Int -> IO Int
run acc = do
line <- getLine
if null line
then return acc
else do
let x = concatFirstLastDigitsInString line
run $ acc + x
concatFirstLastDigitsInString :: String -> Int
concatFirstLastDigitsInString s =
case catMaybes [findDigitFromLeft "" s, findDigitFromRight "" s] of
[x, y] -> x * 10 + y
[x] -> x * 11
_ -> 0
findDigitFromLeft :: String -> String -> Maybe Int
findDigitFromLeft acc "" = findDigit acc
findDigitFromLeft acc (x : xs) = case findDigit acc of
Just v -> Just v
Nothing -> findDigitFromLeft (acc ++ [x]) xs
findDigitFromRight :: String -> String -> Maybe Int
findDigitFromRight acc "" = findDigit acc
findDigitFromRight acc xs = case findDigit acc of
Just v -> Just v
Nothing -> findDigitFromRight (last xs : acc) (init xs)
findDigit :: String -> Maybe Int
findDigit s = case find (`isInfixOf` s) digitAsText of
Just textual -> lookup textual digitMap
Nothing -> Nothing
where
digitMap =
[ ("eight", 8),
("seven", 7),
("three", 3),
("nine", 9),
("four", 4),
("five", 5),
("two", 2),
("one", 1),
("six", 6),
("1", 1),
("2", 2),
("3", 3),
("4", 4),
("5", 5),
("6", 6),
("7", 7),
("8", 8),
("9", 9)
]
digitAsText = map fst digitMap
@@ -1,0 +1,84 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Control.Applicative ((<*))
import Data.Map (Map, elems, fromListWith)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
input <- getContents
case parseString input of
Left err -> print err
Right games -> do
part1PrintValidGamesMaxCubes games
part2PrintMinimumRequiredCubes games
part1PrintValidGamesMaxCubes :: [Game] -> IO ()
part1PrintValidGamesMaxCubes games = do
print $ sum $ map gameId (filter checkGameIsValid games)
part2PrintMinimumRequiredCubes :: [Game] -> IO ()
part2PrintMinimumRequiredCubes games = do
print $ sum $ map (product . elems . getMinimumCubesRequiredForGame) games
getMinimumCubesRequiredForGame :: Game -> Map String Int
getMinimumCubesRequiredForGame game = fromListWith max $ concat (rounds game)
checkGameIsValid :: Game -> Bool
checkGameIsValid game = all (all isCubeAmountAllowed) (rounds game)
isCubeAmountAllowed :: (String, Int) -> Bool
isCubeAmountAllowed (colour, amount) = amount <= cubesAllowed colour
cubesAllowed "red" = 12
cubesAllowed "green" = 13
cubesAllowed "blue" = 14
cubesAllowed _ = 0
data Game = Game
{ gameId :: Int,
rounds :: [[(String, Int)]]
}
deriving (Show)
parseString :: String -> Either ParseError [Game]
parseString = parse fullParser ""
fullParser :: Parser [Game]
fullParser = gameParser `sepBy` char '\n'
gameParser :: Parser Game
gameParser = do
_ <- string "Game "
gameId <- many1 digit <* char ':' <* spaces
rounds <- roundParser `sepBy` (char ';' <* spaces)
return
Game
{ gameId = read gameId,
rounds
}
roundParser :: Parser [(String, Int)]
roundParser = cubeNumberParser `sepBy` (char ',' <* spaces)
cubeNumberParser :: Parser (String, Int)
cubeNumberParser = do
amount <- many1 digit <* spaces
colour <- many1 letter
return (colour, read amount)
@@ -1,0 +1,87 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.Char (isNumber)
import Data.List (findIndices)
main = do
input <- getContents
let (schematics, symbols) = takeSymbols input
print $ sum $ part1FindNumbersAdjacentToSchematic schematics symbols
print $ sum $ map product $ part2FindGearPartNumbers schematics symbols
part1FindNumbersAdjacentToSchematic :: [Schematic] -> [Symbol] -> [Int]
part1FindNumbersAdjacentToSchematic schematics symbols = map partNumber $ filter (isSchematicAdjacentToAnySymbol symbols) schematics
part2FindGearPartNumbers :: [Schematic] -> [Symbol] -> [[Int]]
part2FindGearPartNumbers schematics symbols = map (map partNumber) $ filter (\inner -> length inner == 2) $ map (findSchematicsAdjacentToSymbol schematics) symbols
findSchematicsAdjacentToSymbol :: [Schematic] -> Symbol -> [Schematic]
findSchematicsAdjacentToSymbol schematics symbol = filter (`isSchematicAdjacent` symbol) schematics
isSchematicAdjacentToAnySymbol :: [Symbol] -> Schematic -> Bool
isSchematicAdjacentToAnySymbol symbol schematic = any (isSchematicAdjacent schematic) symbol
isSchematicAdjacent :: Schematic -> Symbol -> Bool
isSchematicAdjacent sc sy = isAdjacent (symbolCoords sy) (schematicCoords sc)
isAdjacent :: (Int, Int) -> (Int, Int, Int) -> Bool
isAdjacent (px, py) (rx, ry, width) =
let leftX = rx
rightX = rx + width - 1
upperY = ry + 1
lowerY = ry - 1
in (px >= leftX - 1 && px <= rightX + 1 && py == ry)
|| (py == upperY || py == lowerY) && (px >= leftX && px <= rightX)
|| (px == leftX - 1 || px == rightX + 1) && (py == upperY || py == lowerY)
data Schematic = Schematic
{ partNumber :: Int,
schematicCoords :: (Int, Int, Int)
}
deriving (Show)
data Symbol = Symbol
{ symbolCoords :: (Int, Int),
symbolType :: Char
}
deriving (Show)
takeSymbols :: String -> ([Schematic], [Symbol])
takeSymbols input = takeSymbol input Nothing (0, 0) [] []
takeSymbol :: String -> Maybe Schematic -> (Int, Int) -> [Schematic] -> [Symbol] -> ([Schematic], [Symbol])
takeSymbol "" Nothing _ schematicAcc symbolAcc = (schematicAcc, symbolAcc)
takeSymbol "" (Just inProgressSchematic) _ schematicAcc symbolAcc = (schematicAcc ++ [inProgressSchematic], symbolAcc)
takeSymbol (x : xs) inProgressSchematic (posX, posY) schematicAcc symbolAcc =
case x of
_ | isNumber x -> takeSymbol xs (Just $ appendToSchematic inProgressSchematic (posX, posY) (read [x])) (posX + 1, posY) schematicAcc symbolAcc
'.' -> takeSymbol xs Nothing (posX + 1, posY) (maybeAppend schematicAcc inProgressSchematic) symbolAcc
'\n' -> takeSymbol xs Nothing (0, posY + 1) (maybeAppend schematicAcc inProgressSchematic) symbolAcc
_ -> takeSymbol xs Nothing (posX + 1, posY) (maybeAppend schematicAcc inProgressSchematic) (symbolAcc ++ [buildSymbol posX posY x])
appendToSchematic :: Maybe Schematic -> (Int, Int) -> Int -> Schematic
appendToSchematic (Just schematic) _ c =
let (x, y, n) = schematicCoords schematic
currPartNumber = partNumber schematic
in schematic {partNumber = currPartNumber * 10 + c, schematicCoords = (x, y, n + 1)}
appendToSchematic Nothing (x, y) c = Schematic {partNumber = c, schematicCoords = (x, y, 1)}
maybeAppend :: [Schematic] -> Maybe Schematic -> [Schematic]
maybeAppend out (Just new) = out ++ [new]
maybeAppend out Nothing = out
buildSymbol :: Int -> Int -> Char -> Symbol
buildSymbol x y symbolType = Symbol {symbolCoords = (x, y), symbolType}
@@ -1,0 +1,81 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.List (intersect)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
cards <- readAndParseStdin []
print $ part1 cards
print $ part2 cards
part1 :: [Card] -> Int
part1 cards = sum $ map (score . length . getWinningNumbers) cards
where
score 0 = 0
score n = 2 ^ (n - 1)
part2 :: [Card] -> Int
part2 cards = sum $ calculateCardCopies $ map (length . getWinningNumbers) cards
calculateCardCopies :: [Int] -> [Int]
calculateCardCopies xs = foldl replicateSingleCardWinnings (replicate (length xs) 1) (zip [0 ..] xs)
replicateSingleCardWinnings :: [Int] -> (Int, Int) -> [Int]
replicateSingleCardWinnings cardReplicas (idx, winningNumbers) = copyCards cardReplicas (idx + 1) idx winningNumbers
copyCards :: [Int] -> Int -> Int -> Int -> [Int]
copyCards cardReplicas currIdx winningCardIdx winningNumbers
| currIdx <= length cardReplicas && winningNumbers > 0 =
let incrementedList = incrementAtIndex cardReplicas currIdx (cardReplicas !! winningCardIdx)
in copyCards incrementedList (currIdx + 1) winningCardIdx (winningNumbers - 1)
| otherwise = cardReplicas
incrementAtIndex :: [Int] -> Int -> Int -> [Int]
incrementAtIndex xs idx amount = take idx xs ++ [(xs !! idx) + amount] ++ drop (idx + 1) xs
getWinningNumbers :: Card -> [Int]
getWinningNumbers card = myNumbers card `intersect` winningNumbers card
data Card = Card
{ winningNumbers :: [Int],
myNumbers :: [Int]
}
deriving (Show)
readAndParseStdin :: [Card] -> IO [Card]
readAndParseStdin acc = do
line <- getLine
if null line
then return acc
else case parse cardParser "" line of
Left parseError -> error $ show parseError
Right card -> readAndParseStdin $ acc ++ [card]
cardParser :: Parser Card
cardParser = do
_ <- string "Card" <* spaces <* many1 digit <* char ':' <* spaces
winningNumbers <- numberParser
_ <- char '|' <* spaces
myNumbers <- numberParser
return Card {winningNumbers, myNumbers}
numberParser :: Parser [Int]
numberParser = map read <$> many1 digit `endBy` spaces
@@ -1,0 +1,246 @@
use std::{collections::HashMap, io::Read, ops::Range, str::FromStr, time::Instant};
use itertools::Itertools;
use nom::IResult;
use rangemap::RangeMap;
const TRANSLATION_PATH: &[MapKind] = &[
MapKind::Soil,
MapKind::Fertilizer,
MapKind::Water,
MapKind::Light,
MapKind::Temperature,
MapKind::Humidity,
MapKind::Location,
];
fn main() {
let mut input = Vec::new();
std::io::stdin().lock().read_to_end(&mut input).unwrap();
let input = std::str::from_utf8(&input).unwrap();
let (rest, input) = parse_input(input).unwrap();
assert!(rest.is_empty());
let i = Instant::now();
let answer = part1(&input);
eprintln!("part 1: {answer} ({:?})", i.elapsed());
let i = Instant::now();
let answer = part2(&input);
eprintln!("part 2: {answer} ({:?})", i.elapsed());
}
fn part1(input: &Input) -> u64 {
let mut lowest_location = u64::MAX;
for seed in &input.seeds {
let mut source = *seed;
let mut from = MapKind::Seed;
for to in TRANSLATION_PATH {
let Some(translation) = input.maps.get(&Translation { from, to: *to }) else {
panic!("invalid path {from:?} to {to:?}");
};
if let Some((source_range, destination_base)) = translation.get_key_value(&source) {
source = destination_base + (source - source_range.start);
}
from = *to;
}
assert_eq!(from, MapKind::Location);
lowest_location = lowest_location.min(source);
}
lowest_location
}
fn part2(input: &Input) -> u64 {
let seed_ranges: Vec<_> = input
.seeds
.iter()
.tuples()
.map(|(start, len)| (*start)..(*start) + len)
.collect();
let mut lowest_bound_seen = u64::MAX;
for seed_range in seed_ranges {
let lowest_for_seed = traverse_path(input, TRANSLATION_PATH, MapKind::Seed, seed_range);
lowest_bound_seen = lowest_bound_seen.min(lowest_for_seed);
}
lowest_bound_seen
}
fn traverse_path(input: &Input, path: &[MapKind], from: MapKind, source_range: Range<u64>) -> u64 {
let mut lowest_bound_seen = u64::MAX;
let Some((next_path, rest)) = path.split_first() else {
return source_range.start;
};
let Some(translation) = input.maps.get(&Translation {
from,
to: *next_path,
}) else {
panic!("invalid path {from:?} to {next_path:?}");
};
for (new_source_range, destination_base) in translation.overlapping(&source_range) {
let start = source_range.start.max(new_source_range.start);
let end = source_range.end.min(new_source_range.end);
let offset = start.saturating_sub(new_source_range.start);
let length = end.saturating_sub(start);
let destination_range = (*destination_base + offset)..(*destination_base + offset + length);
let lowest_in_tree = traverse_path(input, rest, *next_path, destination_range);
lowest_bound_seen = lowest_bound_seen.min(lowest_in_tree);
}
for uncovered_range in split_range(
source_range.clone(),
translation
.overlapping(&source_range)
.map(|v| v.0.clone())
.collect(),
) {
let current_range = traverse_path(input, rest, *next_path, uncovered_range);
lowest_bound_seen = lowest_bound_seen.min(current_range);
}
lowest_bound_seen
}
fn split_range(main_range: Range<u64>, mut ranges: Vec<Range<u64>>) -> Vec<Range<u64>> {
let mut non_intersecting_ranges = Vec::new();
let mut current_start = main_range.start;
ranges.sort_by_key(|r| r.start);
for range in ranges {
if range.start > current_start {
non_intersecting_ranges.push(current_start..range.start);
}
if range.end > current_start {
current_start = range.end;
}
}
if current_start < main_range.end {
non_intersecting_ranges.push(current_start..main_range.end);
}
non_intersecting_ranges
}
#[derive(strum::EnumString, Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum MapKind {
Seed,
Soil,
Fertilizer,
Water,
Light,
Temperature,
Humidity,
Location,
}
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)]
struct Translation {
from: MapKind,
to: MapKind,
}
impl From<(MapKind, MapKind)> for Translation {
fn from((from, to): (MapKind, MapKind)) -> Self {
Self { from, to }
}
}
#[derive(Debug)]
struct Input {
seeds: Vec<u64>,
maps: HashMap<Translation, RangeMap<u64, u64>>,
}
fn parse_input(rest: &str) -> IResult<&str, Input> {
use nom::{
bytes::complete::tag, character::complete::digit1, combinator::map_res,
multi::separated_list1, sequence::delimited,
};
let (rest, seeds) = delimited(
tag("seeds: "),
separated_list1(tag(" "), map_res(digit1, u64::from_str)),
tag("\n\n"),
)(rest)?;
let (rest, maps) = separated_list1(tag("\n"), parse_single_map)(rest)?;
Ok((
rest,
Input {
seeds,
maps: maps.into_iter().collect(),
},
))
}
fn parse_single_map(rest: &str) -> IResult<&str, (Translation, RangeMap<u64, u64>)> {
use nom::multi::many1;
let (rest, header) = parse_header(rest)?;
let (rest, lines) = many1(parse_map_line)(rest)?;
Ok((rest, (header, lines.into_iter().collect())))
}
fn parse_map_line(rest: &str) -> IResult<&str, (Range<u64>, u64)> {
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::digit1,
combinator::{eof, map_res},
sequence::terminated,
};
let (rest, destination) = terminated(map_res(digit1, u64::from_str), tag(" "))(rest)?;
let (rest, source) = terminated(map_res(digit1, u64::from_str), tag(" "))(rest)?;
let (rest, size) = terminated(map_res(digit1, u64::from_str), alt((tag("\n"), eof)))(rest)?;
Ok((rest, (source..source + size, destination)))
}
fn parse_header(rest: &str) -> IResult<&str, Translation> {
use nom::{
bytes::complete::{tag, take_until},
combinator::{map, map_res},
sequence::{separated_pair, terminated},
};
map(
terminated(
separated_pair(
map_res(take_until("-"), MapKind::from_str),
tag("-to-"),
map_res(take_until(" "), MapKind::from_str),
),
tag(" map:\n"),
),
Translation::from,
)(rest)
}
@@ -1,0 +1,46 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Control.Applicative ((<*))
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
game <- readAndParseStdin gameParser
print $ part1 game
print $ part2 game
part1 :: [(Int, Int)] -> Int
part1 = product . map (length . getWinningTimes)
where
getWinningTimes (time, winCond) = filter (> winCond) $ map (`distance` time) [1 .. time - 1]
part2 :: [(Int, Int)] -> Int
part2 game = part1 [foldl concatenate (0, 0) game]
where
concatenate (tAcc, dAcc) (t, d) = (tAcc * scale t + t, dAcc * scale d + d)
scale :: Int -> Int
scale n
| n < 10 = 10
| otherwise = 10 * scale (n `div` 10)
distance :: Int -> Int -> Int
distance v t = v * (t - v)
gameParser :: Parser [(Int, Int)]
gameParser = do
time <- map read <$> (string "Time:" *> spaces *> many1 digit `endBy` spaces)
distance <- map read <$> (string "Distance:" *> spaces *> many1 digit `endBy` spaces)
return $ zip time distance
@@ -1,0 +1,108 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Data.List
import Data.Ord
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main :: IO ()
main = do
games <- readAndParseStdin parseAllGames
print $ part1 games
print $ part2 games
part1 :: [Game] -> Int
part1 games = calcScore $ sortBy gameComparator games
where
gameComparator = comparing (Down . getHandStrength . groupCards . cards) <> comparing cards
part2 :: [Game] -> Int
part2 games = calcScore $ sortBy gameComparator $ mapAllJacksToJokers games
where
gameComparator = comparing (Down . getHandStrength . transmorphJokers . cards) <> comparing cards
mapAllJacksToJokers :: [Game] -> [Game]
mapAllJacksToJokers = map (\game -> game {cards = map mapJackToJoker $ cards game})
where
mapJackToJoker Jack = Joker
mapJackToJoker a = a
transmorphJokers :: [Rank] -> [Int]
transmorphJokers [Joker, Joker, Joker, Joker, Joker] = groupCards [Joker, Joker, Joker, Joker, Joker]
transmorphJokers cards = (head grouped + jokerCount) : tail grouped
where
cardsWithoutJokers = filter (/= Joker) cards
jokerCount = length cards - length cardsWithoutJokers
grouped = groupCards cardsWithoutJokers
calcScore :: [Game] -> Int
calcScore game = sum $ zipWith (curry formula) [1 ..] game
where
formula (idx, game) = baseScore game * idx
getHandStrength :: [Int] -> HandStrength
getHandStrength sortedCardCount = case sortedCardCount of
[5] -> FiveOfAKind
[4, 1] -> FourOfAKind
[3, 2] -> FullHouse
(3 : _) -> ThreeOfAKind
(2 : 2 : _) -> TwoPair
(2 : _) -> OnePair
_ -> HighCard
groupCards :: [Rank] -> [Int]
groupCards = sortOn Down . map length . group . sort
parseAllGames :: Parser [Game]
parseAllGames = parseGame `endBy` char '\n'
parseGame :: Parser Game
parseGame = do
cards <- many1 parseRank <* spaces
baseScore <- read <$> many1 digit
return Game {cards, baseScore}
parseRank :: Parser Rank
parseRank = do
c <- oneOf "23456789TJQKA"
return $ case c of
'2' -> Two
'3' -> Three
'4' -> Four
'5' -> Five
'6' -> Six
'7' -> Seven
'8' -> Eight
'9' -> Nine
'T' -> Ten
'J' -> Jack
'Q' -> Queen
'K' -> King
'A' -> Ace
data Game = Game
{ cards :: [Rank],
baseScore :: Int
}
deriving (Show)
data Rank = Joker | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Eq, Ord, Enum, Show)
data HandStrength = FiveOfAKind | FourOfAKind | FullHouse | ThreeOfAKind | TwoOfAKind | TwoPair | OnePair | HighCard deriving (Eq, Ord, Enum, Show)
@@ -1,0 +1,83 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Data.List
import qualified Data.Map as Map
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
document <- readAndParseStdin parseDocument
print $ part1 document
print $ part2 document
part1 :: Document -> Int
part1 doc = traverseTreeUntilD doc (== "ZZZ") "AAA"
part2 :: Document -> Int
part2 doc = foldl1 lcm . map (traverseTreeUntilD doc ("Z" `isSuffixOf`)) $ startingPositions
where
startingPositions = filter ("A" `isSuffixOf`) $ (Map.keys . docMap) doc
traverseTreeUntilD :: Document -> (String -> Bool) -> String -> Int
traverseTreeUntilD doc = traverseTreeUntil (cycle $ directions doc) (docMap doc) 0
traverseTreeUntil :: [Direction] -> Map.Map String (String, String) -> Int -> (String -> Bool) -> String -> Int
traverseTreeUntil (x : xs) m n predicate elem
| predicate elem = n
| otherwise = traverseTreeUntil xs m (n + 1) predicate (readNext getNode x)
where
getNode = case Map.lookup elem m of
Just a -> a
Nothing -> error (show elem)
readNext (n, _) DirectionLeft = n
readNext (_, n) DirectionRight = n
parseDocument :: Parser Document
parseDocument = do
directions <- many1 parseDirection
_ <- string "\n\n"
nodes <- parseNode `endBy` char '\n'
return
Document
{ directions,
docMap = Map.fromList nodes
}
parseNode :: Parser (String, (String, String))
parseNode = do
node <- many1 alphaNum
_ <- spaces <* char '=' <* spaces
_ <- char '('
left <- many1 alphaNum
_ <- char ',' <* spaces
right <- many1 alphaNum
_ <- char ')'
return (node, (left, right))
parseDirection :: Parser Direction
parseDirection = do
c <- oneOf "LR"
return $ case c of
'L' -> DirectionLeft
'R' -> DirectionRight
data Document = Document
{ directions :: [Direction],
docMap :: Map.Map String (String, String)
}
deriving (Show)
data Direction = DirectionLeft | DirectionRight deriving (Enum, Show)
@@ -1,0 +1,69 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Control.Monad (guard)
import Data.List (unfoldr)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
input <- readAndParseStdin parseInput
print $ part1 input
print $ part2 input
part1 :: [[Int]] -> Int
part1 = sum . map (round . interpolateNext)
part2 :: [[Int]] -> Int
part2 = sum . map (round . interpolatePrevious)
interpolateNext :: [Int] -> Double
interpolateNext i = interpolatePolynomial (length i) i
interpolatePrevious :: [Int] -> Double
interpolatePrevious = interpolatePolynomial (-1)
interpolatePolynomial :: Int -> [Int] -> Double
interpolatePolynomial nth seq =
let divDiff = (dividedDifference . buildDifferenceTable) seq
initialValue = (1, 0)
(_, val) = foldl foldFunction initialValue $ zip [0 ..] (tail divDiff)
in head divDiff + val
where
foldFunction (productAcc, valueAcc) (idx, val) =
let prod = productAcc * fromIntegral (nth - idx)
in (prod, valueAcc + (val * prod))
dividedDifference :: [[Int]] -> [Double]
dividedDifference table = [fromIntegral (head row) / fromIntegral (fac i) | (i, row) <- zip [0 ..] table]
where
fac i = product [1 .. i]
buildDifferenceTable :: [Int] -> [[Int]]
buildDifferenceTable input = input : unfoldr buildRow input
where
zipPairs list = zip list $ tail list
diffPairs = map $ uncurry subtract
buildRow lst =
let row = diffPairs $ zipPairs lst
in guard (not $ null row) >> Just (row, row)
parseInput :: Parser [[Int]]
parseInput = parseSequence `sepBy` char '\n'
parseSequence :: Parser [Int]
parseSequence = map read <$> many1 (digit <|> char '-') `sepBy` char ' '
@@ -1,68 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.List (find, isInfixOf)
import Data.Maybe (catMaybes)
main = print =<< run 0
run :: Int -> IO Int
run acc = do
line <- getLine
if null line
then return acc
else do
let x = concatFirstLastDigitsInString line
run $ acc + x
concatFirstLastDigitsInString :: String -> Int
concatFirstLastDigitsInString s =
case catMaybes [findDigitFromLeft "" s, findDigitFromRight "" s] of
[x, y] -> x * 10 + y
[x] -> x * 11
_ -> 0
findDigitFromLeft :: String -> String -> Maybe Int
findDigitFromLeft acc "" = findDigit acc
findDigitFromLeft acc (x : xs) = case findDigit acc of
Just v -> Just v
Nothing -> findDigitFromLeft (acc ++ [x]) xs
findDigitFromRight :: String -> String -> Maybe Int
findDigitFromRight acc "" = findDigit acc
findDigitFromRight acc xs = case findDigit acc of
Just v -> Just v
Nothing -> findDigitFromRight (last xs : acc) (init xs)
findDigit :: String -> Maybe Int
findDigit s = case find (`isInfixOf` s) digitAsText of
Just textual -> lookup textual digitMap
Nothing -> Nothing
where
digitMap =
[ ("eight", 8),
("seven", 7),
("three", 3),
("nine", 9),
("four", 4),
("five", 5),
("two", 2),
("one", 1),
("six", 6),
("1", 1),
("2", 2),
("3", 3),
("4", 4),
("5", 5),
("6", 6),
("7", 7),
("8", 8),
("9", 9)
]
digitAsText = map fst digitMap
@@ -1,84 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Control.Applicative ((<*))
import Data.Map (Map, elems, fromListWith)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
input <- getContents
case parseString input of
Left err -> print err
Right games -> do
part1PrintValidGamesMaxCubes games
part2PrintMinimumRequiredCubes games
part1PrintValidGamesMaxCubes :: [Game] -> IO ()
part1PrintValidGamesMaxCubes games = do
print $ sum $ map gameId (filter checkGameIsValid games)
part2PrintMinimumRequiredCubes :: [Game] -> IO ()
part2PrintMinimumRequiredCubes games = do
print $ sum $ map (product . elems . getMinimumCubesRequiredForGame) games
getMinimumCubesRequiredForGame :: Game -> Map String Int
getMinimumCubesRequiredForGame game = fromListWith max $ concat (rounds game)
checkGameIsValid :: Game -> Bool
checkGameIsValid game = all (all isCubeAmountAllowed) (rounds game)
isCubeAmountAllowed :: (String, Int) -> Bool
isCubeAmountAllowed (colour, amount) = amount <= cubesAllowed colour
cubesAllowed "red" = 12
cubesAllowed "green" = 13
cubesAllowed "blue" = 14
cubesAllowed _ = 0
data Game = Game
{ gameId :: Int,
rounds :: [[(String, Int)]]
}
deriving (Show)
parseString :: String -> Either ParseError [Game]
parseString = parse fullParser ""
fullParser :: Parser [Game]
fullParser = gameParser `sepBy` char '\n'
gameParser :: Parser Game
gameParser = do
_ <- string "Game "
gameId <- many1 digit <* char ':' <* spaces
rounds <- roundParser `sepBy` (char ';' <* spaces)
return
Game
{ gameId = read gameId,
rounds
}
roundParser :: Parser [(String, Int)]
roundParser = cubeNumberParser `sepBy` (char ',' <* spaces)
cubeNumberParser :: Parser (String, Int)
cubeNumberParser = do
amount <- many1 digit <* spaces
colour <- many1 letter
return (colour, read amount)
@@ -1,87 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.Char (isNumber)
import Data.List (findIndices)
main = do
input <- getContents
let (schematics, symbols) = takeSymbols input
print $ sum $ part1FindNumbersAdjacentToSchematic schematics symbols
print $ sum $ map product $ part2FindGearPartNumbers schematics symbols
part1FindNumbersAdjacentToSchematic :: [Schematic] -> [Symbol] -> [Int]
part1FindNumbersAdjacentToSchematic schematics symbols = map partNumber $ filter (isSchematicAdjacentToAnySymbol symbols) schematics
part2FindGearPartNumbers :: [Schematic] -> [Symbol] -> [[Int]]
part2FindGearPartNumbers schematics symbols = map (map partNumber) $ filter (\inner -> length inner == 2) $ map (findSchematicsAdjacentToSymbol schematics) symbols
findSchematicsAdjacentToSymbol :: [Schematic] -> Symbol -> [Schematic]
findSchematicsAdjacentToSymbol schematics symbol = filter (`isSchematicAdjacent` symbol) schematics
isSchematicAdjacentToAnySymbol :: [Symbol] -> Schematic -> Bool
isSchematicAdjacentToAnySymbol symbol schematic = any (isSchematicAdjacent schematic) symbol
isSchematicAdjacent :: Schematic -> Symbol -> Bool
isSchematicAdjacent sc sy = isAdjacent (symbolCoords sy) (schematicCoords sc)
isAdjacent :: (Int, Int) -> (Int, Int, Int) -> Bool
isAdjacent (px, py) (rx, ry, width) =
let leftX = rx
rightX = rx + width - 1
upperY = ry + 1
lowerY = ry - 1
in (px >= leftX - 1 && px <= rightX + 1 && py == ry)
|| (py == upperY || py == lowerY) && (px >= leftX && px <= rightX)
|| (px == leftX - 1 || px == rightX + 1) && (py == upperY || py == lowerY)
data Schematic = Schematic
{ partNumber :: Int,
schematicCoords :: (Int, Int, Int)
}
deriving (Show)
data Symbol = Symbol
{ symbolCoords :: (Int, Int),
symbolType :: Char
}
deriving (Show)
takeSymbols :: String -> ([Schematic], [Symbol])
takeSymbols input = takeSymbol input Nothing (0, 0) [] []
takeSymbol :: String -> Maybe Schematic -> (Int, Int) -> [Schematic] -> [Symbol] -> ([Schematic], [Symbol])
takeSymbol "" Nothing _ schematicAcc symbolAcc = (schematicAcc, symbolAcc)
takeSymbol "" (Just inProgressSchematic) _ schematicAcc symbolAcc = (schematicAcc ++ [inProgressSchematic], symbolAcc)
takeSymbol (x : xs) inProgressSchematic (posX, posY) schematicAcc symbolAcc =
case x of
_ | isNumber x -> takeSymbol xs (Just $ appendToSchematic inProgressSchematic (posX, posY) (read [x])) (posX + 1, posY) schematicAcc symbolAcc
'.' -> takeSymbol xs Nothing (posX + 1, posY) (maybeAppend schematicAcc inProgressSchematic) symbolAcc
'\n' -> takeSymbol xs Nothing (0, posY + 1) (maybeAppend schematicAcc inProgressSchematic) symbolAcc
_ -> takeSymbol xs Nothing (posX + 1, posY) (maybeAppend schematicAcc inProgressSchematic) (symbolAcc ++ [buildSymbol posX posY x])
appendToSchematic :: Maybe Schematic -> (Int, Int) -> Int -> Schematic
appendToSchematic (Just schematic) _ c =
let (x, y, n) = schematicCoords schematic
currPartNumber = partNumber schematic
in schematic {partNumber = currPartNumber * 10 + c, schematicCoords = (x, y, n + 1)}
appendToSchematic Nothing (x, y) c = Schematic {partNumber = c, schematicCoords = (x, y, 1)}
maybeAppend :: [Schematic] -> Maybe Schematic -> [Schematic]
maybeAppend out (Just new) = out ++ [new]
maybeAppend out Nothing = out
buildSymbol :: Int -> Int -> Char -> Symbol
buildSymbol x y symbolType = Symbol {symbolCoords = (x, y), symbolType}
@@ -1,81 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Data.List (intersect)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
cards <- readAndParseStdin []
print $ part1 cards
print $ part2 cards
part1 :: [Card] -> Int
part1 cards = sum $ map (score . length . getWinningNumbers) cards
where
score 0 = 0
score n = 2 ^ (n - 1)
part2 :: [Card] -> Int
part2 cards = sum $ calculateCardCopies $ map (length . getWinningNumbers) cards
calculateCardCopies :: [Int] -> [Int]
calculateCardCopies xs = foldl replicateSingleCardWinnings (replicate (length xs) 1) (zip [0 ..] xs)
replicateSingleCardWinnings :: [Int] -> (Int, Int) -> [Int]
replicateSingleCardWinnings cardReplicas (idx, winningNumbers) = copyCards cardReplicas (idx + 1) idx winningNumbers
copyCards :: [Int] -> Int -> Int -> Int -> [Int]
copyCards cardReplicas currIdx winningCardIdx winningNumbers
| currIdx <= length cardReplicas && winningNumbers > 0 =
let incrementedList = incrementAtIndex cardReplicas currIdx (cardReplicas !! winningCardIdx)
in copyCards incrementedList (currIdx + 1) winningCardIdx (winningNumbers - 1)
| otherwise = cardReplicas
incrementAtIndex :: [Int] -> Int -> Int -> [Int]
incrementAtIndex xs idx amount = take idx xs ++ [(xs !! idx) + amount] ++ drop (idx + 1) xs
getWinningNumbers :: Card -> [Int]
getWinningNumbers card = myNumbers card `intersect` winningNumbers card
data Card = Card
{ winningNumbers :: [Int],
myNumbers :: [Int]
}
deriving (Show)
readAndParseStdin :: [Card] -> IO [Card]
readAndParseStdin acc = do
line <- getLine
if null line
then return acc
else case parse cardParser "" line of
Left parseError -> error $ show parseError
Right card -> readAndParseStdin $ acc ++ [card]
cardParser :: Parser Card
cardParser = do
_ <- string "Card" <* spaces <* many1 digit <* char ':' <* spaces
winningNumbers <- numberParser
_ <- char '|' <* spaces
myNumbers <- numberParser
return Card {winningNumbers, myNumbers}
numberParser :: Parser [Int]
numberParser = map read <$> many1 digit `endBy` spaces
@@ -1,246 +1,0 @@
use std::{collections::HashMap, io::Read, ops::Range, str::FromStr, time::Instant};
use itertools::Itertools;
use nom::IResult;
use rangemap::RangeMap;
const TRANSLATION_PATH: &[MapKind] = &[
MapKind::Soil,
MapKind::Fertilizer,
MapKind::Water,
MapKind::Light,
MapKind::Temperature,
MapKind::Humidity,
MapKind::Location,
];
fn main() {
let mut input = Vec::new();
std::io::stdin().lock().read_to_end(&mut input).unwrap();
let input = std::str::from_utf8(&input).unwrap();
let (rest, input) = parse_input(input).unwrap();
assert!(rest.is_empty());
let i = Instant::now();
let answer = part1(&input);
eprintln!("part 1: {answer} ({:?})", i.elapsed());
let i = Instant::now();
let answer = part2(&input);
eprintln!("part 2: {answer} ({:?})", i.elapsed());
}
fn part1(input: &Input) -> u64 {
let mut lowest_location = u64::MAX;
for seed in &input.seeds {
let mut source = *seed;
let mut from = MapKind::Seed;
for to in TRANSLATION_PATH {
let Some(translation) = input.maps.get(&Translation { from, to: *to }) else {
panic!("invalid path {from:?} to {to:?}");
};
if let Some((source_range, destination_base)) = translation.get_key_value(&source) {
source = destination_base + (source - source_range.start);
}
from = *to;
}
assert_eq!(from, MapKind::Location);
lowest_location = lowest_location.min(source);
}
lowest_location
}
fn part2(input: &Input) -> u64 {
let seed_ranges: Vec<_> = input
.seeds
.iter()
.tuples()
.map(|(start, len)| (*start)..(*start) + len)
.collect();
let mut lowest_bound_seen = u64::MAX;
for seed_range in seed_ranges {
let lowest_for_seed = traverse_path(input, TRANSLATION_PATH, MapKind::Seed, seed_range);
lowest_bound_seen = lowest_bound_seen.min(lowest_for_seed);
}
lowest_bound_seen
}
fn traverse_path(input: &Input, path: &[MapKind], from: MapKind, source_range: Range<u64>) -> u64 {
let mut lowest_bound_seen = u64::MAX;
let Some((next_path, rest)) = path.split_first() else {
return source_range.start;
};
let Some(translation) = input.maps.get(&Translation {
from,
to: *next_path,
}) else {
panic!("invalid path {from:?} to {next_path:?}");
};
for (new_source_range, destination_base) in translation.overlapping(&source_range) {
let start = source_range.start.max(new_source_range.start);
let end = source_range.end.min(new_source_range.end);
let offset = start.saturating_sub(new_source_range.start);
let length = end.saturating_sub(start);
let destination_range = (*destination_base + offset)..(*destination_base + offset + length);
let lowest_in_tree = traverse_path(input, rest, *next_path, destination_range);
lowest_bound_seen = lowest_bound_seen.min(lowest_in_tree);
}
for uncovered_range in split_range(
source_range.clone(),
translation
.overlapping(&source_range)
.map(|v| v.0.clone())
.collect(),
) {
let current_range = traverse_path(input, rest, *next_path, uncovered_range);
lowest_bound_seen = lowest_bound_seen.min(current_range);
}
lowest_bound_seen
}
fn split_range(main_range: Range<u64>, mut ranges: Vec<Range<u64>>) -> Vec<Range<u64>> {
let mut non_intersecting_ranges = Vec::new();
let mut current_start = main_range.start;
ranges.sort_by_key(|r| r.start);
for range in ranges {
if range.start > current_start {
non_intersecting_ranges.push(current_start..range.start);
}
if range.end > current_start {
current_start = range.end;
}
}
if current_start < main_range.end {
non_intersecting_ranges.push(current_start..main_range.end);
}
non_intersecting_ranges
}
#[derive(strum::EnumString, Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum MapKind {
Seed,
Soil,
Fertilizer,
Water,
Light,
Temperature,
Humidity,
Location,
}
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)]
struct Translation {
from: MapKind,
to: MapKind,
}
impl From<(MapKind, MapKind)> for Translation {
fn from((from, to): (MapKind, MapKind)) -> Self {
Self { from, to }
}
}
#[derive(Debug)]
struct Input {
seeds: Vec<u64>,
maps: HashMap<Translation, RangeMap<u64, u64>>,
}
fn parse_input(rest: &str) -> IResult<&str, Input> {
use nom::{
bytes::complete::tag, character::complete::digit1, combinator::map_res,
multi::separated_list1, sequence::delimited,
};
let (rest, seeds) = delimited(
tag("seeds: "),
separated_list1(tag(" "), map_res(digit1, u64::from_str)),
tag("\n\n"),
)(rest)?;
let (rest, maps) = separated_list1(tag("\n"), parse_single_map)(rest)?;
Ok((
rest,
Input {
seeds,
maps: maps.into_iter().collect(),
},
))
}
fn parse_single_map(rest: &str) -> IResult<&str, (Translation, RangeMap<u64, u64>)> {
use nom::multi::many1;
let (rest, header) = parse_header(rest)?;
let (rest, lines) = many1(parse_map_line)(rest)?;
Ok((rest, (header, lines.into_iter().collect())))
}
fn parse_map_line(rest: &str) -> IResult<&str, (Range<u64>, u64)> {
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::digit1,
combinator::{eof, map_res},
sequence::terminated,
};
let (rest, destination) = terminated(map_res(digit1, u64::from_str), tag(" "))(rest)?;
let (rest, source) = terminated(map_res(digit1, u64::from_str), tag(" "))(rest)?;
let (rest, size) = terminated(map_res(digit1, u64::from_str), alt((tag("\n"), eof)))(rest)?;
Ok((rest, (source..source + size, destination)))
}
fn parse_header(rest: &str) -> IResult<&str, Translation> {
use nom::{
bytes::complete::{tag, take_until},
combinator::{map, map_res},
sequence::{separated_pair, terminated},
};
map(
terminated(
separated_pair(
map_res(take_until("-"), MapKind::from_str),
tag("-to-"),
map_res(take_until(" "), MapKind::from_str),
),
tag(" map:\n"),
),
Translation::from,
)(rest)
}
@@ -1,46 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Control.Applicative ((<*))
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
game <- readAndParseStdin gameParser
print $ part1 game
print $ part2 game
part1 :: [(Int, Int)] -> Int
part1 = product . map (length . getWinningTimes)
where
getWinningTimes (time, winCond) = filter (> winCond) $ map (`distance` time) [1 .. time - 1]
part2 :: [(Int, Int)] -> Int
part2 game = part1 [foldl concatenate (0, 0) game]
where
concatenate (tAcc, dAcc) (t, d) = (tAcc * scale t + t, dAcc * scale d + d)
scale :: Int -> Int
scale n
| n < 10 = 10
| otherwise = 10 * scale (n `div` 10)
distance :: Int -> Int -> Int
distance v t = v * (t - v)
gameParser :: Parser [(Int, Int)]
gameParser = do
time <- map read <$> (string "Time:" *> spaces *> many1 digit `endBy` spaces)
distance <- map read <$> (string "Distance:" *> spaces *> many1 digit `endBy` spaces)
return $ zip time distance
@@ -1,108 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Data.List
import Data.Ord
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main :: IO ()
main = do
games <- readAndParseStdin parseAllGames
print $ part1 games
print $ part2 games
part1 :: [Game] -> Int
part1 games = calcScore $ sortBy gameComparator games
where
gameComparator = comparing (Down . getHandStrength . groupCards . cards) <> comparing cards
part2 :: [Game] -> Int
part2 games = calcScore $ sortBy gameComparator $ mapAllJacksToJokers games
where
gameComparator = comparing (Down . getHandStrength . transmorphJokers . cards) <> comparing cards
mapAllJacksToJokers :: [Game] -> [Game]
mapAllJacksToJokers = map (\game -> game {cards = map mapJackToJoker $ cards game})
where
mapJackToJoker Jack = Joker
mapJackToJoker a = a
transmorphJokers :: [Rank] -> [Int]
transmorphJokers [Joker, Joker, Joker, Joker, Joker] = groupCards [Joker, Joker, Joker, Joker, Joker]
transmorphJokers cards = (head grouped + jokerCount) : tail grouped
where
cardsWithoutJokers = filter (/= Joker) cards
jokerCount = length cards - length cardsWithoutJokers
grouped = groupCards cardsWithoutJokers
calcScore :: [Game] -> Int
calcScore game = sum $ zipWith (curry formula) [1 ..] game
where
formula (idx, game) = baseScore game * idx
getHandStrength :: [Int] -> HandStrength
getHandStrength sortedCardCount = case sortedCardCount of
[5] -> FiveOfAKind
[4, 1] -> FourOfAKind
[3, 2] -> FullHouse
(3 : _) -> ThreeOfAKind
(2 : 2 : _) -> TwoPair
(2 : _) -> OnePair
_ -> HighCard
groupCards :: [Rank] -> [Int]
groupCards = sortOn Down . map length . group . sort
parseAllGames :: Parser [Game]
parseAllGames = parseGame `endBy` char '\n'
parseGame :: Parser Game
parseGame = do
cards <- many1 parseRank <* spaces
baseScore <- read <$> many1 digit
return Game {cards, baseScore}
parseRank :: Parser Rank
parseRank = do
c <- oneOf "23456789TJQKA"
return $ case c of
'2' -> Two
'3' -> Three
'4' -> Four
'5' -> Five
'6' -> Six
'7' -> Seven
'8' -> Eight
'9' -> Nine
'T' -> Ten
'J' -> Jack
'Q' -> Queen
'K' -> King
'A' -> Ace
data Game = Game
{ cards :: [Rank],
baseScore :: Int
}
deriving (Show)
data Rank = Joker | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Eq, Ord, Enum, Show)
data HandStrength = FiveOfAKind | FourOfAKind | FullHouse | ThreeOfAKind | TwoOfAKind | TwoPair | OnePair | HighCard deriving (Eq, Ord, Enum, Show)
@@ -1,83 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Data.List
import qualified Data.Map as Map
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
document <- readAndParseStdin parseDocument
print $ part1 document
print $ part2 document
part1 :: Document -> Int
part1 doc = traverseTreeUntilD doc (== "ZZZ") "AAA"
part2 :: Document -> Int
part2 doc = foldl1 lcm . map (traverseTreeUntilD doc ("Z" `isSuffixOf`)) $ startingPositions
where
startingPositions = filter ("A" `isSuffixOf`) $ (Map.keys . docMap) doc
traverseTreeUntilD :: Document -> (String -> Bool) -> String -> Int
traverseTreeUntilD doc = traverseTreeUntil (cycle $ directions doc) (docMap doc) 0
traverseTreeUntil :: [Direction] -> Map.Map String (String, String) -> Int -> (String -> Bool) -> String -> Int
traverseTreeUntil (x : xs) m n predicate elem
| predicate elem = n
| otherwise = traverseTreeUntil xs m (n + 1) predicate (readNext getNode x)
where
getNode = case Map.lookup elem m of
Just a -> a
Nothing -> error (show elem)
readNext (n, _) DirectionLeft = n
readNext (_, n) DirectionRight = n
parseDocument :: Parser Document
parseDocument = do
directions <- many1 parseDirection
_ <- string "\n\n"
nodes <- parseNode `endBy` char '\n'
return
Document
{ directions,
docMap = Map.fromList nodes
}
parseNode :: Parser (String, (String, String))
parseNode = do
node <- many1 alphaNum
_ <- spaces <* char '=' <* spaces
_ <- char '('
left <- many1 alphaNum
_ <- char ',' <* spaces
right <- many1 alphaNum
_ <- char ')'
return (node, (left, right))
parseDirection :: Parser Direction
parseDirection = do
c <- oneOf "LR"
return $ case c of
'L' -> DirectionLeft
'R' -> DirectionRight
data Document = Document
{ directions :: [Direction],
docMap :: Map.Map String (String, String)
}
deriving (Show)
data Direction = DirectionLeft | DirectionRight deriving (Enum, Show)
@@ -1,69 +1,0 @@
#!/usr/bin/env nix-shell
#!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Aoc (readAndParseStdin)
import Control.Monad (guard)
import Data.List (unfoldr)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
input <- readAndParseStdin parseInput
print $ part1 input
print $ part2 input
part1 :: [[Int]] -> Int
part1 = sum . map (round . interpolateNext)
part2 :: [[Int]] -> Int
part2 = sum . map (round . interpolatePrevious)
interpolateNext :: [Int] -> Double
interpolateNext i = interpolatePolynomial (length i) i
interpolatePrevious :: [Int] -> Double
interpolatePrevious = interpolatePolynomial (-1)
interpolatePolynomial :: Int -> [Int] -> Double
interpolatePolynomial nth seq =
let divDiff = (dividedDifference . buildDifferenceTable) seq
initialValue = (1, 0)
(_, val) = foldl foldFunction initialValue $ zip [0 ..] (tail divDiff)
in head divDiff + val
where
foldFunction (productAcc, valueAcc) (idx, val) =
let prod = productAcc * fromIntegral (nth - idx)
in (prod, valueAcc + (val * prod))
dividedDifference :: [[Int]] -> [Double]
dividedDifference table = [fromIntegral (head row) / fromIntegral (fac i) | (i, row) <- zip [0 ..] table]
where
fac i = product [1 .. i]
buildDifferenceTable :: [Int] -> [[Int]]
buildDifferenceTable input = input : unfoldr buildRow input
where
zipPairs list = zip list $ tail list
diffPairs = map $ uncurry subtract
buildRow lst =
let row = diffPairs $ zipPairs lst
in guard (not $ null row) >> Just (row, row)
parseInput :: Parser [[Int]]
parseInput = parseSequence `sepBy` char '\n'
parseSequence :: Parser [Int]
parseSequence = map read <$> many1 (digit <|> char '-') `sepBy` char ' '
@@ -1,0 +1,17 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation {
name = "aoc-2022-1";
buildInputs = [ pkgs.gfortran ];
src = ./.;
buildPhase = ''
gfortran -o aoc-2022-1 main.f90
'';
installPhase = ''
mkdir -p $out/bin
cp aoc-2022-1 $out/bin/
'';
}
@@ -1,0 +1,90 @@
program day_1
implicit none
integer, dimension(300, 20) :: result
integer, dimension(300) :: summed
integer :: eof, i, out, size
integer, external :: top3
eof = 0
result = 0
i = 0
do
i = i + 1
if (i > 300) then
print *, 'Main read overflow: more than 300 entries read.'
exit
end if
call read_block(result(i, :), eof)
if (eof /= 0) exit
end do
summed = sum(result, dim=2)
print *, 'Part 1: ', maxval(summed)
print *, 'Part 2: ', top3(summed)
end program day_1
function top3(input) result(retval)
implicit none
integer, dimension(300), intent(in) :: input
integer, dimension(3) :: topValues
integer :: i, j, k, retval
retval = 0
topValues = 0
do i = 1, 300
do j = 1, 3
if (input(i) > topValues(j)) then
topValues(j) = input(i)
exit
end if
end do
end do
retval = sum(topValues)
end function top3
subroutine read_block(result, eof)
implicit none
integer, dimension(20), intent(out) :: result
integer, intent(out) :: eof
integer :: iostatus, n, parsedCalories
character(len=10) :: line
result = 0
n = 0
eof = 0
parsedCalories = 0
do
read (*, '(A)', iostat=iostatus) line
if (iostatus /= 0) then
eof = iostatus
exit
else if (trim(line) == '') then
exit
end if
read (line, '(I8)', iostat=iostatus) parsedCalories
if (iostatus /= 0) then
print *, 'Conversion error with iostat = ', iostatus
exit
end if
n = n + 1
if (n > 20) then
print *, 'Read block overflow: more than 20 entries read.'
exit
end if
result(n) = parsedCalories
end do
end subroutine read_block
@@ -1,0 +1,49 @@
{ pkgs ? import <nixpkgs> { } }:
with builtins;
let
inherit (pkgs) lib;
input = builtins.readFile ./input;
leftCipher = { "A" = "Rock"; "B" = "Paper"; "C" = "Scissors"; };
winConditions = { "Rock" = "Scissors"; "Scissors" = "Paper"; "Paper" = "Rock"; };
loseConditions = builtins.listToAttrs (map (pair: lib.nameValuePair pair.value pair.name) (lib.attrsToList winConditions));
determineScore = game:
let
shapeScore = { "Rock" = 1; "Paper" = 2; "Scissors" = 3; };
outcomeScore = { "L" = 0; "D" = 3; "W" = 6; };
gameOutcome = if winConditions.${elemAt game 0} == elemAt game 1 then "W" else if elemAt game 0 == elemAt game 1 then "D" else "L";
in
outcomeScore.${gameOutcome} + shapeScore.${elemAt game 0};
splitAndDecipher = x:
let
rightCipher = { "X" = "Rock"; "Y" = "Paper"; "Z" = "Scissors"; };
split = lib.splitString " " x;
us = rightCipher.${elemAt split 1};
them = leftCipher.${elemAt split 0};
in
[ us them ];
splitAndMapToResult = x:
let
split = lib.splitString " " x;
them = leftCipher.${elemAt split 0};
desiredOutcome = elemAt split 1;
us = if desiredOutcome == "X" then winConditions.${them} else if desiredOutcome == "Z" then loseConditions.${them} else them;
in
[ us them ];
games = lib.splitString "\n" input;
playGame = f: lib.foldl (x: y: x + y) 0 (map determineScore (map f games));
part1 = playGame splitAndDecipher;
part2 = playGame splitAndMapToResult;
out = builtins.toJSON { inherit part1; inherit part2; };
in
pkgs.writeText "out" out
@@ -1,0 +1,18 @@
local input = importstr './input';
local parsedInput = [[[std.parseInt(z) for z in std.split(y, '-')] for y in std.split(x, ',')] for x in std.split(input, '\n')];
local part1 = std.sum([
if ((x[0][0] >= x[1][0] && x[0][1] <= x[1][1]) || (x[1][0] >= x[0][0] && x[1][1] <= x[0][1])) then 1 else 0
for x in parsedInput
]);
local part2 = std.sum([
if (x[0][0] <= x[1][1] && x[0][1] >= x[1][0]) then 1 else 0
for x in parsedInput
]);
{
part1: part1,
part2: part2,
}
@@ -1,0 +1,14 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
name = "aoc-2022-4";
src = ./.;
phases = [ "buildPhase" ];
doCheck = true;
buildInputs = with pkgs; [ go-jsonnet ];
buildPhase = ''
jsonnet $src/4.jsonnet > $out
'';
}
@@ -1,0 +1,19 @@
provider "registry.opentofu.org/hashicorp/local" {
version = "2.4.0"
hashes = [
"h1:cZKMbwSvmjK4bgXY+1gSBd5nodgkhRnw90lYvB8j0hI=",
"zh:184d6ec1f0e77713b37f0d9cf943b1371f2aa2f44c2c5a618978e897ce3dccab",
"zh:2205a7955a4051c2c25e69646a60746d9416b73001491808ae5d10620f7b7ac1",
"zh:256ddc56457f725819dc6be62f2d0bb3b9fee40a61771317bb32353df5b5c1a0",
"zh:70146e603f540523f6fa2251dd52c225db5a92bda8c07fd198ed51ae2b50176b",
"zh:8c3f9fe12ab8843e25ff7edabc26e01df4a0e8db204e432600a4c77a95ec0535",
"zh:b003e421f643d14247d31dcb7f0f6470c46f772d0e15a175a555a525ce344bf2",
"zh:b4c8ad7c5696aeb2a52adf6047d1e01943fafa57dc123d5192542527406ffd72",
"zh:c3b6fbfa431f3c085621c74596ee63681a278fd433a4758f33c627e8936d5cb3",
"zh:d2e57b19295b326d84ca5f39b797849d901170d5509aa7558f2a6545c9ce72a9",
"zh:e2307421b0b380eb0e8fcee008e0af98ae30fccbfc9e9a1d24d952489e9b0df9",
]
}
@@ -1,0 +1,19 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
name = "aoc-2022-5";
src = ./.;
phases = [ "buildPhase" ];
doCheck = true;
buildInputs = with pkgs; [ opentofu jinja2-cli ];
buildPhase = ''
cp -r $src/* .
jinja2 main.tf.jinja2 > main.tf
tofu init
tofu apply -auto-approve -lock=false -var=outpath=$out
'';
}
@@ -1,0 +1,54 @@
{% set maxRecursion = 501 %}
locals {
input = file("input")
inputParts = split("\n\n", local.input)
blocks = split("\n", local.inputParts[0])
parsedBlocks = [for s in [for s in slice(local.blocks, 0, length(local.blocks) - 1) : split("", s)] : [for j in range(1, length(s), 4) : s[j]]]
stackedBlocks = [for i in range(length(local.parsedBlocks[0])) : [for j in range(length(local.parsedBlocks)) : local.parsedBlocks[j][i] if local.parsedBlocks[j][i] != " "]]
instructions = split("\n", replace(replace(replace(local.inputParts[1], "move ", ""), " from ", ","), " to ", ","))
output = {
"part1" = join("", [for v in module.executor-{{ maxRecursion - 1 }}.finalBlocks : v[0]]),
"part2" = join("", [for v in module.executor-non-reversed-{{ maxRecursion - 1 }}.finalBlocks : v[0]])
}
}
# you'll have to forgive me for this blasphemy. hashicorp did everything in their power
# to prevent any forms of recursion, and i can't be bothered copy and pasting this block
# 1000 times.
{% for i in range(maxRecursion) %}
module "executor-{{ i }}" {
source = "./executor"
{% if i == 0 %}
stackedBlocks = local.stackedBlocks
instructions = local.instructions
{% else %}
stackedBlocks = module.executor-{{ i - 1 }}.finalBlocks
instructions = module.executor-{{ i - 1 }}.remainingInstructions
{% endif %}
reverse = true
}
module "executor-non-reversed-{{ i }}" {
source = "./executor"
{% if i == 0 %}
stackedBlocks = local.stackedBlocks
instructions = local.instructions
{% else %}
stackedBlocks = module.executor-non-reversed-{{ i - 1 }}.finalBlocks
instructions = module.executor-non-reversed-{{ i - 1 }}.remainingInstructions
{% endif %}
reverse = false
}
{% endfor %}
resource "local_file" "out" {
filename = var.outpath
content = jsonencode(local.output)
}
variable "outpath" {}
@@ -1,0 +1,2 @@
httpd.pid
logs
@@ -1,0 +1,156 @@
LoadModule mpm_prefork_module libexec/apache2/mod_mpm_prefork.so
LoadModule alias_module libexec/apache2/mod_alias.so
LoadModule unixd_module libexec/apache2/mod_unixd.so
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
LoadModule headers_module libexec/apache2/mod_headers.so
ServerRoot .
PidFile ./httpd.pid
RewriteEngine On
Listen 8888
<VirtualHost *:8888>
LogLevel debug
RedirectMatch 301 ^/part1/(.*)$ /part1-iter/$1/1
RewriteRule ^/part1-iter/(.)(.)(.)(.) - [E=C1:$1,E=C2:$2,E=C3:$3,E=C4:$4]
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C2}
RewriteCond %{ENV:C2}
RewriteCond %{ENV:C3}
RewriteRule ^ - [E=ALL_PART1_CHARS_DISTINCT:1]
RewriteCond %{ENV:ALL_PART1_CHARS_DISTINCT} =1
RewriteRule ^/part1-iter/(.{4})[^/]*/(1+)$ /part1-complete/$1/111$2 [R=301,L]
RewriteCond %{ENV:ALL_PART1_CHARS_DISTINCT} !=1
RewriteRule ^/part1-iter/.([^/]+)/(1+)$ /part1-iter/$1/1$2 [R=301,L]
RedirectMatch 301 ^/part2/(.*)$ /part2-iter/$1/1
RewriteRule ^/part2-iter/(.)(.)(.)(.)(.)(.)(.)(.)(.) - [E=CC1:$1,E=CC2:$2,E=CC3:$3,E=CC4:$4,E=CC5:$5,E=CC6:$6,E=CC7:$7,E=CC8:$8,E=CC9:$9]
RewriteRule ^/part2-iter/.{9}(.)(.)(.)(.)(.) - [E=CC10:$1,E=CC11:$2,E=CC12:$3,E=CC13:$4,E=CC14:$5]
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC12}
RewriteCond %{ENV:CC12}
RewriteCond %{ENV:CC13}
RewriteRule ^ - [E=ALL_PART2_CHARS_DISTINCT:1]
RewriteCond %{ENV:ALL_PART2_CHARS_DISTINCT} =1
RewriteRule ^/part2-iter/(.{14})[^/]*/(1+)$ /part2-complete/$1/1111111111111$2 [R=301,L]
RewriteCond %{ENV:ALL_PART2_CHARS_DISTINCT} !=1
RewriteRule ^/part2-iter/.([^/]+)/(1+)$ /part2-iter/$1/1$2 [R=301,L]
</VirtualHost>
@@ -1,0 +1,9 @@
STDIN=$(cat -)
PART1=$(curl --max-redirs 100000 -Ls "http://127.0.0.1:8888/part1/$STDIN" -o /dev/null -w %{url_effective})
echo $(echo $PART1 | gsed -E 's/.*\/([1]+)/\1/' | tr -d '\n' | wc -c)
PART2=$(curl --max-redirs 100000 -Ls "http://127.0.0.1:8888/part2/$STDIN" -o /dev/null -w %{url_effective})
echo $(echo $PART2 | gsed -E 's/.*\/([1]+)/\1/' | tr -d '\n' | wc -c)
@@ -1,17 +1,0 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation {
name = "aoc-2022-1";
buildInputs = [ pkgs.gfortran ];
src = ./.;
buildPhase = ''
gfortran -o aoc-2022-1 main.f90
'';
installPhase = ''
mkdir -p $out/bin
cp aoc-2022-1 $out/bin/
'';
}
@@ -1,90 +1,0 @@
program day_1
implicit none
integer, dimension(300, 20) :: result
integer, dimension(300) :: summed
integer :: eof, i, out, size
integer, external :: top3
eof = 0
result = 0
i = 0
do
i = i + 1
if (i > 300) then
print *, 'Main read overflow: more than 300 entries read.'
exit
end if
call read_block(result(i, :), eof)
if (eof /= 0) exit
end do
summed = sum(result, dim=2)
print *, 'Part 1: ', maxval(summed)
print *, 'Part 2: ', top3(summed)
end program day_1
function top3(input) result(retval)
implicit none
integer, dimension(300), intent(in) :: input
integer, dimension(3) :: topValues
integer :: i, j, k, retval
retval = 0
topValues = 0
do i = 1, 300
do j = 1, 3
if (input(i) > topValues(j)) then
topValues(j) = input(i)
exit
end if
end do
end do
retval = sum(topValues)
end function top3
subroutine read_block(result, eof)
implicit none
integer, dimension(20), intent(out) :: result
integer, intent(out) :: eof
integer :: iostatus, n, parsedCalories
character(len=10) :: line
result = 0
n = 0
eof = 0
parsedCalories = 0
do
read (*, '(A)', iostat=iostatus) line
if (iostatus /= 0) then
eof = iostatus
exit
else if (trim(line) == '') then
exit
end if
read (line, '(I8)', iostat=iostatus) parsedCalories
if (iostatus /= 0) then
print *, 'Conversion error with iostat = ', iostatus
exit
end if
n = n + 1
if (n > 20) then
print *, 'Read block overflow: more than 20 entries read.'
exit
end if
result(n) = parsedCalories
end do
end subroutine read_block
@@ -1,49 +1,0 @@
{ pkgs ? import <nixpkgs> { } }:
with builtins;
let
inherit (pkgs) lib;
input = builtins.readFile ./input;
leftCipher = { "A" = "Rock"; "B" = "Paper"; "C" = "Scissors"; };
winConditions = { "Rock" = "Scissors"; "Scissors" = "Paper"; "Paper" = "Rock"; };
loseConditions = builtins.listToAttrs (map (pair: lib.nameValuePair pair.value pair.name) (lib.attrsToList winConditions));
determineScore = game:
let
shapeScore = { "Rock" = 1; "Paper" = 2; "Scissors" = 3; };
outcomeScore = { "L" = 0; "D" = 3; "W" = 6; };
gameOutcome = if winConditions.${elemAt game 0} == elemAt game 1 then "W" else if elemAt game 0 == elemAt game 1 then "D" else "L";
in
outcomeScore.${gameOutcome} + shapeScore.${elemAt game 0};
splitAndDecipher = x:
let
rightCipher = { "X" = "Rock"; "Y" = "Paper"; "Z" = "Scissors"; };
split = lib.splitString " " x;
us = rightCipher.${elemAt split 1};
them = leftCipher.${elemAt split 0};
in
[ us them ];
splitAndMapToResult = x:
let
split = lib.splitString " " x;
them = leftCipher.${elemAt split 0};
desiredOutcome = elemAt split 1;
us = if desiredOutcome == "X" then winConditions.${them} else if desiredOutcome == "Z" then loseConditions.${them} else them;
in
[ us them ];
games = lib.splitString "\n" input;
playGame = f: lib.foldl (x: y: x + y) 0 (map determineScore (map f games));
part1 = playGame splitAndDecipher;
part2 = playGame splitAndMapToResult;
out = builtins.toJSON { inherit part1; inherit part2; };
in
pkgs.writeText "out" out
@@ -1,18 +1,0 @@
local input = importstr './input';
local parsedInput = [[[std.parseInt(z) for z in std.split(y, '-')] for y in std.split(x, ',')] for x in std.split(input, '\n')];
local part1 = std.sum([
if ((x[0][0] >= x[1][0] && x[0][1] <= x[1][1]) || (x[1][0] >= x[0][0] && x[1][1] <= x[0][1])) then 1 else 0
for x in parsedInput
]);
local part2 = std.sum([
if (x[0][0] <= x[1][1] && x[0][1] >= x[1][0]) then 1 else 0
for x in parsedInput
]);
{
part1: part1,
part2: part2,
}
@@ -1,14 +1,0 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
name = "aoc-2022-4";
src = ./.;
phases = [ "buildPhase" ];
doCheck = true;
buildInputs = with pkgs; [ go-jsonnet ];
buildPhase = ''
jsonnet $src/4.jsonnet > $out
'';
}
@@ -1,19 +1,0 @@
provider "registry.opentofu.org/hashicorp/local" {
version = "2.4.0"
hashes = [
"h1:cZKMbwSvmjK4bgXY+1gSBd5nodgkhRnw90lYvB8j0hI=",
"zh:184d6ec1f0e77713b37f0d9cf943b1371f2aa2f44c2c5a618978e897ce3dccab",
"zh:2205a7955a4051c2c25e69646a60746d9416b73001491808ae5d10620f7b7ac1",
"zh:256ddc56457f725819dc6be62f2d0bb3b9fee40a61771317bb32353df5b5c1a0",
"zh:70146e603f540523f6fa2251dd52c225db5a92bda8c07fd198ed51ae2b50176b",
"zh:8c3f9fe12ab8843e25ff7edabc26e01df4a0e8db204e432600a4c77a95ec0535",
"zh:b003e421f643d14247d31dcb7f0f6470c46f772d0e15a175a555a525ce344bf2",
"zh:b4c8ad7c5696aeb2a52adf6047d1e01943fafa57dc123d5192542527406ffd72",
"zh:c3b6fbfa431f3c085621c74596ee63681a278fd433a4758f33c627e8936d5cb3",
"zh:d2e57b19295b326d84ca5f39b797849d901170d5509aa7558f2a6545c9ce72a9",
"zh:e2307421b0b380eb0e8fcee008e0af98ae30fccbfc9e9a1d24d952489e9b0df9",
]
}
@@ -1,19 +1,0 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
name = "aoc-2022-5";
src = ./.;
phases = [ "buildPhase" ];
doCheck = true;
buildInputs = with pkgs; [ opentofu jinja2-cli ];
buildPhase = ''
cp -r $src/* .
jinja2 main.tf.jinja2 > main.tf
tofu init
tofu apply -auto-approve -lock=false -var=outpath=$out
'';
}
@@ -1,54 +1,0 @@
{% set maxRecursion = 501 %}
locals {
input = file("input")
inputParts = split("\n\n", local.input)
blocks = split("\n", local.inputParts[0])
parsedBlocks = [for s in [for s in slice(local.blocks, 0, length(local.blocks) - 1) : split("", s)] : [for j in range(1, length(s), 4) : s[j]]]
stackedBlocks = [for i in range(length(local.parsedBlocks[0])) : [for j in range(length(local.parsedBlocks)) : local.parsedBlocks[j][i] if local.parsedBlocks[j][i] != " "]]
instructions = split("\n", replace(replace(replace(local.inputParts[1], "move ", ""), " from ", ","), " to ", ","))
output = {
"part1" = join("", [for v in module.executor-{{ maxRecursion - 1 }}.finalBlocks : v[0]]),
"part2" = join("", [for v in module.executor-non-reversed-{{ maxRecursion - 1 }}.finalBlocks : v[0]])
}
}
# you'll have to forgive me for this blasphemy. hashicorp did everything in their power
# to prevent any forms of recursion, and i can't be bothered copy and pasting this block
# 1000 times.
{% for i in range(maxRecursion) %}
module "executor-{{ i }}" {
source = "./executor"
{% if i == 0 %}
stackedBlocks = local.stackedBlocks
instructions = local.instructions
{% else %}
stackedBlocks = module.executor-{{ i - 1 }}.finalBlocks
instructions = module.executor-{{ i - 1 }}.remainingInstructions
{% endif %}
reverse = true
}
module "executor-non-reversed-{{ i }}" {
source = "./executor"
{% if i == 0 %}
stackedBlocks = local.stackedBlocks
instructions = local.instructions
{% else %}
stackedBlocks = module.executor-non-reversed-{{ i - 1 }}.finalBlocks
instructions = module.executor-non-reversed-{{ i - 1 }}.remainingInstructions
{% endif %}
reverse = false
}
{% endfor %}
resource "local_file" "out" {
filename = var.outpath
content = jsonencode(local.output)
}
variable "outpath" {}
@@ -1,2 +1,0 @@
httpd.pid
logs
@@ -1,156 +1,0 @@
LoadModule mpm_prefork_module libexec/apache2/mod_mpm_prefork.so
LoadModule alias_module libexec/apache2/mod_alias.so
LoadModule unixd_module libexec/apache2/mod_unixd.so
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
LoadModule headers_module libexec/apache2/mod_headers.so
ServerRoot .
PidFile ./httpd.pid
RewriteEngine On
Listen 8888
<VirtualHost *:8888>
LogLevel debug
RedirectMatch 301 ^/part1/(.*)$ /part1-iter/$1/1
RewriteRule ^/part1-iter/(.)(.)(.)(.) - [E=C1:$1,E=C2:$2,E=C3:$3,E=C4:$4]
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C1}
RewriteCond %{ENV:C2}
RewriteCond %{ENV:C2}
RewriteCond %{ENV:C3}
RewriteRule ^ - [E=ALL_PART1_CHARS_DISTINCT:1]
RewriteCond %{ENV:ALL_PART1_CHARS_DISTINCT} =1
RewriteRule ^/part1-iter/(.{4})[^/]*/(1+)$ /part1-complete/$1/111$2 [R=301,L]
RewriteCond %{ENV:ALL_PART1_CHARS_DISTINCT} !=1
RewriteRule ^/part1-iter/.([^/]+)/(1+)$ /part1-iter/$1/1$2 [R=301,L]
RedirectMatch 301 ^/part2/(.*)$ /part2-iter/$1/1
RewriteRule ^/part2-iter/(.)(.)(.)(.)(.)(.)(.)(.)(.) - [E=CC1:$1,E=CC2:$2,E=CC3:$3,E=CC4:$4,E=CC5:$5,E=CC6:$6,E=CC7:$7,E=CC8:$8,E=CC9:$9]
RewriteRule ^/part2-iter/.{9}(.)(.)(.)(.)(.) - [E=CC10:$1,E=CC11:$2,E=CC12:$3,E=CC13:$4,E=CC14:$5]
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC1}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC2}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC3}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC4}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC5}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC6}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC7}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC8}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC9}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC10}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC11}
RewriteCond %{ENV:CC12}
RewriteCond %{ENV:CC12}
RewriteCond %{ENV:CC13}
RewriteRule ^ - [E=ALL_PART2_CHARS_DISTINCT:1]
RewriteCond %{ENV:ALL_PART2_CHARS_DISTINCT} =1
RewriteRule ^/part2-iter/(.{14})[^/]*/(1+)$ /part2-complete/$1/1111111111111$2 [R=301,L]
RewriteCond %{ENV:ALL_PART2_CHARS_DISTINCT} !=1
RewriteRule ^/part2-iter/.([^/]+)/(1+)$ /part2-iter/$1/1$2 [R=301,L]
</VirtualHost>
@@ -1,9 +1,0 @@
STDIN=$(cat -)
PART1=$(curl --max-redirs 100000 -Ls "http://127.0.0.1:8888/part1/$STDIN" -o /dev/null -w %{url_effective})
echo $(echo $PART1 | gsed -E 's/.*\/([1]+)/\1/' | tr -d '\n' | wc -c)
PART2=$(curl --max-redirs 100000 -Ls "http://127.0.0.1:8888/part2/$STDIN" -o /dev/null -w %{url_effective})
echo $(echo $PART2 | gsed -E 's/.*\/([1]+)/\1/' | tr -d '\n' | wc -c)
@@ -1,0 +1,24 @@
locals {
remainingInstructions = length(var.instructions) == 0 ? [] : slice(var.instructions, 1, length(var.instructions))
currentInstruction = length(var.instructions) == 0 ? null : split(",", var.instructions[0])
amount = local.currentInstruction == null ? null : tonumber(local.currentInstruction[0])
fromStack = local.currentInstruction == null ? null : (tonumber(local.currentInstruction[1]) - 1)
toStack = local.currentInstruction == null ? null : (tonumber(local.currentInstruction[2]) - 1)
setFromBlocks = local.currentInstruction == null ? null : slice(var.stackedBlocks[local.fromStack], local.amount, length(var.stackedBlocks[local.fromStack]))
newToBlocks = local.currentInstruction == null ? null : slice(var.stackedBlocks[local.fromStack], 0, local.amount)
newToBlocksMaybeReversed = var.reverse ? reverse(local.newToBlocks) : local.newToBlocks
setToBlocks = local.currentInstruction == null ? null : concat(local.newToBlocksMaybeReversed, var.stackedBlocks[local.toStack])
finalBlocks = local.currentInstruction == null ? var.stackedBlocks : [for i in range(length(var.stackedBlocks)) : (i == local.fromStack ? local.setFromBlocks : (i == local.toStack ? local.setToBlocks : var.stackedBlocks[i]))]
}
variable "stackedBlocks" {}
variable "instructions" {}
variable "reverse" {}
output "finalBlocks" {
value = local.finalBlocks
}
output "remainingInstructions" {
value = local.remainingInstructions
}
@@ -1,24 +1,0 @@
locals {
remainingInstructions = length(var.instructions) == 0 ? [] : slice(var.instructions, 1, length(var.instructions))
currentInstruction = length(var.instructions) == 0 ? null : split(",", var.instructions[0])
amount = local.currentInstruction == null ? null : tonumber(local.currentInstruction[0])
fromStack = local.currentInstruction == null ? null : (tonumber(local.currentInstruction[1]) - 1)
toStack = local.currentInstruction == null ? null : (tonumber(local.currentInstruction[2]) - 1)
setFromBlocks = local.currentInstruction == null ? null : slice(var.stackedBlocks[local.fromStack], local.amount, length(var.stackedBlocks[local.fromStack]))
newToBlocks = local.currentInstruction == null ? null : slice(var.stackedBlocks[local.fromStack], 0, local.amount)
newToBlocksMaybeReversed = var.reverse ? reverse(local.newToBlocks) : local.newToBlocks
setToBlocks = local.currentInstruction == null ? null : concat(local.newToBlocksMaybeReversed, var.stackedBlocks[local.toStack])
finalBlocks = local.currentInstruction == null ? var.stackedBlocks : [for i in range(length(var.stackedBlocks)) : (i == local.fromStack ? local.setFromBlocks : (i == local.toStack ? local.setToBlocks : var.stackedBlocks[i]))]
}
variable "stackedBlocks" {}
variable "instructions" {}
variable "reverse" {}
output "finalBlocks" {
value = local.finalBlocks
}
output "remainingInstructions" {
value = local.remainingInstructions
}