trackler 2.2.1.77 → 2.2.1.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/transpose/canonical-data.json +94 -116
  4. data/tracks/bash/config/maintainers.json +11 -1
  5. data/tracks/clojure/config.json +10 -2
  6. data/tracks/clojure/exercises/reverse-string/README.md +14 -0
  7. data/tracks/clojure/exercises/reverse-string/project.clj +4 -0
  8. data/tracks/clojure/exercises/reverse-string/src/example.clj +5 -0
  9. data/tracks/clojure/exercises/reverse-string/src/reverse_string.clj +5 -0
  10. data/tracks/clojure/exercises/reverse-string/test/reverse_string_test.clj +18 -0
  11. data/tracks/clojure/exercises/say/README.md +70 -0
  12. data/tracks/clojure/exercises/{two-fer → say}/project.clj +3 -3
  13. data/tracks/clojure/exercises/say/src/example.clj +9 -0
  14. data/tracks/clojure/exercises/say/src/say.clj +5 -0
  15. data/tracks/clojure/exercises/say/test/say_test.clj +48 -0
  16. data/tracks/coffeescript/docs/ABOUT.md +4 -9
  17. data/tracks/coffeescript/docs/INSTALLATION.md +2 -2
  18. data/tracks/erlang/exercises/pangram/rebar.config +1 -1
  19. data/tracks/erlang/exercises/pangram/src/example.erl +2 -3
  20. data/tracks/erlang/exercises/pangram/test/pangram_tests.erl +12 -13
  21. data/tracks/fsharp/exercises/grep/Example.fs +16 -20
  22. data/tracks/fsharp/exercises/grep/GrepTest.fs +154 -233
  23. data/tracks/fsharp/generators/Common.fs +7 -2
  24. data/tracks/fsharp/generators/Exercise.fs +51 -16
  25. data/tracks/fsharp/generators/Formatting.fs +13 -6
  26. data/tracks/fsharp/generators/Generators.fs +44 -14
  27. data/tracks/fsharp/generators/Rendering.fs +8 -1
  28. data/tracks/fsharp/generators/Templates/Generators/_GrepSetup.liquid +37 -0
  29. data/tracks/fsharp/generators/Templates/_TestClass.liquid +9 -3
  30. data/tracks/fsharp/generators/Templates/{_TestMethod.liquid → _TestFunction.liquid} +0 -0
  31. data/tracks/fsharp/generators/Templates/{_TestMethodBody.liquid → _TestFunctionBody.liquid} +0 -0
  32. data/tracks/fsharp/generators/Templates/_TestMember.liquid +3 -0
  33. data/tracks/fsharp/generators/Templates/_TestMemberBody.liquid +4 -0
  34. data/tracks/fsharp/generators/Templates/_TestModule.liquid +17 -0
  35. data/tracks/go/config.json +24 -0
  36. data/tracks/go/exercises/acronym/.meta/gen.go +5 -3
  37. data/tracks/go/exercises/acronym/acronym.go +0 -2
  38. data/tracks/go/exercises/acronym/cases_test.go +2 -2
  39. data/tracks/go/exercises/acronym/example.go +2 -3
  40. data/tracks/go/exercises/all-your-base/.meta/gen.go +9 -7
  41. data/tracks/go/exercises/all-your-base/cases_test.go +2 -2
  42. data/tracks/go/exercises/allergies/.meta/gen.go +10 -6
  43. data/tracks/go/exercises/allergies/cases_test.go +2 -2
  44. data/tracks/go/exercises/anagram/.meta/gen.go +7 -5
  45. data/tracks/go/exercises/anagram/cases_test.go +2 -2
  46. data/tracks/go/exercises/armstrong-numbers/.meta/gen.go +54 -0
  47. data/tracks/go/exercises/armstrong-numbers/armstrong_test.go +12 -0
  48. data/tracks/go/exercises/armstrong-numbers/cases_test.go +52 -0
  49. data/tracks/go/exercises/armstrong-numbers/example.go +24 -0
  50. data/tracks/go/exercises/binary-search/.meta/gen.go +7 -5
  51. data/tracks/go/exercises/binary-search/cases_test.go +2 -2
  52. data/tracks/go/exercises/book-store/.meta/gen.go +5 -3
  53. data/tracks/go/exercises/book-store/cases_test.go +2 -2
  54. data/tracks/go/exercises/connect/example.go +4 -5
  55. data/tracks/go/exercises/dominoes/.meta/gen.go +61 -0
  56. data/tracks/go/exercises/dominoes/.meta/hints.md +29 -0
  57. data/tracks/go/exercises/dominoes/README.md +67 -0
  58. data/tracks/go/exercises/dominoes/cases_test.go +72 -0
  59. data/tracks/go/exercises/dominoes/dominoes_test.go +106 -0
  60. data/tracks/go/exercises/dominoes/example.go +146 -0
  61. data/tracks/go/exercises/ledger/example.go +1 -1
  62. data/tracks/groovy/.gitignore +3 -0
  63. data/tracks/groovy/.travis.yml +10 -1
  64. data/tracks/groovy/bin/prepeare_exercise_builds.groovy +77 -0
  65. data/tracks/groovy/build.gradle +25 -0
  66. data/tracks/groovy/exercises/difference-of-squares/Example.groovy +3 -3
  67. data/tracks/groovy/exercises/gigasecond/GigasecondSpec.groovy +3 -3
  68. data/tracks/groovy/exercises/linked-list/{DoubleLinkedList.groovy → LinkedList.groovy} +0 -0
  69. data/tracks/groovy/exercises/linked-list/{DoubleLinkedListSpec.groovy → LinkedListSpec.groovy} +0 -0
  70. data/tracks/groovy/exercises/linked-list/README.md +1 -1
  71. data/tracks/groovy/gradle/wrapper/gradle-wrapper.jar +0 -0
  72. data/tracks/groovy/gradle/wrapper/gradle-wrapper.properties +6 -0
  73. data/tracks/groovy/gradlew +172 -0
  74. data/tracks/groovy/gradlew.bat +84 -0
  75. data/tracks/groovy/settings.gradle +9 -0
  76. data/tracks/ocaml/exercises/acronym/.merlin +1 -1
  77. data/tracks/ocaml/exercises/all-your-base/.merlin +1 -1
  78. data/tracks/ocaml/exercises/anagram/.merlin +1 -1
  79. data/tracks/ocaml/exercises/atbash-cipher/.merlin +1 -1
  80. data/tracks/ocaml/exercises/beer-song/.merlin +1 -1
  81. data/tracks/ocaml/exercises/binary-search/.merlin +1 -1
  82. data/tracks/ocaml/exercises/bob/.merlin +1 -1
  83. data/tracks/ocaml/exercises/bowling/.merlin +1 -1
  84. data/tracks/ocaml/exercises/change/.merlin +1 -1
  85. data/tracks/ocaml/exercises/difference-of-squares/.merlin +1 -1
  86. data/tracks/ocaml/exercises/etl/.merlin +1 -1
  87. data/tracks/ocaml/exercises/forth/.merlin +1 -1
  88. data/tracks/ocaml/exercises/grade-school/.merlin +1 -1
  89. data/tracks/ocaml/exercises/hamming/.merlin +1 -1
  90. data/tracks/ocaml/exercises/hangman/.merlin +1 -1
  91. data/tracks/ocaml/exercises/hello-world/.merlin +1 -1
  92. data/tracks/ocaml/exercises/hexadecimal/.merlin +1 -1
  93. data/tracks/ocaml/exercises/leap/.merlin +1 -1
  94. data/tracks/ocaml/exercises/list-ops/.merlin +1 -1
  95. data/tracks/ocaml/exercises/luhn/.merlin +1 -1
  96. data/tracks/ocaml/exercises/meetup/.merlin +1 -1
  97. data/tracks/ocaml/exercises/minesweeper/.merlin +1 -1
  98. data/tracks/ocaml/exercises/nucleotide-count/.merlin +1 -1
  99. data/tracks/ocaml/exercises/phone-number/.merlin +1 -1
  100. data/tracks/ocaml/exercises/prime-factors/.merlin +1 -1
  101. data/tracks/ocaml/exercises/raindrops/.merlin +1 -1
  102. data/tracks/ocaml/exercises/react/.merlin +1 -1
  103. data/tracks/ocaml/exercises/rectangles/.merlin +1 -1
  104. data/tracks/ocaml/exercises/rna-transcription/.merlin +1 -1
  105. data/tracks/ocaml/exercises/run-length-encoding/.merlin +1 -1
  106. data/tracks/ocaml/exercises/say/.merlin +1 -1
  107. data/tracks/ocaml/exercises/space-age/.merlin +1 -1
  108. data/tracks/ocaml/exercises/triangle/.merlin +1 -1
  109. data/tracks/ocaml/exercises/word-count/.merlin +1 -1
  110. data/tracks/ocaml/exercises/zipper/.merlin +1 -1
  111. data/tracks/typescript/config/maintainers.json +2 -2
  112. metadata +37 -12
  113. data/tracks/clojure/exercises/two-fer/README.md +0 -19
  114. data/tracks/clojure/exercises/two-fer/src/example.clj +0 -5
  115. data/tracks/clojure/exercises/two-fer/src/two_fer.clj +0 -5
  116. data/tracks/clojure/exercises/two-fer/test/two_fer_test.clj +0 -12
  117. 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
- Basket []int
35
- Expected float64
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: a636903 Update description per @insti comments
5
- // Problem Specifications Version: 1.1.0
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 := int(len(lines))
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 := int(len(lines[0]))
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
- board := board{
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
+ }