mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
dumping 5 days of unrefined solutions :)
This commit is contained in:
@@ -0,0 +1,126 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) string {
|
||||||
|
stacks, steps := parseInput(input)
|
||||||
|
|
||||||
|
for _, step := range steps {
|
||||||
|
// move crates ONE AT A TIME
|
||||||
|
for q := 0; q < step.qty; q++ {
|
||||||
|
top := stacks[step.from][len(stacks[step.from])-1]
|
||||||
|
stacks[step.to] = append(stacks[step.to], top)
|
||||||
|
stacks[step.from] = stacks[step.from][:len(stacks[step.from])-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ans := ""
|
||||||
|
for _, stack := range stacks {
|
||||||
|
ans += stack[len(stack)-1]
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) string {
|
||||||
|
stacks, steps := parseInput(input)
|
||||||
|
|
||||||
|
for _, step := range steps {
|
||||||
|
// move crates ONCE
|
||||||
|
fromIndex := len(stacks[step.from]) - step.qty
|
||||||
|
stacks[step.to] = append(stacks[step.to], stacks[step.from][fromIndex:]...)
|
||||||
|
stacks[step.from] = stacks[step.from][:fromIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
ans := ""
|
||||||
|
for _, stack := range stacks {
|
||||||
|
ans += stack[len(stack)-1]
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
// move 4 from 3 to 1
|
||||||
|
type step struct {
|
||||||
|
qty, from, to int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s step) String() string {
|
||||||
|
return fmt.Sprintf("move %d from %d to %d", s.qty, s.from, s.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(input string) ([][]string, []step) {
|
||||||
|
parts := strings.Split(input, "\n\n")
|
||||||
|
|
||||||
|
state := parts[0]
|
||||||
|
oversized := [][]string{}
|
||||||
|
for _, row := range strings.Split(state, "\n") {
|
||||||
|
oversized = append(oversized, strings.Split(row, ""))
|
||||||
|
}
|
||||||
|
oRows, oCols := len(oversized), len(oversized[0])
|
||||||
|
|
||||||
|
actual := [][]string{}
|
||||||
|
|
||||||
|
for c := 0; c < oCols-1; c++ {
|
||||||
|
if oversized[oRows-1][c] != " " {
|
||||||
|
// hit a column with values... move up from here
|
||||||
|
stack := []string{}
|
||||||
|
for r := oRows - 2; r >= 0; r-- {
|
||||||
|
char := oversized[r][c]
|
||||||
|
if char != " " {
|
||||||
|
stack = append(stack, char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual = append(actual, stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stepsRaw := parts[1]
|
||||||
|
steps := []step{}
|
||||||
|
for _, row := range strings.Split(stepsRaw, "\n") {
|
||||||
|
inst := step{}
|
||||||
|
_, err := fmt.Sscanf(row, "move %d from %d to %d", &inst.qty, &inst.from, &inst.to)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// subtract one so they're zero indexed...
|
||||||
|
inst.from--
|
||||||
|
inst.to--
|
||||||
|
steps = append(steps, inst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual, steps
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var example = ` [D]
|
||||||
|
[N] [C]
|
||||||
|
[Z] [M] [P]
|
||||||
|
1 2 3
|
||||||
|
|
||||||
|
move 1 from 2 to 1
|
||||||
|
move 3 from 1 to 3
|
||||||
|
move 2 from 2 to 1
|
||||||
|
move 1 from 1 to 2`
|
||||||
|
|
||||||
|
func Test_part1(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: "CMZ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: "QNHWJVJZW",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_part2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: "MCD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: "BPCZJLFJW",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) int {
|
||||||
|
// packet starts w/ 4 characters that are all different
|
||||||
|
for i := 0; i+4 <= len(input); i++ {
|
||||||
|
if allDifferentLetters(input[i : i+4]) {
|
||||||
|
return i + 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazy but easier than sliding window...
|
||||||
|
func allDifferentLetters(str string) bool {
|
||||||
|
// if len(str) != 4 {
|
||||||
|
// panic(fmt.Sprintf("invalid length %q", str))
|
||||||
|
// }
|
||||||
|
for i := 0; i < len(str); i++ {
|
||||||
|
for j := i + 1; j < len(str); j++ {
|
||||||
|
if str[i] == str[j] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) int {
|
||||||
|
// wow super lazy but fast to write... ok
|
||||||
|
for i := 0; i+14 <= len(input); i++ {
|
||||||
|
if allDifferentLetters(input[i : i+14]) {
|
||||||
|
return i + 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var example = `mjqjpqmgbljsphdztnvjfqwrcgsmlb`
|
||||||
|
|
||||||
|
func Test_part1(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 1109,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_part2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 3965,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alexchao26/advent-of-code-go/cast"
|
||||||
|
"github.com/alexchao26/advent-of-code-go/mathy"
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) int {
|
||||||
|
root := parseInput(input)
|
||||||
|
|
||||||
|
return sumDirsUnder100000(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumDirsUnder100000(itr *dir) int {
|
||||||
|
SizeLimit := 100000
|
||||||
|
|
||||||
|
sum := 0
|
||||||
|
if itr.totalSize <= SizeLimit {
|
||||||
|
sum += itr.totalSize
|
||||||
|
}
|
||||||
|
for _, child := range itr.childDirs {
|
||||||
|
sum += sumDirsUnder100000(child)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) int {
|
||||||
|
root := parseInput(input)
|
||||||
|
totalSapceAvailable := 70000000
|
||||||
|
spaceNeeded := 30000000
|
||||||
|
|
||||||
|
// find smallest directory to be deleted that would free up enough space...
|
||||||
|
directoryMinSize := spaceNeeded - (totalSapceAvailable - root.totalSize)
|
||||||
|
return findSmallestDirToDelete(root, directoryMinSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findSmallestDirToDelete(itr *dir, directoryMinSize int) int {
|
||||||
|
smallest := math.MaxInt64
|
||||||
|
if itr.totalSize >= directoryMinSize {
|
||||||
|
smallest = mathy.MinInt(smallest, itr.totalSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, childDirs := range itr.childDirs {
|
||||||
|
smallest = mathy.MinInt(smallest, findSmallestDirToDelete(childDirs, directoryMinSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
return smallest
|
||||||
|
}
|
||||||
|
|
||||||
|
type dir struct {
|
||||||
|
name string
|
||||||
|
parentDir *dir
|
||||||
|
childDirs map[string]*dir
|
||||||
|
files map[string]int
|
||||||
|
totalSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(input string) *dir {
|
||||||
|
root := &dir{
|
||||||
|
name: "root",
|
||||||
|
childDirs: map[string]*dir{},
|
||||||
|
}
|
||||||
|
itr := root
|
||||||
|
|
||||||
|
cmds := strings.Split(input, "\n")
|
||||||
|
c := 0
|
||||||
|
|
||||||
|
for c < len(cmds) {
|
||||||
|
switch cmd := cmds[c]; cmd[0:1] {
|
||||||
|
case "$":
|
||||||
|
if cmd == "$ ls" {
|
||||||
|
// just move on, we will assume we're always in an listing state
|
||||||
|
c++
|
||||||
|
} else {
|
||||||
|
changeDir := strings.Split(cmd, "cd ")[1]
|
||||||
|
changeDir = strings.TrimSpace(changeDir)
|
||||||
|
if changeDir == ".." {
|
||||||
|
itr = itr.parentDir
|
||||||
|
} else {
|
||||||
|
// if changeDir doesn't exist..
|
||||||
|
if _, ok := itr.childDirs[changeDir]; !ok {
|
||||||
|
itr.childDirs[changeDir] = &dir{
|
||||||
|
name: changeDir,
|
||||||
|
parentDir: itr,
|
||||||
|
childDirs: map[string]*dir{},
|
||||||
|
files: map[string]int{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = itr.childDirs[changeDir]
|
||||||
|
}
|
||||||
|
c++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// assume we're listing a dir's contents... add it
|
||||||
|
if strings.HasPrefix(cmd, "dir") {
|
||||||
|
childDirName := cmd[4:]
|
||||||
|
if _, ok := itr.childDirs[childDirName]; !ok {
|
||||||
|
itr.childDirs[childDirName] = &dir{
|
||||||
|
name: childDirName,
|
||||||
|
parentDir: itr,
|
||||||
|
childDirs: map[string]*dir{},
|
||||||
|
files: map[string]int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// file name
|
||||||
|
parts := strings.Split(cmd, " ")
|
||||||
|
itr.files[parts[0]] = cast.ToInt(parts[0])
|
||||||
|
}
|
||||||
|
c++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateFileSizes(root)
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateFileSizes(itr *dir) int {
|
||||||
|
totalSize := 0
|
||||||
|
|
||||||
|
for _, childItr := range itr.childDirs {
|
||||||
|
totalSize += populateFileSizes(childItr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sz := range itr.files {
|
||||||
|
totalSize += sz
|
||||||
|
}
|
||||||
|
|
||||||
|
itr.totalSize = totalSize
|
||||||
|
|
||||||
|
return totalSize
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var example = `$ cd /
|
||||||
|
$ ls
|
||||||
|
dir a
|
||||||
|
14848514 b.txt
|
||||||
|
8504156 c.dat
|
||||||
|
dir d
|
||||||
|
$ cd a
|
||||||
|
$ ls
|
||||||
|
dir e
|
||||||
|
29116 f
|
||||||
|
2557 g
|
||||||
|
62596 h.lst
|
||||||
|
$ cd e
|
||||||
|
$ ls
|
||||||
|
584 i
|
||||||
|
$ cd ..
|
||||||
|
$ cd ..
|
||||||
|
$ cd d
|
||||||
|
$ ls
|
||||||
|
4060174 j
|
||||||
|
8033020 d.log
|
||||||
|
5626152 d.ext
|
||||||
|
7214296 k`
|
||||||
|
|
||||||
|
func Test_part1(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 95437,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 1423358,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_part2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 24933642,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 545729,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) int {
|
||||||
|
pairs := parseInput(input)
|
||||||
|
// sum all the indexes that are in the right order
|
||||||
|
// ONE INDEXED NOT ZERO
|
||||||
|
goodIndexSum := 0
|
||||||
|
for i, pair := range pairs {
|
||||||
|
left, right := pair[0], pair[1]
|
||||||
|
if isInOrder(left, right) {
|
||||||
|
goodIndexSum += i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return goodIndexSum
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) int {
|
||||||
|
pairs := parseInput(input)
|
||||||
|
allPackets := [][]interface{}{
|
||||||
|
// good reminder that json.Unmarshal will convert numbers to float64...
|
||||||
|
// so need this to match for the way to isInOrder() function works..
|
||||||
|
{[]interface{}{float64(2)}},
|
||||||
|
{[]interface{}{float64(6)}},
|
||||||
|
}
|
||||||
|
for _, pair := range pairs {
|
||||||
|
allPackets = append(allPackets, pair[0])
|
||||||
|
allPackets = append(allPackets, pair[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(allPackets, func(i, j int) bool {
|
||||||
|
left, right := allPackets[i], allPackets[j]
|
||||||
|
return isInOrder(left, right)
|
||||||
|
})
|
||||||
|
|
||||||
|
ans := 1
|
||||||
|
for i, p := range allPackets {
|
||||||
|
if fmt.Sprint(p) == "[[2]]" || fmt.Sprint(p) == "[[6]]" {
|
||||||
|
ans *= i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(input string) (ans [][2][]interface{}) {
|
||||||
|
for _, packetPairs := range strings.Split(input, "\n\n") {
|
||||||
|
pairs := strings.Split(packetPairs, "\n")
|
||||||
|
ans = append(ans, [2][]interface{}{
|
||||||
|
parseRawString(pairs[0]),
|
||||||
|
parseRawString(pairs[1]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
// will parse as JSON with elements as either int or []int...
|
||||||
|
func parseRawString(raw string) []interface{} {
|
||||||
|
ans := []interface{}{}
|
||||||
|
json.Unmarshal([]byte(raw), &ans)
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInOrder(left, right []interface{}) bool {
|
||||||
|
for l := 0; l < len(left); l++ {
|
||||||
|
if l > len(right)-1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to convert both to ints...
|
||||||
|
leftNum, isLeftNum := left[l].(float64)
|
||||||
|
rightNum, isRightNum := right[l].(float64)
|
||||||
|
|
||||||
|
leftList, isLeftList := left[l].([]interface{})
|
||||||
|
rightList, isRightList := right[l].([]interface{})
|
||||||
|
if isLeftNum && isRightNum {
|
||||||
|
if leftNum != rightNum {
|
||||||
|
return leftNum < rightNum
|
||||||
|
}
|
||||||
|
} else if isLeftNum || isRightNum {
|
||||||
|
if isLeftNum {
|
||||||
|
leftList = []interface{}{leftNum}
|
||||||
|
} else if isRightNum {
|
||||||
|
rightList = []interface{}{rightNum}
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("expected one num %T:%v, %T:%v", left[l],
|
||||||
|
left[l], right[l], right[l]))
|
||||||
|
}
|
||||||
|
return isInOrder(leftList, rightList)
|
||||||
|
} else {
|
||||||
|
// both lists
|
||||||
|
if !isLeftList || !isRightList {
|
||||||
|
panic(fmt.Sprintf("expected two lists %T:%v, %T:%v", left[l],
|
||||||
|
left[l], right[l], right[l]))
|
||||||
|
}
|
||||||
|
return isInOrder(leftList, rightList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var example = `[1,1,3,1,1]
|
||||||
|
[1,1,5,1,1]
|
||||||
|
|
||||||
|
[[1],[2,3,4]]
|
||||||
|
[[1],4]
|
||||||
|
|
||||||
|
[9]
|
||||||
|
[[8,7,6]]
|
||||||
|
|
||||||
|
[[4,4],4,4]
|
||||||
|
[[4,4],4,4,4]
|
||||||
|
|
||||||
|
[7,7,7,7]
|
||||||
|
[7,7,7]
|
||||||
|
|
||||||
|
[]
|
||||||
|
[3]
|
||||||
|
|
||||||
|
[[[]]]
|
||||||
|
[[]]
|
||||||
|
|
||||||
|
[1,[2,[3,[4,[5,6,7]]]],8,9]
|
||||||
|
[1,[2,[3,[4,[5,6,0]]]],8,9]`
|
||||||
|
|
||||||
|
func Test_part1(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 5760,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_part2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 26670,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alexchao26/advent-of-code-go/cast"
|
||||||
|
"github.com/alexchao26/advent-of-code-go/mathy"
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(input string) int {
|
||||||
|
matrix := parseInput(input)
|
||||||
|
originCol := 0
|
||||||
|
for i, c := range matrix[0] {
|
||||||
|
if c == "+" {
|
||||||
|
originCol = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ans := 0
|
||||||
|
for !dropSand(matrix, originCol) {
|
||||||
|
ans++
|
||||||
|
}
|
||||||
|
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func part2(input string) int {
|
||||||
|
matrix := parseInput(input)
|
||||||
|
originCol := 0
|
||||||
|
for i, c := range matrix[0] {
|
||||||
|
if c == "+" {
|
||||||
|
originCol = i
|
||||||
|
}
|
||||||
|
matrix[len(matrix)-1][i] = "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
ans := 0
|
||||||
|
for !dropSand(matrix, originCol) {
|
||||||
|
ans++
|
||||||
|
// COULD incorporate this into the for loop conditional but then the
|
||||||
|
// ordering is important... must check origin cell BEFORE running
|
||||||
|
// dropSand... it's easier to read here...
|
||||||
|
if matrix[0][originCol] == "o" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(input string) (matrix [][]string) {
|
||||||
|
coordSets := [][][2]int{}
|
||||||
|
lowestCol := math.MaxInt64
|
||||||
|
highestRow := 0
|
||||||
|
for _, line := range strings.Split(input, "\n") {
|
||||||
|
rawCoords := strings.Split(line, " -> ")
|
||||||
|
coords := [][2]int{}
|
||||||
|
for _, rawCoord := range rawCoords {
|
||||||
|
rawNums := strings.Split(rawCoord, ",")
|
||||||
|
col, row := cast.ToInt(rawNums[0]), cast.ToInt(rawNums[1])
|
||||||
|
coord := [2]int{
|
||||||
|
col, row,
|
||||||
|
}
|
||||||
|
coords = append(coords, coord)
|
||||||
|
|
||||||
|
lowestCol = mathy.MinInt(lowestCol, col)
|
||||||
|
highestRow = mathy.MaxInt(highestRow, row)
|
||||||
|
}
|
||||||
|
coordSets = append(coordSets, coords)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lowering this number to 1 makes it easier to print the matrix, which I
|
||||||
|
// used for part 1... but then needed to up it for part 2... or just have a
|
||||||
|
// massive screen and make the terminal text tiny...
|
||||||
|
ExtraLeftSpace := 200
|
||||||
|
|
||||||
|
highestCol := 0
|
||||||
|
for s, set := range coordSets {
|
||||||
|
for i := range set {
|
||||||
|
coordSets[s][i][0] -= lowestCol - ExtraLeftSpace
|
||||||
|
highestCol = mathy.MaxInt(highestCol, coordSets[s][i][0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix = make([][]string, highestRow+3)
|
||||||
|
for r := range matrix {
|
||||||
|
matrix[r] = make([]string, highestCol+ExtraLeftSpace*2)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, set := range coordSets {
|
||||||
|
for i := 1; i < len(set); i++ {
|
||||||
|
cols := []int{set[i-1][0], set[i][0]}
|
||||||
|
rows := []int{set[i-1][1], set[i][1]}
|
||||||
|
|
||||||
|
sort.Ints(cols)
|
||||||
|
sort.Ints(rows)
|
||||||
|
|
||||||
|
if cols[0] == cols[1] {
|
||||||
|
for r := rows[0]; r <= rows[1]; r++ {
|
||||||
|
matrix[r][cols[0]] = "#"
|
||||||
|
}
|
||||||
|
} else if rows[0] == rows[1] {
|
||||||
|
for c := cols[0]; c <= cols[1]; c++ {
|
||||||
|
matrix[rows[0]][c] = "#"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
originCol := 500 - lowestCol + ExtraLeftSpace
|
||||||
|
// make it a plus so it's searchable in the next step... or could just
|
||||||
|
// return this value too...
|
||||||
|
matrix[0][originCol] = "+"
|
||||||
|
|
||||||
|
for i, r := range matrix {
|
||||||
|
for j := range r {
|
||||||
|
if matrix[i][j] == "" {
|
||||||
|
matrix[i][j] = "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printMatrix(matrix)
|
||||||
|
return matrix
|
||||||
|
}
|
||||||
|
|
||||||
|
func printMatrix(matrix [][]string) {
|
||||||
|
for _, r := range matrix {
|
||||||
|
fmt.Println(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropSand(matrix [][]string, originCol int) (fallsIntoAbyss bool) {
|
||||||
|
r, c := 0, originCol
|
||||||
|
|
||||||
|
for r < len(matrix)-1 {
|
||||||
|
below := matrix[r+1][c]
|
||||||
|
diagonallyLeft := matrix[r+1][c-1]
|
||||||
|
diagonallyRight := matrix[r+1][c+1]
|
||||||
|
if below == "." {
|
||||||
|
r++
|
||||||
|
} else if diagonallyLeft == "." {
|
||||||
|
r++
|
||||||
|
c--
|
||||||
|
} else if diagonallyRight == "." {
|
||||||
|
r++
|
||||||
|
c++
|
||||||
|
} else {
|
||||||
|
matrix[r][c] = "o"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var example = `498,4 -> 498,6 -> 496,6
|
||||||
|
503,4 -> 502,4 -> 502,9 -> 494,9`
|
||||||
|
|
||||||
|
func Test_part1(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 961,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part1(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part1() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_part2(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "example",
|
||||||
|
input: example,
|
||||||
|
want: 93,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actual",
|
||||||
|
input: input,
|
||||||
|
want: 26375,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := part2(tt.input); got != tt.want {
|
||||||
|
t.Errorf("part2() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user