152 lines
3.2 KiB
Go
152 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"fmt"
|
|
"strings"
|
|
"errors"
|
|
)
|
|
|
|
|
|
// data types
|
|
|
|
type Field struct {
|
|
garden *Garden
|
|
x uint32
|
|
y uint32
|
|
}
|
|
|
|
func (f Field) val() byte {
|
|
return (*f.garden).fields[int(f.y)][int(f.x)]
|
|
}
|
|
|
|
func (f Field) eval_region(r *Region) []Field {
|
|
field_val := f.val()
|
|
neighbours := make([]Field, 0, 4)
|
|
garden := (*f.garden).fields
|
|
|
|
if field_val >= 'a' { return neighbours }
|
|
|
|
if f.x > 0 {
|
|
if garden[f.y][f.x-1] == field_val {
|
|
// has fitting left field
|
|
neighbours = append(neighbours, Field{f.garden, f.x-1, f.y})
|
|
} else if garden[f.y][f.x-1] != field_val + ('a' - 'A') {
|
|
r.perimeter++
|
|
}
|
|
} else {
|
|
r.perimeter++
|
|
}
|
|
|
|
if int(f.x) < len(garden[0]) - 1 {
|
|
if garden[f.y][f.x+1] == field_val {
|
|
// has fitting right field
|
|
neighbours = append(neighbours, Field{f.garden, f.x+1, f.y})
|
|
} else if garden[f.y][f.x+1] != field_val + ('a' - 'A') {
|
|
r.perimeter++
|
|
}
|
|
} else {
|
|
r.perimeter++
|
|
}
|
|
|
|
if int(f.y) > 0 {
|
|
if garden[f.y-1][f.x] == field_val {
|
|
// har fitting lower field
|
|
neighbours = append(neighbours, Field{f.garden, f.x, f.y-1})
|
|
} else if garden[f.y-1][f.x] != field_val + ('a' - 'A') {
|
|
r.perimeter++
|
|
}
|
|
} else {
|
|
r.perimeter++
|
|
}
|
|
|
|
if int(f.y) < len(garden) - 1 {
|
|
if garden[f.y+1][f.x] == field_val {
|
|
// has fitting upper field
|
|
neighbours = append(neighbours, Field{f.garden, f.x, f.y+1})
|
|
} else if garden[f.y+1][f.x] != field_val + ('a' - 'A') {
|
|
r.perimeter++
|
|
}
|
|
} else {
|
|
r.perimeter++
|
|
}
|
|
|
|
// mark field as processed (change to lower case)
|
|
garden[f.y][f.x] = f.val() + ('a' - 'A')
|
|
|
|
r.area++
|
|
|
|
return neighbours
|
|
}
|
|
|
|
type Region struct {
|
|
area uint32
|
|
perimeter uint32
|
|
}
|
|
|
|
|
|
type Garden struct {
|
|
fields [][]byte
|
|
}
|
|
|
|
func (g Garden) field() (Field, error) {
|
|
for i := range g.fields {
|
|
for j := range g.fields[i] {
|
|
if g.fields[i][j] < 'a' {
|
|
return Field{&g, uint32(j), uint32(i)}, nil
|
|
}
|
|
}
|
|
}
|
|
return Field{nil, 0, 0}, errors.New("no more fields")
|
|
}
|
|
|
|
|
|
|
|
// general util
|
|
func check(e error) {
|
|
if e != nil { panic(e) }
|
|
}
|
|
|
|
func new_garden_matrix(input string) Garden {
|
|
dat_str := strings.TrimSpace(input)
|
|
lines := strings.Split(dat_str, "\n")
|
|
out := make([][]byte, 0, len(lines))
|
|
|
|
for _, line := range lines {
|
|
out = append(out, []byte(line))
|
|
}
|
|
return Garden{out}
|
|
}
|
|
|
|
|
|
// main loop
|
|
func main() {
|
|
|
|
// read in file
|
|
dat, err := os.ReadFile("data.txt")
|
|
check(err)
|
|
garden := new_garden_matrix(string(dat))
|
|
neighbours := make(chan Field, 1000)
|
|
|
|
price_acc := 0
|
|
|
|
f, err := garden.field()
|
|
for err == nil {
|
|
r := Region{0,0}
|
|
neighbours <- f
|
|
for len(neighbours) > 0 {
|
|
field := <- neighbours
|
|
for _, new_field := range field.eval_region(&r) {
|
|
neighbours <- new_field
|
|
}
|
|
}
|
|
|
|
price_acc += int(r.area * r.perimeter)
|
|
|
|
f, err = garden.field()
|
|
}
|
|
close(neighbours)
|
|
|
|
fmt.Println(price_acc)
|
|
}
|