This commit is contained in:
onyx-and-iris 2023-12-03 23:57:05 +00:00
parent 4593c171d8
commit c9ea61c7f4
8 changed files with 277 additions and 0 deletions

5
day-3/go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/onyx-and-iris/aoc2023/day-3
go 1.20
require github.com/go-playground/assert/v2 v2.2.0

2
day-3/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=

117
day-3/one.go Normal file
View File

@ -0,0 +1,117 @@
package main
import (
"fmt"
"strconv"
"unicode"
)
var symbols = [][]bool{}
// enginePart represents a single engine part
type enginePart struct {
digits []digit
}
// realValue returns the numeric value of all digits combined
func (e *enginePart) realValue() (int, error) {
var numStr string
for _, digit := range e.digits {
numStr += fmt.Sprintf("%d", digit.raw)
}
n, err := strconv.Atoi(numStr)
if err != nil {
return 0, err
}
return n, nil
}
// digit represents a single digit in an engine part
type digit struct {
row int
col int
raw int
pass bool
}
// checkDigit checks if any neighbouring characters are true (in symbol matrix)
func checkDigit(d digit) digit {
i := d.col - 1
for ; i < len(symbols[d.row]) && i <= d.col+1; i += 1 {
if i < 0 {
continue
}
if d.row != 0 {
if symbols[d.row-1][i] {
d.pass = true
break
}
}
if symbols[d.row][i] {
d.pass = true
break
}
if d.row != len(symbols)-1 {
if symbols[d.row+1][i] {
d.pass = true
break
}
}
}
return d
}
// checkDigits runs each digit in an engine part through checkDigit()
func checkDigits(i, j int, line string) (int, int, error) {
enginePart := enginePart{}
for ; j < len(line); j += 1 {
if !unicode.IsNumber(rune(line[j])) {
break
}
enginePart.digits = append(enginePart.digits, checkDigit(digit{row: i, col: j, raw: int(line[j]) - '0', pass: false}))
}
if anyTrue(enginePart.digits) {
engineParts[i] = append(engineParts[i], enginePart)
n, err := enginePart.realValue()
if err != nil {
return 0, 0, err
}
return j, n, nil
}
return j, 0, nil
}
// symbolToBool generates a boolean matrix that represents the placement of symbols
func symbolToBool(line string) []bool {
bool_arr := []bool{}
for _, c := range line {
bool_arr = append(bool_arr, !(unicode.IsNumber(c) || c == '.'))
}
return bool_arr
}
// one deciphers which numbers represent engine parts
// it returns the sum of all engine parts
func one(lines []string) (int, error) {
for _, line := range lines {
symbols = append(symbols, symbolToBool(line))
}
engineParts = make([][]enginePart, len(lines))
sum := 0
for i, line := range lines {
for j := 0; j < len(line); j += 1 {
if unicode.IsNumber(rune(line[j])) {
next, n, err := checkDigits(i, j, line)
if err != nil {
return 0, err
}
sum += n
j = next
}
}
}
return sum, nil
}

37
day-3/one_test.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"testing"
"github.com/go-playground/assert/v2"
)
func TestSymbolToBool(t *testing.T) {
//t.Skip("skipping test")
input := []string{
"467..114..",
"...*......",
"..35..633.",
"......#...",
}
expected := [][]bool{
{false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false, false},
}
bool_matrix := [][]bool{}
for _, line := range input {
bool_matrix = append(bool_matrix, symbolToBool(line))
}
t.Run("Should produce equal boolean matrices", func(t *testing.T) {
assert.Equal(t, expected, bool_matrix)
})
}
func TestCheckDigit(t *testing.T) {
//t.Skip("skipping test")
}

5
day-3/run.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
INPUT="input.txt"
cat $INPUT | go run .

22
day-3/solution.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"log"
)
func main() {
lines := readlines()
ans, err := one(lines)
if err != nil {
log.Fatal(err)
}
fmt.Printf("solution one: %d\n", ans)
ans, err = two(lines)
if err != nil {
log.Fatal(err)
}
fmt.Printf("solution two: %d\n", ans)
}

55
day-3/two.go Normal file
View File

@ -0,0 +1,55 @@
package main
var engineParts [][]enginePart
// getGearRatio returns the product of engine parts if exactly two neighbour a gear
func getGearRatio(row, col int) (int, error) {
partsConsidered := []enginePart{}
i := row - 1
for ; i < len(engineParts) && i <= row+1; i += 1 {
if i < 0 {
continue
}
for _, enginePart := range engineParts[i] {
for _, part := range enginePart.digits {
if part.col == col-1 || part.col == col || part.col == col+1 {
if anyTrue(enginePart.digits) {
partsConsidered = append(partsConsidered, enginePart)
break
}
}
}
}
}
if len(partsConsidered) == 2 {
n1, err := partsConsidered[0].realValue()
if err != nil {
return 0, err
}
n2, err := partsConsidered[1].realValue()
if err != nil {
return 0, err
}
return n1 * n2, nil
}
return 0, nil
}
// two returns the sum of all gear ratios
func two(lines []string) (int, error) {
sum := 0
for i, line := range lines {
for j, c := range line {
if rune(c) == '*' {
n, err := getGearRatio(i, j)
if err != nil {
return 0, err
}
sum += n
}
}
}
return sum, nil
}

34
day-3/util.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"bufio"
"log"
"os"
)
// readlines reads lines from stdin.
// Then it returns them as an array of strings
func readlines() []string {
lines := []string{}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return lines
}
// anyTrue checks if any digits passed
func anyTrue(digits []digit) bool {
for _, digit := range digits {
if digit.pass {
return true
}
}
return false
}