This commit is contained in:
Gabriel Bizdoc 2025-12-14 01:52:15 +02:00
parent 796a7a1b4f
commit 0d12942dfe
No known key found for this signature in database
GPG Key ID: 3F0EDAECA5BE9ED9
4 changed files with 167 additions and 88 deletions

5
.gitignore vendored
View File

@ -2,8 +2,3 @@
tmp tmp
# Ignore these to avoid leaking local replacements to the devkit # Ignore these to avoid leaking local replacements to the devkit
./go.mod
./go.sum
go.mod
go.sum

149
year25/day12/day.go Normal file
View File

@ -0,0 +1,149 @@
package day12
import (
"fmt"
"io"
"strconv"
"strings"
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aoc"
"git.bizdoc.ro/private/devkit.git/gg"
)
func Part1(ctx aoc.Context) (int, error) {
input, err := parseInput(ctx)
if err != nil {
return 0, fmt.Errorf("day12: failed to parse input: %w", err)
}
ares := make([]int, len(input.Shapes))
for i, shape := range input.Shapes {
ares[i] = shape.Area()
}
solution := 0
for _, region := range input.Regions {
regionSize := region.Rows * region.Cols
sum := 0
for i, count := range region.ShapeCounts {
sum += count * ares[i]
}
canFit := sum < regionSize
if canFit {
solution += 1
}
}
ctx.Println(input.String())
return solution, nil
}
type Shape [3][3]byte
func (s *Shape) Area() int {
sum := 0
for i := range s {
for j := range s[i] {
if s[i][j] == '#' {
sum += 1
}
}
}
return sum
}
type Region struct {
Rows, Cols int
ShapeCounts []int
}
func (r *Region) WriteTo(w io.Writer) (n int64, err error) {
out := gg.NewSafeWriter(w)
fmt.Fprintf(out, "%dx%d ", r.Rows, r.Cols)
for _, count := range r.ShapeCounts {
out.WriteString(strconv.Itoa(count) + " ")
}
out.WriteString("\n")
return out.BytesWritten(), out.Err()
}
type Input struct {
Shapes []Shape
Regions []Region
}
func parseInput(ctx aoc.Context) (input Input, err error) {
text, err := ctx.BodyString()
if err != nil {
return input, fmt.Errorf("day12: failed to read input file: %w", err)
}
parts := strings.Split(text, "\n\n")
regionPart := parts[len(parts)-1]
parts = parts[0 : len(parts)-1]
input.Shapes = make([]Shape, len(parts))
for _, part := range parts {
// 0:
// ###
// ##.
// ##.
lines := strings.Split(part, "\n")
id, err := strconv.Atoi(lines[0][:len(lines[0])-1])
if err != nil {
return input, fmt.Errorf("day12: failed to parse shape id: %w", err)
}
shape := input.Shapes[id]
for i := range shape {
for j := range shape[i] {
shape[i][j] = lines[1+i][j]
}
}
input.Shapes[id] = shape // shape is value object
}
for _, regionStr := range strings.Split(regionPart, "\n") {
// 12x5: 1 0 1 0 3 2
var region Region
parts := strings.Split(regionStr, ": ")
for _, idstr := range strings.Split(parts[1], " ") {
id, err := strconv.Atoi(idstr)
if err != nil {
return input, fmt.Errorf("day12: failed to parse region id: %w", err)
}
region.ShapeCounts = append(region.ShapeCounts, id)
}
parts2 := strings.Split(parts[0], "x")
region.Rows, err = strconv.Atoi(parts2[0])
if err != nil {
return input, fmt.Errorf("day12: failed to parse region rows: %w", err)
}
region.Cols, err = strconv.Atoi(parts2[1])
if err != nil {
return input, fmt.Errorf("day12: failed to parse region cols: %w", err)
}
input.Regions = append(input.Regions, region)
}
return input, nil
}
func (i Input) String() string {
buf := strings.Builder{}
for shapeid, shape := range i.Shapes {
fmt.Fprintln(&buf, shapeid)
for i := range shape {
for j := range shape[i] {
buf.WriteString(string(shape[i][j]) + " ")
}
buf.WriteString("\n")
}
buf.WriteString("\n")
}
for _, region := range i.Regions {
region.WriteTo(&buf)
}
return buf.String()
}

18
year25/day12/day_test.go Normal file
View File

@ -0,0 +1,18 @@
package day12
import (
"testing"
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/tests"
)
func TestPart1Example(t *testing.T) {
const want = 2
tests.Test(t, "example.txt", tests.Handler(Part1), want)
}
func TestPart1(t *testing.T) {
t.Helper()
const want = 510
tests.Test(t, "input.txt", tests.Handler(Part1), want)
}

View File

@ -1,83 +0,0 @@
package main_test
import (
"bytes"
"fmt"
"io"
"log"
"os"
"testing"
"time"
"git.bizdoc.ro/gabi-public/Advent-of-Code.git/aoc"
)
func getInput(name string) io.Reader {
name = fmt.Sprintf("./data/%s.txt", name)
input, err := os.ReadFile(name)
if err != nil {
panic(err)
}
return bytes.NewReader(input)
}
func assert(t *testing.T, err error, a, b any) {
t.Helper()
if err != nil {
t.Error(err)
}
want := fmt.Sprintf("%v", a)
got := fmt.Sprintf("%v", b)
if got != want {
err = fmt.Errorf("want %s, got %s", want, got)
t.Error(err)
}
}
type TestCase struct {
Name string
File string
Want any
Handler func(ctx aoc.Context) (any, error)
}
var StderrLogger = log.New(os.Stderr, "", 0)
var DiscardLogger = log.New(io.Discard, "", 0)
func handler[T any](f func(ctx aoc.Context) (T, error)) func(ctx aoc.Context) (any, error) {
return func(c aoc.Context) (any, error) {
return f(c)
}
}
func handler2[T any, K any](param T, f func(ctx aoc.Context, param T) (K, error)) func(ctx aoc.Context) (any, error) {
return func(c aoc.Context) (any, error) {
return f(c, param)
}
}
func runTestCase(t *testing.T, cases []TestCase, l *log.Logger) {
t.Helper()
var totalTime time.Duration
defer func() {
t.Log("Total time:", totalTime.String())
}()
for _, test := range cases {
t.Run(test.Name, func(t *testing.T) {
start := time.Now()
defer func() {
end := time.Since(start)
t.Log("elapsed:", end)
totalTime += end
}()
input := getInput(test.File)
if test.Handler == nil {
t.Fatalf("%s handler is nil", test.Name)
}
got, err := test.Handler(aoc.Context{Body: input, Logger: l})
assert(t, err, test.Want, got)
})
}
}