From f99af72e2e733d4611ccb72c2cf9c64a2f6d0283 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:18:01 -0600 Subject: [PATCH] feat: wip on day 12 part 1 --- cmd/12/main.go | 59 ++++++++++ cmd/12/main_test.go | 17 +++ cmd/12/shape/shape.go | 217 +++++++++++++++++++++++++++++++++++++ cmd/12/shape/shape_test.go | 9 ++ 4 files changed, 302 insertions(+) create mode 100644 cmd/12/main.go create mode 100644 cmd/12/main_test.go create mode 100644 cmd/12/shape/shape.go create mode 100644 cmd/12/shape/shape_test.go diff --git a/cmd/12/main.go b/cmd/12/main.go new file mode 100644 index 0000000..53ce3ff --- /dev/null +++ b/cmd/12/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/StevanFreeborn/advent-of-code-2025/cmd/12/shape" + "github.com/StevanFreeborn/advent-of-code-2025/internal/file" +) + +// TODO: Make the splitting +// safe for LF and CRLF +const NEWLINE = "\r\n" +const COLON = ":" +const X = "x" + +func SolvePartOne(filePath string) int { + input := file.ReadAllText(filePath) + sections := strings.Split(strings.TrimSpace(input), NEWLINE+NEWLINE) + + shapes := []shape.Shape{} + regions := []string{} + + for _, section := range sections { + lines := strings.Split(strings.TrimSpace(section), NEWLINE) + header := lines[0] + + if strings.Contains(header, COLON) && strings.Contains(header, X) == false { + shapes = append(shapes, shape.From(lines)) + continue + } + + for _, line := range lines { + if strings.Contains(line, COLON) { + regions = append(regions, line) + } + } + } + + // TODO: We are currently not generating + // all the expected variants. Need to debug this + // WRITE AUTOMATED UNIT TESTS DUMMY + // WHEN YOU READ THIS IN THE FUTURE YOU ARE GOING + // TO WANT TO IGNORE IT...DON'T! + // - Past Stevan + for _, s := range shapes { + if s.Id() != 1 && s.Id() != 2 && s.Id() != 0 { + continue + } + + fmt.Println("SHAPE ID", s.Id()) + + for _, v := range s.GenerateVariants() { + fmt.Println(v) + } + } + + return 0 +} diff --git a/cmd/12/main_test.go b/cmd/12/main_test.go new file mode 100644 index 0000000..ffe9186 --- /dev/null +++ b/cmd/12/main_test.go @@ -0,0 +1,17 @@ +package main_test + +import ( + "testing" + + solution "github.com/StevanFreeborn/advent-of-code-2025/cmd/12" +) + +func TestSolvePartOne(t *testing.T) { + expected := -1 + + result := solution.SolvePartOne("EXAMPLE.txt") + + if result != expected { + t.Errorf("got %d but wanted %d", result, expected) + } +} diff --git a/cmd/12/shape/shape.go b/cmd/12/shape/shape.go new file mode 100644 index 0000000..d4173e4 --- /dev/null +++ b/cmd/12/shape/shape.go @@ -0,0 +1,217 @@ +// Package shape provides a model and methods for representing and manipulating shapes +package shape + +import ( + "fmt" + "maps" + "slices" + "strconv" + "strings" + + "github.com/StevanFreeborn/advent-of-code-2025/internal/position" +) + +// shape.GenerateAllVariants() []Shape + +type Shape interface { + Id() int + GetKey() string + GenerateVariants() []Shape + RotateClockwise() Shape + FlipVertically() Shape +} + +type shape struct { + id int + positions []position.Position +} + +func From(lines []string) Shape { + idStr := strings.TrimSuffix(lines[0], ":") + id, _ := strconv.Atoi(idStr) + + positions := []position.Position{} + + for r, line := range lines[1:] { + for c, char := range line { + if char != '#' { + continue + } + + position := position.From(r, c) + positions = append(positions, position) + } + } + + return shape{id: id, positions: positions} +} + +func (s shape) GenerateVariants() []Shape { + shapes := []Shape{} + var current Shape + current = s + numberOfTransformations := 4 + + // Rotate 90 clockwise + for range numberOfTransformations { + current = current.RotateClockwise() + shapes = append(shapes, current) + } + + current = s + + // Flip then rotate 90 clockwise + for range numberOfTransformations { + shapes = append(shapes, current) + flipped := current.FlipVertically() + shapes = append(shapes, flipped) + current = flipped.RotateClockwise() + } + + current = s + + // Rotate 90 clockwise then flip + for range numberOfTransformations { + shapes = append(shapes, current) + rotated := current.RotateClockwise() + shapes = append(shapes, rotated) + current = rotated.FlipVertically() + } + + seenShapes := map[string]Shape{} + + for _, s := range shapes { + seenShapes[s.GetKey()] = s + } + + return slices.Collect(maps.Values(seenShapes)) +} + +func (s shape) RotateClockwise() Shape { + rotatedPositions := []position.Position{} + + for _, p := range s.positions { + rotatedPosition := position.From(p.Column(), -p.Row()) + rotatedPositions = append(rotatedPositions, rotatedPosition) + } + + rotatedShape := shape{ + id: s.id, + positions: rotatedPositions, + } + + return normalize(rotatedShape) +} + +func (s shape) FlipVertically() Shape { + flippedPositions := []position.Position{} + + for _, p := range s.positions { + flippedPosition := position.From(p.Row(), -p.Column()) + flippedPositions = append(flippedPositions, flippedPosition) + } + + flippedShape := shape{ + id: s.id, + positions: flippedPositions, + } + + return normalize(flippedShape) +} + +func (s shape) GetKey() string { + var sb strings.Builder + + for _, p := range s.positions { + fmt.Fprintf(&sb, "%d,%d|", p.Row(), p.Column()) + } + + return sb.String() +} + +func (s shape) Id() int { + return s.id +} + +func normalize(s shape) Shape { + if len(s.positions) == 0 { + return shape{ + id: s.id, + positions: s.positions, + } + } + + minRow := s.positions[0].Row() + minColumn := s.positions[0].Column() + + for _, p := range s.positions { + if p.Row() < minRow { + minRow = p.Row() + } + + if p.Column() < minColumn { + minColumn = p.Column() + } + } + + nps := []position.Position{} + + for _, p := range s.positions { + nr := p.Row() - minRow + nc := p.Column() - minColumn + np := position.From(nr, nc) + nps = append(nps, np) + } + + return shape{ + id: s.id, + positions: nps, + } +} + +func (s shape) String() string { + if len(s.positions) == 0 { + return "(EMPTY SHAPE)" + } + + maxRow := 0 + maxColumn := 0 + + for _, p := range s.positions { + if p.Row() > maxRow { + maxRow = p.Row() + } + + if p.Column() > maxColumn { + maxColumn = p.Column() + } + } + + tempGrid := make([][]string, maxRow+1) + + for r := range tempGrid { + tempGrid[r] = make([]string, maxColumn+1) + + for c := range tempGrid[r] { + tempGrid[r][c] = "." + } + } + + for _, p := range s.positions { + tempGrid[p.Row()][p.Column()] = "#" + } + + var shape strings.Builder + + for _, row := range tempGrid { + var rowString strings.Builder + + for _, str := range row { + rowString.WriteString(str) + } + + shape.WriteString(rowString.String() + "\n") + } + + return shape.String() +} diff --git a/cmd/12/shape/shape_test.go b/cmd/12/shape/shape_test.go new file mode 100644 index 0000000..8ebf5d0 --- /dev/null +++ b/cmd/12/shape/shape_test.go @@ -0,0 +1,9 @@ +package shape_test + +import ( + "testing" +) + +func TestFrom(t *testing.T) { + +}