From fd59e62ebcd120cbf437b125efdb36449840cc30 Mon Sep 17 00:00:00 2001 From: Gitea Date: Fri, 18 Oct 2019 20:40:16 -0600 Subject: [PATCH 1/4] day 313: BFS for fastest way to solve combination lock with dead ends --- day313/problem.go | 88 +++++++++++++++++++++++++++++++++++++++++ day313/problem_test.go | 89 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 day313/problem.go create mode 100644 day313/problem_test.go diff --git a/day313/problem.go b/day313/problem.go new file mode 100644 index 0000000..4e9f432 --- /dev/null +++ b/day313/problem.go @@ -0,0 +1,88 @@ +package day313 + +// MovesToUnlock returns the minimum number of moves to unlock a rotary +// lock avoid all dead ends. +// Returns -1 if it's not possible. +// Panics if the input data is inconsistent. +func MovesToUnlock(start, target [3]int, deadEnds [][3]int) int { + if start == target { + return 0 + } + + for _, v := range start { + if v < 0 || v > 9 { + panic("start values must all be 0-9") + } + } + + for _, v := range target { + if v < 0 || v > 9 { + panic("target values must all be 0-9") + } + } + + graph := make([][][]int, 10) + for i := range graph { + graph[i] = make([][]int, 10) + for j := range graph[i] { + graph[i][j] = make([]int, 10) + } + } + + for _, de := range deadEnds { + for _, v := range de { + if v < 0 || v > 9 { + panic("dead end values must all be 0-9") + } + } + + x, y, z := de[0], de[1], de[2] + graph[x][y][z] = -1 + } + + bfs(graph, start) + + return graph[target[0]][target[1]][target[2]] - 1 +} + +func bfs(g [][][]int, start [3]int) { + var q [][3]int + + g[start[0]][start[1]][start[2]] = 1 + + q = append(q, start) + + for len(q) != 0 { + var v [3]int + v, q = q[0], q[1:] + dist := g[v[0]][v[1]][v[2]] + + for _, dir := range [][3]int{ + {-1, 0, 0}, {0, -1, 0}, {0, 0, -1}, + {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, + } { + next := coord(v, dir) + x, y, z := next[0], next[1], next[2] + + if g[x][y][z] == 0 { + g[x][y][z] = dist + 1 + + q = append(q, next) + } + } + } +} + +func coord(start, direction [3]int) [3]int { + var result [3]int + for i := range result { + result[i] = start[i] + direction[i] + if result[i] > 9 { + result[i] = 0 + } else if result[i] < 0 { + result[i] = 9 + } + } + + return result +} diff --git a/day313/problem_test.go b/day313/problem_test.go new file mode 100644 index 0000000..9890da1 --- /dev/null +++ b/day313/problem_test.go @@ -0,0 +1,89 @@ +package day313 + +import "testing" + +// nolint +var testcases = []struct { + start, target [3]int + deadEnds [][3]int + expected int +}{ + { + [3]int{0, 0, 0}, + [3]int{7, 1, 9}, + [][3]int{{1, 1, 1}}, + 5, + }, + { + [3]int{0, 0, 0}, + [3]int{0, 0, 0}, + [][3]int{{1, 1, 1}}, + 0, + }, + { + [3]int{0, 0, 0}, + [3]int{5, 5, 5}, + [][3]int{{1, 0, 0}, {0, 1, 0}, {9, 0, 0}, {0, 9, 0}, {0, 0, 1}, {0, 0, 9}}, + -1, + }, + { + [3]int{0, 0, 0}, + [3]int{5, 5, 5}, + [][3]int{{0, 1, 0}, {9, 0, 0}, {0, 9, 0}, {0, 0, 1}, {0, 0, 9}}, + 15, + }, +} + +func TestMovesToUnlock(t *testing.T) { + t.Parallel() + + for _, tc := range testcases { + if result := MovesToUnlock(tc.start, tc.target, tc.deadEnds); result != tc.expected { + t.Errorf("Expected %v, got %v", tc.expected, result) + } + } +} + +func TestMovesToUnlockBadStartPanic(t *testing.T) { + t.Parallel() + + defer func() { + if err := recover(); err == nil { + t.Errorf("Expected a panic for bad start coordinates") + } + }() + + MovesToUnlock([3]int{-1, 10, 5}, [3]int{1, 1, 1}, [][3]int{}) +} + +func TestMovesToUnlockBadTargetPanic(t *testing.T) { + t.Parallel() + + defer func() { + if err := recover(); err == nil { + t.Errorf("Expected a panic for bad target coordinates") + } + }() + + MovesToUnlock([3]int{0, 0, 0}, [3]int{10, 1, 1}, [][3]int{}) +} + +func TestMovesToUnlockBadDeadEndPanic(t *testing.T) { + t.Parallel() + + defer func() { + if err := recover(); err == nil { + t.Errorf("Expected a panic for bad dead end coordinates") + } + }() + + MovesToUnlock([3]int{0, 0, 0}, [3]int{1, 1, 1}, [][3]int{{-1, 0, 0}}) +} + +func BenchmarkMovesToUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tc := range testcases { + MovesToUnlock(tc.start, tc.target, tc.deadEnds) + } + } +} From 12cc105efccb49f39d4f4e90c7497bd3cb3912da Mon Sep 17 00:00:00 2001 From: Gitea Date: Fri, 18 Oct 2019 20:41:43 -0600 Subject: [PATCH 2/4] add day 313 to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 71df4e1..a2e6518 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,7 @@ problems from * [Day 310](https://github.com/vaskoz/dailycodingproblem-go/issues/633) * [Day 311](https://github.com/vaskoz/dailycodingproblem-go/issues/635) * [Day 312](https://github.com/vaskoz/dailycodingproblem-go/issues/637) +* [Day 313](https://github.com/vaskoz/dailycodingproblem-go/issues/639) * [Day 314](https://github.com/vaskoz/dailycodingproblem-go/issues/640) * [Day 315](https://github.com/vaskoz/dailycodingproblem-go/issues/642) * [Day 317](https://github.com/vaskoz/dailycodingproblem-go/issues/645) From 203ba57e618ab89385866642e0900a691b4af85b Mon Sep 17 00:00:00 2001 From: Gitea Date: Fri, 18 Oct 2019 20:46:45 -0600 Subject: [PATCH 3/4] day 313: add runtime performance comment --- day313/problem.go | 1 + 1 file changed, 1 insertion(+) diff --git a/day313/problem.go b/day313/problem.go index 4e9f432..d58e21e 100644 --- a/day313/problem.go +++ b/day313/problem.go @@ -4,6 +4,7 @@ package day313 // lock avoid all dead ends. // Returns -1 if it's not possible. // Panics if the input data is inconsistent. +// Runs in O(N) or O(V+E) time since BFS is a linear algorithm. func MovesToUnlock(start, target [3]int, deadEnds [][3]int) int { if start == target { return 0 From f0ec21dcebc61154314873684c1a4e8ed0bf83f0 Mon Sep 17 00:00:00 2001 From: Gitea Date: Fri, 18 Oct 2019 20:51:04 -0600 Subject: [PATCH 4/4] day 333: exported function comment fix --- day333/problem.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/day333/problem.go b/day333/problem.go index c323255..b2005f8 100644 --- a/day333/problem.go +++ b/day333/problem.go @@ -5,6 +5,8 @@ package day333 // NOTE: This is not bidirectional. type Knowing map[int]map[int]bool +// Knows is a O(1) method that answers if 'a' "knows" 'b'. +// This does not imply that 'b' "knows" 'a'. func (k *Knowing) Knows(a, b int) bool { if known, exists := (*k)[a]; exists { return known[b]