Day 5: Print Queue
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
1 point
*
Python
(Part 1) omg I can’t believe this actually worked first try!
with open('input') as data:
parts = data.read().rstrip().split("\n\n")
ordering_rules = parts[0].split("\n")
updates = parts[1].split("\n")
correct_updates = []
middle_updates = []
def find_relevant_rules(pg_num: str, rules: list[str]) -> list[str] | None:
for rule in rules:
return list(filter(lambda x: x.split("|")[0] == pg_num, rules))
def interpret_rule(rule: str) -> list[str]:
return rule.split("|")
def interpret_update(update: str) -> list[str]:
return update.split(",")
def find_middle_update_index(update: list[str]) -> int:
num_of_elements = len(update)
return num_of_elements // 2
for update in updates:
is_correct = True
for i, page in enumerate(interpret_update(update)):
rules_to_check = find_relevant_rules(page, ordering_rules)
for rule in rules_to_check:
if rule.split("|")[1] in interpret_update(update)[:i]:
is_correct = False
if is_correct:
correct_updates.append(update)
for update in correct_updates:
split_update = update.split(",")
middle_updates.append(int(split_update[find_middle_update_index(split_update)]))
print(sum(middle_updates))
6 points
*
Nim
Solution: sort numbers using custom rules and compare if sorted == original. Part 2 is trivial.
Runtime for both parts: 1.05 ms
proc parseRules(input: string): Table[int, seq[int]] =
for line in input.splitLines():
let pair = line.split('|')
let (a, b) = (pair[0].parseInt, pair[1].parseInt)
discard result.hasKeyOrPut(a, newSeq[int]())
result[a].add b
proc solve(input: string): AOCSolution[int, int] =
let chunks = input.split("\n\n")
let later = parseRules(chunks[0])
for line in chunks[1].splitLines():
let numbers = line.split(',').map(parseInt)
let sorted = numbers.sorted(cmp =
proc(a,b: int): int =
if a in later and b in later[a]: -1
elif b in later and a in later[b]: 1
else: 0
)
if numbers == sorted:
result.part1 += numbers[numbers.len div 2]
else:
result.part2 += sorted[sorted.len div 2]
1 point
5 points
C#
using QuickGraph;
using QuickGraph.Algorithms.TopologicalSort;
public class Day05 : Solver
{
private List<int[]> updates;
private List<int[]> updates_ordered;
public void Presolve(string input) {
var blocks = input.Trim().Split("\n\n");
List<(int, int)> rules = new();
foreach (var line in blocks[0].Split("\n")) {
var pair = line.Split('|');
rules.Add((int.Parse(pair[0]), int.Parse(pair[1])));
}
updates = new();
updates_ordered = new();
foreach (var line in input.Trim().Split("\n\n")[1].Split("\n")) {
var update = line.Split(',').Select(int.Parse).ToArray();
updates.Add(update);
var graph = new AdjacencyGraph<int, Edge<int>>();
graph.AddVertexRange(update);
graph.AddEdgeRange(rules
.Where(rule => update.Contains(rule.Item1) && update.Contains(rule.Item2))
.Select(rule => new Edge<int>(rule.Item1, rule.Item2)));
List<int> ordered_update = [];
new TopologicalSortAlgorithm<int, Edge<int>>(graph).Compute(ordered_update);
updates_ordered.Add(ordered_update.ToArray());
}
}
public string SolveFirst() => updates.Zip(updates_ordered)
.Where(unordered_ordered => unordered_ordered.First.SequenceEqual(unordered_ordered.Second))
.Select(unordered_ordered => unordered_ordered.First)
.Select(update => update[update.Length / 2])
.Sum().ToString();
public string SolveSecond() => updates.Zip(updates_ordered)
.Where(unordered_ordered => !unordered_ordered.First.SequenceEqual(unordered_ordered.Second))
.Select(unordered_ordered => unordered_ordered.Second)
.Select(update => update[update.Length / 2])
.Sum().ToString();
}
2 points
2 points
5 points
*
Kotlin
Took me a while to figure out how to sort according to the rules. 🤯
fun part1(input: String): Int {
val (rules, listOfNumbers) = parse(input)
return listOfNumbers
.filter { numbers -> numbers == sort(numbers, rules) }
.sumOf { numbers -> numbers[numbers.size / 2] }
}
fun part2(input: String): Int {
val (rules, listOfNumbers) = parse(input)
return listOfNumbers
.filterNot { numbers -> numbers == sort(numbers, rules) }
.map { numbers -> sort(numbers, rules) }
.sumOf { numbers -> numbers[numbers.size / 2] }
}
private fun sort(numbers: List<Int>, rules: List<Pair<Int, Int>>): List<Int> {
return numbers.sortedWith { a, b -> if (rules.contains(a to b)) -1 else 1 }
}
private fun parse(input: String): Pair<List<Pair<Int, Int>>, List<List<Int>>> {
val (rulesSection, numbersSection) = input.split("\n\n")
val rules = rulesSection.lines()
.mapNotNull { line -> """(\d{2})\|(\d{2})""".toRegex().matchEntire(line) }
.map { match -> match.groups[1]?.value?.toInt()!! to match.groups[2]?.value?.toInt()!! }
val numbers = numbersSection.lines().map { line -> line.split(',').map { it.toInt() } }
return rules to numbers
}
2 points
I guess adding type aliases and removing the regex from parser makes it a bit more readable.
typealias Rule = Pair<Int, Int>
typealias PageNumbers = List<Int>
fun part1(input: String): Int {
val (rules, listOfNumbers) = parse(input)
return listOfNumbers
.filter { numbers -> numbers == sort(numbers, rules) }
.sumOf { numbers -> numbers[numbers.size / 2] }
}
fun part2(input: String): Int {
val (rules, listOfNumbers) = parse(input)
return listOfNumbers
.filterNot { numbers -> numbers == sort(numbers, rules) }
.map { numbers -> sort(numbers, rules) }
.sumOf { numbers -> numbers[numbers.size / 2] }
}
private fun sort(numbers: PageNumbers, rules: List<Rule>): PageNumbers {
return numbers.sortedWith { a, b -> if (rules.contains(a to b)) -1 else 1 }
}
private fun parse(input: String): Pair<List<Rule>, List<PageNumbers>> {
val (rulesSection, numbersSection) = input.split("\n\n")
val rules = rulesSection.lines()
.mapNotNull { line ->
val parts = line.split('|').map { it.toInt() }
if (parts.size >= 2) parts[0] to parts[1] else null
}
val numbers = numbersSection.lines()
.map { line -> line.split(',').map { it.toInt() } }
return rules to numbers
}
1 point
5 points
Factor
: get-input ( -- rules updates )
"vocab:aoc-2024/05/input.txt" utf8 file-lines
{ "" } split1
"|" "," [ '[ [ _ split ] map ] ] bi@ bi* ;
: relevant-rules ( rules update -- rules' )
'[ [ _ in? ] all? ] filter ;
: compliant? ( rules update -- ? )
[ relevant-rules ] keep-under
[ [ index* ] with map first2 < ] with all? ;
: middle-number ( update -- n )
dup length 2 /i nth-of string>number ;
: part1 ( -- n )
get-input
[ compliant? ] with
[ middle-number ] filter-map sum ;
: compare-pages ( rules page1 page2 -- <=> )
[ 2array relevant-rules ] keep-under
[ drop +eq+ ] [ first index zero? +gt+ +lt+ ? ] if-empty ;
: correct-update ( rules update -- update' )
[ swapd compare-pages ] with sort-with ;
: part2 ( -- n )
get-input dupd
[ compliant? ] with reject
[ correct-update middle-number ] with map-sum ;