I build a program that runs various species of automata in raylib and c++. I can send you a link if you like. While running, the species can be interchanged (meaning the rule set). I also added different random densities and a gradient option. Plus I colour coded each cell based on the age (dead alive transition) which is very interesting to see.

  • source
  • [–] 2 points 11 months ago

    Go

    Well... I was about to dive into bin packing and stuff. I started by pruning the obvious candidates (those which can fit all the shapes one next to the other, without more computation) so save CPU time for the real stuff. I ran my code on the real input just to see and... what to do mean there are no candidate left? I write the number of obvious boys into the website's input box, just to check and... Ah. I understand why people on reddit said they felt dirty x)

    Anyway the code:

    day12.go

    package main
    
    import (
    	"aoc/utils"
    	"fmt"
    	"slices"
    	"strconv"
    	"strings"
    )
    
    type shape [3][3]bool
    
    func parseShape(input chan string) shape {
    	// remove the header
    	_ = <-input
    
    	sh := shape{}
    	idx := 0
    	for line := range input {
    		if line == "" {
    			break
    		}
    
    		row := [3]bool{}
    		for idx, c := range []rune(line) {
    			if c == '#' {
    				row[idx] = true
    			}
    		}
    
    		sh[idx] = row
    		idx++
    	}
    
    	return sh
    }
    
    func (sh shape) usedArea() int {
    	sum := 0
    	for _, row := range sh {
    		for _, cell := range row {
    			if cell {
    				sum++
    			}
    		}
    	}
    	return sum
    }
    
    type regionConstraints struct {
    	width, height int
    	shapes        []int
    }
    
    func parseRegionConstraint(line string) (rc regionConstraints) {
    	parts := strings.Split(line, ":")
    	dims := strings.Split(parts[0], "x")
    	rc.width, _ = strconv.Atoi(dims[0])
    	rc.height, _ = strconv.Atoi(dims[1])
    
    	shapes := strings.Fields(parts[1])
    	rc.shapes = make([]int, len(shapes))
    	for idx, shape := range shapes {
    		rc.shapes[idx], _ = strconv.Atoi(shape)
    	}
    	return rc
    }
    
    type problem struct {
    	shapes      []shape
    	constraints []regionConstraints
    }
    
    func newProblem(input chan string) problem {
    	shapes := make([]shape, 6)
    	for idx := range 6 {
    		shapes[idx] = parseShape(input)
    	}
    
    	regionConstraints := []regionConstraints{}
    	for line := range input {
    		rc := parseRegionConstraint(line)
    		regionConstraints = append(regionConstraints, rc)
    	}
    
    	return problem{shapes, regionConstraints}
    }
    
    func (pb *problem) pruneRegionsTooSmall() {
    	toPrune := []int{}
    	for idx, rc := range pb.constraints {
    		availableArea := rc.height * rc.width
    		neededArea := 0
    		for shapeId, count := range rc.shapes {
    			neededArea += pb.shapes[shapeId].usedArea() * count
    			if neededArea > availableArea {
    				toPrune = append(toPrune, idx)
    				break
    			}
    		}
    	}
    
    	slices.Reverse(toPrune)
    	for _, idx := range toPrune {
    		pb.constraints = slices.Delete(pb.constraints, idx, idx+1)
    	}
    }
    
    func (pb *problem) pruneObviousCandidates() int {
    	toPrune := []int{}
    
    	for idx, rc := range pb.constraints {
    		maxShapePlacements := (rc.width / 3) * (rc.height / 3)
    		shapeCount := 0
    		for _, count := range rc.shapes {
    			shapeCount += count
    		}
    		if maxShapePlacements >= shapeCount {
    			toPrune = append(toPrune, idx)
    		}
    	}
    
    	slices.Reverse(toPrune)
    	for _, idx := range toPrune {
    		pb.constraints = slices.Delete(pb.constraints, idx, idx+1)
    	}
    
    	return len(toPrune)
    }
    
    func stepOne(input chan string) (int, error) {
    	pb := newProblem(input)
    	pb.pruneRegionsTooSmall()
    	obviousCandidates := pb.pruneObviousCandidates()
    
    	fmt.Println(pb)
    	fmt.Println(obviousCandidates)
    	return 0, nil
    }
    
    func stepTwo(input chan string) (int, error) {
    	return 0, nil
    }
    
    func main() {
    	input, err := utils.DownloadTodaysInputFile()
    	if err != nil {
    		_ = fmt.Errorf("error fetching the input: %v", err)
    	}
    
    	utils.RunStep(utils.ONE, input, stepOne)
    	utils.RunStep(utils.TWO, input, stepTwo)
    }
    

  • source
  • [–] 2 points 11 months ago (2 children)

    My initial attempt on even-odd got me in trouble with the outline, as given, being 1 unit wide, not zero-width. I had to compute normals to find out outline around the tiles. You write "on the line -> outside" but don't many legal rectangle candidates sit on the edge?

  • source
  • parent
  • context
  • [–] 2 points 11 months ago* (last edited 10 months ago)

    C

    Well this one got me stumped for a bit but after a few false starts, lots of debugging and some cleanup I'd say I have a pretty good solution now! The points being tiles rather than zero-width corners was the tricky bit here.

    Basic working: it walks through the points, keeping track of normal vectors (the "outside" direction). With those, it generates the zero-width outline around the points, and from there on it's pretty much old fashioned line intersection checks.

    The initial nasty solve took 1.2 seconds on my Raspberry Pi 400, but some optimization (like sorting the edges list) brought that down to 0.08s on the Pi, and 0.02s on my 10-year old PC, and I'm quite happy with the code now.

    Code

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <inttypes.h>
    #include <assert.h>
    
    #define MIN(a,b)	((a)<(b)?(a):(b))
    #define MAX(a,b)	((a)>(b)?(a):(b))
    #define LEN(a)		(sizeof(a)/sizeof(*(a)))
    
    #define MAXP	512
    
    /*
     * There are two main problems with part 2:
     *  1. Deciding what is inside and what is outside of the polyline
     *  2. The points being _tiles_, not zero-width coordinates
     *
     * This solution walks the outline and calculates "normals" for each
     * point, which are angles pointing _out_ of the shape. Those are used
     * to derrive the outside boundary of the shape, the lines that go
     * around, not through, the corner tiles. Witth that we can do
     * more-or-less normal intersection checks.
     *
     * Angles are stored as 1/8ths of a circle.
     */
    
    struct point { int x,y; };
    struct edge { int off, lo,hi; };
    
    static struct point pts[MAXP];
    static struct edge hedges[MAXP/2];
    static struct edge vedges[MAXP/2];
    static int norms[MAXP];
    static int np, nhe, nve;
    
    static int wrapa(int a) { return (a+8)%8; }
    
    static int
    cmp_edges(const void *a, const void *b)
    {
    	return ((const struct edge *)a)->off -
    	       ((const struct edge *)b)->off;
    }
    
    static int
    line_dir(struct point a, struct point b)
    {
    	return 4*(a.x>b.x || a.y<b.y) + 2*(a.x!=b.x);
    }
    
    static void
    calculate_normals(void)
    {
    	int i, dir1,dir2, angle;
    	int normal; /* relative to the current line  */
    
    	dir1 = line_dir(pts[np-1], pts[0]);
    	normal = wrapa(dir1 - 2); /* start left, assuming CCW order */
    
    	for (i=0; i<np; i++) {
    		dir2 = line_dir(pts[i], pts[i==np-1 ? 0 : i+1]);
    		angle = dir2 == dir1-2 || dir2 == dir1+6 ? -2 : 2;
    
    		/* the point's normal is diagonal, halfway turned */
    		norms[i] = wrapa(normal + angle/2);
    
    		dir1 = dir2;
    		normal = wrapa(normal + angle);
    	}
    }
    
    static void
    generate_edges(void)
    {
    	struct point a,b;
    	int i,j;
    
    	for (i=0; i<np; i++) {
    		a = pts[i];
    		b = pts[(j = i==np-1 ? 0 : i+1)];
    
    		/* shift the line to be _around_ point */
    		a.x += norms[i]<4; a.y += norms[i]>2 && norms[i]<6;
    		b.x += norms[j]<4; b.y += norms[j]>2 && norms[j]<6;
    
    		if (a.x == b.x) {
    			assert(nve < (int)LEN(vedges));
    			vedges[nve].off = a.x;
    			vedges[nve].lo = MIN(a.y, b.y);
    			vedges[nve].hi = MAX(a.y, b.y);
    			nve++;
    		} else if (a.y == b.y) {
    			assert(nhe < (int)LEN(hedges));
    			hedges[nhe].off = a.y;
    			hedges[nhe].lo = MIN(a.x, b.x);
    			hedges[nhe].hi = MAX(a.x, b.x);
    			nhe++;
    		} else
    			assert(!"diagonal line");
    	}
    
    	qsort(hedges, nhe, sizeof(*hedges), cmp_edges);
    	qsort(vedges, nve, sizeof(*vedges), cmp_edges);
    }
    
    static bool
    rect_inside(struct point a, struct point b)
    {
    	struct edge *e;
    	int x0,y0,x1,y1;
    
    	/*
    	 * Line intersection of the four rectangle edges against all
    	 * the polyline edges, with a caveat: the outline is 1 unit
    	 * thick, not zero-width! This is why some of the comparisons
    	 * are or-equals.
    	 */
    
    	x0 = MIN(a.x, b.x); y0 = MIN(a.y, b.y);
    	x1 = MAX(a.x, b.x); y1 = MAX(a.y, b.y);
    
    	for (e = vedges; e < vedges+nve; e++) {
    		if (e->off <= x0) continue;
    		if (e->off > x1) break;
    		if (y0 >= e->lo && y0 < e->hi) return false;
    		if (y1 >= e->lo && y1 < e->hi) return false;
    	}
    
    	for (e = hedges; e < hedges+nhe; e++) {
    		if (e->off <= y0) continue;
    		if (e->off > y1) break;
    		if (x0 >= e->lo && x0 < e->hi) return false;
    		if (x1 >= e->lo && x1 < e->hi) return false;
    	}
    
    	return true;
    }
    
    int
    main()
    {
    	int64_t p1=0,p2=0, area, i,j;
    
    	for (; scanf(" %d,%d", &pts[np].x, &pts[np].y)==2; np++)
    		assert(np+1 < MAXP);
    	
    	assert(np >= 4);
    
    	calculate_normals();
    	generate_edges();
    
    	for (i=0; i<np-1; i++)
    	for (j=i+1; j<np; j++) {
    		area = (int64_t)(abs(pts[i].x - pts[j].x) + 1) *
    		       (int64_t)(abs(pts[i].y - pts[j].y) + 1);
    		if (area > p1)
    			p1 = area;
    		if (area > p2 && rect_inside(pts[i], pts[j]))
    			p2 = area;
    	}
    
    	printf("09: %"PRIi64" %"PRIi64"\n", p1, p2);
    }
    

  • source
  • [–] 2 points 11 months ago* (1 child)

    I actually did a lot of optimizations before I had to give up and search what I was missing. I did have a look at my input data, but still wouldn't make the connection because in my mind, any solution had to work for the sample too.

  • source
  • parent
  • context
  • [–] [S] 2 points 11 months ago

    Another approach could be to run an image convolution kernel in GIMP (or some other image manipulation program). Something like

    [
     1, 1, 1
     1, 0, 1
     1, 1, 1
    ]
    

    and then filter pixels based on ≥4 or <4

  • source
  • [–] 1 point 11 months ago

    (Browser-based) Javascript

    This was a good opportunity to refresh my grasp on the math involved in losslessly stuffing a tuple into a single number. JS-in-the-browser has Sets and Maps but no Tuples, and Arrays are indexed on their id / memory handle instead of their value contents, so if you want to put coordinates into a set or map and have the collection behave as expected you need to serialize the coordinates into a primitive type. Stuff it into a string if you don't want to think too hard. For this specific problem we don't even need to be able to compute the original coordinates (just count the unique removed points) but implementing that computation was a handy way to verify the "serializer" was working correctly.

    Seeing as the record tuple proposal was withdrawn in February of this year this is still a technique worth knowing when working with coords in JS.

    Code

    function part1(inputText) {
      const gridWidth = inputText.indexOf('\n');
      const lines = inputText.trim().split('\n');
      const gridHeight = lines.length;
      let accessibleRolls = 0;
      for (let y = 0; y < gridHeight; y++) {
        for (let x = 0; x < gridWidth; x++) {
          if (lines[y][x] === '@') {
            let occupiedNeighbors = 0;
            for (const [neighborX, neighborY] of [
                [x - 1, y],
                [x + 1, y],
                [x, y - 1],
                [x, y + 1],
                [x - 1, y - 1],
                [x - 1, y + 1],
                [x + 1, y - 1],
                [x + 1, y + 1],
              ]) {
              if (neighborX < 0 || neighborX >= gridWidth || neighborY < 0 || neighborY >= gridHeight) {
                continue;
              }
              if (lines[neighborY][neighborX] === '@') {
                occupiedNeighbors++;
              }
            }
            if (occupiedNeighbors < 4) {
              accessibleRolls++;
            }
          }
        }
      }
      return accessibleRolls;
    }
    {
      const start = performance.now()
      const result = part1(document.body.textContent)
      const end = performance.now()
      console.info({day: 4, part: 1, time: end - start, result})
    }
    
    function serializeCoords(x, y, gridWidth) {
      const leftShiftAmount = Math.ceil(Math.log10(gridWidth));
      return x * (10 ** leftShiftAmount) + y;
    }
    /*
    {
      const x = 3;
      const y = 4;
      const gridWidth = 13;
      const serialized = serializeCoords(x, y, gridWidth);
      console.debug({ x, y, gridWidth, serialized });
    }
    */
    
    function deserializeCoords(serialized, gridWidth) {
      const leftShiftAmount = Math.ceil(Math.log10(gridWidth));
      const x = Math.floor(serialized / (10 ** leftShiftAmount));
      const y = serialized - x * 10 ** leftShiftAmount;
      return [x, y];
    }
    /*
    {
      const serialized = 304;
      const gridWidth = 13;
      const [x, y] = deserializeCoords(serialized, gridWidth);
      console.debug({ serialized, gridWidth, x, y });
    }
    */
    function part2(inputText) {
      const gridWidth = inputText.indexOf('\n');
      const lines = inputText.trim().split('\n');
      const gridHeight = lines.length;
      let removed = new Set();
      while (true) {
        const toRemove = new Set();
        for (let y = 0; y < gridHeight; y++) {
          for (let x = 0; x < gridWidth; x++) {
            const serialized = serializeCoords(x, y, gridWidth);
            if (lines[y][x] === '@' && !removed.has(serialized)) {
              let occupiedNeighbors = 0;
              for (const [neighborX, neighborY] of [
                  [x - 1, y],
                  [x + 1, y],
                  [x, y - 1],
                  [x, y + 1],
                  [x - 1, y - 1],
                  [x - 1, y + 1],
                  [x + 1, y - 1],
                  [x + 1, y + 1],
                ]) {
                if (neighborX < 0 || neighborX >= gridWidth || neighborY < 0 || neighborY >= gridHeight) {
                  continue;
                }
                const serializedNeighbor = serializeCoords(neighborX, neighborY, gridWidth);
                if (lines[neighborY][neighborX] === '@' && !removed.has(serializedNeighbor)) {
                  occupiedNeighbors++;
                }
              }
              if (occupiedNeighbors < 4) {
                toRemove.add(serialized);
              }
            }
          }
        }
        if (toRemove.size === 0) {
          break;
        }
        removed = removed.union(toRemove);
      }
      return removed.size;
    }
    /*
    {
      const exampleText = `..@@.@@@@.
    @@@.@.@.@@
    @@@@@.@.@@
    @.@@@@..@.
    @@.@@@@.@@
    .@@@@@@@.@
    .@.@.@.@@@
    @.@@@.@@@@
    .@@@@@@@@.
    @.@.@@@.@.
    `;
      const start = performance.now();
      const result = part2(exampleText);
      const end = performance.now();
      console.info({ day: 4, part: 2, time: end - start, result });
    }
    */
    
    {
      const start = performance.now()
      const result = part2(document.body.textContent)
      const end = performance.now()
      console.info({ day: 4, part: 2, time: end - start, result });
    }
    

  • source
  • [–] 3 points 11 months ago* (1 child)

    Kotlin

    Looking at the puzzle, I knew that I had no clue how to solve it. So I came here to see if I was missing something or if there were any hints.

    And the hint I saw was to do the simplest check possible, so I gave it a shot.

    And that got the test input wrong, but I ran it against the real input anyway just to see if it was right. And it was.

    I think if I had gone on my instincts and just tried to solve this, I could have gone around in circles for hours or days trying to get it right.

    fun main() {
        val input = getInput(12)
        val (gifts, regions) = parseInput1(input)
        var total = 0
        for (i in regions.indices) {
            val totalAreaOfGifts = regions[i].gifts.mapIndexed { index, count -> count * gifts[index].area }.sum()
            if (totalAreaOfGifts <= regions[i].area) {
                total++
            }
        }
        println(total)
    }
    
    data class Gift(val shape: List<List<Char>>, val area: Int)
    
    data class Region(val width: Int, val height: Int, val area: Int, val gifts: List<Int>)
    
    fun parseInput1(input: String): Pair<List<Gift>, List<Region>> {
        val gifts: MutableList<Gift> = mutableListOf()
        val regions: MutableList<Region> = mutableListOf()
        val lines = input.lines()
        lines.forEachIndexed { index, line ->
            if (line.contains(":")) {
                if (line.contains("x")) {
                    val split = line.split(" ")
                    val shape = split.first().replace(":", "").split("x")
                    val width = shape.first().toInt()
                    val height = shape.last().toInt()
                    regions.add(
                        Region(
                            width,
                            height,
                            width * height,
                            split.slice(1..<split.size).map { str -> str.toInt() })
                    )
                } else {
                    var nextBlankLineIndex = 0
                    for (i in index + 1..<lines.size) {
                        if (lines[i].isBlank()) {
                            nextBlankLineIndex = i
                            break
                        }
                    }
                    val shape = lines.slice(index + 1..<nextBlankLineIndex).map { it.toCharArray().toList() }
                    val area = shape.flatten().filter { it == '#' }.size
                    gifts.add(Gift(shape, area))
                }
            }
        }
        return gifts to regions
    }
    
  • source
  • [–] 2 points 11 months ago

    (Browser-based) Javascript

    For part 2, I eagerly wrote a nice, clean, generic, functional depth-first search, only to get an out of memory error 😭. Note the top-level code blocks: they scope the variables declared inside them, allowing me to run the whole script repeatedly in the console without getting "redeclared variable name" errors.

    function part1(inputText) {
      let totalOutputJoltage = 0;
      for (const batteryBankDef of inputText.split('\n')) {
        let bestBankJoltage = 0;
        const previousDigits = [];
        for (const character of batteryBankDef) {
          const currentDigit = Number.parseInt(character, 10);
          for (const previousDigit of previousDigits) {
            const possibleVoltage = 10 * previousDigit + currentDigit;
            if (possibleVoltage > bestBankJoltage) {
              bestBankJoltage = possibleVoltage;
            }
          }
          previousDigits.push(currentDigit);
        }
            totalOutputJoltage += bestBankJoltage;
      }
      return totalOutputJoltage;
    }
    {
        const start = performance.now();
        const result = part1(document.body.textContent)
        const end = performance.now();
        console.info({day: 3, part: 1, result, time: end - start})
    }
    
    function findNthDigitForSequence(bankDef, n, startIndex) {
      let digit = 9;
      while (digit > 0) {
        for (let i = startIndex; i < bankDef.length - 11 + n; i++) {
          if (bankDef[i] === digit.toString()) {
            return [digit, i]
          }
        }
        digit--;
      }
      return undefined;
    }
    function findBestJoltageForBank(bankDef) {
      const digits = [];
      let previousFoundDigitIndex = -1;
      for (let i = 0; i < 12; i++) {
        const digitFound = findNthDigitForSequence(bankDef, i, previousFoundDigitIndex + 1);
        if (digitFound === undefined) {
          debugger;
          return undefined;
        }
        const [digit, index] = digitFound;
        digits.push(digit);
        previousFoundDigitIndex = index;
      }
      return Number.parseInt(digits.join(''), 10);
    }
    function part2(inputText) {
      let totalOutputJoltage = 0;
      for (const batteryBankDef of inputText.trim().split('\n')) {
        totalOutputJoltage += findBestJoltageForBank(batteryBankDef) ?? 0;
      }
      return totalOutputJoltage;
    }
    
    {
      const start = performance.now();
      const result = part2(document.body.textContent);
      const end = performance.now();
      console.info({ day: 3, part: 2, time: end - start, result });
    }
    
  • source
  • [–] 2 points 11 months ago

    (Browser-based) Javascript

    This year I'm tired of dealing with reading from files and setting up IDEs, so I'm attempting to solve each day directly in my web browser's console: after opening my problem input in a new tab I can do mySolutionFunction(document.body.textContent) in that tab's console. Thankfully the browser I use (ff) has a mode that lets me write several lines and then run them, otherwise this would not be simpler. Unfortunately, this means I lost my code for day1 when I closed the tab a bit too quickly.

    I didn't want to use regex for today; you need backreferences and those are impossible to optimize if they blow up (computationally speaking). I'm not so sure my solution for part 2 actually does run in more linear time than a regex with a single backreference does...

    function part1(input) {
      let sumOfValidIds = 0;
      for (const rangeDef of input.split(',')) {
        const [start, stop] = rangeDef.split('-').map(s => Number.parseInt(s, 10));
        const rangeLength = stop - start + 1;
        for (let id = start; id <= stop; id++) {
          const idLength = id.toString().length;
          if (idLength % 2 === 1) {
            continue
          }
          const halfLength = idLength / 2;
          const topHalf = Math.floor(id / Math.pow(10, halfLength));
          const bottomHalf = id - topHalf * Math.pow(10, halfLength);
          if (topHalf === bottomHalf) {
            sumOfValidIds += id;
          }
        }
      }
      return sumOfValidIds;
    }
    part1(document.body.textContent);
    
    function extendsPattern(containerString, pattern) {
      let container = containerString;
      while (container.length > pattern.length) {
        if (!container.startsWith(pattern)) {
          return false;
        }
        container = container.slice(pattern.length)
      }
      return pattern.startsWith(container);
    }
    function findContainedPatterns(containerString) {
      const patterns = [];
      for (let i = 0; i < containerString.length; i++) {
        const upTillNow = containerString.substring(0, i+1);
        for (let j = 0; j < patterns.length; j++) {
          if (!extendsPattern(upTillNow, patterns[j])) {
            patterns.splice(j, 1);
            j--;
          }
        }
        patterns.push(upTillNow);
      }
      return patterns;
    }
    function part2(input) {
      let sumOfValidIds = 0;
      for (const rangeDef of input.split(',')) {
        const [start, stop] = rangeDef.split('-').map(s => Number.parseInt(s, 10));
        const rangeLength = stop - start + 1;
        for (let id = start; id <
          const idString = id.toString();
          const patterns = findContainedPatterns(idString);
          if (patterns.length > 1) {
            const shortestPatternCandidate = patterns[0];
            if (idString.length % shortestPatternCandidate.length === 0) {
              sumOfValidIds += id;
            }
          }
        }
      }
      return sumOfValidIds;
    }
    //part2(`11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124`)
    part2(document.body.textContent);
    
  • source
  • [–] 5 points 11 months ago* (last edited 11 months ago)

    Obviously very spoilery code, if you dare read it closely. try it here

    It feels very anticlimactic to be ending so early in the month, but see you all next year!

    # AOC 2025 day 12 - Packing parcels
    ▽⊸≡◇˜∊@x⊜□⊸≠@\n&fras"AOC2025day12.txt"
    /+<⊙×₉≡◇(⊓/×/+⊃↙↘2⊜⋕¬⊸∊": x")
    
    
  • source
  • [–] 2 points 11 months ago (1 child)

    I always have this thought. I'm doing it in Rust, so I check if there are negative numbers: if not, use usize. But I'm always terrified there will be an overflow somewhere.

    If I were using Kotlin or Java, I might always use BigInteger just out of fear.

  • source
  • parent
  • context
  • [–] [S] 2 points 11 months ago

    Rust

    Its not Christmas, but this one was a gift :D. Merry Christmas all, thanks for everyone who has contributed solutions!

    Rest easy little advent bot, your work is done now.

    spoiler

        struct Tree {
            w: u32,
            h: u32,
            presents: Vec<u32>,
        }
    
        fn is_solveable(tree: &Tree) -> bool {
            if tree.h * tree.w < tree.presents.iter().sum::<u32>() * 9 {
                return false;
            }
            true
        }
    
        #[test]
        fn test_y2025_day12_part1() {
            let input = include_str!("../../input/2025/day_12.txt");
            let parts = input.split("\n\n").collect::<Vec<_>>();
    
            let trees: Vec<Tree> = parts
                .last()
                .unwrap()
                .lines()
                .map(|line| {
                    let parts: (&str, &str) = line.split_once(": ").unwrap();
                    let [w, h] = parts
                        .0
                        .split("x")
                        .map(u32::from_str)
                        .map(Result::unwrap)
                        .collect::<Vec<_>>()[..]
                    else {
                        unreachable!()
                    };
                    let num_presents = parts
                        .1
                        .split(" ")
                        .map(u32::from_str)
                        .map(Result::unwrap)
                        .collect::<Vec<_>>();
                    Tree {
                        w,
                        h,
                        presents: num_presents,
                    }
                })
                .collect();
    
            let mut solvable = 0;
            for tree in trees {
                if is_solveable(&tree) {
                    solvable += 1;
                }
            }
            println!("solvable: {}", solvable);
        }
    

  • source
  • [–] 4 points 11 months ago* (last edited 11 months ago) (2 children)

    Mild spoilers ahead, but you're reading the solutions thread.

    I was just doing some preliminary checking of the data on my phone with Nushell (to see how much my ageing laptop would suffer) when I discovered there weren't any non trivial cases.

    Normally I get the test data working before trying the input data, this is definitely teaching me the value of investigating the data before heading down into the code mines.

    Unfortunately I can't get the second star yet because I missed a few days.

  • source
  • parent
  • context
  • [–] 4 points 11 months ago* (last edited 11 months ago) (5 children)

    Python

    You got me good, Eric.

    hintThink of the simplest check you can make for a region.

    click to view code

    # This does not work for the sample :D
    
    def solve(data: str):
        # split input
        blocks = data.split("\n\n")
        shape_blocks = blocks[:-1]
        region_block = blocks[-1]
    
        # for every shape, get the number of occupied cells
        shape_area = []
        for shape_block in shape_blocks:
            shape_area.append(shape_block.count('#'))
    
        fit_regions = 0
    
        # for every region, check if the area is sufficient to fit all shapes
        for region_data in region_block.splitlines():
            size_data, shape_data = region_data.split(': ')
    
            # get region size
            m, n = [int(dim) for dim in size_data.split('x')]
    
            # get area needed to fit all shapes, without considering arrangement
            area_needed = 0
            for id, freq in enumerate(map(int, shape_data.split(' '))):
                area_needed += shape_area[id] * freq
            
            # if the region area is sufficient, count it as a fit (!!!)
            if m * n > area_needed:
                fit_regions += 1
        
        return fit_regions
    

  • source
  • [–] 2 points 11 months ago

    I think I got pretty lucky that the Java library I used was pretty straightforward and had good docs.

    This was definitely an unfulfilling way to solve a puzzle. I did take linear algebra in college, but I really struggled in that class and retained none of it.

  • source
  • parent
  • context
  • [–] [S] 2 points 11 months ago (1 child)

    I was leaning on chatgpt for z3 rust code, and it was terrible. It kept generating code for an old version of the library, but I had no idea which one. Tried to correct it and it starts hallucinating library "features".

    Really need better docs for z3 rust.

  • source
  • parent
  • context
  • view more: ‹ prev next ›