If you read the digits in reverse it matches ISO 8601.
And yes I know it's a joke (:
Now I want to know the kitty's name, is it sword-related?
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 :)
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
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.
Could be Neon Genesis Evangelion, there's a scene where Shinji and Asuka are in the pod together, though they are both human.
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)
.
The sun is also happy :)