🌟 Advent of Code (AoC) 2021 🌟

PermalÀnk
●

Dag: 19
SprÄk: Scala

Det var ju fiffigt tÀnkt med avstÄnden! Kom inte pÄ det nÀr jag fnulade hur man kunde "indexera" scanners pÄ nÄgot vis.

SÄ det blev en brute-force variant som gör att min dator tuggar nÀstan tre minuter innan den spottar ur sig svaret...

object Day19 { val data = readLines("19/data.txt") val scanners = data.splitBetween((_, l)=> l.isEmpty).map { lines => val s"--- scanner ${int(scanner)} ---" = lines.head val coords = lines.tail.map { case s"${int(x)},${int(y)},${int(z)}" => (x, y, z) } Scanner(scanner, coords.toSet) } type Coord = (Int, Int, Int) type Transform = Coord => Coord val variations = { val colVariants = Seq[Transform]( { case (x, y, z) => (x, y, z) }, { case (x, y, z) => (x, z, y) }, { case (x, y, z) => (y, x, z) }, { case (x, y, z) => (y, z, x) }, { case (x, y, z) => (z, x, y) }, { case (x, y, z) => (z, y, x) } ) val invVariants: Seq[Transform] = (0 until 8).map { n => { case (x, y, z) => (if((n & 1) != 0) -x else x, if((n & 2) != 0) -y else y, if((n & 4) != 0) -z else z) } } for { col <- colVariants inv <- invVariants } yield col.andThen(inv) } implicit class CoordHelper(c: (Int, Int, Int)) { def +(o: (Int, Int, Int)): (Int, Int, Int) = (c._1 + o._1, c._2 + o._2, c._3 + o._3) def -(o: (Int, Int, Int)): (Int, Int, Int) = (c._1 - o._1, c._2 - o._2, c._3 - o._3) } case class Scanner(id: Int, coords: Set[Coord]) { val coordVariants = variations.map(v => coords.map(v)) } private def findOffset(ref: Set[Coord], other: Set[Coord]): Option[Coord] = { val offsetCandidates = for { c <- ref c2 <- other } yield c - c2 offsetCandidates.find { offset => val o = other.map(_ + offset) (ref intersect o).size >= 12 } } def findTransforms(ref: Set[Coord], scanner: Scanner): Seq[Transform] = { for { (variation, coordVariation) <- variations zip scanner.coordVariants offset <- findOffset(ref, coordVariation) } yield variation.andThen(_ + offset) } def dist(c1: Coord, c2: Coord) = { import math._ val diff = c1 - c2 abs(diff._1) + abs(diff._2) + abs(diff._3) } def main(args: Array[String]): Unit = { val ZERO = (0, 0, 0) var beacons = scanners.head.coords val remaining = ArrayBuffer[Scanner](scanners.tail:_*) val scannerCenters = ArrayBuffer[Coord](ZERO) while(remaining.nonEmpty) { for(i <- remaining.length - 1 to 0 by -1) { val scanner = remaining(i) findTransforms(beacons, scanner).map { transform => val scannerCenter = transform(ZERO) scannerCenters.append(scannerCenter) beacons = beacons union scanner.coords.map(transform) remaining.remove(i) println(s"Found $i. Scannes left ${remaining.length}. Center: " + scannerCenter) } } } // part 1 println("Beacons: " + beacons.size) // part 2 val maxDist = scannerCenters.combinations(2).map(pair => dist(pair.head, pair.last)).max println("Max dist: " + maxDist) } }

Dold text
PermalÀnk
Medlem
●

Dag: 19
SprÄk: Carth

Det blev en ful brute-force metod som tar mÄnga minuter att köra. Ush! Men nu Àr den hÀr dagen iallafall överstökad!

Klicka för mer information

(import std) (data Vec3 (Vec3 Int Int Int)) (data Transform (Transform Vec3 Int)) ; translation & rotation index (data Scanner (Scanner Nat (List Vec3))) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/test.txt"))) (let ((scanners (parse-scanners input)) (transforms (scanner-transforms (first! scanners) (rest! scanners))) (scanners' (transform-scanners scanners transforms)))) (display (str-append "Part 1: " (show-nat (count-beacons scanners')))) (let ((origin0 (Vec3 0 0 0)) (origins (list/cons origin0 (list/collect (map (flip apply-transforms origin0) (map/vals transforms))))) (dist (maximum (map manhattan (iter/cartesian (list/iter origins) (list/iter origins))))))) (display (str-append "Part 2: " (show-int dist))))) (define (manhattan [v1 v2]) (let1 (Vec3 dx dy dz) (vec3/- v1 v2) (+s (abs dx) (abs dy) (abs dz)))) (define: (count-beacons scanners) (Fun (List Scanner) Nat) (set/count (set/collect vec3/cmp (flat-map (<o list/iter scanner-beacons) (list/iter scanners))))) (define: (transform-scanners scanners transforms) (Fun (List Scanner) (Map Nat (List Transform)) (List Scanner)) (list/map (fun ((Scanner n bs)) (Scanner n (if (= n (to-nat 0)) bs (let1 tr (map/lookup! num/cmp n transforms) (list/map (apply-transforms tr) bs))))) scanners)) (define: (scanner-transforms scanner0 scanners) (Fun Scanner (List Scanner) (Map Nat (List Transform))) (define n-tot (list/count scanners)) (define (reduce trs) (define (reduce' [m tr]) (if (= m (to-nat 0)) (list/singleton tr) (list/cons tr (reduce' (map/lookup! num/cmp m trs))))) (map/collect num/cmp (map (map-cadr reduce') (map/iter trs)))) (define (go trs targets) (if (>= (map/count trs) n-tot) (reduce trs) (let (([target targets] (unwrap! (list/uncons targets))) (remaining (filter (apps <o not (flip (map/member? num/cmp) trs) scanner-id) (list/iter scanners))) (ovs (find-overlapping (trace-show (apps <o (array/show show-nat) array/collect (map scanner-id)) remaining) target)) (ovs-trs (map (fun ([ov tr]) [(scanner-id ov) [(scanner-id target) tr]]) (list/iter ovs)))) (go (map/extend num/cmp trs ovs-trs) (list/append (list/map car ovs) targets))))) (go map/nil (list/singleton scanner0))) (define: (find-overlapping scanners target) (Fun (Iter Scanner) Scanner (List [Scanner Transform])) (list/collect (filter-map (fun (scanner) (maybe/map (<o (trace-show (fun ([sc tr]) (apps str-append (show-nat (scanner-id sc)) " -> " (show-nat (scanner-id target))))) (cons' scanner)) (overlaps? scanner target))) scanners))) (define: (overlaps? (Scanner _ bs1) (Scanner _ bs2)) (Fun Scanner Scanner (Maybe Transform)) (let1 bs2' (set/collect vec3/cmp (list/iter bs2)) (iter/first (flat-map (fun (rot) (let ((bs1' (list/map (apply-rot rot) bs1)) (translations (apps |> (iter/cartesian (list/iter bs2) (list/iter bs1')) (map (uncurry vec3/-)) (set/collect vec3/cmp) set/iter))) (filter-map (fun (dv) (let1 bs1'' (set/collect vec3/cmp (map (vec3/+ dv) (list/iter bs1'))) (if (>= (set/count (set/isect vec3/cmp bs1'' bs2')) (to-nat 12)) (Some (Transform dv rot)) None))) translations))) (range 0 23))))) (define (scanner-id (Scanner n _)) n) (define (scanner-beacons (Scanner _ bs)) bs) (define (vec3/cmp (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (match (num/cmp x1 x2) (case Eq (match (num/cmp y1 y2) (case Eq (num/cmp z1 z2)) (case x x))) (case x x))) (define (vec3/+ (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (+ x1 x2) (+ y1 y2) (+ z1 z2))) (define (vec3/- (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (- x1 x2) (- y1 y2) (- z1 z2))) (define (apply-transforms trs v) (foldl (flip apply-transform) v (list/iter trs))) (define: (apply-transform (Transform transl rot) v) (Fun Transform Vec3 Vec3) (vec3/+ transl (apply-rot rot v))) (define (apply-rot rot v) (direction (rem rot 6) (roll (/ rot 6) v))) (define (roll i (Vec3 x y z)) (match (rem i 4) (case 0 (Vec3 x y z)) (case 1 (Vec3 (neg y) x z)) (case 2 (Vec3 (neg x) (neg y) z)) (case _ (Vec3 y (neg x) z)))) (define (direction i (Vec3 x y z)) (match (rem i 6) (case 0 (Vec3 x y z )) (case 1 (Vec3 (neg z) y x )) (case 2 (Vec3 (neg x) y (neg z))) (case 3 (Vec3 z y (neg x))) (case 4 (Vec3 x z (neg y))) (case _ (Vec3 x (neg z) y )))) (define (parse-scanners inp) (define pvec (do parse/bind (<- x parse/int) (parse/string ",") (<- y parse/int) (parse/string ",") (<- z parse/int) (parse/pure (Vec3 x y z)))) (define pscanner (do parse/bind (parse/take-bytes-while1 (/= ascii-newline)) parse/space1 (parse/sep-by1 parse/space1 pvec))) (list/collect (map (uncurry Scanner) (enumerate (list/iter (parse! (parse/sep-by1 parse/space pscanner) inp))))))

Visa mer
Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

PermalÀnk
Medlem
●

Dag: 18
SprÄk: C

Tyckte dag 18 var nog en av de roligare. LÄngt mer Àn hÀlften av tiden la jag pÄ fÄ explode att fungera med min valda datastruktur. Blev dock överraskad av del tvÄ eftersom jag organiserade om i orginaldatat och hade inte det tillgÀngligt för ÄteranvÀndning.

https://github.com/te-johan/AoC2021/blob/main/day18/app.c

Visa signatur

i7-8700k, 16GB, GTX 1070, Samsung 850 512 GB

PermalÀnk
●

Dag: 20
SprÄk: Scala

Ganska skönt med en som inte tar sÄ lÄng tid idag, sÄ man fÄr lite "paus"

object Day20 { val lines = readLines("20/data.txt") val enhanchment = lines.head val imageLines = lines.tail.tail type Pos = (Int, Int) case class Image(image: Seq[String], background: Char) { val xSize = image.head.length val ySize = image.length val xRange = 0 until xSize val yRange = 0 until ySize def pixel(p: Pos) = { val (x, y) = p if(xRange.contains(x) && yRange.contains(y)) { image(y)(x) } else background } def index(pos: Pos): Int = { val (x, y) = pos var res = 0 for { dy <- -1 to 1 dx <- -1 to 1 } { val bit = pixel(x + dx, y + dy) match { case '#' => 1 case '.' => 0 } res = (res << 1) + bit } res } def backgroundIndex = index(-2, -2) def enhance(): Image = { val newBackground = enhanchment.charAt(backgroundIndex) val result = Array.fill(ySize + 2, xSize + 2)(newBackground) for { y <- -1 to ySize x <- -1 to xSize } result(y + 1)(x + 1) = enhanchment.charAt(index(x, y)) Image(result.map(chars => new String(chars)), newBackground) } def lit = image.map(_.count('#'.==)).sum override def toString: String = image.mkString("\n") } def main(args: Array[String]): Unit = { // part 1 println(Image(imageLines, '.').enhance().enhance().lit) // part 2 var image = Image(imageLines, '.') for(_ <- 1 to 50) { image = image.enhance() } println(image.lit) } }

Dold text
PermalÀnk
Medlem ★
●

Dag: 20
SprÄk: Python (numpy och scipy)

OÀndligt stor, jo tack, och den togglar mellan # och . varannan iteration. IstÀllet för att mecka med det sÄ tog jag en tillrÀckligt stor oÀndlighet sÄ att man inte skulle smittas av de fel som propageras frÄn kanterna och beskar före summeringen.

Tack @survivalcode som visade oss den fantastiska convolve2d tidigare.

from scipy.signal import convolve2d import numpy as np bits = np.array([1 << i for i in range(9)]).reshape(3,3) line, image = open("input20").read().split("\n\n") t = np.array([int(c == '#') for c in line.replace('\n', '')]) def solve(iterations): a = np.array([[c == '#' for c in l] for l in image.strip().split('\n')], dtype = int) a = np.pad(a,((iterations * 2,iterations * 2),(iterations * 2,iterations * 2)), mode = 'constant', constant_values = 0) for _ in range(iterations): a = t[convolve2d(a, bits, "same")[:,:]] return a[iterations:-iterations,iterations:-iterations].sum() print(solve(2), solve(50))

Dold text
Hade fel dag.
PermalÀnk
Medlem
●

Dag: 20
SprÄk: C#

internal class Puzzle20 : Puzzle<int> { private int _flip = 0; protected override void Solve(string[] l) { var algo = l[0]; var input = l[2..]; One = DecodeImage(algo, input, 2); Two = DecodeImage(algo, input, 50); } private int DecodeImage(string algo, string[] input, int times) { return Enumerable.Range(0, times) .Aggregate(input, (a, _) => DecodeImage(algo, a)) .SelectMany(s => s) .Count(x => x == '#'); } private string[] DecodeImage(string algo, string[] lines) { var inputImage = Grid.CreateGrid(lines, c => c == '#'); var outputImage = new char[lines[0].Length + 2, lines.Length + 2]; foreach (var (x, y) in Grid.Iterate(outputImage)) { var str = Grid.NeighborOffsetsWithDiagsIncludeSelf .Select(c => (x: x + c.x, y: y + c.y)) .Aggregate(new StringBuilder(), (sb, n) => { bool pixel = Grid.IsOutOfRange(inputImage, (n.x - 1, n.y - 1)) ? (algo[0] == '#' && _flip % 2 == 1) : inputImage[n.x - 1, n.y - 1]; return sb.Append(pixel ? '1' : '0'); }) .ToString(); var idx = Convert.ToInt32(str, 2); var outputChar = algo[idx]; outputImage[x, y] = outputChar; } _flip++; return Enumerable.Range(0, outputImage.GetLength(1)) .Select(y => string.Concat(Enumerable .Range(0, outputImage.GetLength(0)) .Select(x => outputImage[x, y]) )) .ToArray(); } }

Dold text
PermalÀnk
Hedersmedlem ★
●

Dag: 20
SprÄk: TypeScript (med Node.js)
Lösning: https://github.com/exscape/AOC2021/blob/main/src/day20.ts

Äntligen en jag orkade lösa. 18 och 19 var för dryga; kanske Ă„tervĂ€nder till dem men jag har inte mycket ork till det. Skulle dock vilja ha alla 50 stjĂ€rnorna men saknar ju 4 nu.

Elakt av dem att sÀtta första biten i enhancement-strÀngen till . i exemplet och # i riktiga uppgiften, sÄ att en naiv lösning klarar exemplet men inte den riktiga uppgiften.

Inte optimerad, tar runt 1900 ms för bÄda delarna, mycket för att jag skapar en ny grid för varje iteration.

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"
Mobil: Moto G200

PermalÀnk
●
Skrivet av Ingetledigtnamn:

Dag: 20
SprÄk: Python (numpy och scipy)

OÀndligt stor, jo tack, och den togglar mellan # och . varannan iteration. IstÀllet för att mecka med det sÄ tog jag en tillrÀckligt stor oÀndlighet sÄ att man inte skulle smittas av de fel som propageras frÄn kanterna och beskar före summeringen.

Tack @survivalcode som visade oss den fantastiska convolve2d tidigare.

from scipy.signal import convolve2d import numpy as np bits = np.array([1 << i for i in range(9)]).reshape(3,3) line, image = open("input20").read().split("\n\n") t = np.array([int(c == '#') for c in line.replace('\n', '')]) def solve(iterations): a = np.array([[c == '#' for c in l] for l in image.strip().split('\n')], dtype = int) a = np.pad(a,((iterations * 2,iterations * 2),(iterations * 2,iterations * 2)), mode = 'constant', constant_values = 0) for _ in range(iterations): a = t[convolve2d(a, bits, "same")[:,:]] return a[iterations:-iterations,iterations:-iterations].sum() print(solve(2), solve(50))

Dold text

Blev inspirerad av din lösning att göra en mer kompakt variant i Scala ocksÄ. Man Àr lite "tydlighetsskadad" i jobbet ibland, sÄ det kan vara kul att försöka trycka ihop koden lite.
Har dock inga fina libraries som gör matrisoperationer importerade sÄ kör lite hemmabyggt.

import aoc.Util._ import scala.util.Try object Day20b { val lines = readLines("20/data.txt") val kern = (0 until 9).map(i => (i % 3 - 1, i / 3 - 1, 1 << (8 - i))) val toInt = Map('#' -> 1, '.' -> 0) val tbl = lines.head.map(toInt) case class Img(pix: Seq[Seq[Int]], bg: Int) { def lit = pix.flatten.count(1.==) } def enh(img: Img) = { val pixels = for(y <- -1 to img.pix.size; x <- -1 to img.pix.size) yield { tbl(kern.map { case (dx, dy, v) => v * Try(img.pix(y + dy)(x + dx)).getOrElse(img.bg) }.sum) } Img(pixels.grouped(img.pix.size + 2).toSeq, tbl(img.bg * 511)) } def main(args: Array[String]): Unit = { val img = Img(lines.drop(2).map(_.map(toInt)), 0) println(2.repeat(img, enh).lit) // part 1 println(50.repeat(img, enh).lit) // part 2 } }

Dold text
PermalÀnk
Medlem ★
●

Dag: 21
SprÄk: Python

from itertools import count, product from collections import Counter import numpy as np pos_size, score_size = 11, 21 outcome = Counter(map(sum, product((1, 2, 3),repeat = 3))) def new_pos(p): return p if p <= 10 else p - 10 def game1(p1, p2): score, position, player = [0,0], [p1,p2], 0 for die in count(0,3): if (max(score) >= 1000): return die * min(score) position[player] = new_pos(position[player] + (die + 1 + die + 2 + die + 3) % 10) score[player] += position[player] player = 1 - player def newus(u): won, nus = 0, np.zeros((score_size, pos_size), dtype=int) for s, p in product(range(score_size), range(pos_size)): if u[s,p]: for roll, count in outcome.items(): p2 = new_pos(p + roll) if (ns := s + p2) >= 21: won += u[s,p] * count else: nus[ns, p2] += u[s,p] * count return won, nus def game2(p1, p2): universes = np.zeros((2, score_size, pos_size), dtype=int) universes[0, 0, p1] = universes[1, 0, p2] = 1 won, player = [0,0], 0 while universes[0].sum() and universes[1].sum(): wins, universes[player] = newus(universes[player]) won[player] += wins * universes[1 - player].sum() player = 1 - player return(max(won)) print(game1(10,7), game2(10,7))

Dold text
PermalÀnk
●

Dag: 21
SprÄk: Scala

object Day21 { case class Player(idx: Int, pos: Int, score: Int) { def advance(steps: Int): Player = { val p = (pos + steps) % 10 copy(pos = p, score = score + p + 1) } } def main(args: Array[String]): Unit = { part1() part2() } def part1() = { case class Dice() { private var v = -1 var count = 0 def next: Int = { count += 1 v = (v + 1) % 100 v + 1 } } val dice = Dice() val players = Seq(Player(0, 0, 0), Player(1, 5, 0)) def next(players: Seq[Player]): Long = { val p = players.head.advance(dice.next + dice.next + dice.next) if(p.score >= 1000) { players.last.score * dice.count } else { next(Seq(players.last, p)) } } println(next(players)) } def part2() = { val SumFreq = Seq(3 -> 1, 4 -> 3, 5 -> 6, 6 -> 7, 7 -> 6, 8 -> 3, 9 -> 1) val WinnerScore = 21 case class GameState(players: Seq[Player]) { def isFinished = players.exists(_.score >= WinnerScore) def winner: Int = players.find(_.score >= WinnerScore).get.idx def nextTurn(count: Long): Seq[(GameState, Long)] = { SumFreq.map { case (sum, freq) => GameState(Seq(players.last, players.head.advance(sum))) -> freq * count } } } def solve(games: Map[GameState, Long]): Seq[(GameState, Long)] = { if(games.isEmpty) { Seq.empty } else { val next = games.toSeq.flatMap { case (state, count) => state.nextTurn(count) } val (finished, unfinished) = next.groupMapReduce(_._1)(_._2)(_ + _).partition(_._1.isFinished) finished.toSeq ++ solve(unfinished) } } val initialState = GameState(Seq(Player(0, 0, 0), Player(1, 5, 0))) val finished = solve(Map(initialState -> 1L)) println(finished.groupMapReduce(_._1.winner)(_._2)(_ + _).values.max) } }

Dold text
PermalÀnk
●

Dag: 8
SprÄk: Python

Ligger lite efter, hÀr kommer min (tyvÀrr ickefungerande) lösning till dag 8. Trots att jag fÄr rÀtt svar pÄ test datan fungerar den inte pÄ den riktiga. Provade att "lÄna" lite kod frÄn nÄgon annan hÀr i trÄden, och fick dÄ ett annat svar, vilket ocksÄ var rÀtt. NÄgot som kan se var min kod brister?

inp = [] with open('D8/test.txt', 'r') as f: for line in f.readlines(): temp = [] for word in line.split(): if word != '|': temp.append(word) inp.append(temp) def find_num(word, key): if len(word) == 2: return 1 elif len(word) == 3: return 7 elif len(word) == 4: return 4 elif len(word) == 7: return 8 elif len(word) == 6: word = list(word) if key[2][0] in word and key[2][1] in word: return 9 else: return 6 elif len(word) == 5: word = list(word) if key[2][0] in word and key[2][1] in word: return 3 elif key[6][0] in word and key[6][1] in word: return 5 else: return 2 res = 0 for line in inp: current_sum = 0 key = [[] for i in range(8)] one_found = False four_found = False while not one_found and not four_found: for word in line: if len(word) == 2 and len(key[2]) == 0: key[2].append(word[0]) key[2].append(word[1]) one_found = True for word in line: if len(word) == 4: for char in word: if char not in key[2]: key[6].append(char) four_found = True for i in range(1,5): num = find_num(line[-i], key) current_sum += num * 10 ** (i-1) res += current_sum print(f"Sum of numbers is {res}")

Dold text
Visa signatur

System: Fractal Design R4 | i7 3770 | GTX 660 DC2 | Intel 330 120 GB | Seagate Barracuda 2 TB | Corsair Vengeance 16 GB | Asus P8Z77 Pro Thunderbolt | Fractal Design Newton R2 1000W | Ducky Mini mx blue | Logitech G300 | Qpad QH90 | Nokia Lumia 925 |

PermalÀnk
Medlem
●
Skrivet av gamingpandahq:

Dag: 8
SprÄk: Python

Ligger lite efter, hÀr kommer min (tyvÀrr ickefungerande) lösning till dag 8. Trots att jag fÄr rÀtt svar pÄ test datan fungerar den inte pÄ den riktiga. Provade att "lÄna" lite kod frÄn nÄgon annan hÀr i trÄden, och fick dÄ ett annat svar, vilket ocksÄ var rÀtt. NÄgot som kan se var min kod brister?

inp = [] with open('D8/test.txt', 'r') as f: for line in f.readlines(): temp = [] for word in line.split(): if word != '|': temp.append(word) inp.append(temp) def find_num(word, key): if len(word) == 2: return 1 elif len(word) == 3: return 7 elif len(word) == 4: return 4 elif len(word) == 7: return 8 elif len(word) == 6: word = list(word) if key[2][0] in word and key[2][1] in word: return 9 else: return 6 elif len(word) == 5: word = list(word) if key[2][0] in word and key[2][1] in word: return 3 elif key[6][0] in word and key[6][1] in word: return 5 else: return 2 res = 0 for line in inp: current_sum = 0 key = [[] for i in range(8)] one_found = False four_found = False while not one_found and not four_found: for word in line: if len(word) == 2 and len(key[2]) == 0: key[2].append(word[0]) key[2].append(word[1]) one_found = True for word in line: if len(word) == 4: for char in word: if char not in key[2]: key[6].append(char) four_found = True for i in range(1,5): num = find_num(line[-i], key) current_sum += num * 10 ** (i-1) res += current_sum print(f"Sum of numbers is {res}")

Dold text

find_num bör kunna returnera 0-9, men du har missat ett nummer.

PermalÀnk
Medlem
●

Dag: 20
SprÄk: C++

NÄgon dag efter nu. Blev lÄngt och omstÀndligt igen, sÄ postar bara den centrala biten hÀr. Resten finns pÄ https://github.com/gbroll/AoC-2021/tree/main/c%2B%2B.

(iea Àr "image enhancement algorithm")

void Puzzle::enhance() { bitset<9> bits; int bit ,bitindex; int n_rows = Puzzle::curr_img_size.first+2; int n_cols = Puzzle::curr_img_size.second+2; vector<vector<int>> output_img(n_rows , vector<int> (n_cols, 0)); for (int i = 0; i < n_rows; i++) { for (int j = 0; j < n_cols; j++) { bitindex = 8; bits.reset(); for (int k1 = -1; k1<2; k1++) { for (int k2 = -1; k2<2; k2++) { bit = get_bit(i+k1-1,j+k2-1); if (bit==1) bits.set(bitindex); bitindex--; } } output_img[i][j] = iea[bits.to_ulong()]; } } //set pad_bit pad_bit = (pad_bit==0)? iea[0] : iea.back(); //replace current data curr_img_size = make_pair(n_rows,n_cols); curr_img = output_img; }

Dold text
PermalÀnk
●
Skrivet av Hot Dogs:

find_num bör kunna returnera 0-9, men du har missat ett nummer.

DÀr hade vi det ja, tack sÄ mycket!

Visa signatur

System: Fractal Design R4 | i7 3770 | GTX 660 DC2 | Intel 330 120 GB | Seagate Barracuda 2 TB | Corsair Vengeance 16 GB | Asus P8Z77 Pro Thunderbolt | Fractal Design Newton R2 1000W | Ducky Mini mx blue | Logitech G300 | Qpad QH90 | Nokia Lumia 925 |

PermalÀnk
Datavetare ★
●

Dag: 21
SprÄk: Rust

Ligger lite efter, har inte gjort dag 19 och 20 Àn. Men alla dagar inklusive denna kan fortfarande lösas pÄ under 100 ms. Denna kan optimeras mer, tar nu runt 30 ms

use crate::solution::Solution; use rayon::prelude::*; struct Day21 { start: [u32; 2], } impl Solution for Day21 { fn part1(&mut self) -> String { self.practice_game().to_string() } fn part2(&mut self) -> String { self.dirac_dice().to_string() } } impl Day21 { fn practice_game(&self) -> u32 { let mut scores = [0, 0]; let mut positions = self.start; let mut die_rolls = 0; let mut cur_player = 1; while scores[cur_player] < 1000 { cur_player = 1 - cur_player; let mut steps = 0; for _ in 0..3 { die_rolls += 1; steps += die_rolls; } positions[cur_player] = (positions[cur_player] + steps - 1) % 10 + 1; scores[cur_player] += positions[cur_player]; } die_rolls as u32 * scores[1 - cur_player] } fn dirac_dice(&self) -> u64 { let mut initial_stack = Vec::new(); let initial_game = Game::new(self.start); for die_sum in 3..=9 { initial_stack.push(initial_game.next(die_sum)); } *initial_stack .into_par_iter() .map(|game| { let mut stack = vec![game]; let mut wins = [0, 0]; while let Some(game) = stack.pop() { for die_sum in 3..=9 { let next_game = game.next(die_sum); if next_game.scores.0 >= 21 { wins[0] += next_game.num_universes; } else if next_game.scores.1 >= 21 { wins[1] += next_game.num_universes; } else { stack.push(next_game); } } } wins }) .collect::<Vec<_>>() .iter() .fold([0, 0], |total_wins, wins| [total_wins[0] + wins[0], total_wins[1] + wins[1]]) .iter() .max() .unwrap() } } pub fn new(input: &str) -> Box<dyn Solution> { let mut players = input.lines(); let mut get_start_pos = || { players .next() .expect("Too few lines") .split_once(": ") .expect("Missing ': '") .1 .parse::<u32>() .expect("Could not find start position") }; Box::new(Day21 { start: [get_start_pos(), get_start_pos()], }) } struct Game { num_universes: u64, scores: (u32, u32), pos: (u32, u32), is_first_player: bool, } impl Game { fn new(start_pos: [u32; 2]) -> Game { Game { num_universes: 1, scores: (0, 0), pos: (start_pos[0], start_pos[1]), is_first_player: true, } } fn next(&self, die_sum: u32) -> Game { const ROLL_PROB: [u64; 10] = [0, 0, 0, 1, 3, 6, 7, 6, 3, 1]; let num_universes = self.num_universes * ROLL_PROB[die_sum as usize]; if self.is_first_player { let mut new_pos = self.pos.0 + die_sum; if new_pos > 10 { new_pos -= 10; } Game { num_universes, scores: (self.scores.0 + new_pos, self.scores.1), pos: (new_pos, self.pos.1), is_first_player: false, } } else { let mut new_pos = self.pos.1 + die_sum; if new_pos > 10 { new_pos -= 10; } Game { num_universes, scores: (self.scores.0, self.scores.1 + new_pos), pos: (self.pos.0, new_pos), is_first_player: true, } } } }

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
Medlem
●

Dag: 20
SprÄk: Carth

Hamnat efter en dag, men 20:an gick ju bra.

Klicka för mer information

(import std) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/day20.txt"))) (let1 [enhancement-algo-s ls] (next! (lines input))) (let1 algo (array/map light? (string/as-array enhancement-algo-s))) (let1 ls (skip 1 ls)) (let1 pixels0 ((set/collect cmp-pt) (flat-map (fun ([y l]) (filter-map (fun ([x c]) (if (light? c) (Some [(to-int y) (to-int x)]) None)) (enumerate (string/bytes l)))) (enumerate ls)))) (display (str-append "Part 1: " (show-nat (set/size (multi-enhance 2 algo [pixels0 False]))))) (display (str-append "Part 2: " (show-nat (set/size (multi-enhance 50 algo [pixels0 False]))))))) (define (multi-enhance n algo in) (if (= n 0) (car in) (multi-enhance (dec n) algo (enhance algo in)))) (define (enhance algo [in-pixels inverted?]) (let (([ymin xmin ymax xmax] (bounds in-pixels)) ((in-bounds? [y x]) (and (and (<= ymin y) (<= y ymax)) (and (<= xmin x) (<= x xmax)))) ((out-pixel-light? [y x]) (let1 alg-i (foldl (fun (n [i px]) (set-bit (- (to-nat 8) i) (if (in-bounds? px) (set/member? cmp-pt px in-pixels) inverted?) n)) (to-nat 0) (enumerate (iter/cartesian (range (dec y) (inc y)) (range (dec x) (inc x))))) (array/lookup! alg-i algo))) (inverted?' (if (array/first! algo) (not inverted?) False))) [(set/collect cmp-pt (filter out-pixel-light? (iter/cartesian (range (- ymin 1) (+ ymax 1)) (range (- xmin 1) (+ xmax 1))))) inverted?'])) (define (bounds light-pixels) (let1 [[y0 x0] rest] (next! (set/iter light-pixels)) (foldl (fun ([ymin xmin ymax xmax] [y x]) [(min ymin y) (min xmin x) (max ymax y) (max xmax x)]) [y0 x0 y0 x0] rest))) (define (cmp-pt [i1 j1] [i2 j2]) (match (num/cmp i1 i2) (case Eq (num/cmp j1 j2)) (case x x))) (define (light? c) (str-eq (string/singleton-byte c) "#"))

Visa mer
Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

PermalÀnk
Medlem
●

Dag: 21
SprÄk: C++

SÄ hÀr blev det idag. Del 2 kÀndes tuff, sÄ jag tjuvkikade pÄ nÄgra andra lösningar pÄ reddit innan jag gav mig pÄ den. Lite fusk, men motivationen Àr lÀgre nu Àn i början...

#include "../puzzle.hpp" using namespace std; typedef unsigned long long ull; struct game_state { int score[2]; int pos[2]; int player_turn; }; class Puzzle: public Puzzle_base<ull> { //inherit constructors from base class using Puzzle_base::Puzzle_base; public: void parse_input(); void solve_part1(); void solve_part2(); void advance_game(); private: int player1_startpos; int player2_startpos; vector<pair<game_state,ull>> games; ull wins[2] = {0}; }; void Puzzle::parse_input() { player1_startpos = stoi(str_data[0].substr(str_data[0].find(':')+2)); player2_startpos = stoi(str_data[1].substr(str_data[1].find(':')+2)); } void Puzzle::solve_part1() { bool playing = true; int score[2]={0}, pos[2], roll_nr = 0, roll = 0, roll_sum = 0; int loser; pos[0] = player1_startpos; pos[1] = player2_startpos; while (playing) { for (int i = 0; i < 2; i++) { roll_sum = 0; for (int j = 0; j < 3; j++) { roll = (j + roll_nr*3) % 100 + 1; roll_sum += roll; } roll_nr++; pos[i] = (pos[i] + roll_sum - 1) % 10 + 1; score[i] += pos[i]; if (score[i]>=1000) { loser = abs(i-1); playing = false; break; } } } part1_solution = score[loser] * 3 *roll_nr; } void Puzzle::advance_game() { vector<int> rolls = {3,4,5,6,7,8,9}; //possible outcomes of rollig dice with 1,2,3 three times vector<int> spawns = {1,3,6,7,6,3,1}; //frequencies of possible outcomes vector<pair<game_state,ull>> next_gen; for (auto const &game_ : games) { game_state game = game_.first; for (int i = 0; i < rolls.size(); i++) { game_state next(game); next.pos[game.player_turn] = (next.pos[game.player_turn] + rolls[i] - 1) % 10 +1; next.score[game.player_turn] = next.score[game.player_turn] + next.pos[game.player_turn]; if (next.score[game.player_turn]>=21) { wins[game.player_turn] += game_.second*spawns[i]; } else { next.player_turn = abs(next.player_turn-1); next_gen.push_back(make_pair(next,game_.second*spawns[i])); } } } games = next_gen; } void Puzzle::solve_part2() { game_state init; init.score[0] = 0; init.score[1] = 0; init.pos[0] = player1_startpos; init.pos[1] = player2_startpos; init.player_turn = 0; games.push_back(make_pair(init,1)); while (games.size()>0) { advance_game(); } part2_solution = (wins[0] > wins[1])? wins[0] : wins[1]; } int main() { int day = 21; bool skip_empty_lines = false; bool test_input = false; Puzzle p = Puzzle(day, skip_empty_lines, test_input); p.solve(); }

Dold text
PermalÀnk
Medlem ★
●

Dag: 22
SprÄk: Python (inte för nybörjaren )

Del 2 Àr ganska hÄrig. Iden Àr att hitta alla punkter dÀr ett rÀtblock startar, per axel, och dela upp blocken i mindre i block som matchar dessa punkter.

p innehÄller dessa punkter, per axel.

s Àr ett set som innehÄller smÄblockens lÀgsta-koordinater. Om s innehÄller (x,y,z) Àr alla delkuber i blocket x.. nÀsta x i p[0], y .. nÀsta y i p[1], z .. nÀsta z i p[2] tÀnda.

För alla linjer i indata, hitta alla delblockskoordinater som ryms i blocket och lÀgg till s/dra ifrÄn frÄn s beroende pÄ om det var ett on- eller off-kommando

Till slut, summera storlekarna pÄ alla delblock som finns i s.

Denna lösning tillhör inte de mest effektiva. Jag hade 134M punkter i s och jag gjorde slut pÄ minnet pÄ min dator. Fick köra den pÄ en burk pÄ jobbet med mer minne...

from collections import Counter, defaultdict import numpy as np import re from bisect import bisect_left, bisect_right from itertools import product from math import prod l = [(l[1], np.array(list(map(int, re.findall("(\-?\d+)", l))))) for l in open("input22").readlines()] origo = 51 sz = 2 * origo cube = np.zeros((sz,sz,sz), dtype = int) for on, coords in l: lo, hi = coords[0::2] + origo, coords[1::2] + origo + 1 if any(lo > sz) or any(hi <= 0): continue # Outside care cube? lo, hi = np.maximum(lo, 0), np.minimum(hi, sz) cube[lo[0]:hi[0], lo[1]:hi[1], lo[2]:hi[2]] = on == 'n' s = set() p = [sorted((Counter(axis_width[0]) + Counter(axis_width[1] + 1)).keys()) for axis_width in np.array([a for _,a in l]).T.reshape((3,2,-1))] for n, coords in l: points = product(*map(lambda i, x: p[i][bisect_left(p[i], x[0]) : bisect_right(p[i], x[1])], *zip(*enumerate(coords.reshape((3,2)))))) [set.difference_update, set.update][n == 'n'](s, points) def r(i, x): xi = bisect_left(p[i], x) return (p[i][xi + 1] - p[i][xi]) print(cube.sum(), sum(prod(map(r, *zip(*enumerate(e)))) for e in s))

Dold text
PermalÀnk
●

Dag: 22
SprÄk: Scala

Del 2 löste jag genom att bara identifiera alla positioner pÄ de tre axlarna dÀr nÄgon kuboid har sin kant. Tar nÄgra minuter att köra, men blev den lösningen av slapphet.
Kanske orkar att komma tillbaka och genomföra min första tanke att splittra kuboiderna i mindre konvexa kuboider dÀr de trÀffar varandra. Skulle förmodligen bli en vÀdligt mycket snabbare körtid pÄ den lösningen.

object Day22 { val lines = readLines("22/data.txt") case class Rng(l: Int, h: Int) { val length = h - l def disparate(r: Rng) = h <= r.l || l >= r.h def intersects(r: Rng) = !disparate(r) def intersect(r: Rng): Option[Rng] = if(intersects(r)) Some(Rng(max(l, r.l), min(h, r.h))) else None def contains(r: Rng) = l <= r.l && h >= r.h def contains(v: Int) = l <= v && v < h def range = l until h } def part1 = { val valid = Rng(-50, 51) val empty = Rng(0, 0) val cubes = new Array[Int](101 * 101 * 101) for(s"$cmd x=${int(xMin)}..${int(xMax)},y=${int(yMin)}..${int(yMax)},z=${int(zMin)}..${int(zMax)}" <- lines) { val value = if(cmd == "on") 1 else 0 for { z <- Rng(zMin, zMax + 1).intersect(valid).getOrElse(empty).range y <- Rng(yMin, yMax + 1).intersect(valid).getOrElse(empty).range x <- Rng(xMin, xMax + 1).intersect(valid).getOrElse(empty).range } cubes((z + 50) * 101 * 101 + (y + 50) * 101 + x + 50) = value } println("Part 1: " + cubes.count(1.==)) } def part2 = { case class Cuboid(xr: Rng, yr: Rng, zr: Rng) { def contains(x: Int, y: Int, z: Int) = xr.contains(x) && yr.contains(y) && zr.contains(z) } val cuboids = lines.map { case s"$cmd x=${int(xMin)}..${int(xMax)},y=${int(yMin)}..${int(yMax)},z=${int(zMin)}..${int(zMax)}" => Cuboid(Rng(xMin, xMax+1), Rng(yMin, yMax+1), Rng(zMin, zMax+1)) -> (cmd == "on") }.reverse def slices(f: Cuboid => Rng) = SortedSet(cuboids.flatMap { case (c, _) => Seq(f(c).l, f(c).h) }:_*).toIndexedSeq val xs = slices(_.xr) val ys = slices(_.yr) val zs = slices(_.zr) def isOn(x: Int, y: Int, z: Int) = cuboids.find { case (cuboid, _) => cuboid.contains(x, y, z) }.exists(_._2) var count = 0L for { xi <- 0 until xs.length - 1 yi <- 0 until ys.length - 1 zi <- 0 until zs.length - 1 if isOn(xs(xi), ys(yi), zs(zi)) } count += (xs(xi+1) - xs(xi)).toLong * (ys(yi+1) - ys(yi)) * (zs(zi+1) - zs(zi)) println(count) } def main(args: Array[String]): Unit = { part1 part2 } }

Dold text
PermalÀnk
Datavetare ★
●

Dag: 22
SprÄk: Rust

Nu börjar luckorna regelmÀssigt ta ms och inte ”s... Denna tar 8-9 ms (lite beroende pÄ om man kör pÄ M1 eller Ryzen).

InsÄg redan frÄn första exemplet att brute-force inte var realistiskt i del 2, gÄr annars att lösa del 1 rÀtt snabbt genom att representera x-axeln med ett 128-bitars tal (moderna CPUer stödjer det via SIMD) dÀr varje bit representerar en kub. Det hela kan dÄ representeras i 2D dÀr Y/Z Àr dimensioner med ett 128-bitars tal per cell.

Men löste det i stÀllet med "positiva" resp "negativa" volymer. Varje rad i indata beskriver ett rÀtblock, "on" block har positiv volym medan "off" block har negativ volym.

Snittet mellan tvÄ "on" block lÀgger till ett "off" block, detta dÄ man annars rÀknar den överlappande volymen tvÄ gÄnger. "off" blocket kompenserar för det.

Snittet mellan ett "on" block och ett "off" block blir ocksÄ ett "off" block, skillnaden att man vid hantering av ett "off" block inte lÀgger till "off" blocket sjÀlvt. Resultatet blir att man minskar volymen pÄ existerande "on" block med volymen av snittet.

Snittet mellan ett inlagt "off" block och ett nytt "on" block blir ett "on" block, det för att "lÀgga till" den volym i "off" blocket som nu slagit pÄ.

use std::{ str::FromStr, cmp::{max, min} }; use crate::solution::Solution; struct Day22 { blocks: Vec<Block>, } impl Solution for Day22 { fn part1(&mut self) -> String { self.count_on_cubes(false).to_string() } fn part2(&mut self) -> String { self.count_on_cubes(true).to_string() } } impl Day22 { fn count_on_cubes(&self, include_all_cubes: bool) -> u64 { let mut new_blocks = Vec::new(); let mut blocks = Vec::new(); blocks.reserve(60_000); for &block in self.blocks.iter() { if !include_all_cubes && !block.is_50_50() { continue; } if block.is_on { new_blocks.push(block) } for rhs in blocks.iter() { if let Some(isect_block) = block.intersection(rhs) { new_blocks.push(isect_block); } } blocks.append(&mut new_blocks); } blocks .iter() .fold(0, |volume, block| { if block.is_on { volume + block.volume() } else { volume - block.volume() } }) } } pub fn new(input: &str) -> Box<dyn Solution> { Box::new(Day22 { blocks: input.lines().filter_map(|line| line.parse().ok()).collect(), }) } #[derive(Clone, Copy)] struct Block { is_on: bool, x_lim: (i32, i32), y_lim: (i32, i32), z_lim: (i32, i32), } impl Block { fn is_50_50(&self) -> bool { return self.x_lim.0 >= -50 && self.x_lim.1 <= 51 && self.y_lim.0 >= -50 && self.y_lim.1 <= 51 && self.z_lim.0 >= -50 && self.z_lim.1 <= 51 } fn volume(&self) -> u64 { (self.x_lim.1 - self.x_lim.0) as u64 * (self.y_lim.1 - self.y_lim.0) as u64 * (self.z_lim.1 - self.z_lim.0) as u64 } fn intersection(&self, rhs: &Block) -> Option<Block> { if self.x_lim.0 >= rhs.x_lim.1 || self.x_lim.1 <= rhs.x_lim.0 || self.y_lim.0 >= rhs.y_lim.1 || self.y_lim.1 <= rhs.y_lim.0 || self.z_lim.0 >= rhs.z_lim.1 || self.z_lim.1 <= rhs.z_lim.0 { return None; } let x0 = max(self.x_lim.0, rhs.x_lim.0); let x1 = min(self.x_lim.1, rhs.x_lim.1); let y0 = max(self.y_lim.0, rhs.y_lim.0); let y1 = min(self.y_lim.1, rhs.y_lim.1); let z0 = max(self.z_lim.0, rhs.z_lim.0); let z1 = min(self.z_lim.1, rhs.z_lim.1); Some(Block { is_on: !rhs.is_on, x_lim: (x0, x1), y_lim: (y0, y1), z_lim: (z0, z1), }) } } impl FromStr for Block { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { let (mode, x0, x1, y0, y1, z0, z1) = scan_fmt!( s, "{} x={d}..{d},y={d}..{d},z={d}..{d}", String, i32, i32, i32, i32, i32, i32 ) .expect("Invalid block format"); Ok(Block { is_on: mode == "on", x_lim: (x0, x1 + 1), y_lim: (y0, y1 + 1), z_lim: (z0, z1 + 1), }) } }

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
Medlem
●

Dag: 22
SprÄk: Carth

Del: 1

Suck...

Gjorde först en brute-force lösning till del 1, som sÄklart inte klarade av del 2 sen. Men inga problem, jag kommer pÄ nÄt bÀttre. Octrees kanske? Det lÄter smart, ja men det kör vi pÄ!

Och visst, min lösning med Octrees kan lösa del 1 mycket snabbare Àn innan, men den grötar fortfarande fast totalt i utmaning mot del 2. Det fÄr bli att ÄtergÄ till arbetsbÀnken och börja om frÄn början imorgon helt enkelt. Suck...

(import std) (data Vec3 (Vec3 Int Int Int)) (data Cube (Cube Vec3 Int)) (data Cuboid (Cuboid Vec3 Vec3)) (data Status On Off) (data Octree (Ocleaf Cube Status) (Octree Cube (Array Octree))) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/test.txt"))) (let1 cmds (parse-commands input)) (let ((cmds' (filter-map (fun ([on? xs ys zs]) (if (any (<o (< 50) abs) (list/iter (list (car xs) (cadr xs) (car ys) (cadr ys) (car zs) (cadr zs)))) None (Some [on? xs ys zs]))) cmds)))) (display (str-append "Part 1: " (show-int (count-ons (execute octree/new cmds'))))) (display (str-append "Part 2: " (show-int (count-ons (execute octree/new cmds))))))) (define execute (foldl (fun (octree [on? [x1 x2] [y1 y2] [z1 z2]]) ((trace "executing command" set-status) (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (if on? On Off) octree)))) (define (set-status region status octree) (if (not (cube/contains region (octree/cube octree))) (panic "region not contained in cube") (if (maybe' False (cube/= (octree/cube octree)) (cuboid/as-cube region)) (Ocleaf (octree/cube octree) status) (match octree (case (Ocleaf c s) (if (status/= s status) (Ocleaf c s) (set-status region status (octree/subdivide c s)))) (case (Octree c subs) (let1 subs (array/collect (zip-with (fmatch (case (Some subreg) (set-status subreg status)) (case None id)) (list/iter (cuboid/split (cube/midpoint c) region)) (array/iter subs))) (match (same-status-leaves subs) (case (Some s) (Ocleaf c s)) (case None (Octree c subs))))))))) (define (cuboid/as-cube (Cuboid p1 p2)) (let1 (Vec3 dx dy dz) (vec3/- p2 p1) (if (and (= dx dy) (= dy dz)) (Some (Cube p1 (inc dx))) None))) (define (cuboid/split (Vec3 split-x split-y split-z) (Cuboid (Vec3 px1 py1 pz1) (Vec3 px2 py2 pz2))) (define (split-dim a1 a2 amid) (if (< a1 amid) (if (>= a2 amid) (list (Some [a1 (dec amid)]) (Some [amid a2])) (list (Some [a1 a2]) None)) (list None (Some [a1 a2])))) (do list/bind (<- xs (split-dim px1 px2 split-x)) (<- ys (split-dim py1 py2 split-y)) (<- zs (split-dim pz1 pz2 split-z)) (list/singleton (do maybe/bind (<- [x1 x2] xs) (<- [y1 y2] ys) (<- [z1 z2] zs) (Some (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2))))))) (define (cuboid/show (Cuboid p1 p2)) (apps str-append "(Cuboid " (vec3/show p1) " " (vec3/show p2) ")")) (define (same-status-leaves subs) (let1 ss (list/collect (filter-map leaf-status (array/iter subs))) (if (/= (to-nat 8) (list/count ss)) None (let1 [s0 ss] (list/uncons! ss) (if (and (= (to-nat 8) (list/count ss)) (all (status/= s0) (list/iter ss))) (Some s0) None))))) (define leaf-status (fmatch (case (Ocleaf _ s) (Some s)) (case _ None))) (define: (status/= s1 s2) (Fun Status Status Bool) (= (: (transmute s1) Nat8) (transmute s2))) (define status/show (fmatch (case On "On") (case Off "Off"))) (define (octree/subdivide c s) (Octree c (array/map (flip Ocleaf s) (cube/subdivide c)))) (define (cube/contains (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Cube (Vec3 px py pz) len)) (apps and (>= x1 px) (>= y1 py) (>= z1 pz) (< x2 (+ px len)) (< y2 (+ py len)) (< z2 (+ pz len)))) (define (cube/subdivide (Cube (Vec3 x y z) len)) (let1 l (/ len 2) ((<oo array/collect-list list/map) (flip Cube l) (list (Vec3 x y z ) (Vec3 x y (+ z l)) (Vec3 x (+ y l) z ) (Vec3 x (+ y l) (+ z l)) (Vec3 (+ x l) y z ) (Vec3 (+ x l) y (+ z l)) (Vec3 (+ x l) (+ y l) z ) (Vec3 (+ x l) (+ y l) (+ z l)))))) (define (cube/midpoint (Cube c len)) (vec3/+ c (vec3/repeat (/ len 2)))) (define (cube/volume (Cube _ len)) (*s len len len)) (define (cube/= (Cube pos1 len1) (Cube pos2 len2)) (andalso (= len1 len2) (vec3/= pos1 pos2))) (define (cube/show (Cube pos len)) (apps str-append "(Cube " (vec3/show pos) " " (show-int len) ")")) (define (vec3/= v1 v2) (match (vec3/cmp v1 v2) (case Eq True) (case _ False))) (define (vec3/- (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (- x1 x2) (- y1 y2) (- z1 z2))) (define (vec3/+ (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (+ x1 x2) (+ y1 y2) (+ z1 z2))) (define (vec3/cmp (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (match (num/cmp x1 x2) (case Eq (match (num/cmp y1 y2) (case Eq (num/cmp z1 z2)) (case x x))) (case x x))) (define (vec3/show (Vec3 x y z)) (apps str-append "(Vec3 " (show-int x) " " (show-int y) " " (show-int z) ")")) (define octree/cube (fmatch (case (Ocleaf c _) c) (case (Octree c _) c))) (define octree/new (Ocleaf (Cube (vec3/repeat (neg (powi 2 17))) (powi 2 18)) Off)) (define (vec3/repeat x) (Vec3 x x x)) (define count-ons (fmatch (case (Ocleaf cube On) (cube/volume cube)) (case (Ocleaf _ Off) 0) (case (Octree bounds children) (sum (map count-ons (array/iter children)))))) (define parse-commands (define prange (parse/lift2 cons' parse/int (parse/thenr (parse/string "..") parse/int))) (define pcmd (do parse/bind (<- on (parse/or (parse/thenr (parse/string "on") (parse/pure True)) (parse/thenr (parse/string "off") (parse/pure False)))) (parse/string " x=") (<- xs prange) (parse/string ",y=") (<- ys prange) (parse/string ",z=") (<- zs prange) (parse/pure [on xs ys zs]))) (<o (map (parse! pcmd)) lines))

Dold text
Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

PermalÀnk
●
Skrivet av Yoshman:

Dag: 22
SprÄk: Rust

Nu börjar luckorna regelmÀssigt ta ms och inte ”s... Denna tar 8-9 ms (lite beroende pÄ om man kör pÄ M1 eller Ryzen).

InsÄg redan frÄn första exemplet att brute-force inte var realistiskt i del 2, gÄr annars att lösa del 1 rÀtt snabbt genom att representera x-axeln med ett 128-bitars tal (moderna CPUer stödjer det via SIMD) dÀr varje bit representerar en kub. Det hela kan dÄ representeras i 2D dÀr Y/Z Àr dimensioner med ett 128-bitars tal per cell.

Men löste det i stÀllet med "positiva" resp "negativa" volymer. Varje rad i indata beskriver ett rÀtblock, "on" block har positiv volym medan "off" block har negativ volym.

Snittet mellan tvÄ "on" block lÀgger till ett "off" block, detta dÄ man annars rÀknar den överlappande volymen tvÄ gÄnger. "off" blocket kompenserar för det.

Snittet mellan ett "on" block och ett "off" block blir ocksÄ ett "off" block, skillnaden att man vid hantering av ett "off" block inte lÀgger till "off" blocket sjÀlvt. Resultatet blir att man minskar volymen pÄ existerande "on" block med volymen av snittet.

Snittet mellan ett inlagt "off" block och ett nytt "on" block blir ett "on" block, det för att "lÀgga till" den volym i "off" blocket som nu slagit pÄ.

use std::{ str::FromStr, cmp::{max, min} }; use crate::solution::Solution; struct Day22 { blocks: Vec<Block>, } impl Solution for Day22 { fn part1(&mut self) -> String { self.count_on_cubes(false).to_string() } fn part2(&mut self) -> String { self.count_on_cubes(true).to_string() } } impl Day22 { fn count_on_cubes(&self, include_all_cubes: bool) -> u64 { let mut new_blocks = Vec::new(); let mut blocks = Vec::new(); blocks.reserve(60_000); for &block in self.blocks.iter() { if !include_all_cubes && !block.is_50_50() { continue; } if block.is_on { new_blocks.push(block) } for rhs in blocks.iter() { if let Some(isect_block) = block.intersection(rhs) { new_blocks.push(isect_block); } } blocks.append(&mut new_blocks); } blocks .iter() .fold(0, |volume, block| { if block.is_on { volume + block.volume() } else { volume - block.volume() } }) } } pub fn new(input: &str) -> Box<dyn Solution> { Box::new(Day22 { blocks: input.lines().filter_map(|line| line.parse().ok()).collect(), }) } #[derive(Clone, Copy)] struct Block { is_on: bool, x_lim: (i32, i32), y_lim: (i32, i32), z_lim: (i32, i32), } impl Block { fn is_50_50(&self) -> bool { return self.x_lim.0 >= -50 && self.x_lim.1 <= 51 && self.y_lim.0 >= -50 && self.y_lim.1 <= 51 && self.z_lim.0 >= -50 && self.z_lim.1 <= 51 } fn volume(&self) -> u64 { (self.x_lim.1 - self.x_lim.0) as u64 * (self.y_lim.1 - self.y_lim.0) as u64 * (self.z_lim.1 - self.z_lim.0) as u64 } fn intersection(&self, rhs: &Block) -> Option<Block> { if self.x_lim.0 >= rhs.x_lim.1 || self.x_lim.1 <= rhs.x_lim.0 || self.y_lim.0 >= rhs.y_lim.1 || self.y_lim.1 <= rhs.y_lim.0 || self.z_lim.0 >= rhs.z_lim.1 || self.z_lim.1 <= rhs.z_lim.0 { return None; } let x0 = max(self.x_lim.0, rhs.x_lim.0); let x1 = min(self.x_lim.1, rhs.x_lim.1); let y0 = max(self.y_lim.0, rhs.y_lim.0); let y1 = min(self.y_lim.1, rhs.y_lim.1); let z0 = max(self.z_lim.0, rhs.z_lim.0); let z1 = min(self.z_lim.1, rhs.z_lim.1); Some(Block { is_on: !rhs.is_on, x_lim: (x0, x1), y_lim: (y0, y1), z_lim: (z0, z1), }) } } impl FromStr for Block { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { let (mode, x0, x1, y0, y1, z0, z1) = scan_fmt!( s, "{} x={d}..{d},y={d}..{d},z={d}..{d}", String, i32, i32, i32, i32, i32, i32 ) .expect("Invalid block format"); Ok(Block { is_on: mode == "on", x_lim: (x0, x1 + 1), y_lim: (y0, y1 + 1), z_lim: (z0, z1 + 1), }) } }

Dold text

Bra tÀnkt! Enkel och elegant algoritm.
Nu kÀnner man sig lite snopen att man inte kom pÄ den sjÀlv

PermalÀnk
Medlem
●
Skrivet av Bryal:

Dag: 22
SprÄk: Carth

Del: 1

Suck...

Gjorde först en brute-force lösning till del 1, som sÄklart inte klarade av del 2 sen. Men inga problem, jag kommer pÄ nÄt bÀttre. Octrees kanske? Det lÄter smart, ja men det kör vi pÄ!

Och visst, min lösning med Octrees kan lösa del 1 mycket snabbare Àn innan, men den grötar fortfarande fast totalt i utmaning mot del 2. Det fÄr bli att ÄtergÄ till arbetsbÀnken och börja om frÄn början imorgon helt enkelt. Suck...

(import std) (data Vec3 (Vec3 Int Int Int)) (data Cube (Cube Vec3 Int)) (data Cuboid (Cuboid Vec3 Vec3)) (data Status On Off) (data Octree (Ocleaf Cube Status) (Octree Cube (Array Octree))) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/test.txt"))) (let1 cmds (parse-commands input)) (let ((cmds' (filter-map (fun ([on? xs ys zs]) (if (any (<o (< 50) abs) (list/iter (list (car xs) (cadr xs) (car ys) (cadr ys) (car zs) (cadr zs)))) None (Some [on? xs ys zs]))) cmds)))) (display (str-append "Part 1: " (show-int (count-ons (execute octree/new cmds'))))) (display (str-append "Part 2: " (show-int (count-ons (execute octree/new cmds))))))) (define execute (foldl (fun (octree [on? [x1 x2] [y1 y2] [z1 z2]]) ((trace "executing command" set-status) (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (if on? On Off) octree)))) (define (set-status region status octree) (if (not (cube/contains region (octree/cube octree))) (panic "region not contained in cube") (if (maybe' False (cube/= (octree/cube octree)) (cuboid/as-cube region)) (Ocleaf (octree/cube octree) status) (match octree (case (Ocleaf c s) (if (status/= s status) (Ocleaf c s) (set-status region status (octree/subdivide c s)))) (case (Octree c subs) (let1 subs (array/collect (zip-with (fmatch (case (Some subreg) (set-status subreg status)) (case None id)) (list/iter (cuboid/split (cube/midpoint c) region)) (array/iter subs))) (match (same-status-leaves subs) (case (Some s) (Ocleaf c s)) (case None (Octree c subs))))))))) (define (cuboid/as-cube (Cuboid p1 p2)) (let1 (Vec3 dx dy dz) (vec3/- p2 p1) (if (and (= dx dy) (= dy dz)) (Some (Cube p1 (inc dx))) None))) (define (cuboid/split (Vec3 split-x split-y split-z) (Cuboid (Vec3 px1 py1 pz1) (Vec3 px2 py2 pz2))) (define (split-dim a1 a2 amid) (if (< a1 amid) (if (>= a2 amid) (list (Some [a1 (dec amid)]) (Some [amid a2])) (list (Some [a1 a2]) None)) (list None (Some [a1 a2])))) (do list/bind (<- xs (split-dim px1 px2 split-x)) (<- ys (split-dim py1 py2 split-y)) (<- zs (split-dim pz1 pz2 split-z)) (list/singleton (do maybe/bind (<- [x1 x2] xs) (<- [y1 y2] ys) (<- [z1 z2] zs) (Some (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2))))))) (define (cuboid/show (Cuboid p1 p2)) (apps str-append "(Cuboid " (vec3/show p1) " " (vec3/show p2) ")")) (define (same-status-leaves subs) (let1 ss (list/collect (filter-map leaf-status (array/iter subs))) (if (/= (to-nat 8) (list/count ss)) None (let1 [s0 ss] (list/uncons! ss) (if (and (= (to-nat 8) (list/count ss)) (all (status/= s0) (list/iter ss))) (Some s0) None))))) (define leaf-status (fmatch (case (Ocleaf _ s) (Some s)) (case _ None))) (define: (status/= s1 s2) (Fun Status Status Bool) (= (: (transmute s1) Nat8) (transmute s2))) (define status/show (fmatch (case On "On") (case Off "Off"))) (define (octree/subdivide c s) (Octree c (array/map (flip Ocleaf s) (cube/subdivide c)))) (define (cube/contains (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Cube (Vec3 px py pz) len)) (apps and (>= x1 px) (>= y1 py) (>= z1 pz) (< x2 (+ px len)) (< y2 (+ py len)) (< z2 (+ pz len)))) (define (cube/subdivide (Cube (Vec3 x y z) len)) (let1 l (/ len 2) ((<oo array/collect-list list/map) (flip Cube l) (list (Vec3 x y z ) (Vec3 x y (+ z l)) (Vec3 x (+ y l) z ) (Vec3 x (+ y l) (+ z l)) (Vec3 (+ x l) y z ) (Vec3 (+ x l) y (+ z l)) (Vec3 (+ x l) (+ y l) z ) (Vec3 (+ x l) (+ y l) (+ z l)))))) (define (cube/midpoint (Cube c len)) (vec3/+ c (vec3/repeat (/ len 2)))) (define (cube/volume (Cube _ len)) (*s len len len)) (define (cube/= (Cube pos1 len1) (Cube pos2 len2)) (andalso (= len1 len2) (vec3/= pos1 pos2))) (define (cube/show (Cube pos len)) (apps str-append "(Cube " (vec3/show pos) " " (show-int len) ")")) (define (vec3/= v1 v2) (match (vec3/cmp v1 v2) (case Eq True) (case _ False))) (define (vec3/- (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (- x1 x2) (- y1 y2) (- z1 z2))) (define (vec3/+ (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (+ x1 x2) (+ y1 y2) (+ z1 z2))) (define (vec3/cmp (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (match (num/cmp x1 x2) (case Eq (match (num/cmp y1 y2) (case Eq (num/cmp z1 z2)) (case x x))) (case x x))) (define (vec3/show (Vec3 x y z)) (apps str-append "(Vec3 " (show-int x) " " (show-int y) " " (show-int z) ")")) (define octree/cube (fmatch (case (Ocleaf c _) c) (case (Octree c _) c))) (define octree/new (Ocleaf (Cube (vec3/repeat (neg (powi 2 17))) (powi 2 18)) Off)) (define (vec3/repeat x) (Vec3 x x x)) (define count-ons (fmatch (case (Ocleaf cube On) (cube/volume cube)) (case (Ocleaf _ Off) 0) (case (Octree bounds children) (sum (map count-ons (array/iter children)))))) (define parse-commands (define prange (parse/lift2 cons' parse/int (parse/thenr (parse/string "..") parse/int))) (define pcmd (do parse/bind (<- on (parse/or (parse/thenr (parse/string "on") (parse/pure True)) (parse/thenr (parse/string "off") (parse/pure False)))) (parse/string " x=") (<- xs prange) (parse/string ",y=") (<- ys prange) (parse/string ",z=") (<- zs prange) (parse/pure [on xs ys zs]))) (<o (map (parse! pcmd)) lines))

Dold text

Dag: 22
SprÄk: Carth

Del: 1 & 2

NusÄ!

Körde pÄ en av de första idéerna jag hade, men inte anvÀnde igÄr. Mitt trötta huvud dÄ trodde inte att det skulle vara möjligt att implementera riktigt, och jag lyckades inte kÀmpa mig förbi nÄgra av detaljerna. Nu efter att den andra approachen jag tog misslyckats kÀndes det plötsligt som att detta nog ÀndÄ mÄste vara "rÀtt" sÀtt att lösa problemet pÄ. Och visst, med en lite piggare hjÀrna gick det prima att implementera!

Den hÀr approachen Àr baserad pÄ Constructive Solid Geometry -- unioner, differenser, och snitt av perfekta volymer. Var lite lurigt att komma pÄ exakt hur jag skulle "bokföra" Àndringarna, men jag kom fram till att mest spara alla snitt i en additiv och en subtraktiv lista, och sedan summera alla dessa volymer pÄ slutet. Funkar bra!

Dold text
Klicka för mer information

(import std) (data Vec3 (Vec3 Int Int Int)) (data Cuboid (Cuboid Vec3 Vec3)) (define main (do io/bind (<- input (io/map unwrap! (read-file "inputs/day22.txt"))) (let1 cmds (parse-commands input)) (let ((cmds' (filter-map (fun ([on? xs ys zs]) (if (any (<o (< 50) abs) (list/iter (list (car xs) (cadr xs) (car ys) (cadr ys) (car zs) (cadr zs)))) None (Some [on? xs ys zs]))) cmds)))) (display (str-append "Part 1: " (show-int (count-cubes (execute [LNil LNil] cmds'))))) (display (str-append "Part 2: " (show-int (count-cubes (execute [LNil LNil] cmds))))))) (define (count-cubes [additives xsubtractives]) (- (sum (map cuboid/volume (list/iter additives))) (sum (map cuboid/volume (list/iter subtractives))))) (define execute (foldl (fun (volumes [on? [x1 x2] [y1 y2] [z1 z2]]) (let1 c (Cuboid (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (if on? (add c volumes) (subtract c volumes)))))) (define (add c [additives subtractives]) [(list/append' (filter-map (intersection c) (list/iter subtractives)) (list/cons c additives)) (list/append' (filter-map (intersection c) (list/iter additives)) subtractives)]) (define (subtract c [additives subtractives]) [(list/append' (filter-map (intersection c) (list/iter subtractives)) additives) (list/append' (filter-map (intersection c) (list/iter additives)) subtractives)]) (define (intersection (Cuboid a1 a2) (Cuboid b1 b2)) (let ((c1 (vec3/zip-with max a1 b1)) (c2 (vec3/zip-with min a2 b2)) ((Vec3 dx dy dz) (vec3/- c2 c1))) (if (apps or (< dx 0) (< dy 0) (< dz 0)) None (Some (Cuboid c1 c2))))) (define (cuboid/volume (Cuboid p1 p2)) (let1 (Vec3 dx dy dz) (vec3/+ (vec3/- p2 p1) (vec3/repeat 1)) (*s dx dy dz))) (define (vec3/- v1 v2) (vec3/zip-with - v1 v2)) (define (vec3/+ v1 v2) (vec3/zip-with + v1 v2)) (define (vec3/zip-with f (Vec3 x1 y1 z1) (Vec3 x2 y2 z2)) (Vec3 (f x1 x2) (f y1 y2) (f z1 z2))) (define (vec3/repeat x) (Vec3 x x x)) (define parse-commands (define prange (parse/lift2 cons' parse/int (parse/thenr (parse/string "..") parse/int))) (define pcmd (do parse/bind (<- on (parse/or (parse/thenr (parse/string "on") (parse/pure True)) (parse/thenr (parse/string "off") (parse/pure False)))) (parse/string " x=") (<- xs prange) (parse/string ",y=") (<- ys prange) (parse/string ",z=") (<- zs prange) (parse/pure [on xs ys zs]))) (<o (map (parse! pcmd)) lines))

Visa mer
Visa signatur

Arbets- / Spelstation: Arch Linux - Ryzen 5 3600 - RX 7900 XT - 32G DDR4
Server: Arch Linux - Core i5-10400F - 16G DDR4

PermalÀnk
Medlem ★
●
Skrivet av GLaDER:

Dag: 17
SprÄk: Python 3
Lösning: GitHub

Rolig uppgift idag! Kom fram till lösningen med ren brute-force men efter lite diskussion pÄ Slack hittade vi vettiga randvillkor och jag fick ner exekveringstiden frÄn ~15s till drygt 1s.

Dold text

Dagar och Lösningar: [18, 20, 21, 22, 23]
SprÄk: Python 3

Är hos svĂ€rförĂ€ldrarna i annan tidszon och har sĂ„ledes slutat försöka lösa uppgifterna med snabbhet i Ă„tanke. Har fortfarande inte börjat titta pĂ„ dag 19..

Visa signatur

:(){ :|:& };:

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

PermalÀnk
Medlem ★
●

Dag: 23
SprÄk: Python

I dag blev det hemskt. Det tog alldeles för lÄng tid och det blev nog den minst eleganta lösningen hittills

Jag har ingen graf utan jobbade bara med 8 koordinater man fick. Var inte en lika bra ide nÀr det kom tvÄ extra rader.

from heapq import heappush, heappop import re destinations = [0,1,3,5,7,9,10] f = [letters for l in open("input23").readlines() if (letters := re.findall("(\w)", l.rstrip()))] A,B,C,D = [],[],[],[] for i, line in enumerate(f[::-1]): for j, var in enumerate(line): eval(var).append((j * 2 + 2, i)) depth = i + 1 pos = A + B + C + D costs = ([1] * depth) + ([10] * depth) + ([100] * depth) + ([1000] * depth) def getpath(p, x, y): return ([(i, depth) for i in range(p, x, 1 if p < x else -1)] + [(x, i) for i in range(y, depth + 1)]) def nocollision(p, path, state): return all(p2 not in path for p2 in state if p2 != p) def pushnewstate(state, cost, path, p, i): s = list(state) s[i] = p t = tuple(s) ncost = cost + (len(path) -1) * costs[i] heappush(h, (ncost, t)) h = [] visited = set() heappush(h, (0, tuple(pos))) while h: cost, state = heappop(h) if state in visited: continue visited.add(state) if all(p[1] != depth for p in state) and cost: break for i, p in enumerate(state): destx = (i // depth) * 2 + 2 desty = 0 for j in range(depth): ndx = i // depth * depth if (destx, desty) in state[ndx : ndx + depth]: desty = j + 1 else: break if p[0] == destx and p[1] < desty: # Already in the right place? continue if p[1] != depth: # Move up? if p[0] != destx: # and down? path = getpath(destx, *p) + getpath(destx, destx, desty)[:-1] if nocollision(p, path, state): pushnewstate(state, cost, path, (destx,desty), i) break for d in destinations: # No, try all other positions path = getpath(d, *p) if nocollision(p, path, state): pushnewstate(state, cost, path, (d,depth), i) else: # Move back down path = getpath(p[0], destx, desty) if nocollision(p, path, state): pushnewstate(state, cost, path, (destx,desty), i) print(cost)

Dold text
PermalÀnk
Medlem
●

Dag: 23
SprÄk: C#
Github: https://github.com/langebangen/AOC/blob/main/2021/AOC2021/Puz...

Äntlingen fick jag rĂ€tt pĂ„ denna. Med inspiration av andra sĂ„ blev det en dijkstra algoritm dĂ€r jag för varje "brĂ€de" tar fram alla möjliga nĂ€sta brĂ€den och sen lĂ„ter dijkstra-algoritmen rĂ€kna fram den mest optimala med hjĂ€lp av en prioritetskö. Är fortfarande inte nöjd dĂ„ det tar över en minut att fĂ„ fram bĂ€gge svaren. SĂ„ det gĂ„r sĂ€kerligen att optimera..

Edit: Äntligen fick jag ner tiden till 2 sekunder totalt för bĂ€gge delar.. Lösningen var att istĂ€llet för att spara ner hela Board-objekt i kön sĂ„ sparade jag endast ner en strĂ€ng av deras state, som jag sedan kan anvĂ€nda för att bygga upp ett Board-objekt med igen.

God jul pÄ er alla

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

Dagar och Lösningar: [18, 20, 21, 22, 23]
SprÄk: Python 3

Är hos svĂ€rförĂ€ldrarna i annan tidszon och har sĂ„ledes slutat försöka lösa uppgifterna med snabbhet i Ă„tanke. Har fortfarande inte börjat titta pĂ„ dag 19..

Dag: 24
SprÄk: Python 3
Lösning: GitHub

Jag vill sÀga att dagens uppgift Àr rÀtt sorts uppgift för mig. Jag har alltid tyckt om assemblering och brukar ha lÀtt för att se mönster; men icke. NÀr jag snubblade över https://github.com/dphilipson/advent-of-code-2021/blob/master... blev det dock lÀttare och jag kunde inspireras fram till lösningen ovan.

Dold text
Visa signatur

:(){ :|:& };:

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

PermalÀnk
Datavetare ★
●

Dag: 23
SprÄk: Rust

GÄr att se denna som ett 1D-problem, i alla fall sÄ lÀnge som rummen har en relativ begrÀnsad storlek (i mitt fall fungerar det ha ha upp till 9 st i varje rum, 19 st och man utökar storleken pÄ tillstÄndet frÄn 32-bitar per position till 64-bitar).

Testade först, likt mÄnga andra, med Dijkstra dÄ det "rimligen borde vara mer effektivt". Fungerade, men för mig var en DFS betydligt snabbare + en sÄdan Àr lÀttare att sprida över flera kÀrnor.

Ihop med DFS körs en memoize för att eliminera kombinationer av samma lösning.

GÄr att köra pÄ ~90 ms pÄ M1 och ~65 ms pÄ 5950X. Men Àr inte precis den kortaste lösningen... Givet att kÀrnorna lastas rÀtt nÀra 100 % Àr det brutalt ineffektiv anvÀndning av kÀrnor, inte miljöklassad... Tar ca 150 ms att lösa pÄ en trÄd pÄ M1, bl.a. fungerar memoize-delen betydligt bÀttre ju fÀrre trÄdar man anvÀnder.

use crate::solution::Solution; use fnv::FnvHashSet; use rayon::prelude::*; struct Day23 { initial_state: State, } impl Solution for Day23 { fn part1(&mut self) -> String { self.organize().to_string() } fn part2(&mut self) -> String { for (i, a) in [(4, 4), (3, 2), (2, 1), (1, 3)].iter().enumerate() { let mut loc = self.initial_state.map[i * 2 + 2]; let p4 = (loc >> 6) & 0x7; loc &= !0xffc0; loc |= (p4 << 12) + (a.1 << 9) + (a.0 << 6); loc += 0x2_0000; self.initial_state.map[i * 2 + 2] = loc; } self.organize().to_string() } } impl Day23 { fn organize(&self) -> u32 { let mut start_states = Vec::new(); for i in 0..11 { if let Some(new_state) = self.initial_state.try_move_to_final_pos(i) { start_states.push(new_state); } else if is_room(i) { for new_state in self.initial_state.hallway_moves(i) { start_states.push(new_state); } } } start_states.par_iter().map(sub_organize).min().unwrap() } } fn sub_organize(start_state: &State) -> u32 { let mut memoize = FnvHashSet::default(); let mut stack = vec![*start_state]; let mut min_energy = u32::MAX; while let Some(state) = stack.pop() { if state.energy >= min_energy { continue; } if state.is_done() { min_energy = state.energy; } else { memoize.insert(state); } for i in 0..11 { if let Some(new_state) = state.try_move_to_final_pos(i) { if !memoize.contains(&new_state) { stack.push(new_state); } } else if is_room(i) { for new_state in state.hallway_moves(i) { if !memoize.contains(&new_state) { stack.push(new_state); } } } } } min_energy } pub fn new(input: &str) -> Box<dyn Solution> { let start_pos = input .chars() .filter(|ch| ch.is_alphabetic()) .map(|ch| (ch as u8 - b'A' + 1) as u32) .collect::<Vec<_>>(); Box::new(Day23 { initial_state: State::new(&start_pos), }) } // Each slot has this format | 16-bit A | 1-bit unused | 5x 3-bit B | // A is always 0 for hallways, number of slots available in burrows for the "right" amphipod type // B is amphipod type (3-bits, 1-4 and 0 means empty). Which of the slots determine "depth" // hallways only use depth 0 and rooms only use depth 1-4 type Map = [u32; 11]; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] struct State { map: Map, energy: u32, } impl State { fn new(start_pos: &[u32]) -> Self { let mut d = State { map: [0; 11], energy: 0, }; for i in 0..4 { let r0 = start_pos[i]; let r1 = start_pos[i + 4]; if r1 == i as u32 + 1 { if r0 == i as u32 + 1 { d.map[2 * (i + 1)] = 0; } else { d.map[2 * (i + 1)] = (1 << 16) + (r0 << 3); } } else { d.map[2 * (i + 1)] = (2 << 16) + (r0 << 3) + (r1 << 6); } } d } fn is_done(&self) -> bool { (self.map[2] == 0) & (self.map[4] == 0) & (self.map[6] == 0) & (self.map[8] == 0) } fn try_move_to_final_pos(&self, map_idx: usize) -> Option<State> { let loc = self.map[map_idx]; if location_is_empty(loc) { return None; } let (amphipod_type, depth) = if is_room(map_idx) { outermost_amphipod_in_room(loc) } else { (loc, 0) }; if let Some(steps) = self.steps_to_home_burrow(amphipod_type, map_idx) { let mut map = self.map; remove_amphipod(&mut map, map_idx, depth); add_amphipod_to_home_burrow(&mut map, amphipod_type); Some(State { map, energy: self.energy + (depth + steps) * step_cost(amphipod_type), }) } else { None } } fn steps_to_home_burrow(&self, amphipod_type: u32, map_idx: usize) -> Option<u32> { let home_idx = home_burrow_idx(amphipod_type); let home_loc = self.map[home_idx]; if !location_is_empty(home_loc) { // Something of wrong type in home burrow return None; } let (from, to) = if home_idx > map_idx { (map_idx, home_idx) } else { (home_idx, map_idx) }; for i in (from + 1)..to { if (self.map[i] & 0x7) != 0 { return None; } } Some((to - from) as u32 + (home_loc >> 16)) } fn hallway_moves(&self, room_idx: usize) -> impl Iterator<Item = State> { let loc = self.map[room_idx]; if location_is_empty(loc) { return vec![].into_iter(); } let (amphipod_type, depth) = outermost_amphipod_in_room(loc); let mut new_state = *self; remove_amphipod(&mut new_state.map, room_idx, depth); let start = (room_idx >> 1) + 1; let mut moves = Vec::new(); add_moves( &mut moves, &new_state, (0..start).rev(), amphipod_type, depth, room_idx, ); add_moves( &mut moves, &new_state, start..7, amphipod_type, depth, room_idx, ); moves.into_iter() } } fn add_moves<I>( moves: &mut Vec<State>, state: &State, idxs: I, amphipod_type: u32, depth: u32, room_idx: usize, ) where I: Iterator<Item = usize>, { const HALLWAY_IDXS: [usize; 7] = [0, 1, 3, 5, 7, 9, 10]; for i in idxs { let map_idx = HALLWAY_IDXS[i]; let l = state.map[map_idx]; if l != 0 { break; } let move_cost = (depth + (map_idx as i32 - room_idx as i32).abs() as u32) * step_cost(amphipod_type); let mut new_state = *state; new_state.map[map_idx] = amphipod_type; new_state.energy += move_cost; moves.push(new_state); } } fn is_room(idx: usize) -> bool { (idx == 2) | (idx == 4) | (idx == 6) | (idx == 8) } fn location_is_empty(loc: u32) -> bool { (loc & 0xffff) == 0 } fn home_burrow_idx(amphipod_type: u32) -> usize { (amphipod_type * 2) as usize } fn remove_amphipod(map: &mut Map, room_idx: usize, depth: u32) { map[room_idx] &= !(0x7 << (depth * 3)); } fn add_amphipod_to_home_burrow(map: &mut Map, amphipod_type: u32) { map[home_burrow_idx(amphipod_type)] -= 0x1_0000; } fn step_cost(amphipod_type: u32) -> u32 { const ENERGY_PER_STEP: [u32; 4] = [1, 10, 100, 1000]; ENERGY_PER_STEP[(amphipod_type - 1) as usize] } fn outermost_amphipod_in_room(loc: u32) -> (u32, u32) { let mut depth = 1; loop { let amphipod_type = (loc >> (3 * depth)) & 0x7; if amphipod_type != 0 { break (amphipod_type, depth); } depth += 1; } }

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
Medlem ★
●
Skrivet av GLaDER:

Damn you GLaDER,
tvÄ dagar i rad dÀr du visar lösningen men inte nÄgon hint om hur man programmerar en generell algoritm för att lösa det hela.
Nu funkade inte min manuella lösning av 23 del 2 (fattar inte vad jag missat) men jag kanske ÀndÄ lÀrt mig tillrÀckligt för att göra en bruteforce.

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

Damn you GLaDER,
tvÄ dagar i rad dÀr du visar lösningen men inte nÄgon hint om hur man programmerar en generell algoritm för att lösa det hela.
Nu funkade inte min manuella lösning av 23 del 2 (fattar inte vad jag missat) men jag kanske ÀndÄ lÀrt mig tillrÀckligt för att göra en bruteforce.

Vad snackar du om? Klicka pÄ Github-lÀnken ovan för ett Python-program som borde fungera för alla giltiga inputs.

Visa signatur

:(){ :|:& };:

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