trackler 2.2.1.77 → 2.2.1.78
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/exercises/transpose/canonical-data.json +94 -116
- data/tracks/bash/config/maintainers.json +11 -1
- data/tracks/clojure/config.json +10 -2
- data/tracks/clojure/exercises/reverse-string/README.md +14 -0
- data/tracks/clojure/exercises/reverse-string/project.clj +4 -0
- data/tracks/clojure/exercises/reverse-string/src/example.clj +5 -0
- data/tracks/clojure/exercises/reverse-string/src/reverse_string.clj +5 -0
- data/tracks/clojure/exercises/reverse-string/test/reverse_string_test.clj +18 -0
- data/tracks/clojure/exercises/say/README.md +70 -0
- data/tracks/clojure/exercises/{two-fer → say}/project.clj +3 -3
- data/tracks/clojure/exercises/say/src/example.clj +9 -0
- data/tracks/clojure/exercises/say/src/say.clj +5 -0
- data/tracks/clojure/exercises/say/test/say_test.clj +48 -0
- data/tracks/coffeescript/docs/ABOUT.md +4 -9
- data/tracks/coffeescript/docs/INSTALLATION.md +2 -2
- data/tracks/erlang/exercises/pangram/rebar.config +1 -1
- data/tracks/erlang/exercises/pangram/src/example.erl +2 -3
- data/tracks/erlang/exercises/pangram/test/pangram_tests.erl +12 -13
- data/tracks/fsharp/exercises/grep/Example.fs +16 -20
- data/tracks/fsharp/exercises/grep/GrepTest.fs +154 -233
- data/tracks/fsharp/generators/Common.fs +7 -2
- data/tracks/fsharp/generators/Exercise.fs +51 -16
- data/tracks/fsharp/generators/Formatting.fs +13 -6
- data/tracks/fsharp/generators/Generators.fs +44 -14
- data/tracks/fsharp/generators/Rendering.fs +8 -1
- data/tracks/fsharp/generators/Templates/Generators/_GrepSetup.liquid +37 -0
- data/tracks/fsharp/generators/Templates/_TestClass.liquid +9 -3
- data/tracks/fsharp/generators/Templates/{_TestMethod.liquid → _TestFunction.liquid} +0 -0
- data/tracks/fsharp/generators/Templates/{_TestMethodBody.liquid → _TestFunctionBody.liquid} +0 -0
- data/tracks/fsharp/generators/Templates/_TestMember.liquid +3 -0
- data/tracks/fsharp/generators/Templates/_TestMemberBody.liquid +4 -0
- data/tracks/fsharp/generators/Templates/_TestModule.liquid +17 -0
- data/tracks/go/config.json +24 -0
- data/tracks/go/exercises/acronym/.meta/gen.go +5 -3
- data/tracks/go/exercises/acronym/acronym.go +0 -2
- data/tracks/go/exercises/acronym/cases_test.go +2 -2
- data/tracks/go/exercises/acronym/example.go +2 -3
- data/tracks/go/exercises/all-your-base/.meta/gen.go +9 -7
- data/tracks/go/exercises/all-your-base/cases_test.go +2 -2
- data/tracks/go/exercises/allergies/.meta/gen.go +10 -6
- data/tracks/go/exercises/allergies/cases_test.go +2 -2
- data/tracks/go/exercises/anagram/.meta/gen.go +7 -5
- data/tracks/go/exercises/anagram/cases_test.go +2 -2
- data/tracks/go/exercises/armstrong-numbers/.meta/gen.go +54 -0
- data/tracks/go/exercises/armstrong-numbers/armstrong_test.go +12 -0
- data/tracks/go/exercises/armstrong-numbers/cases_test.go +52 -0
- data/tracks/go/exercises/armstrong-numbers/example.go +24 -0
- data/tracks/go/exercises/binary-search/.meta/gen.go +7 -5
- data/tracks/go/exercises/binary-search/cases_test.go +2 -2
- data/tracks/go/exercises/book-store/.meta/gen.go +5 -3
- data/tracks/go/exercises/book-store/cases_test.go +2 -2
- data/tracks/go/exercises/connect/example.go +4 -5
- data/tracks/go/exercises/dominoes/.meta/gen.go +61 -0
- data/tracks/go/exercises/dominoes/.meta/hints.md +29 -0
- data/tracks/go/exercises/dominoes/README.md +67 -0
- data/tracks/go/exercises/dominoes/cases_test.go +72 -0
- data/tracks/go/exercises/dominoes/dominoes_test.go +106 -0
- data/tracks/go/exercises/dominoes/example.go +146 -0
- data/tracks/go/exercises/ledger/example.go +1 -1
- data/tracks/groovy/.gitignore +3 -0
- data/tracks/groovy/.travis.yml +10 -1
- data/tracks/groovy/bin/prepeare_exercise_builds.groovy +77 -0
- data/tracks/groovy/build.gradle +25 -0
- data/tracks/groovy/exercises/difference-of-squares/Example.groovy +3 -3
- data/tracks/groovy/exercises/gigasecond/GigasecondSpec.groovy +3 -3
- data/tracks/groovy/exercises/linked-list/{DoubleLinkedList.groovy → LinkedList.groovy} +0 -0
- data/tracks/groovy/exercises/linked-list/{DoubleLinkedListSpec.groovy → LinkedListSpec.groovy} +0 -0
- data/tracks/groovy/exercises/linked-list/README.md +1 -1
- data/tracks/groovy/gradle/wrapper/gradle-wrapper.jar +0 -0
- data/tracks/groovy/gradle/wrapper/gradle-wrapper.properties +6 -0
- data/tracks/groovy/gradlew +172 -0
- data/tracks/groovy/gradlew.bat +84 -0
- data/tracks/groovy/settings.gradle +9 -0
- data/tracks/ocaml/exercises/acronym/.merlin +1 -1
- data/tracks/ocaml/exercises/all-your-base/.merlin +1 -1
- data/tracks/ocaml/exercises/anagram/.merlin +1 -1
- data/tracks/ocaml/exercises/atbash-cipher/.merlin +1 -1
- data/tracks/ocaml/exercises/beer-song/.merlin +1 -1
- data/tracks/ocaml/exercises/binary-search/.merlin +1 -1
- data/tracks/ocaml/exercises/bob/.merlin +1 -1
- data/tracks/ocaml/exercises/bowling/.merlin +1 -1
- data/tracks/ocaml/exercises/change/.merlin +1 -1
- data/tracks/ocaml/exercises/difference-of-squares/.merlin +1 -1
- data/tracks/ocaml/exercises/etl/.merlin +1 -1
- data/tracks/ocaml/exercises/forth/.merlin +1 -1
- data/tracks/ocaml/exercises/grade-school/.merlin +1 -1
- data/tracks/ocaml/exercises/hamming/.merlin +1 -1
- data/tracks/ocaml/exercises/hangman/.merlin +1 -1
- data/tracks/ocaml/exercises/hello-world/.merlin +1 -1
- data/tracks/ocaml/exercises/hexadecimal/.merlin +1 -1
- data/tracks/ocaml/exercises/leap/.merlin +1 -1
- data/tracks/ocaml/exercises/list-ops/.merlin +1 -1
- data/tracks/ocaml/exercises/luhn/.merlin +1 -1
- data/tracks/ocaml/exercises/meetup/.merlin +1 -1
- data/tracks/ocaml/exercises/minesweeper/.merlin +1 -1
- data/tracks/ocaml/exercises/nucleotide-count/.merlin +1 -1
- data/tracks/ocaml/exercises/phone-number/.merlin +1 -1
- data/tracks/ocaml/exercises/prime-factors/.merlin +1 -1
- data/tracks/ocaml/exercises/raindrops/.merlin +1 -1
- data/tracks/ocaml/exercises/react/.merlin +1 -1
- data/tracks/ocaml/exercises/rectangles/.merlin +1 -1
- data/tracks/ocaml/exercises/rna-transcription/.merlin +1 -1
- data/tracks/ocaml/exercises/run-length-encoding/.merlin +1 -1
- data/tracks/ocaml/exercises/say/.merlin +1 -1
- data/tracks/ocaml/exercises/space-age/.merlin +1 -1
- data/tracks/ocaml/exercises/triangle/.merlin +1 -1
- data/tracks/ocaml/exercises/word-count/.merlin +1 -1
- data/tracks/ocaml/exercises/zipper/.merlin +1 -1
- data/tracks/typescript/config/maintainers.json +2 -2
- metadata +37 -12
- data/tracks/clojure/exercises/two-fer/README.md +0 -19
- data/tracks/clojure/exercises/two-fer/src/example.clj +0 -5
- data/tracks/clojure/exercises/two-fer/src/two_fer.clj +0 -5
- data/tracks/clojure/exercises/two-fer/test/two_fer_test.clj +0 -12
- data/tracks/erlang/exercises/pangram/include/exercism.hrl +0 -11
@@ -31,8 +31,10 @@ type TestGroup struct {
|
|
31
31
|
|
32
32
|
type OneCase struct {
|
33
33
|
Description string
|
34
|
-
|
35
|
-
|
34
|
+
Input struct {
|
35
|
+
Basket []int
|
36
|
+
}
|
37
|
+
Expected float64
|
36
38
|
}
|
37
39
|
|
38
40
|
// template applied to above data structure generates the Go test cases
|
@@ -47,7 +49,7 @@ var testCases = []struct {
|
|
47
49
|
}{{range .J.Groups}}{
|
48
50
|
{{range .Cases}} {
|
49
51
|
description: "{{.Description}}",
|
50
|
-
basket: {{printf "%#v" .Basket}},
|
52
|
+
basket: {{printf "%#v" .Input.Basket}},
|
51
53
|
expected: {{printf "%.2f" .Expected}},
|
52
54
|
},
|
53
55
|
{{end}}}{{end}}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
package bookstore
|
2
2
|
|
3
3
|
// Source: exercism/problem-specifications
|
4
|
-
// Commit:
|
5
|
-
// Problem Specifications Version: 1.
|
4
|
+
// Commit: 78006a2 move "targetgrouping" to "comments"
|
5
|
+
// Problem Specifications Version: 1.2.0
|
6
6
|
|
7
7
|
var testCases = []struct {
|
8
8
|
description string
|
@@ -43,11 +43,11 @@ func newBoard(lines []string) (board, error) {
|
|
43
43
|
if len(lines) < 1 {
|
44
44
|
return board{}, errors.New("No lines given")
|
45
45
|
}
|
46
|
-
height :=
|
46
|
+
height := len(lines)
|
47
47
|
if len(lines[0]) < 1 {
|
48
48
|
return board{}, errors.New("First line is empty string")
|
49
49
|
}
|
50
|
-
width :=
|
50
|
+
width := len(lines[0])
|
51
51
|
// This trick for 2D arrays comes from Effective Go
|
52
52
|
fields := make([][]int8, height)
|
53
53
|
fieldsBacker := make([]int8, height*width)
|
@@ -65,12 +65,11 @@ func newBoard(lines []string) (board, error) {
|
|
65
65
|
// No need for default, zero value already means no stone
|
66
66
|
}
|
67
67
|
}
|
68
|
-
|
68
|
+
return board{
|
69
69
|
height: height,
|
70
70
|
width: width,
|
71
71
|
fields: fields,
|
72
|
-
}
|
73
|
-
return board, nil
|
72
|
+
}, nil
|
74
73
|
}
|
75
74
|
|
76
75
|
// Whether there is a stone of the given color at the given location.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"log"
|
6
|
+
"text/template"
|
7
|
+
|
8
|
+
"../../../gen"
|
9
|
+
)
|
10
|
+
|
11
|
+
func main() {
|
12
|
+
t, err := template.New("").Parse(tmpl)
|
13
|
+
if err != nil {
|
14
|
+
log.Fatal(err)
|
15
|
+
}
|
16
|
+
var j js
|
17
|
+
if err := gen.Gen("dominoes", &j, t); err != nil {
|
18
|
+
log.Fatal(err)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
// The JSON structure we expect to be able to unmarshal into
|
23
|
+
type js struct {
|
24
|
+
Exercise string
|
25
|
+
Version string
|
26
|
+
Comments []string
|
27
|
+
Cases []OneCase
|
28
|
+
}
|
29
|
+
|
30
|
+
type Dominoe [2]int
|
31
|
+
|
32
|
+
func (d Dominoe) String() string {
|
33
|
+
return fmt.Sprintf("Dominoe{%d, %d}", d[0], d[1])
|
34
|
+
}
|
35
|
+
|
36
|
+
// template applied to above data structure generates the Go test cases
|
37
|
+
|
38
|
+
type OneCase struct {
|
39
|
+
Description string
|
40
|
+
Dominoes []Dominoe `json:"input"`
|
41
|
+
Expected bool
|
42
|
+
}
|
43
|
+
|
44
|
+
//func (c OneCase)
|
45
|
+
// Template to generate list of test cases.
|
46
|
+
var tmpl = `package dominoes
|
47
|
+
|
48
|
+
{{.Header}}
|
49
|
+
|
50
|
+
var testCases = []struct {
|
51
|
+
description string
|
52
|
+
dominoes []Dominoe
|
53
|
+
valid bool // true => can chain, false => cannot chain
|
54
|
+
}{ {{range .J.Cases}}
|
55
|
+
{
|
56
|
+
{{printf "%q" .Description}},
|
57
|
+
[]Dominoe{ {{range .Dominoes}} {{printf "%v" .}}, {{end}} },
|
58
|
+
{{printf "%v" .Expected}},
|
59
|
+
}, {{end}}
|
60
|
+
}
|
61
|
+
`
|
@@ -0,0 +1,29 @@
|
|
1
|
+
## Implementation
|
2
|
+
|
3
|
+
Define a single Go func, MakeChain, which accepts a slice
|
4
|
+
of dominoes and attempts to construct a legal chain of dominoes.
|
5
|
+
|
6
|
+
MakeChain should have the following signature:
|
7
|
+
|
8
|
+
```
|
9
|
+
type Dominoe [2]int
|
10
|
+
|
11
|
+
func MakeChain(input []Dominoe) (chain []Dominoe, ok bool)
|
12
|
+
```
|
13
|
+
|
14
|
+
The 'ok' bool result indicates whether the given input dominoe list
|
15
|
+
could be arranged in a legal chain. An empty input list is considered legal,
|
16
|
+
and a single dominoe whose sides are the same is also considered legal.
|
17
|
+
|
18
|
+
The 'chain' result is a slice of zero or more dominoes
|
19
|
+
arranged in an order which shows the legal chain.
|
20
|
+
It is acceptable (and expected) that dominoes in 'input' may need
|
21
|
+
to be rotated so that each side matches their adjacent dominoe in the chain.
|
22
|
+
Dominoes at the beginning and the end of the chain must also match their outer side.
|
23
|
+
|
24
|
+
If the given input slice of dominoes cannot be arranged in a legal chain
|
25
|
+
MakeChain may return nil for the chain result, but must return false for the ok result.
|
26
|
+
|
27
|
+
Since there may be more than one legal chain arrangement for a given input list,
|
28
|
+
when ok is true, the test program will check the chain for validity only.
|
29
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Dominoes
|
2
|
+
|
3
|
+
Make a chain of dominoes.
|
4
|
+
|
5
|
+
Compute a way to order a given set of dominoes in such a way that they form a
|
6
|
+
correct domino chain (the dots on one half of a stone match the dots on the
|
7
|
+
neighbouring half of an adjacent stone) and that dots on the halfs of the stones
|
8
|
+
which don't have a neighbour (the first and last stone) match each other.
|
9
|
+
|
10
|
+
For example given the stones `[2|1]`, `[2|3]` and `[1|3]` you should compute something
|
11
|
+
like `[1|2] [2|3] [3|1]` or `[3|2] [2|1] [1|3]` or `[1|3] [3|2] [2|1]` etc, where the first and last numbers are the same.
|
12
|
+
|
13
|
+
For stones `[1|2]`, `[4|1]` and `[2|3]` the resulting chain is not valid: `[4|1] [1|2] [2|3]`'s first and last numbers are not the same. 4 != 3
|
14
|
+
|
15
|
+
Some test cases may use duplicate stones in a chain solution, assume that multiple Domino sets are being used.
|
16
|
+
|
17
|
+
## Implementation
|
18
|
+
|
19
|
+
Define a single Go func, MakeChain, which accepts a slice
|
20
|
+
of dominoes and attempts to construct a legal chain of dominoes.
|
21
|
+
|
22
|
+
MakeChain should have the following signature:
|
23
|
+
|
24
|
+
```
|
25
|
+
type Dominoe [2]int
|
26
|
+
|
27
|
+
func MakeChain(input []Dominoe) (chain []Dominoe, ok bool)
|
28
|
+
```
|
29
|
+
|
30
|
+
The 'ok' bool result indicates whether the given input dominoe list
|
31
|
+
could be arranged in a legal chain. An empty input list is considered legal,
|
32
|
+
and a single dominoe whose sides are the same is also considered legal.
|
33
|
+
|
34
|
+
The 'chain' result is a slice of zero or more dominoes
|
35
|
+
arranged in an order which shows the legal chain.
|
36
|
+
It is acceptable (and expected) that dominoes in 'input' may need
|
37
|
+
to be rotated so that each side matches their adjacent dominoe in the chain.
|
38
|
+
Dominoes at the beginning and the end of the chain must also match their outer side.
|
39
|
+
|
40
|
+
If the given input slice of dominoes cannot be arranged in a legal chain
|
41
|
+
MakeChain may return nil for the chain result, but must return false for the ok result.
|
42
|
+
|
43
|
+
Since there may be more than one legal chain arrangement for a given input list,
|
44
|
+
when ok is true, the test program will check the chain for validity only.
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
## Running the tests
|
49
|
+
|
50
|
+
To run the tests run the command `go test` from within the exercise directory.
|
51
|
+
|
52
|
+
If the test suite contains benchmarks, you can run these with the `-bench`
|
53
|
+
flag:
|
54
|
+
|
55
|
+
go test -bench .
|
56
|
+
|
57
|
+
Keep in mind that each reviewer will run benchmarks on a different machine, with
|
58
|
+
different specs, so the results from these benchmark tests may vary.
|
59
|
+
|
60
|
+
## Further information
|
61
|
+
|
62
|
+
For more detailed information about the Go track, including how to get help if
|
63
|
+
you're having trouble, please visit the exercism.io [Go language page](http://exercism.io/languages/go/about).
|
64
|
+
|
65
|
+
|
66
|
+
## Submitting Incomplete Solutions
|
67
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1,72 @@
|
|
1
|
+
package dominoes
|
2
|
+
|
3
|
+
// Source: exercism/problem-specifications
|
4
|
+
// Commit: b4ceaf4 Update dominoes canonical-data expected key (#823)
|
5
|
+
// Problem Specifications Version: 2.0.0
|
6
|
+
|
7
|
+
var testCases = []struct {
|
8
|
+
description string
|
9
|
+
dominoes []Dominoe
|
10
|
+
valid bool // true => can chain, false => cannot chain
|
11
|
+
}{
|
12
|
+
{
|
13
|
+
"empty input = empty output",
|
14
|
+
[]Dominoe{},
|
15
|
+
true,
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"singleton input = singleton output",
|
19
|
+
[]Dominoe{Dominoe{1, 1}},
|
20
|
+
true,
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"singleton that can't be chained",
|
24
|
+
[]Dominoe{Dominoe{1, 2}},
|
25
|
+
false,
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"three elements",
|
29
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{3, 1}, Dominoe{2, 3}},
|
30
|
+
true,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"can reverse dominoes",
|
34
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{1, 3}, Dominoe{2, 3}},
|
35
|
+
true,
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"can't be chained",
|
39
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{4, 1}, Dominoe{2, 3}},
|
40
|
+
false,
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"disconnected - simple",
|
44
|
+
[]Dominoe{Dominoe{1, 1}, Dominoe{2, 2}},
|
45
|
+
false,
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"disconnected - double loop",
|
49
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{2, 1}, Dominoe{3, 4}, Dominoe{4, 3}},
|
50
|
+
false,
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"disconnected - single isolated",
|
54
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{2, 3}, Dominoe{3, 1}, Dominoe{4, 4}},
|
55
|
+
false,
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"need backtrack",
|
59
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{2, 3}, Dominoe{3, 1}, Dominoe{2, 4}, Dominoe{2, 4}},
|
60
|
+
true,
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"separate loops",
|
64
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{2, 3}, Dominoe{3, 1}, Dominoe{1, 1}, Dominoe{2, 2}, Dominoe{3, 3}},
|
65
|
+
true,
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"nine elements",
|
69
|
+
[]Dominoe{Dominoe{1, 2}, Dominoe{5, 3}, Dominoe{3, 1}, Dominoe{1, 2}, Dominoe{2, 4}, Dominoe{1, 6}, Dominoe{2, 3}, Dominoe{3, 4}, Dominoe{5, 6}},
|
70
|
+
true,
|
71
|
+
},
|
72
|
+
}
|
@@ -0,0 +1,106 @@
|
|
1
|
+
package dominoes
|
2
|
+
|
3
|
+
import (
|
4
|
+
"errors"
|
5
|
+
"reflect"
|
6
|
+
"sort"
|
7
|
+
"testing"
|
8
|
+
)
|
9
|
+
|
10
|
+
func TestMakeChain(t *testing.T) {
|
11
|
+
for _, test := range testCases {
|
12
|
+
c, ok := MakeChain(test.dominoes)
|
13
|
+
if ok != test.valid {
|
14
|
+
t.Fatalf("FAIL: %s\nMakeChain(%v)\nExpected 'ok' result: %t Actual 'ok': %t",
|
15
|
+
test.description, test.dominoes, test.valid, ok)
|
16
|
+
}
|
17
|
+
if ok {
|
18
|
+
// There can be a variety of "valid" chains. Verify the chain is valid.
|
19
|
+
if err := verifyChain(test.dominoes, c); err != nil {
|
20
|
+
t.Fatalf("FAIL: %s\nVerifying chain failed with error: %v\ninput: %v\nchain: %v",
|
21
|
+
test.description, err, test.dominoes, c)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
t.Logf("PASS: %s", test.description)
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
var (
|
29
|
+
errWrongLengthChain = errors.New("wrong length chain")
|
30
|
+
errChainIsNotLegalAdj = errors.New("chain is not legal - adjacent mismatch")
|
31
|
+
errChainIsNotLegalEnd = errors.New("chain is not legal - ends mismatch")
|
32
|
+
errChainSetNotSameAsInputSet = errors.New("chain dominoes not same as input")
|
33
|
+
)
|
34
|
+
|
35
|
+
func verifyChain(input []Dominoe, chain []Dominoe) error {
|
36
|
+
if len(input) != len(chain) {
|
37
|
+
return errWrongLengthChain
|
38
|
+
}
|
39
|
+
|
40
|
+
switch len(input) {
|
41
|
+
case 0:
|
42
|
+
return nil
|
43
|
+
case 1:
|
44
|
+
if input[0] != chain[0] {
|
45
|
+
return errChainSetNotSameAsInputSet
|
46
|
+
}
|
47
|
+
return nil
|
48
|
+
}
|
49
|
+
|
50
|
+
// Check adjacent pairs.
|
51
|
+
for i := 0; i < len(chain)-1; i++ {
|
52
|
+
if chain[i][1] != chain[i+1][0] {
|
53
|
+
return errChainIsNotLegalAdj
|
54
|
+
}
|
55
|
+
}
|
56
|
+
// Check end dominoes.
|
57
|
+
if chain[0][0] != chain[len(chain)-1][1] {
|
58
|
+
return errChainIsNotLegalEnd
|
59
|
+
}
|
60
|
+
|
61
|
+
// Make copies of input and chain.
|
62
|
+
cinput := copyDominoes(input)
|
63
|
+
cchain := copyDominoes(chain)
|
64
|
+
|
65
|
+
sortDominoes(cinput)
|
66
|
+
sortDominoes(cchain)
|
67
|
+
|
68
|
+
// Compare for equality (same set in input and chain).
|
69
|
+
if !reflect.DeepEqual(cinput, cchain) {
|
70
|
+
return errChainSetNotSameAsInputSet
|
71
|
+
}
|
72
|
+
return nil
|
73
|
+
}
|
74
|
+
|
75
|
+
func copyDominoes(d []Dominoe) (c []Dominoe) {
|
76
|
+
c = make([]Dominoe, len(d))
|
77
|
+
// Put each dominoe in "canonical position" [a,b] where a <= b.
|
78
|
+
for i := range d {
|
79
|
+
c[i] = d[i]
|
80
|
+
if c[i][0] > c[i][1] {
|
81
|
+
c[i][0], c[i][1] = c[i][1], c[i][0]
|
82
|
+
}
|
83
|
+
}
|
84
|
+
return c
|
85
|
+
}
|
86
|
+
|
87
|
+
func sortDominoes(d []Dominoe) {
|
88
|
+
sort.Slice(d,
|
89
|
+
func(i, j int) bool {
|
90
|
+
if d[i][0] < d[j][0] {
|
91
|
+
return true
|
92
|
+
}
|
93
|
+
if d[i][0] > d[j][0] {
|
94
|
+
return false
|
95
|
+
}
|
96
|
+
return d[i][1] < d[j][1]
|
97
|
+
})
|
98
|
+
}
|
99
|
+
|
100
|
+
func BenchmarkMakeChain(b *testing.B) {
|
101
|
+
for i := 0; i < b.N; i++ {
|
102
|
+
for _, test := range testCases {
|
103
|
+
MakeChain(test.dominoes)
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
@@ -0,0 +1,146 @@
|
|
1
|
+
package dominoes
|
2
|
+
|
3
|
+
type Dominoe [2]int
|
4
|
+
|
5
|
+
// MakeChain returns a chain and true if the dominoes in input
|
6
|
+
// can be arranged in a legal chain; otherwise it returns nil, false.
|
7
|
+
func MakeChain(input []Dominoe) (chain []Dominoe, ok bool) {
|
8
|
+
switch len(input) {
|
9
|
+
case 0:
|
10
|
+
return []Dominoe{}, true
|
11
|
+
case 1:
|
12
|
+
// A single is legal if the ends match.
|
13
|
+
if input[0][0] == input[0][1] {
|
14
|
+
return input, true
|
15
|
+
}
|
16
|
+
return nil, false
|
17
|
+
}
|
18
|
+
chain, ok = buildDominoeChain(input)
|
19
|
+
if !ok {
|
20
|
+
return nil, false
|
21
|
+
}
|
22
|
+
return chain, true
|
23
|
+
}
|
24
|
+
|
25
|
+
// buildDominoeChain looks for a matching dominoe chain using the 2 or more dominoes in d.
|
26
|
+
func buildDominoeChain(d []Dominoe) ([]Dominoe, bool) {
|
27
|
+
permuteList := dominoePermutations(d, len(d))
|
28
|
+
for _, p := range permuteList {
|
29
|
+
chain, ok := arrangeChain(p)
|
30
|
+
if ok {
|
31
|
+
return chain, true
|
32
|
+
}
|
33
|
+
}
|
34
|
+
return nil, false
|
35
|
+
}
|
36
|
+
|
37
|
+
// arrangeChain uses the dominoes positionally in d[] attempting to make a matching chain;
|
38
|
+
// reversing of sides of any dominoe is permitted, but the order of dominoes in d remains
|
39
|
+
// the same left to right.
|
40
|
+
func arrangeChain(d []Dominoe) (chain []Dominoe, ok bool) {
|
41
|
+
// A good chain will match length of d, so preallocate the chain.
|
42
|
+
chain = make([]Dominoe, len(d))
|
43
|
+
n := 0
|
44
|
+
// Put first dominoe into the chain for starting point.
|
45
|
+
chain[n] = d[0]
|
46
|
+
// Loop through the remaining dominoes in d, attempting to add them into the chain,
|
47
|
+
// allowing a reverse of either single one in chain or the new dominoe t at each step.
|
48
|
+
for i := 1; i < len(d); i++ {
|
49
|
+
t := d[i]
|
50
|
+
last := chain[n]
|
51
|
+
if n == 0 && last[0] == t[0] {
|
52
|
+
// reverse first and only item in chain to match t, and add t to chain.
|
53
|
+
chain[n] = reverseDominoe(last)
|
54
|
+
chain[n+1] = t
|
55
|
+
} else if n == 0 && last[0] == t[1] {
|
56
|
+
// reverse only item in chain and t to match, and add reversed t to chain.
|
57
|
+
chain[n] = reverseDominoe(last)
|
58
|
+
chain[n+1] = reverseDominoe(t)
|
59
|
+
} else if last[1] == t[0] {
|
60
|
+
// add t as-is into chain.
|
61
|
+
chain[n+1] = t
|
62
|
+
} else if last[1] == t[1] {
|
63
|
+
// reverse t to match last one in chain, and add reversed t to chain.
|
64
|
+
chain[n+1] = reverseDominoe(t)
|
65
|
+
} else {
|
66
|
+
// no match for this chain configuration of d, even with swapping.
|
67
|
+
return nil, false
|
68
|
+
}
|
69
|
+
// chain is now filled in with one more dominoe.
|
70
|
+
n++
|
71
|
+
}
|
72
|
+
// Both ends of the chain must also match.
|
73
|
+
if chain[0][0] != chain[len(chain)-1][1] {
|
74
|
+
return nil, false
|
75
|
+
}
|
76
|
+
return chain, true
|
77
|
+
}
|
78
|
+
|
79
|
+
// reverseDominoe returns given Dominoe x, with its sides reversed/rotated.
|
80
|
+
func reverseDominoe(x Dominoe) Dominoe {
|
81
|
+
return Dominoe{x[1], x[0]}
|
82
|
+
}
|
83
|
+
|
84
|
+
// dominoePermutations returns a slice containing the r length permutations of the elements in iterable.
|
85
|
+
// The implementation is modeled after the Python itertools.permutations().
|
86
|
+
func dominoePermutations(iterable []Dominoe, r int) (perms [][]Dominoe) {
|
87
|
+
pool := iterable
|
88
|
+
n := len(pool)
|
89
|
+
|
90
|
+
if r > n {
|
91
|
+
return
|
92
|
+
}
|
93
|
+
|
94
|
+
indices := make([]int, n)
|
95
|
+
for i := range indices {
|
96
|
+
indices[i] = i
|
97
|
+
}
|
98
|
+
|
99
|
+
cycles := make([]int, r)
|
100
|
+
for i := range cycles {
|
101
|
+
cycles[i] = n - i
|
102
|
+
}
|
103
|
+
|
104
|
+
result := make([]Dominoe, r)
|
105
|
+
for i, el := range indices[:r] {
|
106
|
+
result[i] = pool[el]
|
107
|
+
}
|
108
|
+
|
109
|
+
p := make([]Dominoe, len(result))
|
110
|
+
copy(p, result)
|
111
|
+
perms = append(perms, p)
|
112
|
+
|
113
|
+
for n > 0 {
|
114
|
+
i := r - 1
|
115
|
+
for ; i >= 0; i-- {
|
116
|
+
cycles[i]--
|
117
|
+
if cycles[i] == 0 {
|
118
|
+
index := indices[i]
|
119
|
+
for j := i; j < n-1; j++ {
|
120
|
+
indices[j] = indices[j+1]
|
121
|
+
}
|
122
|
+
indices[n-1] = index
|
123
|
+
cycles[i] = n - i
|
124
|
+
} else {
|
125
|
+
j := cycles[i]
|
126
|
+
indices[i], indices[n-j] = indices[n-j], indices[i]
|
127
|
+
|
128
|
+
for k := i; k < r; k++ {
|
129
|
+
result[k] = pool[indices[k]]
|
130
|
+
}
|
131
|
+
|
132
|
+
p = make([]Dominoe, len(result))
|
133
|
+
copy(p, result)
|
134
|
+
perms = append(perms, p)
|
135
|
+
|
136
|
+
break
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
if i < 0 {
|
141
|
+
return
|
142
|
+
}
|
143
|
+
|
144
|
+
}
|
145
|
+
return
|
146
|
+
}
|