🌟 Advent of Code (AoC) 2023 🌟

PermalÀnk
●

Dag 14 i Python

lines = open('input14.txt', 'rt').read().split('\n') pattern0 = tuple(tuple(l) for l in lines) H = len(pattern0) W = len(pattern0[0]) def tilt_north(pattern): mutable_pattern = [list(l) for l in pattern] for y in range(1, H): for x in range(W): if mutable_pattern[y][x] == 'O': new_y = y for y2 in range(y-1, -1, -1): if mutable_pattern[y2][x] == '.': new_y = y2 else: break if new_y != y: mutable_pattern[y][x] = '.' mutable_pattern[new_y][x] = 'O' return tuple(tuple(l) for l in mutable_pattern) def rotate_90(pattern): return tuple(tuple(pattern[H - 1 - x][y] for x in range(W)) for y in range(H)) period = 0 offset = 0 patterns = {} pattern = pattern0 N = 1000000000 for spins in range(1, N): for _ in range(4): pattern = tilt_north(pattern) pattern = rotate_90(pattern) last_occurrence = patterns.get(pattern) if last_occurrence is not None: period = spins - last_occurrence offset = spins break patterns[pattern] = spins patterns = ((i, t) for t, i in patterns.items()) patterns = [t for _, t in sorted(patterns)] patterns = patterns[-period:] def calculate_load(pattern): return sum(sum(c == 'O' for c in row) * (H - y) for y, row in enumerate(pattern)) pattern_index = (N - offset) % period pattern = patterns[pattern_index] print(calculate_load(pattern))

Dold text
PermalÀnk
Medlem ★
●

Dag: 12
SprÄk: Python

Ganska nöjd med gÄrdagens uppgift. Kanske inte den mest kompakta, men ganska enkel logik.
Tog dock ganska lÄng tid att hitta de sista buggarna...

def get_difference(str1, str2): return sum ( str1[i] != str2[i] for i in range(len(str1)) ) def find_reflection(pattern, diff): for i, line in enumerate(pattern): identical = False if i >= 0 and i < len(pattern)-1: scope = min(i, (len(pattern)-2)-i) l=i r=i+1 j=0 diff_sum = 0 while j <= scope: if get_difference(pattern[l-j], pattern[r+j]) <= diff: diff_sum+=get_difference(pattern[l-j], pattern[r+j]) identical = True else: identical = False break j+=1 if identical and diff_sum == diff: return i+1 return 0 with open('input.txt') as file: lines=file.read() patterns = [] for pattern in lines.split("\n\n"): patterns.append(pattern.strip().split("\n")) horizontal_p1 = [] vertical_p1 = [] horizontal_p2 = [] vertical_p2 = [] for x, pattern in enumerate(patterns): horizontal_p1.append(find_reflection(pattern,0)) vertical_p1.append(find_reflection(list(zip(*pattern[::-1])),0)) horizontal_p2.append(find_reflection(pattern,1)) vertical_p2.append(find_reflection(list(zip(*pattern[::-1])),1)) print("Part one:", sum(horizontal_p1)*100+sum(vertical_p1)) print("Part two:", sum(horizontal_p2)*100+sum(vertical_p2))

Dold text
PermalÀnk
Hedersmedlem ★
●

Dag: 14
SprÄk: Python
Lösning: GitHub

ÖverlĂ€gset lĂ„ngsammaste lösningen hittills, omkring 16 sekunder. Om jag kĂ€nner för det kollar jag nog vidare pĂ„ att göra om sĂ„ den Ă€r bĂ€ttre optimerad... fast jag Ă€r inte klar med dag 12 Ă€nnu heller, sĂ„ denna fĂ„r nog vĂ€nta. Har alla stjĂ€rnor utöver dag 12, vill inte missa den.

Edit: Optimerade en aning Ă„tminstone, genom att spara texten och inte en massa objekt (ett objekt per tecken) som kopieras via deepcopy-modulen.
FrÄn 15 sek och 612 MB RAM till 3.86 sek och 13 MB RAM.
Fortfarande sannolikt lÄngsammaste lösningen, men det fÄr duga.

Edit 2: Och eftersom min grid-klass Àr lÄngsam, klumpig och ger fulare kod sÄ började jag strukturera om den ocksÄ. 1.41 sek, 9.6 MB nu, enbart genom att a bort att varje (x,y)-koordinat wrappades i en separat klass.

Visa signatur

Asus ROG STRIX B550-F / Ryzen 5800X3D / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + NAS / Corsair RM650x V3 / Acer XB271HU (1440p165) / LG C1 55"
NAS: 6700K/16GB/Debian+ZFS | Backup (offsite): 9600K/16GB/Debian+ZFS

PermalÀnk
Medlem
●

Dag 13 (Del 1 & 2)
SprÄk: Java
GitHub

GĂ„rdagens problem.

Dag 14 (Del 1 & 2)
SprÄk: Java
GitHub

Kul problem.
Plan A för del 2 var bruteforce. Det blev plan B istÀllet (cycle detection). 54 ms istÀllet för flera timmar.

PermalÀnk
●

Dag 15 i Python

Efter allt prat om hashmaps i uppgiftens beskrivning sÄ var jag lite överraskad över att det fungerar bra att anvÀnda bara en array för att hÄlla linserna i varje box.

commands = open('input15.txt', 'rt').read().split(',') def hash_algo(s): v = 0 for c in s: v = ((v + ord(c)) * 17) & 255 return v boxes = [[] for _ in range(256)] for command in commands: pos = command.find('=') if pos != -1: label, power = command[:pos], int(command[pos+1:]) box = boxes[hash_algo(label)] in_box = False for i, (lab, _) in enumerate(box): if lab == label: box[i] = (label, power) in_box = True break if not in_box: box.append((label, power)) else: label = command[:-1] box = boxes[hash_algo(label)] for i, (lab, _) in enumerate(box): if lab == label: del box[i] break res = 0 for i, box in enumerate(boxes): for j, (_, power) in enumerate(box): res += (i+1) * (j+1) * power print(res)

Dold text
PermalÀnk
Medlem
●

Dag 15 (Del 1 & 2)
SprÄk: Java
GitHub

PermalÀnk
Hedersmedlem ★
●

Dag: 15
SprÄk: Python
Lösning: GitHub

Kul, men rÀtt enkel.
Andra gÄngen jag anvÀnder Pythons for/else till dessa.

Visa signatur

Asus ROG STRIX B550-F / Ryzen 5800X3D / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + NAS / Corsair RM650x V3 / Acer XB271HU (1440p165) / LG C1 55"
NAS: 6700K/16GB/Debian+ZFS | Backup (offsite): 9600K/16GB/Debian+ZFS

PermalÀnk
Medlem ★
●

Dag: 15
SprÄk: F#

let data = System.IO.File.ReadAllText "input.txt" let instructions = data.Split(",") |> Seq.map Seq.toList |> Seq.toList let hash (input: char list) = List.fold (fun acc chr -> (((acc+(int chr))*17)%256)) 0 input let findIndexAndValue predicate list = let rec findIndexAndValue list' pos = match list' with | [] -> failwith "NotInArray" | x::xs -> if predicate x then (x, pos) else findIndexAndValue xs (pos+1) findIndexAndValue list 0 let parseInstruction boxes (input: char list) = let char, pos = findIndexAndValue (fun x -> x = '-' || x = '=') input let label = input[0..pos-1] let box = label |> hash let replaceLabel label focalLength list = List.tryFindIndex (fst >> (=) label) list |> Option.map (fun pos -> list.[0..pos-1]@((label, focalLength)::list.[pos+1..])) |> Option.defaultValue ((label, focalLength)::list) match char with | '-' -> boxes |> Map.change box (Option.map (List.filter (fst >> (<>) label))) | '=' -> let focalLength = input.[pos+1..] |> List.map ((fun x -> x-'0')>>int) |> List.rev |> List.reduce (fun old new' -> old*10+new') boxes |> Map.change box (Option.map (replaceLabel label focalLength) >> Option.defaultValue [(label,focalLength)] >> Some) | _ -> failwith "No such operator" printfn $"Task 1: %i{instructions |> List.sumBy hash}" printfn $"Task 2: %i{instructions |> List.fold parseInstruction Map.empty |> Map.toList |> List.sumBy (fun (box, lenses) -> (box+1)*(lenses |> List.rev |> List.mapi (fun i (_, focalLength) -> ((i+1)*focalLength)) |> List.sum))}"

Dold text
Visa signatur

Jag Àr en optimist; det Àr aldrig sÄ dÄligt sÄ att det inte kan bli sÀmre.

PermalÀnk
Medlem ★
●

Dag: 14
SprÄk: Python

import numpy as np from itertools import groupby, filterfalse, pairwise, count a = np.array([[c for c in l.strip()] for l in open("input14.txt").readlines()]) a = np.pad(a, 1, constant_values='#') def tilt_east(a): """To the east, sorting order :)""" for l in a[1:-1]: for start, stop in pairwise(np.where(l == '#')[0]): l[start + 1: stop] = sorted(l[start + 1: stop]) return a def load(a): return np.where(np.flip(a, axis=0) == 'O')[0].sum() def spin_cycle(a): a = np.rot90(a, -1) for _ in range(4): a = np.rot90(tilt_east(a), -1) return np.rot90(a, 1) def part1(a): return load(np.rot90(tilt_east(np.rot90(a, -1)), 1)) def part2(a): history = dict() saved = [None] for i in count(1): t = tuple([tuple(l) for l in spin_cycle(a)]) saved.append(t) if cycle_start := history.get(t, 0): return load(np.array(saved[(1000000000 - cycle_start) % (i - cycle_start) + cycle_start])) history[t] = i print(part1(a), part2(a))

Dold text
PermalÀnk
Medlem ★
●

Dag: 15
SprÄk: Python

from functools import reduce from operator import itemgetter l = open("input15.txt").read().strip().split(',') def hash(s): return reduce(lambda a, b: (a + ord(b)) * 17 % 256, s, 0) def part1(l): return sum(map(hash, l)) def part2(l): boxes = [[] for _ in range(256)] for s in l: if s[-1] == '-': ndx, label = hash(s[:-1]), s[:-1] boxes[ndx] = list(filter(lambda a: a[0] != label, boxes[ndx])) else: ndx, label, lens = hash(s[:-2]), s[:-2], int(s[-1]) try: i= list(map(itemgetter(0), boxes[ndx])).index(label) boxes[ndx][i][1] = lens except ValueError: boxes[ndx].append([label, lens]) return sum([(i + 1) * (j + 1) * l for i,b in enumerate(boxes) for j, (_, l) in enumerate(b)]) print(part1(l), part2(l))

Dold text
PermalÀnk
Medlem
●

Dag: 15
SprÄk: Scala

val input = Using.resource(Source.fromFile("15.txt"))(_.mkString.trim) extension (s: String) def hash: Int = s.foldLeft(0)((acc, ch) => ((acc + ch) * 17) % 256) // del 1 input.split(',').map(_.hash).sum // del 2 input .split(',') .groupBy(_.takeWhile(_.isLetter).hash) .map: (box, ops) => ops .foldLeft(mutable.LinkedHashMap.empty[String, Int]): (m, op) => op match case s"$id=$value" => m += id -> value.toInt case s"$id-" => m -= id .valuesIterator.zip(Iterator.from(1)).map(_ * _ * (box + 1)).sum .sum

Dold text
PermalÀnk
●

Dag 16 i Python

lines = open('input16.txt', 'rt').read().split('\n') H = len(lines) W = len(lines[0]) L2R = 1 T2B = 2 R2L = 4 B2T = 8 TURN_SLASH = {L2R: B2T, T2B: R2L, R2L: T2B, B2T: L2R} TURN_BACKSLASH = {L2R: T2B, T2B: L2R, R2L: B2T, B2T: R2L} def follow_beam(y, x, dir): while True: if y < 0 or y >= H or x < 0 or x >= W: return if energized[y][x] & dir: return energized[y][x] |= dir c = lines[y][x] if c == '/': dir = TURN_SLASH[dir] elif c == '\\': dir = TURN_BACKSLASH[dir] elif c == '|' and dir in (L2R, R2L): stack.append((y-1, x, B2T)) dir = T2B elif c == '-' and dir in (T2B, B2T): stack.append((y, x-1, R2L)) dir = L2R if dir == L2R: x += 1 elif dir == T2B: y += 1 elif dir == R2L: x -= 1 elif dir == B2T: y -= 1 energized = [[0] * W for _ in range(H)] stack = [] max_tiles_energized = 0 for start_dir in [L2R, T2B, R2L, B2T]: n = H if start_dir in (L2R, R2L) else W for i in range(n): energized = [[0] * W for _ in range(H)] if start_dir == L2R: stack = [(i, 0, start_dir)] elif start_dir == T2B: stack = [(0, i, start_dir)] elif start_dir == R2L: stack = [(i, W-1, start_dir)] elif start_dir == B2T: stack = [(H-1, i, start_dir)] while stack: y, x, dir = stack.pop(0) follow_beam(y, x, dir) tiles_energized = sum(1 if x else 0 for row in energized for x in row) if max_tiles_energized < tiles_energized: max_tiles_energized = tiles_energized print(max_tiles_energized)

Dold text
PermalÀnk
Medlem ★
●

Dag: 16
SprÄk: Python

import numpy as np a = np.array([[c for c in l.strip()] for l in open("input16.txt").readlines()]) a = np.pad(a, 1, constant_values='#') E, W, S, N = 0, 1, 2, 3 xoffset = [1, -1, 0, 0] yoffset = [0, 0, 1, -1] def forward(x, y, dir, e): return x + xoffset[dir], y + yoffset[dir], dir def splitNS(x, y, dir, e): # | if dir in [N, S]: return forward(x, y, dir, e) follow(x, y + yoffset[N], N, e) return x, y + yoffset[S], S def splitEW(x, y, dir, e): if dir in [E, W]: return forward(x, y, dir, e) follow(x + xoffset[E], y, E, e) return x + xoffset[W], y, W def reflectNE(x, y, dir, e): return x + [ 0, 0, -1, 1][dir], y + [-1, 1, 0, 0][dir], [ N, S, W, E][dir] def reflectNW(x, y, dir, e): return x + [ 0, 0, 1, -1][dir], y + [ 1, -1, 0, 0][dir], [ S, N, E, W][dir] def follow(x, y, dir, e): while f := (forward, splitNS, splitEW, reflectNE, reflectNW, None)[".|-/\\#".find(a[y, x])]: if e[y, x] & (1 << dir): return e e[y, x] |= 1 << dir x, y, dir = f(x, y, dir, e) return e def beam(x, y, dir): return (follow(x, y, dir, np.zeros(a.shape, dtype=np.int_))[1:-1, 1:-1] != 0).sum() print(beam(1, 1, E), max(max([beam(1, i, E) for i in range(a.shape[0])]), max([beam(a.shape[1] - 2, i, W) for i in range(a.shape[0])]), max([beam(i, 1, S) for i in range(a.shape[1])]), max([beam(i, a.shape[0] - 2, N) for i in range(a.shape[1])])))

Dold text
PermalÀnk
Hedersmedlem ★
●

Dag: 16
SprÄk: Python
Lösning: GitHub

Gjorde en rekursiv funktion som delar state mellan anropen, för att hÄlla koll pÄ vilka rutor den redan besökt. Anropar sig sjÀlv nÀr den behöver gÄ Ät bÄda hÄllen vid | eller -.

Dold text
Visa signatur

Asus ROG STRIX B550-F / Ryzen 5800X3D / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + NAS / Corsair RM650x V3 / Acer XB271HU (1440p165) / LG C1 55"
NAS: 6700K/16GB/Debian+ZFS | Backup (offsite): 9600K/16GB/Debian+ZFS

PermalÀnk
Medlem
●

Dag 16 (Del 1 & 2)
SprÄk: Java
GitHub

Behövde lite tid för att hitta buggar. Jag skickade in fel variabel till kontrollfunktion för att förhindra evighetsloopar. suck.

PermalÀnk
Medlem
●

Dag: 16
SprÄk: Scala

val input = Using.resource(Source.fromFile("16.txt"))(_.getLines().toVector) enum Dir: case L, R, U, D def direction: (Int, Int) = this match case L => (-1, 0) case R => (1, 0) case U => (0, -1) case D => (0, 1) import Dir.* case class Beam(x: Int, y: Int, dir: Dir) def move(beam: Beam, grid: Vector[String]): Set[Beam] = val (dx, dy) = beam.dir.direction val x = beam.x + dx val y = beam.y + dy if y < 0 || y >= grid.length || x < 0 || x >= grid.head.length then Set() else (grid(y)(x), beam.dir) match case ('.', dir) => Set(Beam(x, y, dir)) case ('|', L) | ('|', R) => Set(Beam(x, y, U), Beam(x, y, D)) case ('|', dir) => Set(Beam(x, y, dir)) case ('-', U) | ('-', D) => Set(Beam(x, y, L), Beam(x, y, R)) case ('-', dir) => Set(Beam(x, y, dir)) case ('/', L) => Set(Beam(x, y, D)) case ('/', R) => Set(Beam(x, y, U)) case ('/', U) => Set(Beam(x, y, R)) case ('/', D) => Set(Beam(x, y, L)) case ('\\', L) => Set(Beam(x, y, U)) case ('\\', R) => Set(Beam(x, y, D)) case ('\\', U) => Set(Beam(x, y, L)) case ('\\', D) => Set(Beam(x, y, R)) case default => throw Exception(s"wtf! $default") def energize(start: Beam, grid: Vector[String]) = def recur(frontier: Set[Beam], visited: Set[Beam], energized: Set[(Int, Int)]): Int = if frontier.isEmpty then energized.size else val visited0 = visited.union(frontier) val frontier0 = frontier.flatMap(move(_, grid)).diff(visited0) recur(frontier0, visited0, energized.union(frontier0.map(b => (b.x, b.y)))) recur(Set(start), Set.empty, Set.empty) // del1 energize(Beam(-1, 0, R), input) // del2 (input.indices.map(y => Beam(-1, y, R)) ++ input.indices.map(y => Beam(input.head.length, y, L)) ++ input.head.indices.map(x => Beam(x, -1, D)) ++ input.head.indices.map(x => Beam(x, input.length, U))).par.map(energize(_, input)).max

Dold text
PermalÀnk
●

Dag 17 i Python

Dagens övning var inte busenkel. Ljusningen var att del tvÄ gick snabbt nÀr vÀl del ett fungerade.

Min lösning Àr Dijkstra pÄ grafen dÀr varje nod Àr (position, historia), dÀr historian Àr den senaste riktningen samt hur mÄnga steg som tagits i den riktningen för att komma till positionen.

lines = open('input17.txt', 'rt').read().split('\n') H = len(lines) W = len(lines[0]) RIGHT = 0 DOWN = 1 LEFT = 2 UP = 3 x_offs = [1, 0, -1, 0] y_offs = [0, 1, 0, -1] start = (0, 0, RIGHT, 0) import heapq dist_heap = [(0, start)] dist_dict = {start: 0} found = {} part_no = 2 def allowed_dir(dir, dir_count, next_dir): if part_no == 1: if next_dir == dir: if dir_count == 3: return False elif next_dir == ((dir + 2) & 3): return False return True else: if next_dir == dir: if dir_count == 10: return False elif next_dir == ((dir + 2) & 3): return False elif dir_count < 4: return False return True while dist_heap: d, state = heapq.heappop(dist_heap) del dist_dict[state] found[state] = d y, x, dir, dir_count = state if y == H-1 and x == W-1: if part_no == 1 or dir_count >= 4: print(d) break for next_dir in range(4): next_y = y + y_offs[next_dir] next_x = x + x_offs[next_dir] if next_y < 0 or next_y >= H or next_x < 0 or next_x >= W: continue if not allowed_dir(dir, dir_count, next_dir): continue next_dir_count = 1 + (dir_count if next_dir == dir else 0) next_state = (next_y, next_x, next_dir, next_dir_count) if next_state in found: continue cost = int(lines[next_y][next_x]) next_d = d + cost prev_d = dist_dict.get(next_state) add_state = prev_d is None or next_d < prev_d if prev_d is not None and add_state: dist_heap.remove((prev_d, next_state)) heapq.heapify(dist_heap) if add_state: heapq.heappush(dist_heap, (next_d, next_state)) dist_dict[next_state] = next_d

Dold text
PermalÀnk
Datavetare ★
●

Dag: 17
SprÄk: Go

Tar ca 700ms att köra pÄ en M1, vilket kÀnns ganska lÀnge. Blir massor med smÄ allokeringar

package main import ( "container/heap" "fmt" "os" "strings" ) const ( Forward = 0 TurnRight = 1 TurnLeft = 3 ) type CityMap struct { Width int Height int Blocks []uint8 neighs [3]Crucible // temp buffer to get some speed-up paths [3]Path // another temp buffer } type Position struct { X, Y int } type Crucible struct { Position Direction int ForwardCnt int } type Path struct { Crucible HeatLoss int } type PriorityQueue []Path func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].HeatLoss < pq[j].HeatLoss } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } func (pq *PriorityQueue) Push(path any) { *pq = append(*pq, path.(Path)) } func (pq *PriorityQueue) Pop() any { old := *pq n := len(old) smallestHeatLoss := old[n-1] *pq = old[0 : n-1] return smallestHeatLoss } func ParseCityMap(content []string) CityMap { cityMap := CityMap{Width: len(content[0]), Height: len(content)} cityMap.Blocks = make([]byte, cityMap.Width*cityMap.Width) for y, row := range content { for x, tile := range row { cityMap.Blocks[y*cityMap.Width+x] = uint8(tile - '0') } } return cityMap } func (cm *CityMap) HeatLoss(p Position) int { return int(cm.Blocks[p.Y*cm.Width+p.X]) } func (cm *CityMap) InCity(p Position) bool { return p.X >= 0 && p.Y >= 0 && p.X < cm.Width && p.Y < cm.Height } func (cm *CityMap) Neighbors(path Path, minForwardSteps, maxForwardSteps int) []Path { neighbors, i := cm.neighs, 0 if path.ForwardCnt < maxForwardSteps { neighbors[i] = path.Crucible.Step(Forward) i++ } if path.ForwardCnt >= minForwardSteps { neighbors[i] = path.Crucible.Step(TurnRight) neighbors[i+1] = path.Crucible.Step(TurnLeft) i += 2 } validPaths, j := cm.paths, 0 for _, neighbor := range neighbors[:i] { if cm.InCity(neighbor.Position) { validPaths[j] = Path{neighbor, path.HeatLoss + cm.HeatLoss(neighbor.Position)} j++ } } return validPaths[:j] } func (cr *Crucible) Step(dDirection int) Crucible { direction := (cr.Direction + dDirection) % 4 dPos := [4]Position{{0, -1}, {1, 0}, {0, 1}, {-1, 0}}[direction] forwardCnt := 1 if dDirection == Forward { forwardCnt += cr.ForwardCnt } return Crucible{cr.Position.Add(dPos), direction, forwardCnt} } func (p Position) Add(s Position) Position { return Position{p.X + s.X, p.Y + s.Y} } func MinHeatLoss(cityMap CityMap, minForwardSteps, maxForwardSteps int) int { minHeatLosses := map[Crucible]int{} machinePartsFactory := Position{cityMap.Width - 1, cityMap.Height - 1} paths := PriorityQueue{ Path{Crucible: Crucible{Direction: 1}}, Path{Crucible: Crucible{Direction: 2}}, } heap.Init(&paths) for { path := heap.Pop(&paths).(Path) if path.Position == machinePartsFactory { return path.HeatLoss } loss, found := minHeatLosses[path.Crucible] if !found || path.HeatLoss < loss { minHeatLosses[path.Crucible] = path.HeatLoss for _, neighbor := range cityMap.Neighbors(path, minForwardSteps, maxForwardSteps) { heap.Push(&paths, neighbor) } } } } func Input(fn string) string { if content, err := os.ReadFile(fn); err != nil { panic(err) } else { return string(content) } } func main() { cityMap := ParseCityMap(strings.Split(Input("day17.txt"), "\n")) fmt.Println(MinHeatLoss(cityMap, 1, 3)) fmt.Println(MinHeatLoss(cityMap, 4, 10)) }

Dold text

Edit: Go anvĂ€nder sig av AES för att rĂ€kna ut hash till dess inbyggda "map" funktion för att minimera risken för DoS och liknande nĂ€r man hash:ar saker som kommer frĂ„n t.ex. nĂ€tet. ÄndĂ„ rĂ€tt snabb pĂ„ x86/ARM64 dĂ„ dessa har HW-stöd för AES. Om man inte anvĂ€nder inbyggda "map":en gĂ„r det lite snabbare, 470ms pĂ„ en 8559U och 270ms pĂ„ en M3 (lite mer Ă€n 2x speedup i detta fall)...

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Medlem
●

Dag: 17
SprÄk: Scala

Slösade bort löjligt mycket tid pÄ att rÀkna översta hörnet som att man redan tagit ett steg vilket gav rÀtt svar pÄ alla tester och del 1 men inte del 2. NÀra att skippa del 2 eftersom jag var helt övertygad om att algoritmen var korrekt. Borde ha tittat pÄ vad jag skickade in för startvÀrden istÀllet för att lÀsa genom koden en gÄng till och bli Ànnu mer övertygad om att det var datorn som rÀknade fel. Skyller pÄ coronan.
Finns en hel del att optimera i koden men tröttnade. Tar runt 600ms.

val input = Using.resource(Source.fromFile("17.txt"))(_.getLines().to(ArraySeq)) val grid = input.map(_.map(_.asDigit).to(ArraySeq)) enum Dir: case L, R, U, D import Dir.* final case class Crucible(x: Int, y: Int, dir: Dir, fwd: Int, loss: Int): def compact: Int = (fwd << 18) | (dir.ordinal << 16) | (x << 8) | y def left = dir match case L => copy(y = y + 1, dir = D, fwd = 1) case R => copy(y = y - 1, dir = U, fwd = 1) case U => copy(x = x - 1, dir = L, fwd = 1) case D => copy(x = x + 1, dir = R, fwd = 1) def right = dir match case L => copy(y = y - 1, dir = U, fwd = 1) case R => copy(y = y + 1, dir = D, fwd = 1) case U => copy(x = x + 1, dir = R, fwd = 1) case D => copy(x = x - 1, dir = L, fwd = 1) def forward = dir match case L => copy(x = x - 1, fwd = fwd + 1) case R => copy(x = x + 1, fwd = fwd + 1) case U => copy(y = y - 1, fwd = fwd + 1) case D => copy(y = y + 1, fwd = fwd + 1) object Crucible: given Ordering[Crucible] = Ordering.by[Crucible, Int](_.loss).reverse def search(grid: ArraySeq[ArraySeq[Int]], start: List[Crucible], minFwd: Int, maxFwd: Int): Option[Int] = val visited = mutable.HashMap.empty[Int, Int] val pq = mutable.PriorityQueue(start*) val ySize = grid.size val xSize = grid.head.size def neighbours(crusible: Crucible) = if crusible.fwd < minFwd then Iterator(crusible.forward) else if crusible.fwd < maxFwd then Iterator(crusible.left, crusible.forward, crusible.right) else Iterator(crusible.left, crusible.right) boundary: while pq.nonEmpty do val c = pq.dequeue() if c.x == xSize - 1 && c.y == ySize - 1 then boundary.break(Some(c.loss)) if !visited.contains(c.compact) then visited += c.compact -> c.loss neighbours(c) .filter(c => c.x >= 0 && c.x < xSize && c.y >= 0 && c.y < ySize) .foreach(c => if !visited.contains(c.compact) then pq.enqueue(c.copy(loss = c.loss + grid(c.y)(c.x)))) None val start = List(Crucible(0, 0, R, 0, 0), Crucible(0, 0, D, 0, 0)) search(grid, start, 0, 3) search(grid, start, 4, 10)

Dold text
PermalÀnk
●

Dag 18, Python

lines = open('input18.txt', 'rt').read().split('\n') def get_instructions_part1(lines): directions = {'R': 0, 'D': 1, 'L': 2, 'U': 3} instructions = [] for l in lines: dir, length, _ = l.split() dir = directions[dir] length = int(length) instructions.append((dir, length)) return instructions def get_instructions_part2(lines): instructions = [] for l in lines: _, _, instr = l.split() length = int(instr[2:-2], 16) dir = int(instr[-2]) instructions.append((dir, length)) return instructions def generate_vectors(instructions): offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)] vectors = [] x, y = 0, 0 prev_dir_outline = (instructions[-1][0] + 3) & 3 for dir, length in instructions: dir_outline = (dir + 3) & 3 dx1, dy1 = offsets[prev_dir_outline] dx2, dy2 = offsets[dir_outline] outline_x = x + (dx1 + dx2) * 0.5 outline_y = y + (dy1 + dy2) * 0.5 vectors.append((outline_x, outline_y)) dx, dy = offsets[dir] x += dx * length y += dy * length prev_dir_outline = dir_outline return vectors def inside_triangle(v0, v1, v2, v): tr = [v0, v1, v2] for i in range(3): vo = tr[i] vn = tr[(i + 1) % 3] e = (vn[0] - vo[0], vn[1] - vo[1]) w = (v[0] - vo[0], v[1] - vo[1]) cross = e[0] * w[1] - e[1] * w[0] if cross < 0: return False return True def correctly_winded_triangle(v0, v1, v2): e1 = (v1[0] - v0[0], v1[1] - v0[1]) e2 = (v2[0] - v1[0], v2[1] - v1[1]) cross = e1[0] * e2[1] - e1[1] * e2[0] return cross >= 0 def triangulize(vectors): triangles = [] while len(vectors) > 3: n = len(vectors) for i in range(n): vm1 = vectors[(i - 1) % n] v0 = vectors[i] v1 = vectors[(i + 1) % n] if not correctly_winded_triangle(vm1, v0, v1): continue is_ear = True for j in range(n - 3): v = vectors[(i + 2 + j) % n] if inside_triangle(vm1, v0, v1, v): is_ear = False break if is_ear: triangles.append((vm1, v0, v1)) del vectors[i] break triangles.append(tuple(vectors)) return triangles def calculate_total_area(triangles): total_area = 0 for v0, v1, v2 in triangles: x0, y0 = v0 x1, y1 = v1 x2, y2 = v2 area = abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) / 2 total_area += area return total_area instructions = get_instructions_part2(lines) vectors = generate_vectors(instructions) triangles = triangulize(vectors) total_area = calculate_total_area(triangles) print(total_area)

Dold text
PermalÀnk
Medlem ★
●

Dag 18, Python

AnvÀnde shapely hÀr omdagen till puzzlet dÀr man skulle kolla om en punkt var innan för en loop eller inte sÄ tÀnkte pÄ att anvÀnda det hÀr ocksÄ.

import sys, os, math import numpy as np from shapely.geometry import Point from shapely.geometry.polygon import Polygon sys.setrecursionlimit(100000) INFILE = sys.argv[1] if len(sys.argv)>1 else "input.txt" DATA = open(INFILE).read().rstrip() LINES = [line.rstrip() for line in open(INFILE)] U = (-1,0) D = (1,0) L = (0,-1) R = (0,1) def generate_polygon(part): grid = [(0,0)] position = (0,0) for line in LINES: dir_ = line.split()[0] steps = int(line.split()[1]) color = line.split()[2].replace("(","").replace(")","").replace("#","") direction = (0,0) if part == 1: if dir_ == "U": direction = U elif dir_ == "D": direction = D elif dir_ == "L": direction = L elif dir_ == "R": direction = R else: assert False # unhandled direction elif part == 2: hex_steps = color[0:5] hex_dir = color[-1] steps = int(hex_steps, 16) if hex_dir == "0": direction = R elif hex_dir == "1": direction = D elif hex_dir == "2": direction = L elif hex_dir == "3": direction = U else: assert False # unhandled direction ny = position[0] + (direction[0] * steps) nx = position[1] + (direction[1] * steps) node = (ny,nx) grid.append(node) position = (ny,nx) return grid def generate_polygon_list(grid_in): res = [] for g in grid_in: res.append( (g[0], g[1]) ) return res for p in [1,2]: grid = generate_polygon(p) polygon = Polygon(generate_polygon_list(grid)) #print("polygon area", polygon.area) #print("polygon length", polygon.length) res = polygon.area + (polygon.length/2) + 1 # i have no idea why print("part", p, "=", int(res))

Kod

Om nÄgon kan förklara det hÀr sÄ hade jag uppskattat det... jag kom fram till det genom att testa med variablerna jag hade + exempel svaret... men jag har ingen aning varför matten Àr som den Àr

res = polygon.area + (polygon.length/2) + 1

För den mattekunniga
Visa signatur

🍏 MacBook Pro 16" 2023 [M3 Max (16C CPU, 40C GPU), 48 GB RAM, 1 TB SSD]
đŸ•č Ryzen 7500F, Asrock A620I, 32GB DDR5, EVGA RTX 3060 12GB, SF750, NR200P
đŸ–„ïž ROG Swift OLED PG32UCDP

PermalÀnk
Medlem ★
●
Skrivet av lambdan:

Dag 18, Python

AnvÀnde shapely hÀr omdagen till puzzlet dÀr man skulle kolla om en punkt var innan för en loop eller inte sÄ tÀnkte pÄ att anvÀnda det hÀr ocksÄ.

import sys, os, math import numpy as np from shapely.geometry import Point from shapely.geometry.polygon import Polygon sys.setrecursionlimit(100000) INFILE = sys.argv[1] if len(sys.argv)>1 else "input.txt" DATA = open(INFILE).read().rstrip() LINES = [line.rstrip() for line in open(INFILE)] U = (-1,0) D = (1,0) L = (0,-1) R = (0,1) def generate_polygon(part): grid = [(0,0)] position = (0,0) for line in LINES: dir_ = line.split()[0] steps = int(line.split()[1]) color = line.split()[2].replace("(","").replace(")","").replace("#","") direction = (0,0) if part == 1: if dir_ == "U": direction = U elif dir_ == "D": direction = D elif dir_ == "L": direction = L elif dir_ == "R": direction = R else: assert False # unhandled direction elif part == 2: hex_steps = color[0:5] hex_dir = color[-1] steps = int(hex_steps, 16) if hex_dir == "0": direction = R elif hex_dir == "1": direction = D elif hex_dir == "2": direction = L elif hex_dir == "3": direction = U else: assert False # unhandled direction ny = position[0] + (direction[0] * steps) nx = position[1] + (direction[1] * steps) node = (ny,nx) grid.append(node) position = (ny,nx) return grid def generate_polygon_list(grid_in): res = [] for g in grid_in: res.append( (g[0], g[1]) ) return res for p in [1,2]: grid = generate_polygon(p) polygon = Polygon(generate_polygon_list(grid)) #print("polygon area", polygon.area) #print("polygon length", polygon.length) res = polygon.area + (polygon.length/2) + 1 # i have no idea why print("part", p, "=", int(res))

Kod

Om nÄgon kan förklara det hÀr sÄ hade jag uppskattat det... jag kom fram till det genom att testa med variablerna jag hade + exempel svaret... men jag har ingen aning varför matten Àr som den Àr

res = polygon.area + (polygon.length/2) + 1

För den mattekunniga

tolkar jag koden rÀtt sÄ blir polygon.area arean innanför linjen, polygon.length fÄr tvÄ punkter per steg dÀrav delat med 2 och sen +1 för att fÄ med startrutan.

Dold text
PermalÀnk
Medlem ★
●
Skrivet av lambdan:

Dag 18, Python

AnvÀnde shapely hÀr omdagen till puzzlet dÀr man skulle kolla om en punkt var innan för en loop eller inte sÄ tÀnkte pÄ att anvÀnda det hÀr ocksÄ.

import sys, os, math import numpy as np from shapely.geometry import Point from shapely.geometry.polygon import Polygon sys.setrecursionlimit(100000) INFILE = sys.argv[1] if len(sys.argv)>1 else "input.txt" DATA = open(INFILE).read().rstrip() LINES = [line.rstrip() for line in open(INFILE)] U = (-1,0) D = (1,0) L = (0,-1) R = (0,1) def generate_polygon(part): grid = [(0,0)] position = (0,0) for line in LINES: dir_ = line.split()[0] steps = int(line.split()[1]) color = line.split()[2].replace("(","").replace(")","").replace("#","") direction = (0,0) if part == 1: if dir_ == "U": direction = U elif dir_ == "D": direction = D elif dir_ == "L": direction = L elif dir_ == "R": direction = R else: assert False # unhandled direction elif part == 2: hex_steps = color[0:5] hex_dir = color[-1] steps = int(hex_steps, 16) if hex_dir == "0": direction = R elif hex_dir == "1": direction = D elif hex_dir == "2": direction = L elif hex_dir == "3": direction = U else: assert False # unhandled direction ny = position[0] + (direction[0] * steps) nx = position[1] + (direction[1] * steps) node = (ny,nx) grid.append(node) position = (ny,nx) return grid def generate_polygon_list(grid_in): res = [] for g in grid_in: res.append( (g[0], g[1]) ) return res for p in [1,2]: grid = generate_polygon(p) polygon = Polygon(generate_polygon_list(grid)) #print("polygon area", polygon.area) #print("polygon length", polygon.length) res = polygon.area + (polygon.length/2) + 1 # i have no idea why print("part", p, "=", int(res))

Kod

Om nÄgon kan förklara det hÀr sÄ hade jag uppskattat det... jag kom fram till det genom att testa med variablerna jag hade + exempel svaret... men jag har ingen aning varför matten Àr som den Àr

res = polygon.area + (polygon.length/2) + 1

För den mattekunniga

Det Àr en kombination av Shoelace formula och Pick's theorem.

Dold text
Visa signatur

:(){ :|:& };:

đŸŠđŸ»â€â™‚ïž   đŸšŽđŸ»â€â™‚ïž   đŸƒđŸ»â€â™‚ïž   ☕

PermalÀnk
Medlem ★
●
Skrivet av Chibariku:

tolkar jag koden rÀtt sÄ blir polygon.area arean innanför linjen, polygon.length fÄr tvÄ punkter per steg dÀrav delat med 2 och sen +1 för att fÄ med startrutan.

Dold text
Skrivet av GLaDER:

Det Àr en kombination av Shoelace formula och Pick's theorem.

Dold text

Tack! Makes sense. KĂ€nner mig lite klokare.

Visa signatur

🍏 MacBook Pro 16" 2023 [M3 Max (16C CPU, 40C GPU), 48 GB RAM, 1 TB SSD]
đŸ•č Ryzen 7500F, Asrock A620I, 32GB DDR5, EVGA RTX 3060 12GB, SF750, NR200P
đŸ–„ïž ROG Swift OLED PG32UCDP

PermalÀnk
Datavetare ★
●

Dag: 18
SprÄk: Python

Inte helt pÄ med varför skosnöresalgoritmen fungerar, men area pÄ parallelltrapetser kÀndes rÀttfram sÄ körde pÄ det

import re def parse_dig_plan(input): return [parse_line(line) for line in input] def parse_line(line): direction, steps, hex = re.search( r"(\w+) (\d+) \(#(\w+)\)", line).groups() return (direction, int(steps), hex[5], int(hex[:5], base=16)) def into_vertices(digPlan, offset=0): directions = { 'R': (1, 0), 'D': (0, 1), 'L': (-1, 0), 'U': (0, -1), '0': (1, 0), '1': (0, 1), '2': (-1, 0), '3': (0, -1), } pos = (0, 0) vertices = [] for instruction in digPlan: direction, steps = instruction[offset:offset+2] d = directions[direction] pos = (pos[0] + d[0]*steps, pos[1] + d[1]*steps) vertices.append(pos) return vertices def volume(vertices, depth=1): area = 0 for i in range(len(vertices)): x0, y0 = vertices[i] x1, y1 = vertices[(i+1)%len(vertices)] # Trapezoid formula for the interior... area += (y0 + y1) * (x0 - x1) # ...and area of the outline area += abs(x0 - x1) + abs(y0 - y1) area = area // 2 + 1 return area * depth def solve(input): digPlan = parse_dig_plan(input) print(volume(into_vertices(digPlan))) print(volume(into_vertices(digPlan, 2))) if __name__ == "__main__": solve(open("input/day18.txt").read().split("\n"))

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
Hedersmedlem ★
●

Dag: 18
SprÄk: Python
Lösning: GitHub

LÀtt nÀr man kan matten... skitsvÄr nÀr man inte kan. Fick googla pÄ hints och stötte pÄ Shoelace formula och Pick's theorem, sedan blev det rÀtt enkelt.

Dold text
Visa signatur

Asus ROG STRIX B550-F / Ryzen 5800X3D / 48 GB 3200 MHz CL14 / Asus TUF 3080 OC / WD SN850 1 TB, Kingston NV1 2 TB + NAS / Corsair RM650x V3 / Acer XB271HU (1440p165) / LG C1 55"
NAS: 6700K/16GB/Debian+ZFS | Backup (offsite): 9600K/16GB/Debian+ZFS

PermalÀnk
Medlem
●

Dag 18 (Del 1 & 2)
SprÄk: Java
GitHub

AnvÀnde shoelace + polygons perimeter.

PermalÀnk
●

Dag 19, Python

Det kÀnns som att uppgifterna Àr ganska ojÀmna nu mot slutet, ibland kÀnns dom svÄra och ibland kÀnns det som att det inte gÄr att göra fel. Idag var det senare fallet.

lines = open('input19.txt', 'rt').read().split('\n') def parse_rules_parts(lines): LETTER_TO_INDEX = {'x': 0, 'm': 1, 'a': 2, 's': 3} rules = {} parts = [] for line in lines: if line == '': pass elif line[0] != '{': pos = line.find('{') name = line[:pos] xs = line[pos+1:-1].split(',') rule = [] for x in xs: pos = x.find(':') if pos != -1: clause = (LETTER_TO_INDEX[x[0]], x[1], int(x[2:pos]), x[pos+1:]) else: clause = (0, '', 0, x) rule.append(clause) rules[name] = rule else: xs = line[1:-1].split(',') xs = tuple(int(x.split('=')[1]) for x in xs) parts.append(xs) return rules, parts def evaluate(rule, limits, queues_to_evaluate, accepted): for clause in rule: index, comparison, value, target = clause limit_min, limit_max = limits[index] if comparison == '<': match_min, match_max = limit_min, value-1 keep_min, keep_max = value, limit_max elif comparison == '>': match_min, match_max = value+1, limit_max keep_min, keep_max = limit_min, value else: match_min, match_max = limit_min, limit_max keep_min, keep_max = 1, 0 if match_min <= match_max: match_limits = tuple(((match_min, match_max) if i == index else p for i, p in enumerate(limits))) if target == 'A': accepted.append(match_limits) elif target != 'R': queues_to_evaluate.append((target, match_limits)) if keep_min <= keep_max: limits = tuple(((keep_min, keep_max) if i == index else p for i, p in enumerate(limits))) else: break rules, _ = parse_rules_parts(lines) queues_to_evaluate = [('in', ((1, 4000), (1, 4000), (1, 4000), (1, 4000)))] accepted = [] while queues_to_evaluate: queue_name, limits = queues_to_evaluate.pop() rule = rules[queue_name] evaluate(rule, limits, queues_to_evaluate, accepted) res = 0 for limits in accepted: elements = 1 for limit_min, limit_max in limits: elements *= limit_max - limit_min + 1 res += elements print(res)

Dold text
PermalÀnk
Datavetare ★
●

Dag: 19
SprÄk: Python

Lite sÄdÀr uppgift, trasslade in mig i parsning och sedan behövdes rÀtt lite av part 1 i part 2... Mer nöjd med lösningen i andra delen.

import re from functools import reduce from copy import deepcopy def parse_workflows_and_parts(input): ws, ps = input.split("\n\n") workflows = {} for wrk in ws.split(): flow = re.split("[{,]", wrk[:-1]) workflows[flow[0]] = parse_predictions(flow[1:]) parts = [] for part in ps.split(): variables = {} for variable in part[1:-1].split(","): label, value = variable.split("=") variables[label] = int(value) parts.append(variables) return workflows, parts def parse_predictions(desc): predictions = [] for steps in desc[:-1]: pred, action = steps.split(":") predictions.append((pred[0], pred[1], int(pred[2:]), action)) predictions.append((None, None, None, desc[-1])) return predictions def accepted_parts(parts, workflows): acceptedParts = [] for part in parts: workflow = workflows["in"] while True: label, op, value, action = workflow[0] if op == None or eval(f"{part[label]} {op} {value}"): if action == "A": acceptedParts.append(part) if action in "AR": break workflow = workflows[action] else: workflow = workflow[1:] return acceptedParts def rating(parts): return sum(sum([list(part.values()) for part in parts], [])) INDICES = { "x": 0, "m": 1, "a": 2, "s": 3} def accepted_combinations(workflows, action, ratings): if action == "A": return reduce(lambda prod, c: prod * c, [end - start + 1 for start, end in ratings]) if action == "R": return 0 cnt = 0 for label, op, value, trueAction in workflows[action]: trueRatings = deepcopy(ratings) if op == None: cnt += accepted_combinations(workflows, trueAction, trueRatings) else: i = INDICES[label] rating = ratings[i] if op == "<": trueRatings[i] = (rating[0], value - 1) cnt += accepted_combinations(workflows, trueAction, trueRatings) ratings[i] = (value, rating[1]) else: trueRatings[i] = (value + 1, rating[1]) cnt += accepted_combinations(workflows, trueAction, trueRatings) ratings[i] = (rating[0], value) return cnt def solve(input): workflows, parts = parse_workflows_and_parts(input) print(rating(accepted_parts(parts, workflows))) print(accepted_combinations(workflows, "in", [(1, 4000)] * 4)) if __name__ == "__main__": solve(open("input/day19.txt").read())

Dold text
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

PermalÀnk
●

Dag 20, Python

Okej den hÀr var plÄgsam. Först nÀr jag visualiserade grafen sÄ kunde jag se hur jag skulle lösa uppgiften.

lines = open('input20.txt', 'rt').read().split('\n') module_kind = {} module_targets = {} for l in lines: a, b = l.split(' -> ') targets = b.split(', ') if a == 'broadcaster': kind, name = 'b', a else: kind, name = a[0], a[1:] module_kind[name] = kind module_targets[name] = targets def get_initial_memory(): memory = {} for name, kind in module_kind.items(): if kind == '%': memory[name] = 0 elif kind == '&': memory[name] = {} for name, targets in module_targets.items(): for target in targets: kind = module_kind.get(target) if kind == '&': memory[target][name] = 0 return memory def process_pulses(pulses, memory): while pulses: sender, level, name = pulses.pop(0) kind = module_kind.get(name) targets = module_targets.get(name) if kind == 'b': for target in targets: pulses.append((name, level, target)) elif kind == '%': if level == 0: level = memory.get(name, 0) ^ 1 memory[name] = level for target in targets: pulses.append((name, level, target)) elif kind == '&': if level == 1 and 'rx' in targets: return True mem = memory.get(name, {}) mem[sender] = level level = 0 if all(x == 1 for x in mem.values()) else 1 for target in targets: pulses.append((name, level, target)) return False prod = 1 for n in module_targets['broadcaster']: memory = get_initial_memory() steps = 0 while True: steps += 1 pulses = [('broadcaster', 0, n)] if process_pulses(pulses, memory): prod *= steps break print(prod)

Dold text