Nim
import ../aoc, strutils, sequtils, tables type Rules = ref Table[int, seq[int]] #check if an update sequence is valid proc valid(update:seq[int], rules:Rules):bool = for pi, p in update: for r in rules.getOrDefault(p): let ri = update.find(r) if ri != -1 and ri < pi: return false return true proc backtrack(p:int, index:int, update:seq[int], rules: Rules, sorted: var seq[int]):bool = if index == 0: sorted[index] = p return true for r in rules.getOrDefault(p): if r in update and r.backtrack(index-1, update, rules, sorted): sorted[index] = p return true return false #fix an invalid sequence proc fix(update:seq[int], rules: Rules):seq[int] = echo "fixing", update var sorted = newSeqWith(update.len, 0); for p in update: if p.backtrack(update.len-1, update, rules, sorted): return sorted return @[] proc solve*(input:string): array[2,int] = let parts = input.split("\r\n\r\n"); let rulePairs = parts[0].splitLines.mapIt(it.strip.split('|').map(parseInt)) let updates = parts[1].splitLines.mapIt(it.split(',').map(parseInt)) # fill rules table var rules = new Rules for rp in rulePairs: if rules.hasKey(rp[0]): rules[rp[0]].add rp[1]; else: rules[rp[0]] = @[rp[1]] # fill reverse rules table var backRules = new Rules for rp in rulePairs: if backRules.hasKey(rp[1]): backRules[rp[1]].add rp[0]; else: backRules[rp[1]] = @[rp[0]] for u in updates: if u.valid(rules): result[0] += u[u.len div 2] else: let uf = u.fix(backRules) result[1] += uf[uf.len div 2]I thought of doing a sort at first, but dismissed it for some reason, so I came up with this slow and bulky recursive backtracking thing which traverses the rules as a graph until it reaches a depth equal to the given sequence. Not my finest work, but it does solve the puzzle :)
Nim
import ../aoc, strutils type Cell* = tuple[x,y:int] #the 8 grid direction const directions : array[8, Cell] = [ (1, 0), (-1, 0), (0, 1), ( 0,-1), (1, 1), (-1,-1), (1,-1), (-1, 1) ] const xmas = "XMAS" #part 1 proc searchXMAS*(grid:seq[string], x,y:int):int = #search in all 8 directions (provided we can find a full match in that direction) let w = grid[0].len let h = grid.len for dir in directions: # check if XMAS can even fit let xEnd = x + dir.x * 3 let yEnd = y + dir.y * 3 if xEnd < 0 or xEnd >= w or yEnd < 0 or yEnd >= h: continue; #step along direction var matches = 0 for s in 0..3: if grid[y + dir.y * s][x + dir.x * s] == xmas[s]: inc matches if matches == xmas.len: inc result #part 2 proc isMAS(grid:seq[string], c, o:Cell):bool= let ca : Cell = (c.x+o.x, c.y+o.y) let cb : Cell = (c.x-o.x, c.y-o.y) let a = grid[ca.y][ca.x] let b = grid[cb.y][cb.x] (a == 'M' and b == 'S') or (a == 'S' and b == 'M') proc searchCrossMAS*(grid:seq[string], x,y:int):bool = grid[y][x] == 'A' and grid.isMAS((x,y), (1,1)) and grid.isMAS((x,y), (1,-1)) proc solve*(input:string): array[2,int] = let grid = input.splitLines let w = grid[0].len let h = grid.len #part 1 for y in 0..<h: for x in 0..<w: result[0] += grid.searchXMAS(x, y) #part 2, skipping borders for y in 1..<h-1: for x in 1..<w-1: result[1] += (int)grid.searchCrossMAS(x, y)Part 1 was done really quickly. Part 2 as well, but the result was not accepted…
Turns out +MAS isn’t actually a thing :P
Nim
import ../aoc, re, sequtils, strutils, math proc mulsum*(line:string):int= let matches = line.findAll(re"mul\([0-9]{1,3},[0-9]{1,3}\)") let pairs = matches.mapIt(it[4..^2].split(',').map(parseInt)) pairs.mapIt(it[0]*it[1]).sum proc filter*(line:string):int= var state = true; var i=0 while i < line.len: if state: let off = line.find("don't()", i) if off == -1: break result += line[i..<off].mulsum i = off+6 state = false else: let on = line.find("do()", i) if on == -1: break i = on+4 state = true if state: result += line[i..^1].mulsum proc solve*(input:string): array[2,int] = #part 1&2 result = [input.mulsum, input.filter]I had a nicer solution in mind for part 2, but for some reason nre didn’t want to work for me, and re couldn’t give me the start/end or all results, so I ended up doing this skip/toggle approach.
Also initially I was doing it line by line out of habit from other puzzles, but then ofc the don’t()s didn’t propagate to the next line.
Nim
import strutils, times, sequtils, sugar # check if level transition in record is safe proc isSafe*(sign:bool, d:int): bool = sign == (d>0) and d.abs in 1..3; #check if record is valid proc validate*(record:seq[int]): bool = let sign = record[0] > record[1]; return (0..record.len-2).allIt(isSafe(sign, record[it] - record[it+1])) # check if record is valid as-is # or if removing any item makes the record valid proc validate2*(record:seq[int]): bool = return record.validate or (0..<record.len).anyIt(record.dup(delete(it)).validate) proc solve*(input:string): array[2,int] = let lines = input.readFile.strip.splitLines; let records = lines.mapIt(it.splitWhitespace.map(parseInt)); result[0] = records.countIt(it.validate); result[1] = records.countIt(it.validate2);I got stuck on part 2 trying to check everything inside a single loop, which kept getting more ugly. So then I switched to just deleting one item at a time and re-checking the record.
Reworked it after first finding the solution to compress the code a bit, though the range iterators don’t really help with readability.
I did learn about the sugar import, which I used to make the sequence duplication more compact: record.dup(delete(it).