Goated movie. They don’t make them like that anymore.
Goated movie. They don’t make them like that anymore.
Very fiddly solution with lots of debugging required.
type
Vec2 = tuple[x,y: int]
Box = array[2, Vec2]
Dir = enum
U = "^"
R = ">"
D = "v"
L = "<"
proc convertPart2(grid: seq[string]): seq[string] =
for y in 0..grid.high:
result.add ""
for x in 0..grid[0].high:
result[^1] &= (
if grid[y][x] == 'O': "[]"
elif grid[y][x] == '#': "##"
else: "..")
proc shiftLeft(grid: var seq[string], col: int, range: HSlice[int,int]) =
for i in range.a ..< range.b:
grid[col][i] = grid[col][i+1]
grid[col][range.b] = '.'
proc shiftRight(grid: var seq[string], col: int, range: HSlice[int,int]) =
for i in countDown(range.b, range.a+1):
grid[col][i] = grid[col][i-1]
grid[col][range.a] = '.'
proc box(pos: Vec2, grid: seq[string]): array[2, Vec2] =
if grid[pos.y][pos.x] == '[':
[pos, (pos.x+1, pos.y)]
else:
[(pos.x-1, pos.y), pos]
proc step(grid: var seq[string], bot: var Vec2, dir: Dir) =
var (x, y) = bot
case dir
of U:
while (dec y; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y-1][bot.x] == 'O': swap(grid[bot.y-1][bot.x], grid[y][x])
dec bot.y
of R:
while (inc x; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y][bot.x+1] == 'O': swap(grid[bot.y][bot.x+1], grid[y][x])
inc bot.x
of L:
while (dec x; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y][bot.x-1] == 'O': swap(grid[bot.y][bot.x-1], grid[y][x])
dec bot.x
of D:
while (inc y; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y+1][bot.x] == 'O': swap(grid[bot.y+1][bot.x], grid[y][x])
inc bot.y
proc canMoveVert(box: Box, grid: seq[string], boxes: var HashSet[Box], dy: int): bool =
boxes.incl box
var left, right = false
let (lbox, rbox) = (box[0], box[1])
let lbigBox = box((lbox.x, lbox.y+dy), grid)
let rbigBox = box((rbox.x, lbox.y+dy), grid)
if grid[lbox.y+dy][lbox.x] == '#' or
grid[rbox.y+dy][rbox.x] == '#': return false
elif grid[lbox.y+dy][lbox.x] == '.': left = true
else:
left = canMoveVert(box((lbox.x,lbox.y+dy), grid), grid, boxes, dy)
if grid[rbox.y+dy][rbox.x] == '.': right = true
elif lbigBox == rbigBox: right = left
else:
right = canMoveVert(box((rbox.x, rbox.y+dy), grid), grid, boxes, dy)
left and right
proc moveBoxes(grid: var seq[string], boxes: var HashSet[Box], d: Vec2) =
for box in boxes:
grid[box[0].y][box[0].x] = '.'
grid[box[1].y][box[1].x] = '.'
for box in boxes:
grid[box[0].y+d.y][box[0].x+d.x] = '['
grid[box[1].y+d.y][box[1].x+d.x] = ']'
boxes.clear()
proc step2(grid: var seq[string], bot: var Vec2, dir: Dir) =
case dir
of U:
if grid[bot.y-1][bot.x] == '#': return
if grid[bot.y-1][bot.x] == '.': dec bot.y
else:
var boxes: HashSet[Box]
if canMoveVert(box((x:bot.x, y:bot.y-1), grid), grid, boxes, -1):
grid.moveBoxes(boxes, (0, -1))
dec bot.y
of R:
var (x, y) = bot
while (inc x; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y][bot.x+1] == '[': grid.shiftRight(bot.y, bot.x+1..x)
inc bot.x
of L:
var (x, y) = bot
while (dec x; grid[y][x] != '#' and grid[y][x] != '.'): discard
if grid[y][x] == '#': return
if grid[bot.y][bot.x-1] == ']': grid.shiftLeft(bot.y, x..bot.x-1)
dec bot.x
of D:
if grid[bot.y+1][bot.x] == '#': return
if grid[bot.y+1][bot.x] == '.': inc bot.y
else:
var boxes: HashSet[Box]
if canMoveVert(box((x:bot.x, y:bot.y+1), grid), grid, boxes, 1):
grid.moveBoxes(boxes, (0, 1))
inc bot.y
proc solve(input: string): AOCSolution[int, int] =
let chunks = input.split("\n\n")
var grid = chunks[0].splitLines()
let movements = chunks[1].splitLines().join().join()
var robot: Vec2
for y in 0..grid.high:
for x in 0..grid[0].high:
if grid[y][x] == '@':
grid[y][x] = '.'
robot = (x,y)
block p1:
var grid = grid
var robot = robot
for m in movements:
let dir = parseEnum[Dir]($m)
step(grid, robot, dir)
for y in 0..grid.high:
for x in 0..grid[0].high:
if grid[y][x] == 'O':
result.part1 += 100 * y + x
block p2:
var grid = grid.convertPart2()
var robot = (robot.x*2, robot.y)
for m in movements:
let dir = parseEnum[Dir]($m)
step2(grid, robot, dir)
#grid.inspect(robot)
for y in 0..grid.high:
for x in 0..grid[0].high:
if grid[y][x] == '[':
result.part2 += 100 * y + x
Part 1: there’s no need to simulate each step, final position for each robot is
(position + velocity * iterations) modulo grid
Part 2: I solved it interactively: Maybe I just got lucky, but my input has certain pattern: after 99th iteration every 101st iteration looking very different from other. I printed first couple hundred iterations, noticed a pattern and started looking only at “interesting” grids. It took 7371 iterations (I only had to check 72 manually) to reach an easter egg.
type
Vec2 = tuple[x,y: int]
Robot = object
pos, vel: Vec2
var
GridRows = 101
GridCols = 103
proc examine(robots: seq[Robot]) =
for y in 0..<GridCols:
for x in 0..<GridRows:
let c = robots.countIt(it.pos == (x, y))
stdout.write if c == 0: '.' else: char('0'.ord + c)
stdout.write '\n'
stdout.flushFile()
proc solve(input: string): AOCSolution[int, int] =
var robots: seq[Robot]
for line in input.splitLines():
let parts = line.split({' ',',','='})
robots.add Robot(pos: (parts[1].parseInt,parts[2].parseInt),
vel: (parts[4].parseInt,parts[5].parseInt))
block p1:
var quads: array[4, int]
for robot in robots:
let
newX = (robot.pos.x + robot.vel.x * 100).euclmod GridRows
newY = (robot.pos.y + robot.vel.y * 100).euclmod GridCols
relRow = cmp(newX, GridRows div 2)
relCol = cmp(newY, GridCols div 2)
if relRow == 0 or relCol == 0: continue
inc quads[int(relCol>0)*2 + int(relRow>0)]
result.part1 = quads.foldl(a*b)
block p2:
if GridRows != 101: break p2
var interesting = 99
var interval = 101
var i = 0
while true:
for robot in robots.mitems:
robot.pos.x = (robot.pos.x + robot.vel.x).euclmod GridRows
robot.pos.y = (robot.pos.y + robot.vel.y).euclmod GridCols
inc i
if i == interesting:
robots.examine()
echo "Iteration #", i, "; Do you see an xmas tree?[N/y]"
if stdin.readLine().normalize() == "y":
result.part2 = i
break
interesting += interval
I’m embarrasingly bad with math. Couldn’t have solved this one without looking up the solution. =C
type Vec2 = tuple[x,y: int64]
const
PriceA = 3
PriceB = 1
ErrorDelta = 10_000_000_000_000
proc isInteger(n: float): bool = n.round().almostEqual(n)
proc `+`(a: Vec2, b: int): Vec2 = (a.x + b, a.y + b)
proc solveEquation(a, b, prize: Vec2): int =
let res_a = (prize.x*b.y - prize.y*b.x) / (a.x*b.y - a.y*b.x)
let res_b = (a.x*prize.y - a.y*prize.x) / (a.x*b.y - a.y*b.x)
if res_a.isInteger and res_b.isInteger:
res_a.int * PriceA + res_b.int * PriceB
else: 0
proc solve(input: string): AOCSolution[int, int] =
let chunks = input.split("\n\n")
for chunk in chunks:
let lines = chunk.splitLines()
let partsA = lines[0].split({' ', ',', '+'})
let partsB = lines[1].split({' ', ',', '+'})
let partsC = lines[2].split({' ', ',', '='})
let a = (parseBiggestInt(partsA[3]), parseBiggestInt(partsA[6]))
let b = (parseBiggestInt(partsB[3]), parseBiggestInt(partsB[6]))
let c = (parseBiggestInt(partsC[2]), parseBiggestInt(partsC[5]))
result.part1 += solveEquation(a,b,c)
result.part2 += solveEquation(a,b,c+ErrorDelta)
Runtime: 7ms 3.18 ms
Part 1: I use flood fill to count all grouped plants and keep track of each border I see.
Part 2: I use an algorithm similar to “merge overlapping ranges” to count spans of borders (border orientation matters) in each row and column, for each group. Resulting code (hidden under spoiler) is a little messy and not very DRY (it’s completely soaked).
Edit: refactored solution, removed some very stupid code.
proc groupSpans()
proc groupSpans(borders: seq[(Vec2, Dir)]): int =
## returns number of continuous groups of cells with same Direction
## and on the same row or column
var borders = borders
var horiz = borders.filterIt(it[1] in {U, D})
while horiz.len > 0:
var sameYandDir = @[horiz.pop()]
var curY = sameYandDir[^1][0].y
var curDir = sameYandDir[^1][1]
for i in countDown(horiz.high, 0):
if horiz[i][0].y == curY and horiz[i][1] == curDir:
sameYandDir.add horiz[i]
horiz.del i
sameYandDir.sort((a,b)=>cmp(a[0].x, b[0].x), Descending)
var cnt = 1
for i, (p,d) in sameYandDir.toOpenArray(1, sameYandDir.high):
if sameYandDir[i][0].x - p.x != 1: inc cnt
result += cnt
var vert = borders.filterIt(it[1] in {L, R})
while vert.len > 0:
var sameXandDir = @[vert.pop()]
var curX = sameXandDir[^1][0].x
var curDir = sameXandDir[^1][1]
for i in countDown(vert.high, 0):
if vert[i][0].x == curX and vert[i][1] == curDir:
sameXandDir.add vert[i]
vert.del i
sameXandDir.sort((a,b)=>cmp(a[0].y, b[0].y), Descending)
var cnt = 1
for i, (p,d) in sameXandDir.toOpenArray(1, sameXandDir.high):
if sameXandDir[i][0].y - p.y != 1: inc cnt
result += cnt
type
Dir = enum L,R,U,D
Vec2 = tuple[x,y: int]
GroupData = object
plantCount: int
borders: seq[(Vec2, Dir)]
const Adjacent: array[4, Vec2] = [(-1,0),(1,0),(0,-1),(0,1)]
proc solve(input: string): AOCSolution[int, int] =
let grid = input.splitLines()
var visited = newSeqWith(grid.len, newSeq[bool](grid[0].len))
var groups: seq[GroupData]
proc floodFill(pos: Vec2, plant: char, groupId: int) =
visited[pos.y][pos.x] = true
inc groups[groupId].plantCount
for di, d in Adjacent:
let pd: Vec2 = (pos.x+d.x, pos.y+d.y)
if pd.x < 0 or pd.y < 0 or pd.x > grid[0].high or pd.y > grid.high or
grid[pd.y][pd.x] != plant:
groups[groupId].borders.add (pd, Dir(di))
continue
if visited[pd.y][pd.x]: continue
floodFill(pd, plant, groupId)
for y in 0..grid.high:
for x in 0..grid[0].high:
if visited[y][x]: continue
groups.add GroupData()
floodFill((x,y), grid[y][x], groups.high)
for gid, group in groups:
result.part1 += group.plantCount * group.borders.len
result.part2 += group.plantCount * group.borders.groupSpans()
Runtime: 30-40 ms
I’m not very experienced with recursion and memoization, so this took me quite a while.
Edit: slightly better version
template splitNum(numStr: string): seq[int] =
@[parseInt(numStr[0..<numStr.len div 2]), parseInt(numStr[numStr.len div 2..^1])]
template applyRule(stone: int): seq[int] =
if stone == 0: @[1]
else:
let numStr = $stone
if numStr.len mod 2 == 0: splitNum(numStr)
else: @[stone * 2024]
proc memRule(st: int): seq[int] =
var memo {.global.}: Table[int, seq[int]]
if st in memo: return memo[st]
result = st.applyRule
memo[st] = result
proc countAfter(stone: int, targetBlinks: int): int =
var memo {.global.}: Table[(int, int), int]
if (stone,targetBlinks) in memo: return memo[(stone,targetBlinks)]
if targetBlinks == 0: return 1
for st in memRule(stone):
result += st.countAfter(targetBlinks - 1)
memo[(stone,targetBlinks)] = result
proc solve(input: string): AOCSolution[int, int] =
for stone in input.split.map(parseInt):
result.part1 += stone.countAfter(25)
result.part2 += stone.countAfter(75)
As many others today, I’ve solved part 2 first and then fixed a ‘bug’ to solve part 1. =)
type Vec2 = tuple[x,y:int]
const Adjacent = [(x:1,y:0),(-1,0),(0,1),(0,-1)]
proc path(start: Vec2, grid: seq[string]): tuple[ends, trails: int] =
var queue = @[@[start]]
var endNodes: HashSet[Vec2]
while queue.len > 0:
let path = queue.pop()
let head = path[^1]
let c = grid[head.y][head.x]
if c == '9':
inc result.trails
endNodes.incl head
continue
for d in Adjacent:
let nd = (x:head.x + d.x, y:head.y + d.y)
if nd.x < 0 or nd.y < 0 or nd.x > grid[0].high or nd.y > grid.high:
continue
if grid[nd.y][nd.x].ord - c.ord != 1: continue
queue.add path & nd
result.ends = endNodes.len
proc solve(input: string): AOCSolution[int, int] =
let grid = input.splitLines()
var trailstarts: seq[Vec2]
for y, line in grid:
for x, c in line:
if c == '0':
trailstarts.add (x,y)
for start in trailstarts:
let (ends, trails) = start.path(grid)
result.part1 += ends
result.part2 += trails
Wrote ugly-ass code today, but it was surprisingly easy to debug and fast.
Solution:
Part 1: Parse data into a sequence of blocks and empty space like in example (I use -1
for empty space) and two indexes. First index goes 0 -> end, second index starts at the end. When we encounter empty space -> we use value from second index and decrement it (while skipping empty spaces). Repeat until both indexes meet at some point.
Part 2: Parse data into sequence of block objects and try to insert each data block into each empty space block before it. Somehow it all just worked without too many bugs.
Runtime (final version): 123 ms
type
BlockKind = enum Data, Space
Block = object
size: int
case kind: BlockKind
of Data:
index: int
of Space:
discard
func parseBlocks(input: string): tuple[blocks: seq[Block], id: int] =
for i, c in input:
let digit = c.ord - '0'.ord
if i mod 2 == 0:
result.blocks.add Block(kind: Data, size: digit, index: result.id)
if i < input.high: inc result.id
else:
result.blocks.add Block(kind: Space, size: digit)
proc solve(input: string): AOCSolution[int, int] =
block p1:
var memBlocks = newSeqOfCap[int](100_000)
var indBlock = 0
for i, c in input:
let digit = c.ord - '0'.ord
if i mod 2 == 0:
memBlocks.add (indBlock).repeat(digit)
inc indBlock
else:
memBlocks.add -1.repeat(digit)
var ind = 0
var revInd = memBlocks.high
while ind <= revInd:
if memBlocks[ind] == -1:
while memBlocks[revInd] == -1: dec revInd
result.part1 += ind * memBlocks[revInd]
dec revInd
else:
result.part1 += ind * memBlocks[ind]
inc ind
block p2:
var (memBlocks, index) = parseBlocks(input)
var revInd = memBlocks.high
while revInd > 0:
doAssert memBlocks[revInd].kind == Data
var spaceInd = -1
let blockSize = memBlocks[revInd].size
for ind in 0..revInd:
if memBlocks[ind].kind == Space and memBlocks[ind].size >= blockSize:
spaceInd = ind; break
if spaceInd != -1:
let bSize = memBlocks[revInd].size
let diffSize = memBlocks[spaceInd].size - bSize
swap(memBlocks[spaceInd], memBlocks[revInd])
if diffSize != 0:
memBlocks[revInd].size = bSize
memBlocks.insert(Block(kind: Space, size: diffSize), spaceInd + 1)
inc revInd # shift index bc we added object
dec index
# skip space blocks and data blocks with higher index
while (dec revInd; revInd < 0 or
memBlocks[revInd].kind != Data or
memBlocks[revInd].index != index): discard
var unitIndex = 0
for b in memBlocks:
case b.kind
of Data:
for _ in 1..b.size:
result.part2 += unitIndex * b.index
inc unitIndex
of Space:
unitIndex += b.size
maybe Eric and co have begun to realise that family time is more precious than work time
Last year the difficulty was fluctuating from 0 to 100 each day.
This year all problems so far are suspiciously easy. Maybe the second half of the month will be extra hard?
Overall really simple puzzle, but description is so confusing, that I mostly solved it based on example diagrams.
Edit: much shorter and faster one-pass solution. Runtime: 132 us
type Vec2 = tuple[x,y: int]
func delta(a, b: Vec2): Vec2 = (a.x-b.x, a.y-b.y)
func outOfBounds[T: openarray | string](pos: Vec2, grid: seq[T]): bool =
pos.x < 0 or pos.y < 0 or pos.x > grid[0].high or pos.y > grid.high
proc solve(input: string): AOCSolution[int, int] =
var grid = input.splitLines()
var antennas: Table[char, seq[Vec2]]
for y, line in grid:
for x, c in line:
if c != '.':
discard antennas.hasKeyOrPut(c, newSeq[Vec2]())
antennas[c].add (x, y)
var antinodesP1: HashSet[Vec2]
var antinodesP2: HashSet[Vec2]
for _, list in antennas:
for ind, ant1 in list:
antinodesP2.incl ant1 # each antenna is antinode
for ant2 in list.toOpenArray(ind+1, list.high):
let d = delta(ant1, ant2)
for dir in [-1, 1]:
var i = dir
while true:
let antinode = (x: ant1.x+d.x*i, y: ant1.y+d.y*i)
if antinode.outOfBounds(grid): break
if i in [1, -2]: antinodesP1.incl antinode
antinodesP2.incl antinode
i += dir
result.part1 = antinodesP1.len
result.part2 = antinodesP2.len
Where have you seen a human that sits like this? 🙃
Bruteforce, my beloved.
Wasted too much time on part 2 trying to make combinations iterator (it was very slow). In the end solved both parts with 3^n
and toTernary
.
Runtime: 1.5s
func digits(n: int): int =
result = 1; var n = n
while (n = n div 10; n) > 0: inc result
func concat(a: var int, b: int) =
a = a * (10 ^ b.digits) + b
func toTernary(n: int, len: int): seq[int] =
result = newSeq[int](len)
if n == 0: return
var n = n
for i in 0..<len:
result[i] = n mod 3
n = n div 3
proc solve(input: string): AOCSolution[int, int] =
for line in input.splitLines():
let parts = line.split({':',' '})
let res = parts[0].parseInt
var values: seq[int]
for i in 2..parts.high:
values.add parts[i].parseInt
let opsCount = values.len - 1
var solvable = (p1: false, p2: false)
for s in 0 ..< 3^opsCount:
var sum = values[0]
let ternary = s.toTernary(opsCount)
for i, c in ternary:
case c
of 0: sum *= values[i+1]
of 1: sum += values[i+1]
of 2: sum.concat values[i+1]
else: raiseAssert"!!"
if sum == res:
if ternary.count(2) == 0:
solvable.p1 = true
solvable.p2 = true
if solvable == (true, true): break
if solvable.p1: result.part1 += res
if solvable.p2: result.part2 += res
Not the prettiest code, but it runs in 3 seconds. For part 2 I just place an obstacle at every position guard visited in part 1.
Edit: made step
procedure more readable.
type
Vec2 = tuple[x,y: int]
Dir = enum
Up, Right, Down, Left
Guard = object
pos: Vec2
dir: Dir
proc step(guard: var Guard, map: seq[string]): bool =
let next: Vec2 =
case guard.dir
of Up: (guard.pos.x, guard.pos.y-1)
of Right: (guard.pos.x+1, guard.pos.y)
of Down: (guard.pos.x, guard.pos.y+1)
of Left: (guard.pos.x-1, guard.pos.y)
if next.y < 0 or next.x < 0 or next.y > map.high or next.x > map[0].high:
return false
elif map[next.y][next.x] == '#':
guard.dir = Dir((guard.dir.ord + 1) mod 4)
else:
guard.pos = next
true
proc solve(input: string): AOCSolution[int, int] =
var map = input.splitLines()
var guardStart: Vec2
block findGuard:
for y, line in map:
for x, c in line:
if c == '^':
guardStart = (x, y)
map[y][x] = '.'
break findGuard
var visited: HashSet[Vec2]
block p1:
var guard = Guard(pos: guardStart, dir: Up)
while true:
visited.incl guard.pos
if not guard.step(map): break
result.part1 = visited.len
block p2:
for (x, y) in visited - [guardStart].toHashSet:
var loopCond: HashSet[Guard]
var guard = Guard(pos: guardStart, dir: Up)
var map = map
map[y][x] = '#'
while true:
loopCond.incl guard
if not guard.step(map): break
if guard in loopCond:
inc result.part2
break
Good old bubble sort
You can put egregious amount of cheese on anything and it would taste atleast half as good as pizza.
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]
Could be done more elegantly, but I haven’t bothered yet.
proc solve(input: string): AOCSolution[int, int] =
var lines = input.splitLines()
block p1:
# horiz
for line in lines:
for i in 0..line.high-3:
if line[i..i+3] in ["XMAS", "SAMX"]:
inc result.part1
for y in 0..lines.high-3:
#vert
for x in 0..lines[0].high:
let word = collect(for y in y..y+3: lines[y][x])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
#diag \
for x in 0..lines[0].high-3:
let word = collect(for d in 0..3: lines[y+d][x+d])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
#diag /
for x in 3..lines[0].high:
let word = collect(for d in 0..3: lines[y+d][x-d])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
block p2:
for y in 0..lines.high-2:
for x in 0..lines[0].high-2:
let diagNW = collect(for d in 0..2: lines[y+d][x+d])
let diagNE = collect(for d in 0..2: lines[y+d][x+2-d])
if diagNW in [@"MAS", @"SAM"] and diagNE in [@"MAS", @"SAM"]:
inc result.part2
From a first glance it was obviously a regex problem.
I’m using tinyre here instead of stdlib re
library just because I’m more familiar with it.
import pkg/tinyre
proc solve(input: string): AOCSolution[int, int] =
var allow = true
for match in input.match(reG"mul\(\d+,\d+\)|do\(\)|don't\(\)"):
if match == "do()": allow = true
elif match == "don't()": allow = false
else:
let pair = match[4..^2].split(',')
let mult = pair[0].parseInt * pair[1].parseInt
result.part1 += mult
if allow: result.part2 += mult
Nim, because it’s fast and expressive.
Duck me in the ark tonight