diff --git a/day18p1/solution.go b/day18p1/solution.go new file mode 100644 index 0000000..a9e46e0 --- /dev/null +++ b/day18p1/solution.go @@ -0,0 +1,82 @@ +package day18p1 + +import ( + "fmt" + "io" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + isTest := len(lines) < 30 + + numSimulated := 12 + if !isTest { + numSimulated = 1024 + } + size := 6 + if !isTest { + size = 70 + } + + corrupted := make(map[utils.Point]bool) + bytes := make([]utils.Point, 0) + for _, line := range lines { + x, y := 0, 0 + fmt.Sscanf(line, "%d,%d", &x, &y) + bytes = append(bytes, utils.Point{X: x, Y: y}) + } + for i := 0; i < numSimulated; i++ { + corrupted[bytes[i]] = true + } + + return steps(corrupted, size) +} + +func steps(corrupted map[utils.Point]bool, size int) int { + type state struct { + pos utils.Point + steps int + } + queue := make([]state, 0) + visited := make(map[utils.Point]bool) + end := utils.Point{X: size, Y: size} + var enqueue = func(p utils.Point, s int) { + for i := 0; i < len(queue); i++ { + if queue[i].steps > s { + queue = append(queue[:i], append([]state{{pos: p, steps: s}}, queue[i:]...)...) + return + } + } + queue = append(queue, state{pos: p, steps: s}) + } + var dequeue = func() state { + current := queue[0] + queue = queue[1:] + return current + } + enqueue(utils.Point{X: 0, Y: 0}, 0) + for len(queue) > 0 { + current := dequeue() + if current.pos == end { + return current.steps + } + if visited[current.pos] { + continue + } + visited[current.pos] = true + for _, dir := range utils.Directions { + next := current.pos.Add(dir) + if next.X < 0 || next.Y < 0 || next.X > size || next.Y > size { + continue + } + if corrupted[next] { + continue + } + enqueue(next, current.steps+1) + } + } + return -1 +} diff --git a/day18p1/solution_test.go b/day18p1/solution_test.go new file mode 100644 index 0000000..a611c96 --- /dev/null +++ b/day18p1/solution_test.go @@ -0,0 +1,57 @@ +package day18p1 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer int + }{ + {testInput, 22}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(int) + + if result != test.answer { + t.Errorf("Expected %d, got %d", test.answer, result) + } + } +} diff --git a/day18p2/solution.go b/day18p2/solution.go new file mode 100644 index 0000000..69c8616 --- /dev/null +++ b/day18p2/solution.go @@ -0,0 +1,89 @@ +package day18p2 + +import ( + "fmt" + "io" + "strconv" + + "aoc/utils" +) + +func Solve(r io.Reader) any { + lines := utils.ReadLines(r) + + isTest := len(lines) < 30 + + numSimulated := 12 + if !isTest { + numSimulated = 1024 + } + size := 6 + if !isTest { + size = 70 + } + + corrupted := make(map[utils.Point]bool) + bytes := make([]utils.Point, 0) + for _, line := range lines { + x, y := 0, 0 + fmt.Sscanf(line, "%d,%d", &x, &y) + bytes = append(bytes, utils.Point{X: x, Y: y}) + } + for i := 0; i < numSimulated; i++ { + corrupted[bytes[i]] = true + } + for i := size; i < len(bytes); i++ { + corrupted[bytes[i]] = true + if steps(corrupted, size) == -1 { + return strconv.Itoa(bytes[i].X) + "," + strconv.Itoa(bytes[i].Y) + } + } + + return "" +} + +func steps(corrupted map[utils.Point]bool, size int) int { + type state struct { + pos utils.Point + steps int + } + queue := make([]state, 0) + visited := make(map[utils.Point]bool) + end := utils.Point{X: size, Y: size} + var enqueue = func(p utils.Point, s int) { + for i := 0; i < len(queue); i++ { + if queue[i].steps > s { + queue = append(queue[:i], append([]state{{pos: p, steps: s}}, queue[i:]...)...) + return + } + } + queue = append(queue, state{pos: p, steps: s}) + } + var dequeue = func() state { + current := queue[0] + queue = queue[1:] + return current + } + enqueue(utils.Point{X: 0, Y: 0}, 0) + for len(queue) > 0 { + current := dequeue() + if current.pos == end { + return current.steps + } + if visited[current.pos] { + continue + } + visited[current.pos] = true + for _, dir := range utils.Directions { + next := current.pos.Add(dir) + if next.X < 0 || next.Y < 0 || next.X > size || next.Y > size { + continue + } + if corrupted[next] { + continue + } + enqueue(next, current.steps+1) + } + } + return -1 +} diff --git a/day18p2/solution_test.go b/day18p2/solution_test.go new file mode 100644 index 0000000..11e7b68 --- /dev/null +++ b/day18p2/solution_test.go @@ -0,0 +1,57 @@ +package day18p2 + +import ( + "strings" + "testing" + + "aoc/utils" +) + +var testInput = `5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0` + +func TestSolve(t *testing.T) { + tests := []struct { + input string + answer string + }{ + {testInput, "6,1"}, + } + + if testing.Verbose() { + utils.Verbose = true + } + + for _, test := range tests { + r := strings.NewReader(test.input) + + result := Solve(r).(string) + + if result != test.answer { + t.Errorf("Expected %s, got %s", test.answer, result) + } + } +}