Lang: Civet
Something is wrong with global snow production, and you've been selected to take a look. The Elves have even given you a map; on it, they've used stars to mark the top fifty locations that are likely to be having problems.
{ log, getInput, sum, toNumber, entries, values } from ../utils.civet
codes := getInput import.meta.url |> .trim().split(',')
function calcHash(code: string)
[...code].reduce (nr, c) => (nr + c.charCodeAt 0) * 17 % 256, 0
log 'Part 1', sum codes.map calcHash
boxes := {}
for code of codes
label := code.replace /[^a-z]/g, ''
hash := calcHash label
(boxes[hash] ?= [])[label] = toNumber code
delete boxes[hash][label] if code.includes('-')
focusingPower .= 0
for [hash, lenses] of entries boxes
for focal, i of values lenses
focusingPower += (+hash + 1) * (i + 1) * focal
log 'Part 2', focusingPower
{ log, getLines, sum, cloneDeep, zip } from ../utils.civet
stones .= getLines import.meta.url |> .map .split ''
function step(stones: string[][])
return .= 0
for y of [0...stones.length]
for x of [0...stones.0.length]
if stones[y][x] is 'O' and stones[y - 1]?[x] is '.'
stones[y][x] = '.'
stones[y - 1][x] = 'O'
return++
function move(stones: string[][])
while step stones;
stones
function calcWeight(stones: string[][])
sum for line, y of stones
line.filter(& is 'O').length * (stones.length - y)
log 'Part 1', calcWeight move cloneDeep stones
function cycle()
for [0...4]
stones = zip(...move stones).map .reverse()
mem := {}
i .= 0
offset .= 0
loop
memKey := JSON.stringify stones
if mem[memKey]
offset = mem[memKey]
break
mem[memKey] = i++
cycle()
for [0...(1e9 - offset) % (i - offset)]
cycle()
log 'Part 2', calcWeight stones
{ log, getInput, sum, zip } from ../utils.civet
patterns := getInput import.meta.url
.split('\n\n').map &.split('\n')
function diffs(str1: string, str2: string)
sum for c, i of str1
c is not str2[i]
function rotate(pattern: string[])
zip(...pattern.map &.split('')).flatMap &.join('')
function searchMirror(pattern: string[], possibleErrors: number)
for y of [0...pattern.length - 1]
errors .= 0
for i .= 0; y - i >= 0 and y + i + 1 < pattern.length; i++
errors += diffs pattern[y - i], pattern[y + i + 1]
return y + 1 if errors is possibleErrors
0
function getNote(pattern: string[], possibleErrors: number)
100 * searchMirror(pattern, possibleErrors) +
searchMirror(rotate(pattern), possibleErrors)
log 'Part 1', sum patterns.map (p) => getNote p, 0
log 'Part 2', sum patterns.map (p) => getNote p, 1
{ log, getLines, toNumbers, sum } from ../utils.civet
lines := getLines(import.meta.url).map &.split ' '
mem := {}
function combinations(springs: string, groups: number[], counter = 0): number
memKey := springs + groups
return mem[memKey] if mem[memKey]
return '#' is in springs ? 0 : 1 unless groups.length
[nr, ...rest] := groups
for i .= 0; i <= springs.length - rest.length - sum(rest) - nr; i++
break if '#' is in springs[0...i]
if '.' is not in springs[i...i + nr] and springs[i + nr] is not "#"
counter += combinations springs[i + nr + 1..], rest
mem[memKey] = counter
log 'Part 1', sum for [springs, groups] of lines
combinations springs, toNumbers groups
log 'Part 2', sum for [springs, groups] of lines
combinations
new Array(5).fill(springs).join('?'),
new Array(5).fill(toNumbers groups).flatMap (&)
{ log, getLines, sum } from ../utils.civet
lines .= getLines import.meta.url
expandingRows := []
for line, y of lines
expandingRows.push y unless '#' is in line
expandingCols := []
for x of [0...lines.0.length]
expandingCols.push x if lines.every &[x] is '.'
galaxies := []
for y of [0...lines.length]
for x of [0...lines.0.length]
galaxies.push [x, y] if lines[y][x] is '#'
function getDistance([[x1, y1], [x2, y2]])
Math.abs(x1 - x2) + Math.abs(y1 - y2)
function getPairs(galaxies: number[][])
for i of [0...galaxies.length]
for j of [i + 1...galaxies.length]
[galaxies[i], galaxies[j]]
function expand(galaxies: number[][], multiplayer: number)
for [x, y] of galaxies
r := expandingRows.filter(& < y).length
c := expandingCols.filter(& < x).length
[x + c * (multiplayer - 1), y + r * (multiplayer - 1)]
function sumDistances(galaxies: number[][], expandMultiplier: number)
galaxies = expand galaxies, expandMultiplier
sum getPairs(galaxies).flatMap (&).map getDistance
log 'Part 1', sumDistances galaxies, 2
log 'Part 2', sumDistances galaxies, 1e6
pointInPolygon from 'point-in-polygon'
{ log, getLines } from ../utils.civet
lines := getLines import.meta.url
startY := lines.findIndex &.includes 'S'
startX := lines[startY].indexOf 'S'
last .= [startX, startY]
current .= [startX, startY + 1]
polygon := [current]
while next := move current, last
last = current
polygon.push current = next
function move([x, y]: number[], [prevX, prevY]: number[])
disabledDir .= switch prevX - x
> 0 then 'E'
< 0 then 'W'
else prevY - y < 0 ? 'N' : 'S'
switch lines[y][x] + disabledDir
'-E' [x - 1, y]
'-W' [x + 1, y]
'|N' [x, y + 1]
'|S' [x, y - 1]
'7W' [x, y + 1]
'7S' [x - 1, y]
'LN' [x + 1, y]
'LE' [x, y - 1]
'JW' [x, y - 1]
'JN' [x - 1, y]
'FS' [x + 1, y]
'FE' [x, y + 1]
log 'Part 1', polygon.length / 2
area .= 0
for y of [0...lines.length]
for x of [0...lines.length]
unless polygon.some [px, py] => px is x and py is y
area++ if pointInPolygon [x, y], polygon
log 'Part 2', area
{ log, getLines, toNumbers, sum } from ../utils.civet
lines := getLines(import.meta.url).map toNumbers
function getSequence(arr: number[])
for i of [1...arr.length]
arr[i] - arr[i - 1]
function predict(arr: number[])
arrs := [arr]
while sum arrs.-1
arrs.push getSequence arrs.-1
sum arrs.map .-1
log 'Part 1', sum lines.map predict
log 'Part 2', sum lines.map(.reverse()).map predict
{ log, getInput, getLcm, keys } from ../utils.civet
input := getInput import.meta.url |> .split '\n\n'
insNr .= 0
getIns := => input.0[insNr++ % input.0.length] is 'L' ? 0 : 1
nodes := {}
for node of input.1.split('\n').map .match /\w+/g
nodes[node.0] = node[1..]
function movesNr(startNode: string, endNodes: string[])
return .= 0
name .= startNode
node .= nodes[startNode]
until name is in endNodes
return++
name = node[getIns()]
node = nodes[name]
log 'Part 1', movesNr 'AAA', ['ZZZ']
startNodes := keys(nodes).filter .endsWith 'A'
endNodes := keys(nodes).filter .endsWith 'Z'
log 'Part 2', getLcm startNodes.map (start) => movesNr start, endNodes
{ log, getLines, int, sum, desc } from ../utils.civet
type Hand
cards: string
bid: number
type: number
lines := getLines import.meta.url
log 'Part 1', sum getWins(lines, false)
log 'Part 2', sum getWins(lines, true)
function getWins(lines: string[], joker: boolean)
lines
.map (line) => parseHand(line, joker)
.sort compareHands
.map (hand, i) => hand.bid * (i + 1)
function parseHand(line: string, joker: boolean): Hand
[cards, bid] .= line.split ' '
cards: fixCardsStrength cards, joker
bid: int bid
type: getType cards, joker
function getType(cards: string, joker: boolean)
counter := count cards.replaceAll joker ? 'J' : '', ''
if joker then (counter.0 ?= 0) += cards.match(/J/g)?.length ?? 0
switch counter
[5] 6
[4, 1] 5
[3, 2] 4
[3, 1, 1] 3
[2, 2, 1] 2
[2, 1, 1, 1] 1
[...] 0
function count(cards: string)
counter: Record<string, number> := {}
for card of cards.split('')
(counter[card] ?= 0)++
Object.values(counter).sort desc
function compareHands(a: Hand, b: Hand)
a.type is b.type
? a.cards > b.cards ? 1 : -1
: a.type - b.type
function fixCardsStrength(cards: string, joker: boolean)
cards
.replaceAll 'A', 'E'
.replaceAll 'K', 'D'
.replaceAll 'Q', 'C'
.replaceAll 'J', joker ? '1' : 'B'
.replaceAll 'T', 'A'
{ log, getLines, toNumbers, toNumber, multiply } from ../utils.civet
lines := getLines import.meta.url
function timesWins(time: number, record: number)
return .= 0
for t of [1...time]
return++ if time - t > record / t
log 'Part 1', multiply for i of [0...4]
timesWins
toNumbers(lines.0)[i],
toNumbers(lines.1)[i]
log 'Part 2', timesWins
toNumber lines.0.replaceAll ' ', ''
toNumber lines.1.replaceAll ' ', ''
{ log, getInput, toNumbers, inRange, chunk, min } from ../utils.civet
input := getInput(import.meta.url).split '\n\n'
seeds := toNumbers input.0
mappings := input[1..].map &.split('\n')[1..].map toNumbers
function translate(nr: number, map: number[][])
range := map.find (map) => inRange map.1, nr, map.1 + map.2
range ? nr + range.0 - range.1 : nr
function getMin(seeds: number[])
min .= Infinity
for let seed of seeds
for map of mappings
seed = translate seed, map
min = seed if seed < min
min
log 'Part 1', getMin seeds
log 'Part 2', min chunk(seeds, 2).map [start, length] =>
getMin [start...start + length]
// omptimzed part 2 -> src/05/index2.civet
{ log, getLines, sum, toNumbers, values } from ../utils.civet
lines := getLines import.meta.url
function wins (line: string)
[wins, my] := line.split(':').1.split('|').map toNumbers
my.filter(& is in wins).length
log 'Part 1', sum for line of lines
wins line |> (n) => n ? 1 << n - 1 : 0
cards := {}
for i in lines
cards[i] = 1
for line, i of lines
for j .= i + 1; j <= i + wins line; j++
cards[j] += cards[i]
log 'Part 2', sum values cards
{ log, getLines, sum, flatten, values } from ../utils.civet
lines := getLines import.meta.url
parts: Record<string, number[]> := {}
for line, y of lines
for m of line.matchAll /\d+/g
for y of [y - 1..y + 1]
for x of [m.index - 1..m.index + m.0.length]
unless /[0-9.]/.test lines[y]?.[x]
(parts.`${x},${y}` ?= []).push +m.0
log 'Part 1', sum flatten values parts
log 'Part 2', sum values(parts).map (x) => x.0 * x.1 ?= 0
{ log, getLines, max, sum, int, keys, multiply } from ../utils.civet
lines := getLines import.meta.url
games := lines.map (line) => Array.from line.match /\d+ ./g
limits := r:12, g:13, b:14
log 'Part 1', sum for game, id of games
if game.every (draw) => int(draw) <= limits[draw.-1]
id + 1
log 'Part 2', sum games.map (game) =>
multiply keys(limits).map (color) =>
game.filter(.includes color).map(int) |> max
{ getLines, log, sum, int } from ../utils.civet
lines := getLines import.meta.url
function parseValue(line: string)
nr := line.replace /[a-z]/g, ''
int nr.0 + nr.-1
function convertWords(line: string)
line
.replaceAll 'one', 'o1e'
.replaceAll 'two', 't2o'
.replaceAll 'three', 't3e'
.replaceAll 'four', 'f4r'
.replaceAll 'five', 'f5e'
.replaceAll 'six', 's6x'
.replaceAll 'seven', 's7n'
.replaceAll 'eight', 'e8t'
.replaceAll 'nine', 'n9e'
.replaceAll 'zero', 'z0o'
log 'Part 1', sum lines.map(parseValue)
log 'Part 2', sum lines.map(convertWords).map(parseValue)