package aoc
import (
"fmt"
"io"
"strconv"
"strings"
"mono3/advent/lib/lineio"
"mono3/common/log"
"mono3/common/tty"
)
const maxWidth = 160 / 2
type elfAssignment struct {
lo int
hi int
}
func (e *elfAssignment) String() string {
var sb strings.Builder
for i := 1; i < maxWidth; i++ {
if i < e.lo || i > e.hi {
sb.WriteString("..")
} else {
fmt.Fprintf(&sb, "%02d", i)
}
}
fmt.Fprintf(&sb, " %d-%d", e.lo, e.hi)
return sb.String()
}
func rangeToAssignment(v string) (*elfAssignment, error) {
parts := strings.SplitN(v, "-", 2)
loStr := parts[0]
hiStr := parts[1]
lo, err := strconv.ParseInt(loStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("hi is bad: %w", err)
}
hi, err := strconv.ParseInt(hiStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("lo is bad: %w", err)
}
return &elfAssignment{int(lo), int(hi)}, nil
}
func hasFullOverlap(e1, e2 *elfAssignment) (bool, int, int) {
// check if e1 overlaps e2
if e1.lo >= e2.lo && e1.hi <= e2.hi {
return true, e1.lo, e1.hi
}
// check e2 overlaps e1
if e2.lo >= e1.lo && e2.hi <= e1.hi {
return true, e2.lo, e2.hi
}
return false, 0, 0
}
func hasAnyOverlap(e1, e2 *elfAssignment) (bool, int, int) {
min := func(a, b int) int {
if a < b {
return a
}
return b
}
max := func(a, b int) int {
if a > b {
return a
}
return b
}
// check if e1 overlaps e2
if (e1.lo >= e2.lo && e1.lo <= e2.hi) ||
(e1.hi >= e2.lo && e1.hi <= e2.hi) {
return true, max(e1.lo, e2.lo), min(e1.hi, e2.hi)
}
// check e2 overlaps e1
if (e2.lo >= e1.lo && e2.lo <= e1.hi) ||
(e2.hi >= e1.lo && e2.hi <= e1.hi) {
return true, max(e1.lo, e2.lo), min(e1.hi, e2.hi)
}
return false, 0, 0
}
// Each part gets the same input with reader pointing at the start of input.
var PuzzleParts = []func(r io.Reader) error{
PuzzlePart1,
PuzzlePart2,
}
func PuzzlePart1(r io.Reader) error {
return solveit(r, 1, hasFullOverlap)
}
func PuzzlePart2(r io.Reader) error {
return solveit(r, 2, hasAnyOverlap)
}
func solveit(r io.Reader, part int, overlapFun func(e1, e2 *elfAssignment) (bool, int, int)) error {
log.Info("Running")
score := 0
if err := lineio.ForEach(r, func(ln int, line string) error {
parts := strings.SplitN(line, ",", 2)
one, err := rangeToAssignment(parts[0])
if err != nil {
return fmt.Errorf("elf1 bad: %w", err)
}
two, err := rangeToAssignment(parts[1])
if err != nil {
return fmt.Errorf("elf2 bad: %w", err)
}
tty.Printf("%s\n", one)
tty.Printf("%s\n", two)
if yup, lo, hi := overlapFun(one, two); yup {
tty.Print("%F{brightred}")
score += 1
for i := 1; i < maxWidth; i++ {
if i < lo || i > hi {
tty.Printf(" ")
} else {
tty.Printf("^^")
}
}
tty.Printf("%%f\n")
}
tty.Printf("\n")
return nil
}); err != nil {
return err
}
tty.Printf("Total score part %d: %d\n", part, score)
return nil
}