Compare commits
5 Commits
ec912023fd
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 019510f0b6 | |||
| 8b102c2c61 | |||
| ce1b496761 | |||
| 2d5190c044 | |||
| b73303c965 |
@@ -8,7 +8,7 @@ using Golang.
|
|||||||
General pattern to run the code is using **stdin** to pass the data file:
|
General pattern to run the code is using **stdin** to pass the data file:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go run ./day1 < ./day1/input.txt
|
go run ./day1 < ./day1/example.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Majority of solutions support `-debug` flag.
|
Majority of solutions support `-debug` flag.
|
||||||
|
|||||||
11
day5/example.txt
Normal file
11
day5/example.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
3-5
|
||||||
|
10-14
|
||||||
|
16-20
|
||||||
|
12-18
|
||||||
|
|
||||||
|
1
|
||||||
|
5
|
||||||
|
8
|
||||||
|
11
|
||||||
|
17
|
||||||
|
32
|
||||||
123
day5/main.go
Normal file
123
day5/main.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"cmp"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f := os.Stdin
|
||||||
|
if len(os.Args) > 1 && os.Args[1] != "-" {
|
||||||
|
var err error
|
||||||
|
f, err = os.Open(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fresh, available, err := readIngredients(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var availableFresh int
|
||||||
|
for _, id := range available {
|
||||||
|
for _, r := range fresh {
|
||||||
|
if r.Includes(id) {
|
||||||
|
availableFresh++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var freshIds int
|
||||||
|
compactFresh := compactedRanges(fresh)
|
||||||
|
for _, r := range compactFresh {
|
||||||
|
freshIds += r.to - r.from + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("available fresh:", availableFresh)
|
||||||
|
fmt.Println("total fresh:", freshIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
type idsRange struct {
|
||||||
|
from, to int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r idsRange) Includes(n int) bool {
|
||||||
|
return r.from <= n && n <= r.to
|
||||||
|
}
|
||||||
|
|
||||||
|
func readIngredients(r io.Reader) (fresh []idsRange, available []int, err error) {
|
||||||
|
seenEmptyLine := false
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
if sc.Text() == "" {
|
||||||
|
seenEmptyLine = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seenEmptyLine {
|
||||||
|
id, err := strconv.ParseUint(sc.Text(), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
available = append(available, int(id))
|
||||||
|
} else {
|
||||||
|
r, err := parseRange(sc.Text())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
fresh = append(fresh, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fresh, available, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRange(s string) (idsRange, error) {
|
||||||
|
before, after, ok := strings.Cut(s, "-")
|
||||||
|
if !ok {
|
||||||
|
return idsRange{}, fmt.Errorf("range %q: missing hyphen", s)
|
||||||
|
}
|
||||||
|
from, err := strconv.ParseUint(before, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return idsRange{}, fmt.Errorf("range from: %w", err)
|
||||||
|
}
|
||||||
|
to, err := strconv.ParseUint(after, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return idsRange{}, fmt.Errorf("range to: %w", err)
|
||||||
|
}
|
||||||
|
return idsRange{
|
||||||
|
from: int(from),
|
||||||
|
to: int(to),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compactedRanges(ranges []idsRange) []idsRange {
|
||||||
|
ranges = slices.Clone(ranges)
|
||||||
|
|
||||||
|
slices.SortFunc(ranges, func(a, b idsRange) int {
|
||||||
|
if x := cmp.Compare(a.from, b.from); x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return cmp.Compare(a.to, b.to)
|
||||||
|
})
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for j := 1; j < len(ranges); j++ {
|
||||||
|
if !ranges[i].Includes(ranges[j].from) {
|
||||||
|
i++
|
||||||
|
ranges[i] = ranges[j]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ranges[i].to = max(ranges[i].to, ranges[j].to)
|
||||||
|
}
|
||||||
|
return ranges[:i+1]
|
||||||
|
}
|
||||||
68
day5/main_test.go
Normal file
68
day5/main_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_compactedRanges(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string // description of this test case
|
||||||
|
ranges []idsRange
|
||||||
|
want []idsRange
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single",
|
||||||
|
ranges: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
},
|
||||||
|
want: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "identical",
|
||||||
|
ranges: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
{0, 10},
|
||||||
|
{0, 10},
|
||||||
|
{20, 30},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
want: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
ranges: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
{2, 11},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
want: []idsRange{
|
||||||
|
{0, 11},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "swallows",
|
||||||
|
ranges: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
{2, 8},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
want: []idsRange{
|
||||||
|
{0, 10},
|
||||||
|
{20, 30},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, compactedRanges(tt.ranges))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
4
day6/example.txt
Normal file
4
day6/example.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
123 328 51 64
|
||||||
|
45 64 387 23
|
||||||
|
6 98 215 314
|
||||||
|
* + * +
|
||||||
217
day6/main.go
Normal file
217
day6/main.go
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"iter"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f := os.Stdin
|
||||||
|
if len(os.Args) > 1 && os.Args[1] != "-" {
|
||||||
|
var err error
|
||||||
|
f, err = os.Open(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := readLines(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
problems1, err := parseProblemsPart1(lines)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
problems2, err := parseProblemsPart2(lines)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result1, result2 int
|
||||||
|
for _, p := range problems1 {
|
||||||
|
result1 += p.Solve()
|
||||||
|
}
|
||||||
|
for _, p := range problems2 {
|
||||||
|
result2 += p.Solve()
|
||||||
|
}
|
||||||
|
fmt.Println("part one:", result1)
|
||||||
|
fmt.Println("part two:", result2)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ActionAdd Action = iota
|
||||||
|
ActionMultiply
|
||||||
|
)
|
||||||
|
|
||||||
|
type problem struct {
|
||||||
|
Values []int
|
||||||
|
Action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p problem) Solve() int {
|
||||||
|
if len(p.Values) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
answer := p.Values[0]
|
||||||
|
for _, v := range p.Values[1:] {
|
||||||
|
switch p.Action {
|
||||||
|
case ActionAdd:
|
||||||
|
answer += v
|
||||||
|
case ActionMultiply:
|
||||||
|
answer *= v
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported action %d", p.Action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLines(r io.Reader) ([]string, error) {
|
||||||
|
var lines []string
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
lines = append(lines, sc.Text())
|
||||||
|
}
|
||||||
|
return lines, sc.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFields(lines []string) [][]string {
|
||||||
|
cutAll := func(lines []string, sep string) (before, after []string, ok bool) {
|
||||||
|
var found bool
|
||||||
|
var i int
|
||||||
|
for _, line := range lines {
|
||||||
|
j := strings.Index(line, sep)
|
||||||
|
if j < 0 {
|
||||||
|
j = len(line)
|
||||||
|
} else {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
i = max(i, j)
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return lines, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
before = make([]string, 0, len(lines))
|
||||||
|
after = make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
before = append(before, line[:min(i, len(line))])
|
||||||
|
after = append(after, line[min(i+len(sep), len(line)):])
|
||||||
|
}
|
||||||
|
return before, after, true
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make([][]string, len(lines))
|
||||||
|
for {
|
||||||
|
before, after, ok := cutAll(lines, " ")
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i, v := range before {
|
||||||
|
fields[i] = append(fields[i], v)
|
||||||
|
}
|
||||||
|
lines = after
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProblemsPart1(lines []string) ([]problem, error) {
|
||||||
|
if len(lines) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var data [][]string
|
||||||
|
for _, line := range lines {
|
||||||
|
data = append(data, strings.Fields(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
problems := make([]problem, len(data[0]))
|
||||||
|
for i, row := range data {
|
||||||
|
if len(row) != len(problems) {
|
||||||
|
return nil, fmt.Errorf("line %d: unexpected number of fields", i+2)
|
||||||
|
}
|
||||||
|
|
||||||
|
last := i == len(data)-1
|
||||||
|
for j, v := range row {
|
||||||
|
p := &problems[j]
|
||||||
|
if last {
|
||||||
|
switch v {
|
||||||
|
case "+":
|
||||||
|
p.Action = ActionAdd
|
||||||
|
case "*":
|
||||||
|
p.Action = ActionMultiply
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected action %q", v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.Values = append(p.Values, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return problems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseProblemsPart2(lines []string) ([]problem, error) {
|
||||||
|
fields := parseFields(lines)
|
||||||
|
var problems []problem
|
||||||
|
|
||||||
|
for col := range columnsSeq(fields) {
|
||||||
|
var p problem
|
||||||
|
switch s := strings.TrimSpace(col[len(col)-1]); s {
|
||||||
|
case "+":
|
||||||
|
p.Action = ActionAdd
|
||||||
|
case "*":
|
||||||
|
p.Action = ActionMultiply
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected action %q", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var colBytes [][]byte
|
||||||
|
for _, v := range col[:len(col)-1] {
|
||||||
|
colBytes = append(colBytes, []byte(v))
|
||||||
|
}
|
||||||
|
for v := range columnsSeq(colBytes) {
|
||||||
|
s := string(bytes.TrimSpace(v))
|
||||||
|
n, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.Values = append(p.Values, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
problems = append(problems, p)
|
||||||
|
}
|
||||||
|
return problems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func columnsSeq[T any](data [][]T) iter.Seq[[]T] {
|
||||||
|
return func(yield func([]T) bool) {
|
||||||
|
if len(data) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for x := range len(data[0]) {
|
||||||
|
var col []T
|
||||||
|
for y := range data {
|
||||||
|
col = append(col, data[y][x])
|
||||||
|
}
|
||||||
|
if !yield(col) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
day7/example.txt
Normal file
16
day7/example.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.......S.......
|
||||||
|
...............
|
||||||
|
.......^.......
|
||||||
|
...............
|
||||||
|
......^.^......
|
||||||
|
...............
|
||||||
|
.....^.^.^.....
|
||||||
|
...............
|
||||||
|
....^.^...^....
|
||||||
|
...............
|
||||||
|
...^.^...^.^...
|
||||||
|
...............
|
||||||
|
..^...^.....^..
|
||||||
|
...............
|
||||||
|
.^.^.^.^.^...^.
|
||||||
|
...............
|
||||||
145
day7/main.go
Normal file
145
day7/main.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
debug := flag.Bool("debug", false, "Debug.")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
f := os.Stdin
|
||||||
|
if flag.NArg() > 0 && flag.Arg(0) != "-" {
|
||||||
|
var err error
|
||||||
|
f, err = os.Open(flag.Arg(0))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
diagram, err := readDiagram(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
splits := diagram.Propagate()
|
||||||
|
var timelines int
|
||||||
|
for _, row := range slices.Backward(diagram) {
|
||||||
|
for _, c := range row {
|
||||||
|
if c.Marker != BeamMarker {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
timelines += c.WaysToReach
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if *debug {
|
||||||
|
for _, row := range diagram {
|
||||||
|
for _, c := range row {
|
||||||
|
if c.Marker == BeamMarker {
|
||||||
|
if c.WaysToReach < 10 {
|
||||||
|
fmt.Print(c.WaysToReach)
|
||||||
|
} else {
|
||||||
|
b := 'A' + c.WaysToReach - 10
|
||||||
|
if b > 'Z' {
|
||||||
|
b = int(c.Marker)
|
||||||
|
}
|
||||||
|
fmt.Printf("%c", b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%c", c.Marker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("beam split", splits, "times into", timelines, "timelines")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Marker byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
BeamEmitterMarker Marker = 'S'
|
||||||
|
BeamMarker Marker = '|'
|
||||||
|
SplitterMarker Marker = '^'
|
||||||
|
EmptyMarker Marker = '.'
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cell struct {
|
||||||
|
Marker Marker
|
||||||
|
WaysToReach int
|
||||||
|
}
|
||||||
|
|
||||||
|
type diagram [][]Cell
|
||||||
|
|
||||||
|
func (d diagram) Propagate() (splits int) {
|
||||||
|
splitoff := func(x, y, dx int) {
|
||||||
|
x1 := x + dx
|
||||||
|
if x1 < 0 || x1 >= len(d[y]) {
|
||||||
|
return // out of range
|
||||||
|
}
|
||||||
|
switch d[y][x1].Marker {
|
||||||
|
case EmptyMarker:
|
||||||
|
d[y][x1] = Cell{
|
||||||
|
Marker: BeamMarker,
|
||||||
|
WaysToReach: d[y-1][x].WaysToReach,
|
||||||
|
}
|
||||||
|
case BeamMarker:
|
||||||
|
d[y][x1].WaysToReach += d[y-1][x].WaysToReach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
propagateCell := func(x, y int) {
|
||||||
|
switch d[y-1][x].Marker {
|
||||||
|
case BeamEmitterMarker:
|
||||||
|
d[y-1][x].WaysToReach = 1
|
||||||
|
case BeamMarker:
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// we have a beam above
|
||||||
|
|
||||||
|
switch d[y][x].Marker {
|
||||||
|
case EmptyMarker:
|
||||||
|
d[y][x] = Cell{
|
||||||
|
Marker: BeamMarker,
|
||||||
|
WaysToReach: d[y-1][x].WaysToReach,
|
||||||
|
}
|
||||||
|
case BeamMarker:
|
||||||
|
d[y][x].WaysToReach += d[y-1][x].WaysToReach
|
||||||
|
case SplitterMarker:
|
||||||
|
splitoff(x, y, -1)
|
||||||
|
splitoff(x, y, +1)
|
||||||
|
splits++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 1; y < len(d); y++ {
|
||||||
|
for x := range d[y] {
|
||||||
|
propagateCell(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return splits
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDiagram(r io.Reader) (diagram, error) {
|
||||||
|
var diagram diagram
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
cells := make([]Cell, 0, len(sc.Bytes()))
|
||||||
|
for _, b := range sc.Bytes() {
|
||||||
|
cells = append(cells, Cell{Marker: Marker(b)})
|
||||||
|
}
|
||||||
|
diagram = append(diagram, cells)
|
||||||
|
}
|
||||||
|
return diagram, sc.Err()
|
||||||
|
}
|
||||||
20
day8/example.txt
Normal file
20
day8/example.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
162,817,812
|
||||||
|
57,618,57
|
||||||
|
906,360,560
|
||||||
|
592,479,940
|
||||||
|
352,342,300
|
||||||
|
466,668,158
|
||||||
|
542,29,236
|
||||||
|
431,825,988
|
||||||
|
739,650,466
|
||||||
|
52,470,668
|
||||||
|
216,146,977
|
||||||
|
819,987,18
|
||||||
|
117,168,530
|
||||||
|
805,96,715
|
||||||
|
346,949,466
|
||||||
|
970,615,88
|
||||||
|
941,993,340
|
||||||
|
862,61,35
|
||||||
|
984,92,344
|
||||||
|
425,690,689
|
||||||
187
day8/main.go
Normal file
187
day8/main.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"maps"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
connections := flag.Int("connect", -1, "Number of connections to perform (-1 unlimited).")
|
||||||
|
debug := flag.Bool("debug", false, "Debug.")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
f := os.Stdin
|
||||||
|
if flag.NArg() > 0 && flag.Arg(0) != "-" {
|
||||||
|
var err error
|
||||||
|
f, err = os.Open(flag.Arg(0))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
positions, err := readPositions(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
circuit := make([]int, len(positions))
|
||||||
|
for i := range positions {
|
||||||
|
circuit[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
distances := computeDistances(positions)
|
||||||
|
var last connection
|
||||||
|
fmt.Println("distances computed in", time.Since(start))
|
||||||
|
|
||||||
|
start = time.Now()
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if *connections >= 0 && i >= *connections {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c, ok := distances.FindMin()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bCircuit := circuit[c.B]
|
||||||
|
for i := range circuit {
|
||||||
|
if circuit[i] == bCircuit {
|
||||||
|
circuit[i] = circuit[c.A]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if *debug {
|
||||||
|
fmt.Println("connect", positions[c.A], "and", positions[c.B])
|
||||||
|
}
|
||||||
|
if allEqual(circuit) {
|
||||||
|
last = c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
delete(distances, c)
|
||||||
|
}
|
||||||
|
fmt.Println("connections computed in", time.Since(start))
|
||||||
|
|
||||||
|
countsMap := make(map[int]int)
|
||||||
|
for _, c := range circuit {
|
||||||
|
countsMap[c]++
|
||||||
|
}
|
||||||
|
counts := slices.Sorted(maps.Values(countsMap))
|
||||||
|
// if *debug {
|
||||||
|
// fmt.Println("counts:", counts)
|
||||||
|
// }
|
||||||
|
|
||||||
|
ans := 1
|
||||||
|
for _, c := range counts[max(0, len(counts)-3):] {
|
||||||
|
ans *= c
|
||||||
|
}
|
||||||
|
fmt.Println("three largest mult:", ans)
|
||||||
|
fmt.Println("last connection X mult:", positions[last.A].X*positions[last.B].X)
|
||||||
|
}
|
||||||
|
|
||||||
|
type position struct {
|
||||||
|
X, Y, Z int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p position) String() string {
|
||||||
|
return fmt.Sprintf("%d,%d,%d", p.X, p.Y, p.Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p position) DistanceTo(p1 position) float64 {
|
||||||
|
compX := float64((p.X - p1.X) * (p.X - p1.X))
|
||||||
|
compY := float64((p.Y - p1.Y) * (p.Y - p1.Y))
|
||||||
|
compZ := float64((p.Z - p1.Z) * (p.Z - p1.Z))
|
||||||
|
return math.Sqrt(compX + compY + compZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPositions(r io.Reader) ([]position, error) {
|
||||||
|
var positions []position
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
fields := strings.Split(sc.Text(), ",")
|
||||||
|
if len(fields) != 3 {
|
||||||
|
return nil, fmt.Errorf("position %q: unexpected number of fields", sc.Text())
|
||||||
|
}
|
||||||
|
x, err := strconv.Atoi(fields[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("position %q: x: %w", sc.Text(), err)
|
||||||
|
}
|
||||||
|
y, err := strconv.Atoi(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("position %q: y: %w", sc.Text(), err)
|
||||||
|
}
|
||||||
|
z, err := strconv.Atoi(fields[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("position %q: z: %w", sc.Text(), err)
|
||||||
|
}
|
||||||
|
positions = append(positions, position{
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
Z: z,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return positions, sc.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
type connection struct {
|
||||||
|
A, B int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c connection) Reverse() connection {
|
||||||
|
return connection{c.B, c.A}
|
||||||
|
}
|
||||||
|
|
||||||
|
type distances map[connection]float64
|
||||||
|
|
||||||
|
func (d distances) Get(aIndex, bIndex int) float64 {
|
||||||
|
c := connection{aIndex, bIndex}
|
||||||
|
if dist, ok := d[c]; ok {
|
||||||
|
return dist
|
||||||
|
}
|
||||||
|
return d[c.Reverse()]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d distances) Set(aIndex, bIndex int, dist float64) {
|
||||||
|
d[connection{aIndex, bIndex}] = dist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d distances) FindMin() (c connection, ok bool) {
|
||||||
|
for pair, dist := range d {
|
||||||
|
if !ok || dist < d[c] {
|
||||||
|
c = pair
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeDistances(positions []position) distances {
|
||||||
|
table := make(distances)
|
||||||
|
for i, p := range positions {
|
||||||
|
for j := i + 1; j < len(positions); j++ {
|
||||||
|
dist := p.DistanceTo(positions[j])
|
||||||
|
table.Set(i, j, dist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
func allEqual(s []int) bool {
|
||||||
|
if len(s) < 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, v := range s[1:] {
|
||||||
|
if v != s[0] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user