Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: Fixing day 12 solution #30

Merged
merged 1 commit into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 93 additions & 62 deletions src/day12/day12.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@ import (
"strings"
)

type point struct {
x, y int
type plot struct {
crop rune
top, bottom, left, right bool
regionId uint
}

type fence struct {
x, y int
direction rune
}

func (p point) surroundingFences() []fence {
top := fence{x: p.x, y: p.y, direction: '-'}
bottom := fence{x: p.x, y: p.y + 1, direction: '-'}
left := fence{x: p.x, y: p.y, direction: '|'}
right := fence{x: p.x + 1, y: p.y, direction: '|'}
return []fence{top, bottom, left, right}
type region struct {
crop rune
id uint
}

type patch struct {
Expand All @@ -27,79 +21,116 @@ type patch struct {
}

func Solve(input string) uint {
crops := parseInput(input)
garden := parseInput(input)
updateFences(&garden)
assignCropId(&garden)
mergeNeighbors(&garden)

var cost uint = 0
for _, crop := range crops {
uniquePatches, fencesUsed := findUniquePatches(crop)
details := patchDetails(uniquePatches, fencesUsed)
for _, d := range details {
cost += d.area * d.perimeter
}
for _, p := range getPatches(&garden) {
cost += p.area * p.perimeter
}
return cost
}

func parseInput(input string) map[rune][]point {
crops := make(map[rune][]point)
func parseInput(input string) [][]plot {
garden := make([][]plot, 0)
for j, line := range strings.Split(input, "\n") {
for i, plant := range line {
crops[plant] = append(crops[plant], point{x: i, y: j})
garden = append(garden, make([]plot, 0))
for _, r := range line {
garden[j] = append(garden[j], plot{crop: r})
}
}
return crops
return garden
}

func findUniquePatches(crop []point) (map[uint][]point, map[fence]uint) {
var nextPatchId uint = 1
patches := make(map[uint][]point)
fencesByPatchId := make(map[fence]uint)
for _, p := range crop {
usedNewPatchId := true
currentPatchId := nextPatchId
newlyUsedFences := make([]fence, 0)
func updateFences(garden *[][]plot) {
max := len((*garden)) - 1
for j, line := range *garden {
for i, plot := range line {
(*garden)[j][i].top = j == 0 || (*garden)[j-1][i].crop != plot.crop
(*garden)[j][i].bottom = j == max || (*garden)[j+1][i].crop != plot.crop
(*garden)[j][i].left = i == 0 || (*garden)[j][i-1].crop != plot.crop
(*garden)[j][i].right = i == max || (*garden)[j][i+1].crop != plot.crop
}
}
}

for _, f := range p.surroundingFences() {
if fencesByPatchId[f] == 0 {
newlyUsedFences = append(newlyUsedFences, f)
continue
func assignCropId(garden *[][]plot) {
nextRegionId := make(map[rune]uint)
for j, line := range *garden {
for i, plot := range line {
var plotId uint
if !plot.top {
plotId = (*garden)[j-1][i].regionId
} else if !plot.left {
plotId = (*garden)[j][i-1].regionId
} else {
plotId = nextRegionId[plot.crop] + 1
}
(*garden)[j][i].regionId = plotId

if fencesByPatchId[f] != currentPatchId {
patches[fencesByPatchId[f]] = append(patches[fencesByPatchId[f]], patches[currentPatchId]...)
delete(patches, currentPatchId)
currentPatchId = fencesByPatchId[f]
usedNewPatchId = false
if plot.right {
nextRegionId[plot.crop]++
}
delete(fencesByPatchId, f)
}
}
}

for _, f := range newlyUsedFences {
fencesByPatchId[f] = currentPatchId
}
func mergeNeighbors(garden *[][]plot) {
for {
mergesMade := false
mergedRegions := make(map[region]region)
for j, line := range *garden {
for i := 0; i < len(line)-1; i++ {
plot := line[i]
if plot.right || (*garden)[j][i+1].regionId == plot.regionId {
continue
}

patches[currentPatchId] = append(patches[currentPatchId], p)
var t, f uint
if plot.regionId > (*garden)[j][i+1].regionId {
t = plot.regionId
f = (*garden)[j][i+1].regionId
} else {
t = (*garden)[j][i+1].regionId
f = plot.regionId
}
to := region{crop: plot.crop, id: t}
from := region{crop: plot.crop, id: f}
mergedRegions[from] = to
mergesMade = true
}
}
if !mergesMade {
break
}

if usedNewPatchId {
nextPatchId++
for j, line := range *garden {
for i, plot := range line {
r := region{id: plot.regionId, crop: plot.crop}
if update, ok := mergedRegions[r]; ok {
(*garden)[j][i].regionId = update.id
}
}
}
}

return patches, fencesByPatchId
}

func patchDetails(patches map[uint][]point, fencesUsed map[fence]uint) []patch {
details := make([]patch, len(patches))
i := 0
for _, points := range patches {
details[i].area = uint(len(points))
for _, p := range points {
for _, f := range p.surroundingFences() {
if fencesUsed[f] > 0 {
details[i].perimeter++
func getPatches(garden *[][]plot) map[region]patch {
result := make(map[region]patch)
for _, line := range *garden {
for _, plot := range line {
r := region{id: plot.regionId, crop: plot.crop}
p := result[r]
p.area++
for _, f := range []bool{plot.top, plot.bottom, plot.left, plot.right} {
if f {
p.perimeter++
}
}
result[r] = p
}
i++
}
return details
return result
}
Loading
Loading