day 12
This commit is contained in:
parent
796a7a1b4f
commit
0d12942dfe
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,8 +2,3 @@
|
||||
tmp
|
||||
|
||||
# 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
149
year25/day12/day.go
Normal 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
18
year25/day12/day_test.go
Normal 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)
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user