mirror of
https://github.com/onyx-and-iris/aoc2023.git
synced 2024-11-24 19:30:48 +00:00
day-3
This commit is contained in:
parent
4593c171d8
commit
c9ea61c7f4
5
day-3/go.mod
Normal file
5
day-3/go.mod
Normal 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
2
day-3/go.sum
Normal 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
117
day-3/one.go
Normal 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
37
day-3/one_test.go
Normal 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
5
day-3/run.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
INPUT="input.txt"
|
||||
|
||||
cat $INPUT | go run .
|
22
day-3/solution.go
Normal file
22
day-3/solution.go
Normal 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
55
day-3/two.go
Normal 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
34
day-3/util.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user