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.
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
+ }