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) }