Compare commits

..

No commits in common. "c87eb85e465b86e1b5902e31df4e32300a487be1" and "8e7fa3c5a8dc3ae3827aef0dc88d9e2203d52a4a" have entirely different histories.

11 changed files with 246 additions and 387 deletions

View File

@ -1,30 +0,0 @@
name: Go Bump
on:
push:
branches: [ "main" ]
jobs:
bump:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
fetch-tags: true
- name: Deploy to external repository
uses: https://git.sour.is/actions/github-action-push-to-another-repository@main
env:
API_TOKEN_GITHUB: ${{ secrets.GH_TOKEN }}
with:
# GitHub Action output files
source-directory: .
destination-github-username: sour-is
destination-repository-name: go-pkg
user-email: jon@xuu.cc
# It defaults to `main`
target-branch: "main"
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@ -1,33 +0,0 @@
name: Go Test
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- uses: actions/checkout@v3
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.21.3
- name: Test
run: go test --race -cover ./...
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@ -1,4 +0,0 @@
1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet

View File

@ -1,7 +0,0 @@
two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen

View File

@ -2,41 +2,25 @@ package main
import ( import (
"bufio" "bufio"
"bytes"
_ "embed" _ "embed"
"fmt" "fmt"
"os"
"strings" "strings"
aoc "go.sour.is/advent-of-code-2023"
) )
//go:embed input.txt
var input []byte
func main() { func main() {
result, err := aoc.Runner(run) buf := bytes.NewReader(input)
if err != nil { scan := bufio.NewScanner(buf)
fmt.Println("ERR", err)
os.Exit(1)
}
fmt.Println(result)
}
type result struct {
sum int
sum2 int
}
func (r result) String() string {
return fmt.Sprintln("result pt1:", r.sum, "\nresult pt2:", r.sum2)
}
var numbers = []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}
func run(scan *bufio.Scanner) (*result, error) {
result := &result{}
sum := 0
for scan.Scan() { for scan.Scan() {
var first, last int var first, last int
var first2, last2 int
orig := scan.Text()
_ = orig
text := scan.Text() text := scan.Text()
@ -46,66 +30,110 @@ func run(scan *bufio.Scanner) (*result, error) {
switch { switch {
case slice[0] >= '0' && slice[0] <= '9': case slice[0] >= '0' && slice[0] <= '9':
if first == 0 {
first = int(slice[0] - '0') first = int(slice[0] - '0')
} case strings.HasPrefix(string(slice), "one"):
if first2 == 0 { first = 1
first2 = int(slice[0] - '0') case strings.HasPrefix(string(slice), "two"):
} first = 2
default: case strings.HasPrefix(string(slice), "three"):
if first2 != 0 { first = 3
continue case strings.HasPrefix(string(slice), "four"):
} first = 4
case strings.HasPrefix(string(slice), "five"):
first = 5
case strings.HasPrefix(string(slice), "six"):
first = 6
case strings.HasPrefix(string(slice), "seven"):
first = 7
case strings.HasPrefix(string(slice), "eight"):
first = 8
case strings.HasPrefix(string(slice), "nine"):
first = 9
for i, s := range numbers {
if strings.HasPrefix(string(slice), s) {
first2 = i
break
} }
} if first != 0 {
}
if first != 0 && first2 != 0 {
break break
} }
} }
text = string(aoc.Reverse([]rune(text))) text = string(reverse([]rune(text)))
for i := range text { for i := range text {
copy(slice, []rune(text[i:])) copy(slice, []rune(text[i:]))
slice = aoc.Reverse(slice) slice = reverse(slice)
switch { switch {
case slice[4] >= '0' && slice[4] <= '9': case slice[4] >= '0' && slice[4] <= '9':
if last == 0 {
last = int(slice[4] - '0') last = int(slice[4] - '0')
case strings.HasSuffix(string(slice), "one"):
last = 1
case strings.HasSuffix(string(slice), "two"):
last = 2
case strings.HasSuffix(string(slice), "three"):
last = 3
case strings.HasSuffix(string(slice), "four"):
last = 4
case strings.HasSuffix(string(slice), "five"):
last = 5
case strings.HasSuffix(string(slice), "six"):
last = 6
case strings.HasSuffix(string(slice), "seven"):
last = 7
case strings.HasSuffix(string(slice), "eight"):
last = 8
case strings.HasSuffix(string(slice), "nine"):
last = 9
} }
if last2 == 0 { if last != 0 {
last2 = int(slice[4] - '0')
}
default:
if last2 != 0 {
continue
}
for i, s := range numbers {
if strings.HasSuffix(string(slice), s) {
last2 = i
break break
} }
} }
} sum += first*10 + last
if last != 0 && last2 != 0 {
break
}
}
result.sum += first*10 + last
result.sum2 += first2*10 + last2
} }
if err := scan.Err(); err != nil { if err := scan.Err(); err != nil {
return nil, err panic(err)
} }
return result, nil fmt.Println(sum)
} }
func reverse[T any](arr []T) []T{
for i := 0; i < len(arr)/2; i++ {
arr[i], arr[len(arr)-i-1] = arr[len(arr)-i-1], arr[i]
}
return arr
}
// type sorter[T rune | int] []T
// func (s sorter[T]) Less(i, j int) bool { return s[i] < s[j] }
// func (s sorter[T]) Len() int { return len(s) }
// func (s sorter[T]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
/*
0
1
2
3
4
5
6
7
8
9
one
two
three
four
five
six
seven
eight
nine
ten
*/

View File

@ -1,54 +0,0 @@
package main
import (
"bufio"
"bytes"
"testing"
_ "embed"
"github.com/matryer/is"
)
//go:embed example1.txt
var example1 []byte
//go:embed example2.txt
var example2 []byte
//go:embed input.txt
var input []byte
func TestExample1(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(example1))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
is.Equal(result.sum, 142)
}
func TestExample2(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(example2))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
is.Equal(result.sum2, 281)
}
func TestInput(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(input))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
is.Equal(result.sum, 54573)
is.Equal(result.sum2, 54591)
}

View File

@ -2,39 +2,24 @@ package main
import ( import (
"bufio" "bufio"
"bytes"
_ "embed" _ "embed"
"fmt" "fmt"
"os"
"strconv" "strconv"
"strings" "strings"
aoc "go.sour.is/advent-of-code-2023"
) )
func main() { //go:embed input.txt
result, err := aoc.Runner(run) var input []byte
if err != nil {
aoc.Log("ERR", err)
os.Exit(1)
}
aoc.Log(result)
}
type result struct {
sum int
powerSum int
}
func (r result) String() string {
return fmt.Sprintln("result pt1:", r.sum, "\nresult pt2:", r.powerSum)
}
type gameResult struct { type gameResult struct {
red, green, blue int red, green, blue int
} }
func run(scan *bufio.Scanner) (*result, error) { func main() {
buf := bytes.NewReader(input)
scan := bufio.NewScanner(buf)
// only 12 red cubes, 13 green cubes, and 14 blue cubes // only 12 red cubes, 13 green cubes, and 14 blue cubes
maxCounts := gameResult{ maxCounts := gameResult{
red: 12, red: 12,
@ -77,8 +62,8 @@ func run(scan *bufio.Scanner) (*result, error) {
} }
} }
aoc.Log(games) fmt.Println(games)
aoc.Log(len(games)) fmt.Println(len(games))
sum := 0 sum := 0
powerSum := 0 powerSum := 0
@ -96,24 +81,24 @@ func run(scan *bufio.Scanner) (*result, error) {
mins.blue = max(mins.blue, round.blue) mins.blue = max(mins.blue, round.blue)
if maxCounts.red < round.red { if maxCounts.red < round.red {
aoc.Log("game", i, round, "too many red", round.red) fmt.Println("game", i, round, "too many red", round.red)
ok = false ok = false
} else if maxCounts.blue < round.blue { } else if maxCounts.blue < round.blue {
aoc.Log("game", i, round, "too many blue", round.blue) fmt.Println("game", i, round, "too many blue", round.blue)
ok = false ok = false
} else if maxCounts.green < round.green { } else if maxCounts.green < round.green {
aoc.Log("game", i, round, "too many green", round.green) fmt.Println("game", i, round, "too many green", round.green)
ok = false ok = false
} }
aoc.Log("game", i, round, ok) fmt.Println("game", i, round, ok)
} }
if ok { if ok {
sum += i sum += i
aoc.Log("game", i, "passes", sum) fmt.Println("game", i, "passes", sum)
} }
power := mins.red*mins.blue*mins.green power := mins.red*mins.blue*mins.green
aoc.Log("game", i, "mins", mins, power) fmt.Println("game", i, "mins", mins, power)
powerSum += power powerSum += power
} }
return &result{sum, powerSum}, nil fmt.Println("sum", sum, "power", powerSum)
} }

View File

@ -1,41 +0,0 @@
package main
import (
"bufio"
"bytes"
"testing"
_ "embed"
"github.com/matryer/is"
)
//go:embed example.txt
var example []byte
//go:embed input.txt
var input []byte
func TestExample(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(example))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
is.Equal(result.sum, 8)
is.Equal(result.powerSum, 2286)
}
func TestSolution(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(input))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
is.Equal(result.sum, 2317)
is.Equal(result.powerSum, 74804)
}

View File

@ -5,12 +5,21 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
aoc "go.sour.is/advent-of-code-2023"
) )
func main() { func main() {
result, err := aoc.Runner(run) if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: day08 FILE")
}
input, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
scan := bufio.NewScanner(input)
result, err := run(scan)
if err != nil { if err != nil {
fmt.Println("ERR", err) fmt.Println("ERR", err)
os.Exit(1) os.Exit(1)
@ -20,14 +29,10 @@ func main() {
} }
type result struct { type result struct {
stepsPT1 uint64 stepsPT1 int
stepsPT2 uint64 stepsPT2 uint64
} }
func (r result) String() string {
return fmt.Sprintf("solution 1: %v\nsolution 2: %v\n", r.stepsPT1, r.stepsPT2)
}
func run(scan *bufio.Scanner) (*result, error) { func run(scan *bufio.Scanner) (*result, error) {
var path []rune var path []rune
m := make(nodeMap) m := make(nodeMap)
@ -59,8 +64,8 @@ func run(scan *bufio.Scanner) (*result, error) {
return nil, err return nil, err
} }
steps1 := m.SolvePT1(path) steps1 := SolutionPT1(m, path)
steps2 := m.SolvePT2(path) steps2 := SolutionPT2(m, path)
return &result{steps1, steps2}, nil return &result{steps1, steps2}, nil
} }
@ -91,25 +96,26 @@ func (m nodeMap) mapNodes() error {
return nil return nil
} }
func (m nodeMap) solver(start string, isEnd func(string) bool, path []rune) uint64 { func SolutionPT1(m nodeMap, path []rune) int {
position, ok := m[start] fmt.Println("---- PART 1 BEGIN ----")
position, ok := m["AAA"]
if !ok { if !ok {
return 0 return 0
} }
var i int var i int
var steps uint64 var steps int
for steps < ^uint64(0) { for steps < 100000 {
steps++ steps++
if path[i] == 'R' { if path[i] == 'R' {
// fmt.Println("step", steps, position.value, "R->", position.rvalue) fmt.Println("step", steps, position.value, "R->", position.rvalue)
position = position.right position = position.right
} else { } else {
// fmt.Println("step", steps, position.value, "L->", position.lvalue) fmt.Println("step", steps, position.value, "L->", position.lvalue)
position = position.left position = position.left
} }
if isEnd(position.value) { if position.value == "ZZZ" {
break break
} }
@ -118,33 +124,126 @@ func (m nodeMap) solver(start string, isEnd func(string) bool, path []rune) uint
i = 0 i = 0
} }
} }
fmt.Println("---- PART 1 END ----")
return steps return steps
} }
func (m nodeMap) SolvePT1(path []rune) uint64 { func SolutionPT2(m nodeMap, path []rune) uint64 {
fmt.Println("---- PART 1 BEGIN ----")
defer fmt.Println("---- PART 1 END ----")
return m.solver("AAA", func(s string) bool { return s == "ZZZ" }, path)
}
func (m nodeMap) SolvePT2(path []rune) uint64 {
fmt.Println("---- PART 2 BEGIN ----") fmt.Println("---- PART 2 BEGIN ----")
defer fmt.Println("---- PART 2 END ----")
var starts []*node type loop struct {
start, position, end *node
steps uint64
}
loops := make(map[*node]loop)
endpoints := make(map[*node]struct{})
for k, n := range m { for k, n := range m {
if strings.HasSuffix(k, "A") { if strings.HasSuffix(k, "A") {
fmt.Println("start", k) fmt.Println("start", k)
starts = append(starts, n) loops[n] = loop{start: n, position: n}
}
if strings.HasSuffix(k, "Z") {
fmt.Println("stop", k)
endpoints[n] = struct{}{}
} }
} }
loops := make([]uint64, len(starts)) var i int
for i, n := range starts { var steps uint64
loops[i] = m.solver(n.value, func(s string) bool { return strings.HasSuffix(s, "Z") }, path) var stops int
maxUint := ^uint64(0)
loopsFound := 0
for steps < maxUint {
steps++
if path[i] == 'R' {
for k, loop := range loops {
// fmt.Println("step", steps, position.value, "R->", position.rvalue)
loop.position = loop.position.right
loops[k] = loop
}
} else {
for k, loop := range loops {
// fmt.Println("step", steps, position.value, "L->", position.lvalue)
loop.position = loop.position.left
loops[k] = loop
} }
return aoc.LCM(loops...)
} }
done := true
s := 0
for k, loop := range loops {
if _, ok := endpoints[loop.position]; !ok {
// fmt.Println("no stop", i, position.value)
done = false
// break
} else {
// fmt.Println("stop", i, position.value)
if loop.end == nil {
loop.end = loop.position
loop.steps = steps
fmt.Println("loop found", loop.position.value, "steps", steps)
loops[k] = loop
loopsFound++
}
s++
}
}
if loopsFound == len(loops) {
var values []uint64
for _, loop := range loops {
values = append(values, loop.steps)
}
return LCM(values...)
}
if s > stops {
stops = s
fmt.Println("stops", stops, "steps", steps)
}
if done {
break
}
i++
if i > len(path)-1 {
i = 0
}
}
fmt.Println("---- PART 2 END ----")
return steps
}
// greatest common divisor (GCD) via Euclidean algorithm
func GCD(a, b uint64) uint64 {
for b != 0 {
t := b
b = a % b
a = t
}
return a
}
// find Least Common Multiple (LCM) via GCD
func LCM(integers ...uint64) uint64 {
if len(integers) == 0 {
return 0
}
if len(integers) == 1 {
return integers[0]
}
a, b := integers[0], integers[1]
result := a * b / GCD(a, b)
for _, c := range integers[2:] {
result = LCM(result, c)
}
return result
}

View File

@ -30,7 +30,7 @@ func TestExample1(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log(result.stepsPT1) t.Log(result.stepsPT1)
is.Equal(result.stepsPT1, uint64(2)) is.Equal(result.stepsPT1, 2)
} }
func TestExample2(t *testing.T) { func TestExample2(t *testing.T) {
@ -41,7 +41,7 @@ func TestExample2(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log(result.stepsPT1) t.Log(result.stepsPT1)
is.Equal(result.stepsPT1, uint64(6)) is.Equal(result.stepsPT1, 6)
} }
func TestExample3(t *testing.T) { func TestExample3(t *testing.T) {
@ -63,18 +63,10 @@ func TestInput(t *testing.T) {
is.NoErr(err) is.NoErr(err)
t.Log("part1 solution", result.stepsPT1) t.Log("part1 solution", result.stepsPT1)
is.Equal(result.stepsPT1, uint64(14429)) is.Equal(result.stepsPT1, 14429)
t.Log("part2 solution", result.stepsPT2) t.Log("part2 solution", result.stepsPT2)
is.Equal(result.stepsPT2, uint64(10921547990923)) is.Equal(result.stepsPT2, uint64(10921547990923))
} }
// first: 14429 // first: 14429
// second: 10921547990923 // second: 10921547990923
// Brüt
// stops 1 steps 13201
// stops 2 steps 620447
// stops 3 steps 36606373
// stops 4 steps 2232988753
// stops 5 steps 149610246451

View File

@ -1,76 +0,0 @@
package aoc
import (
"bufio"
"fmt"
"os"
"strings"
)
func Runner[R any, F func(*bufio.Scanner) (R, error)](run F) (R, error) {
if len(os.Args) != 2 {
Log("Usage:", os.Args[0], "FILE")
os.Exit(22)
}
input, err := os.Open(os.Args[1])
if err != nil {
Log(err)
os.Exit(1)
}
scan := bufio.NewScanner(input)
return run(scan)
}
func Log(v ...any) { fmt.Fprintln(os.Stderr, v...) }
func Logf(format string, v ...any) {
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(os.Stderr, format, v...)
}
func Reverse[T any](arr []T) []T {
for i := 0; i < len(arr)/2; i++ {
arr[i], arr[len(arr)-i-1] = arr[len(arr)-i-1], arr[i]
}
return arr
}
type integer interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64
}
// type float interface {
// complex64 | complex128 | float32 | float64
// }
// type number interface{ integer | float }
// greatest common divisor (GCD) via Euclidean algorithm
func GCD[T integer](a, b T) T {
for b != 0 {
t := b
b = a % b
a = t
}
return a
}
// find Least Common Multiple (LCM) via GCD
func LCM[T integer](integers ...T) T {
if len(integers) == 0 {
return 0
}
if len(integers) == 1 {
return integers[0]
}
a, b := integers[0], integers[1]
result := a * b / GCD(a, b)
for _, c := range integers[2:] {
result = LCM(result, c)
}
return result
}