Files
advent-of-code-go/2023/day03/main.go
T
2024-01-22 23:37:55 -05:00

199 lines
4.0 KiB
Go

package main
import (
_ "embed"
"flag"
"fmt"
"regexp"
"strings"
"github.com/alexchao26/advent-of-code-go/cast"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed input.txt
var input string
func init() {
// do this in init (not main) so test file has same input
input = strings.TrimRight(input, "\n")
if len(input) == 0 {
panic("empty input.txt file")
}
}
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
var numReg = regexp.MustCompile("[0-9]")
func part1(input string) int {
matrix := [][]string{}
// collect special characters
specialChars := map[string]bool{}
for _, row := range strings.Split(input, "\n") {
matrix = append(matrix, strings.Split(row, ""))
for _, val := range strings.Split(row, "") {
if !numReg.MatchString(val) && val != "." {
specialChars[val] = true
}
}
}
specialCharsString := ""
for k := range specialChars {
specialCharsString += k
}
var sum int
diffs := [8][2]int{
{-1, -1},
{-1, 0},
{-1, 1},
{0, -1},
{0, 1},
{1, -1},
{1, 0},
{1, 1},
}
seen := map[[2]int]bool{}
for r, row := range matrix {
for c, val := range row {
coords := [2]int{r, c}
if seen[coords] {
continue
}
seen[coords] = true
// if we hit a number, collect the entire number and check along the way if it's
// adjacent to a special char
if numReg.MatchString(val) {
hasAdjacentSpecialChar := false
numStr := ""
for j := 0; j+c < len(matrix[0]); j++ {
char := row[c+j]
// breaks on period or special character, loop itself breaks on out of range
if !numReg.MatchString(char) {
break
}
// keep collecting number
numStr += char
// check all 8 directions for special char
for _, d := range diffs {
dr, dc := r+d[0], c+j+d[1]
if dr >= 0 && dr < len(matrix) && dc >= 0 && dc < len(matrix[0]) {
if strings.ContainsAny(matrix[dr][dc], specialCharsString) {
hasAdjacentSpecialChar = true
}
seen[[2]int{r, c + j}] = true
}
}
}
if hasAdjacentSpecialChar {
sum += cast.ToInt(numStr)
}
}
}
}
return sum
}
// getNumber returns -1 if a number is "not found" which could include the number
// already being seen
func getNumber(matrix [][]string, coord [2]int, seen map[[2]int]bool) int {
if !numReg.MatchString(matrix[coord[0]][coord[1]]) {
return -1
}
if seen[coord] {
return -1
}
// go to the left most digit
r, c := coord[0], coord[1]
for c-1 >= 0 {
if numReg.MatchString(matrix[r][c-1]) {
c--
} else {
break
}
}
numStr := ""
for c < len(matrix[0]) && numReg.MatchString(matrix[r][c]) {
numStr += matrix[r][c]
seen[[2]int{r, c}] = true
c++
}
return cast.ToInt(numStr)
}
func part2(input string) int {
// lucky edge case that there are not multiple gears that share the same number such as
// ....*....
// .123.456. OR ...123*456*789...
// ....*....
// with the current useage of "seen", this would only get counted once
seen := map[[2]int]bool{}
matrix := [][]string{}
for _, row := range strings.Split(input, "\n") {
matrix = append(matrix, strings.Split(row, ""))
}
diffs := [8][2]int{
{-1, -1},
{-1, 0},
{-1, 1},
{0, -1},
{0, 1},
{1, -1},
{1, 0},
{1, 1},
}
sum := 0
for r, rows := range matrix {
for c, val := range rows {
if val == "*" {
nums := []int{}
for _, diff := range diffs {
dr, dc := r+diff[0], c+diff[1]
if dr >= 0 && dr < len(matrix) && dc >= 0 && dc < len(matrix[0]) {
foundNum := getNumber(matrix, [2]int{dr, dc}, seen)
if foundNum != -1 {
nums = append(nums, foundNum)
}
}
}
if len(nums) == 2 {
sum += nums[0] * nums[1]
}
}
}
}
return sum
}