package main import ( "bufio" _ "embed" "fmt" "strconv" "strings" aoc "go.sour.is/advent-of-code" "golang.org/x/exp/maps" ) // var log = aoc.Log func main() { aoc.MustResult(aoc.Runner(run)) } type result struct { valuePT1 int valuePT2 int } func (r result) String() string { return fmt.Sprintf("%#v", r) } func run(scan *bufio.Scanner) (*result, error) { var vecsPT1 []vector var vecsPT2 []vector for scan.Scan() { text := scan.Text() if len(text) == 0 { continue } v, color := fromLine(text) vecsPT1 = append(vecsPT1, v) vecsPT2 = append(vecsPT2, fromColor(color)) } return &result{ valuePT1: findArea(vecsPT1), valuePT2: findArea(vecsPT2), }, nil } type vector struct { offset point scale int } type point [2]int func (p point) add(a point) point { return point{p[0] + a[0], p[1] + a[1]} } func (p point) scale(m int) point { return point{p[0] * m, p[1] * m} } // numPoints the number of the points inside an outline plus the number of points in the outline func numPoints(outline []point, borderLength int) int { // shoelace - find the float area in a shape sum := 0 for _, p := range pairwise(outline) { row1, col1 := p[0][0], p[0][1] row2, col2 := p[1][0], p[1][1] sum += row1*col2 - row2*col1 } area := sum / 2 // pick's theorem - find the number of points in a shape given its area return (aoc.ABS(area) - borderLength/2 + 1) + borderLength } func pairwise[T any](arr []T) [][2]T { var pairs [][2]T for i := range arr[:len(arr)-1] { pairs = append(pairs, [2]T{arr[i], arr[i+1]}) } return pairs } var OFFSET = map[string]point{ "R": {0, 1}, "D": {1, 0}, "L": {0, -1}, "U": {-1, 0}, } var OFFSET_INDEXES = maps.Values(OFFSET) func fromLine(text string) (vector, string) { v := vector{} s, text, _ := strings.Cut(text, " ") v.offset = OFFSET[s] s, text, _ = strings.Cut(text, " ") v.scale = aoc.Atoi(s) _, text, _ = strings.Cut(text, "(#") s, _, _ = strings.Cut(text, ")") return v, s } func fromColor(c string) vector { scale, _ := strconv.ParseInt(c[:5], 16, 64) offset := OFFSET_INDEXES[c[5]-'0'] return vector{ offset: offset, scale: int(scale), } } func findArea(vecs []vector) int { shoelace := []point{{0,0}} borderLength := 0 for _, vec := range vecs { scaled_offset := vec.offset.scale(vec.scale) shoelace = append(shoelace, shoelace[len(shoelace)-1].add(scaled_offset)) borderLength += vec.scale } return numPoints(shoelace, borderLength) }