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
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))
Smalltalk
parsing logic is duplicated between the two, and I probably could use part2βs logic for part 1, but yeah
part 1
day5p1: in
| rules pages i j input |
input := in lines.
i := input indexOf: ''.
rules := ((input copyFrom: 1 to: i-1) collect: [:l | (l splitOn: '|') collect: #asInteger]).
pages := (input copyFrom: i+1 to: input size) collect: [:l | (l splitOn: ',') collect: #asInteger].
^ pages sum: [ :p |
(rules allSatisfy: [ :rule |
i := p indexOf: (rule at: 1).
j := p indexOf: (rule at: 2).
(i ~= 0 & (j ~= 0)) ifTrue: [ i < j ] ifFalse: [ true ]
])
ifTrue: [p at: ((p size / 2) round: 0) ]
ifFalse: [0].
]
part 2
day5p2: in
| rules pages i pnew input |
input := in lines.
i := input indexOf: ''.
rules := ((input copyFrom: 1 to: i-1) collect: [:l | (l splitOn: '|') collect: #asInteger]).
pages := (input copyFrom: i+1 to: input size) collect: [:l | (l splitOn: ',') collect: #asInteger].
^ pages sum: [ :p |
pnew := p sorted: [ :x :y |
rules anySatisfy: [ :r | (r at: 1) = x and: [ (r at: 2) = y]]
].
pnew ~= p
ifTrue: [ pnew at: ((pnew size / 2) round: 0) ]
ifFalse: [0].
]
Iβve got a βsmartβ solution and a really dumb one. Iβll start with the smart one (incomplete but you can infer). I did four different ways to try to get it faster, less memory, etc.
// this is from a nuget package. My Mathy roommate told me this was a topological sort.
// It's also my preferred, since it'd perform better on larger data sets.
return lines
.AsParallel()
.Where(line => !IsInOrder(GetSoonestOccurrences(line), aggregateRules))
.Sum(line => line.StableOrderTopologicallyBy(
getDependencies: page =>
aggregateRules.TryGetValue(page, out var mustPreceed) ? mustPreceed.Intersect(line) : Enumerable.Empty<Page>())
.Middle()
);
The dumb solution. These comparisons arenβt fully transitive. I canβt believe it works.
public static SortedSet<Page> Sort3(Page[] line,
Dictionary<Page, System.Collections.Generic.HashSet<Page>> rules)
{
// how the hell is this working?
var sorted = new SortedSet<Page>(new Sort3Comparer(rules));
foreach (var page in line)
sorted.Add(page);
return sorted;
}
public static Page[] OrderBy(Page[] line, Dictionary<Page, System.Collections.Generic.HashSet<Page>> rules)
{
return line.OrderBy(identity, new Sort3Comparer(rules)).ToArray();
}
sealed class Sort3Comparer : IComparer<Page>
{
private readonly Dictionary<Page, System.Collections.Generic.HashSet<Page>> _rules;
public Sort3Comparer(Dictionary<Page, System.Collections.Generic.HashSet<Page>> rules) => _rules = rules;
public int Compare(Page x, Page y)
{
if (_rules.TryGetValue(x, out var xrules))
{
if (xrules.Contains(y))
return -1;
}
if (_rules.TryGetValue(y, out var yrules))
{
if (yrules.Contains(x))
return 1;
}
return 0;
}
}
Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
---|---|---|---|---|---|---|
Part2_UsingList (literally just Insert) | 660.3 us | 12.87 us | 23.20 us | 187.5000 | 35.1563 | 1144.86 KB |
Part2_TrackLinkedList (wrong now) | 1,559.7 us | 6.91 us | 6.46 us | 128.9063 | 21.4844 | 795.03 KB |
Part2_TopologicalSort | 732.3 us | 13.97 us | 16.09 us | 285.1563 | 61.5234 | 1718.36 KB |
Part2_SortedSet | 309.1 us | 4.13 us | 3.45 us | 54.1992 | 10.2539 | 328.97 KB |
Part2_OrderBy | 304.5 us | 6.09 us | 9.11 us | 48.8281 | 7.8125 | 301.29 KB |
Uiua
This is the first one that caused me some headache because I didnβt read the instructions carefully enough.
I kept trying to create a sorted list for when all available pages were used, which got me stuck in an endless loop.
Another fun part was figuring out to use memberof (β)
instead of find (β)
in the last line of FindNext
. So much time spent on debugging other areas of the code
Run with example input here
FindNext β β(
β‘1β,
ββ½(β½Β¬)βΈβ
ββ(β‘0β.)
:β(β(β½Β¬β))
)
# find the order of pages for a given set of rules
FindOrder β (
β΄β.
[]
β’(βFindNext|β
(>1⧻))
βββ
)
PartOne β (
&rs β &fo "input-5.txt"
β©Β°β‘Β°βββ‘Β¬β"\n\n".
β(β(β‘βββ @,.)β @\n.β1)
β(βββ @|.)β @\n.
β.
Β€
β(β‘(Β°β‘:)
β:β(Β°ββ)
=2+β©β
β½
FindOrder
βΈβΒ°β‘:
ββ
)
β‘β(β‘βΓ·2⧻.)β½β
/+
)
PartTwo β (
&rs β &fo "input-5.txt"
β©Β°β‘Β°βββ‘Β¬β"\n\n".
β(β(β‘βββ @,.)β @\n.β1)
β(βββ @|.)β @\n.
β.
βΒ€β(
β‘(Β°β‘:)
β:β(Β°ββ)
=2+β©β
β½
FindOrder
βΈβΒ°β‘:
ββ©β‘
)
ββ
β(β‘0)(β‘1)β
β‘β(β‘βΓ·2⧻.)β½Β¬β‘Β°β‘
/+
)
&p "Day 5:"
&pf "Part 1: "
&p PartOne
&pf "Part 2: "
&p PartTwo
Lisp
Part 1 and 2
(defun p1-process-rules (line)
(mapcar #'parse-integer (uiop:split-string line :separator "|")))
(defun p1-process-pages (line)
(mapcar #'parse-integer (uiop:split-string line :separator ",")))
(defun middle (pages)
(nth (floor (length pages) 2) pages))
(defun check-rule-p (rule pages)
(let ((p1 (position (car rule) pages))
(p2 (position (cadr rule) pages)))
(or (not p1) (not p2) (< p1 p2))))
(defun ordered-p (pages rules)
(loop for r in rules
unless (check-rule-p r pages)
return nil
finally
(return t)))
(defun run-p1 (rules-file pages-file)
(let ((rules (read-file rules-file #'p1-process-rules))
(pages (read-file pages-file #'p1-process-pages)))
(loop for p in pages
when (ordered-p p rules)
sum (middle p)
)))
(defun fix-pages (rules pages)
(sort pages (lambda (p1 p2) (ordered-p (list p1 p2) rules)) ))
(defun run-p2 (rules-file pages-file)
(let ((rules (read-file rules-file #'p1-process-rules))
(pages (read-file pages-file #'p1-process-pages)))
(loop for p in pages
unless (ordered-p p rules)
sum (middle (fix-pages rules p))
)))