Merge pull request #30 from StevanFreeborn/stevanfreeborn/refactor/clean-up-day-12
refactor: extract region logic into separate package
This commit is contained in:
+4
-118
@@ -2,10 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/StevanFreeborn/advent-of-code-2025/cmd/12/region"
|
||||||
"github.com/StevanFreeborn/advent-of-code-2025/cmd/12/shape"
|
"github.com/StevanFreeborn/advent-of-code-2025/cmd/12/shape"
|
||||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/file"
|
"github.com/StevanFreeborn/advent-of-code-2025/internal/file"
|
||||||
)
|
)
|
||||||
@@ -22,7 +21,7 @@ func SolvePartOne(filePath string) int {
|
|||||||
|
|
||||||
total := 0
|
total := 0
|
||||||
shapes := map[int][]shape.Shape{}
|
shapes := map[int][]shape.Shape{}
|
||||||
regions := []string{}
|
regions := []region.Region{}
|
||||||
|
|
||||||
for _, section := range sections {
|
for _, section := range sections {
|
||||||
lines := newLineRegex.Split(strings.TrimSpace(section), -1)
|
lines := newLineRegex.Split(strings.TrimSpace(section), -1)
|
||||||
@@ -36,129 +35,16 @@ func SolvePartOne(filePath string) int {
|
|||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
if strings.Contains(line, COLON) {
|
if strings.Contains(line, COLON) {
|
||||||
regions = append(regions, line)
|
regions = append(regions, region.From(line))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, region := range regions {
|
for _, region := range regions {
|
||||||
parts := strings.Split(region, COLON)
|
if region.CanFit(shapes) {
|
||||||
dims := strings.Split(parts[0], X)
|
|
||||||
width, _ := strconv.Atoi(dims[0])
|
|
||||||
height, _ := strconv.Atoi(dims[1])
|
|
||||||
|
|
||||||
countsStr := strings.Fields(parts[1])
|
|
||||||
requiredShapes := []int{}
|
|
||||||
|
|
||||||
for id, s := range countsStr {
|
|
||||||
count, _ := strconv.Atoi(s)
|
|
||||||
|
|
||||||
for range count {
|
|
||||||
requiredShapes = append(requiredShapes, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if canFit(width, height, requiredShapes, shapes) {
|
|
||||||
total++
|
total++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
type item struct {
|
|
||||||
id int
|
|
||||||
area int
|
|
||||||
variants []shape.Shape
|
|
||||||
}
|
|
||||||
|
|
||||||
func canFit(width int, height int, requiredShapes []int, shapes map[int][]shape.Shape) bool {
|
|
||||||
totalArea := 0
|
|
||||||
|
|
||||||
itemsToPlace := make([]item, 0, len(requiredShapes))
|
|
||||||
|
|
||||||
for _, id := range requiredShapes {
|
|
||||||
shapeVariants := shapes[id]
|
|
||||||
area := shapeVariants[0].Area()
|
|
||||||
totalArea += area
|
|
||||||
itemsToPlace = append(itemsToPlace, item{id: id, area: area, variants: shapeVariants})
|
|
||||||
}
|
|
||||||
|
|
||||||
if totalArea > width*height {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(itemsToPlace, func(i, j int) bool {
|
|
||||||
return itemsToPlace[i].area > itemsToPlace[j].area
|
|
||||||
})
|
|
||||||
|
|
||||||
grid := make([][]bool, height)
|
|
||||||
|
|
||||||
for i := range grid {
|
|
||||||
grid[i] = make([]bool, width)
|
|
||||||
}
|
|
||||||
|
|
||||||
return checkFit(0, itemsToPlace, grid, width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFit(index int, itemsToPlace []item, grid [][]bool, width int, height int) bool {
|
|
||||||
if index == len(itemsToPlace) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
itemToPlace := itemsToPlace[index]
|
|
||||||
|
|
||||||
for _, variant := range itemToPlace.variants {
|
|
||||||
maxRow := variant.MaxRow()
|
|
||||||
maxColumn := variant.MaxColumn()
|
|
||||||
|
|
||||||
lastRow := height - maxRow
|
|
||||||
lastColumn := width - maxColumn
|
|
||||||
|
|
||||||
for r := range lastRow {
|
|
||||||
for c := range lastColumn {
|
|
||||||
if canPlace(grid, r, c, variant) {
|
|
||||||
place(grid, r, c, variant)
|
|
||||||
|
|
||||||
if checkFit(index+1, itemsToPlace, grid, width, height) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
unplace(grid, r, c, variant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func canPlace(grid [][]bool, r, c int, variant shape.Shape) bool {
|
|
||||||
for _, position := range variant.Positions() {
|
|
||||||
absoluteRow := r + position.Row()
|
|
||||||
absoluteColumn := c + position.Column()
|
|
||||||
isOccupied := grid[absoluteRow][absoluteColumn]
|
|
||||||
|
|
||||||
if isOccupied {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func place(grid [][]bool, r, c int, variant shape.Shape) {
|
|
||||||
for _, position := range variant.Positions() {
|
|
||||||
absoluteRow := r + position.Row()
|
|
||||||
absoluteColumn := c + position.Column()
|
|
||||||
grid[absoluteRow][absoluteColumn] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unplace(grid [][]bool, r, c int, variant shape.Shape) {
|
|
||||||
for _, position := range variant.Positions() {
|
|
||||||
absoluteRow := r + position.Row()
|
|
||||||
absoluteColumn := c + position.Column()
|
|
||||||
grid[absoluteRow][absoluteColumn] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// Package region provides a model and methods for manipulating a region
|
||||||
|
package region
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/StevanFreeborn/advent-of-code-2025/cmd/12/shape"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Region interface {
|
||||||
|
CanFit(shapes map[int][]shape.Shape) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type region struct {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
requiredShapes []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func From(line string) Region {
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
dims := strings.Split(parts[0], "x")
|
||||||
|
width, _ := strconv.Atoi(dims[0])
|
||||||
|
height, _ := strconv.Atoi(dims[1])
|
||||||
|
|
||||||
|
countsStr := strings.Fields(parts[1])
|
||||||
|
requiredShapes := []int{}
|
||||||
|
|
||||||
|
for id, s := range countsStr {
|
||||||
|
count, _ := strconv.Atoi(s)
|
||||||
|
|
||||||
|
for range count {
|
||||||
|
requiredShapes = append(requiredShapes, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return region{
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
requiredShapes: requiredShapes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r region) CanFit(shapes map[int][]shape.Shape) bool {
|
||||||
|
totalArea := 0
|
||||||
|
|
||||||
|
itemsToPlace := r.createItems(shapes)
|
||||||
|
|
||||||
|
for _, item := range itemsToPlace {
|
||||||
|
totalArea += item.area
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalArea > r.area() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.checkFit(0, itemsToPlace, r.createGrid())
|
||||||
|
}
|
||||||
|
|
||||||
|
type item struct {
|
||||||
|
id int
|
||||||
|
area int
|
||||||
|
variants []shape.Shape
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r region) checkFit(index int, itemsToPlace []item, grid [][]bool) bool {
|
||||||
|
if index == len(itemsToPlace) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
currentItem := itemsToPlace[index]
|
||||||
|
|
||||||
|
for _, variant := range currentItem.variants {
|
||||||
|
maxRow := variant.MaxRow()
|
||||||
|
maxColumn := variant.MaxColumn()
|
||||||
|
|
||||||
|
lastRow := r.height - maxRow
|
||||||
|
lastColumn := r.width - maxColumn
|
||||||
|
|
||||||
|
for rowNumber := range lastRow {
|
||||||
|
for columnNumber := range lastColumn {
|
||||||
|
if canPlace(grid, rowNumber, columnNumber, variant) {
|
||||||
|
place(grid, rowNumber, columnNumber, variant)
|
||||||
|
|
||||||
|
if r.checkFit(index+1, itemsToPlace, grid) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
unplace(grid, rowNumber, columnNumber, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r region) area() int {
|
||||||
|
return r.width * r.height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r region) createGrid() [][]bool {
|
||||||
|
grid := make([][]bool, r.height)
|
||||||
|
|
||||||
|
for i := range grid {
|
||||||
|
grid[i] = make([]bool, r.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r region) createItems(shapes map[int][]shape.Shape) []item {
|
||||||
|
itemsToPlace := make([]item, 0, len(r.requiredShapes))
|
||||||
|
|
||||||
|
for _, id := range r.requiredShapes {
|
||||||
|
shapeVariants := shapes[id]
|
||||||
|
area := shapeVariants[0].Area()
|
||||||
|
itemsToPlace = append(itemsToPlace, item{id: id, area: area, variants: shapeVariants})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(itemsToPlace, func(i, j int) bool {
|
||||||
|
return itemsToPlace[i].area > itemsToPlace[j].area
|
||||||
|
})
|
||||||
|
|
||||||
|
return itemsToPlace
|
||||||
|
}
|
||||||
|
|
||||||
|
func canPlace(grid [][]bool, r, c int, variant shape.Shape) bool {
|
||||||
|
for _, position := range variant.Positions() {
|
||||||
|
absoluteRow := r + position.Row()
|
||||||
|
absoluteColumn := c + position.Column()
|
||||||
|
isOccupied := grid[absoluteRow][absoluteColumn]
|
||||||
|
|
||||||
|
if isOccupied {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func place(grid [][]bool, r, c int, variant shape.Shape) {
|
||||||
|
for _, position := range variant.Positions() {
|
||||||
|
absoluteRow := r + position.Row()
|
||||||
|
absoluteColumn := c + position.Column()
|
||||||
|
grid[absoluteRow][absoluteColumn] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unplace(grid [][]bool, r, c int, variant shape.Shape) {
|
||||||
|
for _, position := range variant.Positions() {
|
||||||
|
absoluteRow := r + position.Row()
|
||||||
|
absoluteColumn := c + position.Column()
|
||||||
|
grid[absoluteRow][absoluteColumn] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user