Advent of Code 2025 in Charly
Day 12
This article is part of my series on implementing each Advent of Code 2025 challenge in my own programming language Charly.
Task
Today’s puzzle dropped us into a large cavern full of christmas trees and a bunch of differently shaped presents. The elves hand me a description of how each present is shaped, how much space is available below each christmas tree, and how many of each type of present must be placed below that tree.
For example:
0:
###
##.
##.
1:
###
##.
.##
2:
.##
###
##.
3:
##.
###
##.
4:
###
#..
###
5:
###
.#.
###
4x4: 0 0 0 0 2 0
12x5: 1 0 1 0 2 2
12x5: 1 0 1 0 3 2
The input starts with a list of standard present shapes. Each shape
is given an index and shown as a small 2D grid of # and
. characters. A # marks a unit cell that the
present occupies, while . marks empty space inside the
shape’s bounding box.
After the shape list comes a list of rectangular regions under the
christmas trees. Each region is described by its width and height (for
example 12x5), followed by a list of counts that specifies
how many presents of each shape index must be placed into that
region.
Presents must be placed on the region’s unit grid and cannot be
stacked. Shapes may be rotated and flipped. Occupied cells
(#) from different presents may not overlap, but empty
cells (.) do not block anything and can be shared with
other shapes.
The task is to check each region and determine whether all required presents can fit. The final answer is how many of the listed regions are solvable.
Implementation
I began my attempt by determining the minimum total number of cells
that each region must contain to be able to accommodate all occupied
(#) cells from all required presents. If the region doesn’t
have enough space to accommodate the occupied cells, then that means
there is no way we could possibly arrange all the presents to make them
fit.
const input_path = ARGV[1]
const lines = readfile(input_path).lines()
const (presents, regions) = parse_input(lines)
const valid_regions = regions.filter(->(region, region_id) {
const (size, present_ids) = region
const (x, y) = size
const gridArea = x * y
const minimumRequiredArea = present_ids.map(->(count, i) {
count * presents[i].map(->(r) [...r].sum()).sum()
}).sum()
gridArea >= minimumRequiredArea
})
print("there are {valid_regions.length} regions that might fit the presents")The next step would have been to attempt to place each present, in any possible orientation, flipped horizontally and vertically, in any possible position on the grid. You then would have to iterate over all possible combinations of those factors to figure out if the region is solvable or not. This approach could’ve been implemented as a DFS search over all possible present placements.
The validation input had regions with areas up to 50x50
with hundreds of presents that had to be placed. I did some napkin
calculations and figured out that this would take an astronomical amount
of time.
At this point it dawned on me that I must be missing some key part of how to solve this problem. There had to be an easier way of solving this than to just search the entire space of possible present placements.
I figured it couldn’t hurt to check whether all the regions that
might be solvable are indeed solvable. I copied the count of
regions that might be solvable, pasted it into the solution
submission field and clicked Submit.
My guess turned out to be correct. The site accepted my answer and I
got the star :P. I do realize that I’ve kind of cheesed
today’s puzzle, but I’m satisfied with it.
There was no second part of the puzzle, just a short message congratulating you on your success.
Changes to the stdlib / VM
Modifications:
hashmapNow allows integers to be used as keys
Methods added to the standard library:
List::to_tupleConvenience method for(...list)List::distinctReturns a list with duplicate elements removed
You can find the individual commits below:
Links
Copyright © 2024 - 2025 Leonard Schütz | Attributions This post was written by a human and reviewed and proof-read by LLMs