trackler 2.0.8.15 → 2.0.8.16
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.
- checksums.yaml +4 -4
- data/common/exercises/clock/canonical-data.json +473 -425
- data/common/exercises/crypto-square/canonical-data.json +105 -95
- data/common/exercises/difference-of-squares/canonical-data.json +76 -62
- data/common/exercises/etl/canonical-data.json +67 -59
- data/common/exercises/meetup/canonical-data.json +859 -762
- data/common/exercises/meetup/description.md +13 -7
- data/common/exercises/minesweeper/canonical-data.json +21 -5
- data/common/exercises/nth-prime/canonical-data.json +23 -16
- data/common/exercises/nucleotide-count/canonical-data.json +49 -41
- data/common/exercises/ocr-numbers/canonical-data.json +204 -183
- data/common/exercises/pascals-triangle/canonical-data.json +38 -27
- data/common/exercises/queen-attack/canonical-data.json +125 -109
- data/common/exercises/rotational-cipher/canonical-data.json +1 -1
- data/common/exercises/triangle/canonical-data.json +103 -74
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/docs/TESTS.md +7 -1
- data/tracks/csharp/exercises/{exercises.sln → Exercises.All.sln} +0 -0
- data/tracks/csharp/exercises/Exercises.Default.sln +1433 -0
- data/tracks/csharp/exercises/Exercises.Refactoring.sln +61 -0
- data/tracks/csharp/exercises/acronym/AcronymTest.cs +35 -11
- data/tracks/csharp/exercises/parallel-letter-frequency/ParallelLetterFrequencyTest.cs +2 -2
- data/tracks/delphi/docs/TESTS.md +2 -2
- data/tracks/elixir/exercises/bowling/bowling.exs +1 -1
- data/tracks/fsharp/docs/LEARNING.md +2 -1
- data/tracks/go/config.json +7 -1
- data/tracks/go/exercises/prime-factors/{primefactors_test.go → prime_factors_test.go} +4 -1
- data/tracks/go/exercises/protein-translation/protein_translation_test.go +6 -6
- data/tracks/go/exercises/pythagorean-triplet/example.go +2 -0
- data/tracks/go/exercises/pythagorean-triplet/pythagorean_triplet_test.go +8 -0
- data/tracks/julia/README.md +2 -0
- data/tracks/julia/config.json +9 -0
- data/tracks/julia/exercises/rotational-cipher/HINTS.md +21 -0
- data/tracks/julia/exercises/rotational-cipher/example.jl +16 -0
- data/tracks/julia/exercises/rotational-cipher/rotational-cipher.jl +0 -0
- data/tracks/julia/exercises/rotational-cipher/runtests.jl +51 -0
- data/tracks/ocaml/config.json +5 -0
- data/tracks/ocaml/exercises/connect/.merlin +3 -0
- data/tracks/ocaml/exercises/connect/Makefile +15 -0
- data/tracks/ocaml/exercises/connect/connect.mli +4 -0
- data/tracks/ocaml/exercises/connect/example.ml +80 -0
- data/tracks/ocaml/exercises/connect/test.ml +121 -0
- data/tracks/ocaml/tools/test-generator/templates/connect/template.ml +23 -0
- data/tracks/python/exercises/all-your-base/all_your_base_test.py +2 -0
- data/tracks/python/exercises/luhn/example.py +5 -8
- data/tracks/python/exercises/luhn/luhn_test.py +34 -24
- data/tracks/ruby/README.md +138 -23
- metadata +16 -4
@@ -0,0 +1,121 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Connect
|
4
|
+
|
5
|
+
let show_player = function
|
6
|
+
| Some X -> "X"
|
7
|
+
| Some O -> "O"
|
8
|
+
| None -> "None"
|
9
|
+
|
10
|
+
let ae exp got = assert_equal ~printer:show_player exp got
|
11
|
+
|
12
|
+
let tests = [
|
13
|
+
"an empty board has no winner" >::(fun _ctxt ->
|
14
|
+
let board = [
|
15
|
+
". . . . .";
|
16
|
+
" . . . . .";
|
17
|
+
" . . . . .";
|
18
|
+
" . . . . .";
|
19
|
+
" . . . . ."
|
20
|
+
]
|
21
|
+
in
|
22
|
+
ae None (connect board)
|
23
|
+
);
|
24
|
+
"X can win on a 1x1 board" >::(fun _ctxt ->
|
25
|
+
let board = [
|
26
|
+
"X"
|
27
|
+
]
|
28
|
+
in
|
29
|
+
ae (Some X) (connect board)
|
30
|
+
);
|
31
|
+
"O can win on a 1x1 board" >::(fun _ctxt ->
|
32
|
+
let board = [
|
33
|
+
"O"
|
34
|
+
]
|
35
|
+
in
|
36
|
+
ae (Some O) (connect board)
|
37
|
+
);
|
38
|
+
"only edges does not make a winner" >::(fun _ctxt ->
|
39
|
+
let board = [
|
40
|
+
"O O O X";
|
41
|
+
" X . . X";
|
42
|
+
" X . . X";
|
43
|
+
" X O O O"
|
44
|
+
]
|
45
|
+
in
|
46
|
+
ae None (connect board)
|
47
|
+
);
|
48
|
+
"illegal diagonal does not make a winner" >::(fun _ctxt ->
|
49
|
+
let board = [
|
50
|
+
"X O . .";
|
51
|
+
" O X X X";
|
52
|
+
" O X O .";
|
53
|
+
" . O X .";
|
54
|
+
" X X O O"
|
55
|
+
]
|
56
|
+
in
|
57
|
+
ae None (connect board)
|
58
|
+
);
|
59
|
+
"nobody wins crossing adjacent angles" >::(fun _ctxt ->
|
60
|
+
let board = [
|
61
|
+
"X . . .";
|
62
|
+
" . X O .";
|
63
|
+
" O . X O";
|
64
|
+
" . O . X";
|
65
|
+
" . . O ."
|
66
|
+
]
|
67
|
+
in
|
68
|
+
ae None (connect board)
|
69
|
+
);
|
70
|
+
"X wins crossing from left to right" >::(fun _ctxt ->
|
71
|
+
let board = [
|
72
|
+
". O . .";
|
73
|
+
" O X X X";
|
74
|
+
" O X O .";
|
75
|
+
" X X O X";
|
76
|
+
" . O X ."
|
77
|
+
]
|
78
|
+
in
|
79
|
+
ae (Some X) (connect board)
|
80
|
+
);
|
81
|
+
"O wins crossing from top to bottom" >::(fun _ctxt ->
|
82
|
+
let board = [
|
83
|
+
". O . .";
|
84
|
+
" O X X X";
|
85
|
+
" O O O .";
|
86
|
+
" X X O X";
|
87
|
+
" . O X ."
|
88
|
+
]
|
89
|
+
in
|
90
|
+
ae (Some O) (connect board)
|
91
|
+
);
|
92
|
+
"X wins using a convoluted path" >::(fun _ctxt ->
|
93
|
+
let board = [
|
94
|
+
". X X . .";
|
95
|
+
" X . X . X";
|
96
|
+
" . X . X .";
|
97
|
+
" . X X . .";
|
98
|
+
" O O O O O"
|
99
|
+
]
|
100
|
+
in
|
101
|
+
ae (Some X) (connect board)
|
102
|
+
);
|
103
|
+
"X wins using a spiral path" >::(fun _ctxt ->
|
104
|
+
let board = [
|
105
|
+
"O X X X X X X X X";
|
106
|
+
" O X O O O O O O O";
|
107
|
+
" O X O X X X X X O";
|
108
|
+
" O X O X O O O X O";
|
109
|
+
" O X O X X X O X O";
|
110
|
+
" O X O O O X O X O";
|
111
|
+
" O X X X X X O X O";
|
112
|
+
" O O O O O O O X O";
|
113
|
+
" X X X X X X X X O"
|
114
|
+
]
|
115
|
+
in
|
116
|
+
ae (Some X) (connect board)
|
117
|
+
);
|
118
|
+
]
|
119
|
+
|
120
|
+
let () =
|
121
|
+
run_test_tt_main ("connect tests" >::: tests)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Connect
|
4
|
+
|
5
|
+
let show_player = function
|
6
|
+
| Some X -> "X"
|
7
|
+
| Some O -> "O"
|
8
|
+
| None -> "None"
|
9
|
+
|
10
|
+
let ae exp got = assert_equal ~printer:show_player exp got
|
11
|
+
|
12
|
+
let tests = [
|
13
|
+
(* TEST
|
14
|
+
"$description" >::(fun _ctxt ->
|
15
|
+
let board = $board
|
16
|
+
in
|
17
|
+
ae $expected (connect board)
|
18
|
+
);
|
19
|
+
END TEST *)
|
20
|
+
]
|
21
|
+
|
22
|
+
let () =
|
23
|
+
run_test_tt_main ("connect tests" >::: tests)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
class Luhn(object):
|
2
|
-
def __init__(self,
|
3
|
-
self.
|
2
|
+
def __init__(self, string):
|
3
|
+
self.string = string.replace(" ", "")
|
4
4
|
|
5
5
|
def addends(self):
|
6
6
|
def luhn_transform(n):
|
7
7
|
return (2 * n - 9) if (n > 4) else (2 * n)
|
8
|
-
old_digits = [int(d) for d in str(self.
|
8
|
+
old_digits = [int(d) for d in str(self.string)]
|
9
9
|
return [(luhn_transform(n) if (i % 2 == 0) else n)
|
10
10
|
for i, n in enumerate(old_digits, start=len(old_digits) % 2)]
|
11
11
|
|
@@ -13,9 +13,6 @@ class Luhn(object):
|
|
13
13
|
return sum(self.addends())
|
14
14
|
|
15
15
|
def is_valid(self):
|
16
|
+
if len(self.string) <= 1 or not self.string.isdigit():
|
17
|
+
return False
|
16
18
|
return self.checksum() % 10 == 0
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def create(n):
|
20
|
-
diff = (10 - Luhn(n * 10).checksum()) % 10
|
21
|
-
return 10 * n + diff
|
@@ -1,46 +1,56 @@
|
|
1
|
-
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
2
3
|
import unittest
|
3
4
|
|
4
5
|
from luhn import Luhn
|
5
6
|
|
6
7
|
|
7
8
|
class LuhnTests(unittest.TestCase):
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def test_single_digit_strings_can_not_be_valid(self):
|
10
|
+
self.assertFalse(Luhn("1").is_valid())
|
11
|
+
|
12
|
+
def test_a_single_zero_is_invalid(self):
|
13
|
+
self.assertFalse(Luhn("0").is_valid())
|
14
|
+
|
15
|
+
def test_a_simple_valid_SIN_that_remains_valid_if_reversed(self):
|
16
|
+
self.assertTrue(Luhn("059").is_valid())
|
17
|
+
|
18
|
+
def test_a_simple_valid_SIN_that_becomes_invalid_if_reversed(self):
|
19
|
+
self.assertTrue(Luhn("59").is_valid())
|
20
|
+
|
21
|
+
def test_a_valid_Canadian_SIN(self):
|
22
|
+
self.assertTrue(Luhn("055 444 285").is_valid())
|
12
23
|
|
13
|
-
def
|
14
|
-
|
15
|
-
self.assertEqual(Counter([7, 6, 6, 1]),
|
16
|
-
Counter(Luhn(8631).addends()))
|
24
|
+
def test_invalid_Canadian_SIN(self):
|
25
|
+
self.assertFalse(Luhn("055 444 286").is_valid())
|
17
26
|
|
18
|
-
def
|
19
|
-
self.
|
27
|
+
def test_invalid_credit_card(self):
|
28
|
+
self.assertFalse(Luhn("8273 1232 7352 0569").is_valid())
|
20
29
|
|
21
|
-
def
|
22
|
-
self.
|
30
|
+
def test_valid_strings_with_a_non_digit_included_become_invalid(self):
|
31
|
+
self.assertFalse(Luhn("055a 444 285").is_valid())
|
23
32
|
|
24
|
-
def
|
25
|
-
self.assertFalse(Luhn(
|
33
|
+
def test_valid_strings_with_punctuation_included_become_invalid(self):
|
34
|
+
self.assertFalse(Luhn("055-444-285").is_valid())
|
26
35
|
|
27
|
-
def
|
28
|
-
self.
|
36
|
+
def test_valid_strings_with_symbols_included_become_invalid(self):
|
37
|
+
self.assertFalse(Luhn("055£ 444$ 285").is_valid())
|
29
38
|
|
30
|
-
def
|
31
|
-
self.
|
39
|
+
def test_single_zero_with_space_is_invalid(self):
|
40
|
+
self.assertFalse(Luhn("0").is_valid())
|
32
41
|
|
33
|
-
def
|
34
|
-
self.
|
42
|
+
def test_more_than_a_single_zero_is_valid(self):
|
43
|
+
self.assertTrue(Luhn("0000 0").is_valid())
|
35
44
|
|
36
|
-
def
|
37
|
-
self.
|
45
|
+
def test_input_digit_9_is_correctly_converted_to_output_digit_9(self):
|
46
|
+
self.assertTrue(Luhn("091").is_valid())
|
38
47
|
|
39
48
|
def test_is_valid_can_be_called_repeatedly(self):
|
49
|
+
# Additional track specific test case
|
40
50
|
# This test was added, because we saw many implementations
|
41
51
|
# in which the first call to is_valid() worked, but the
|
42
52
|
# second call failed().
|
43
|
-
number = Luhn(
|
53
|
+
number = Luhn("055 444 285")
|
44
54
|
self.assertTrue(number.is_valid())
|
45
55
|
self.assertTrue(number.is_valid())
|
46
56
|
|
data/tracks/ruby/README.md
CHANGED
@@ -65,11 +65,17 @@ Note that flags which have an attached value, like above, must take the form
|
|
65
65
|
### Generated Test Suites
|
66
66
|
|
67
67
|
If you find an `example.tt` file in a problem directory, then the test suite is
|
68
|
-
generated from shared data
|
69
|
-
|
68
|
+
generated from shared data, which can be found in the exercise definition in the [x-common][]
|
69
|
+
repository.
|
70
70
|
|
71
|
-
|
72
|
-
|
71
|
+
Typically you will want to do one of the following:
|
72
|
+
|
73
|
+
* [Regenerate the test suite](#regenerating-an-exercise) based on updated canonical data
|
74
|
+
* [Make changes to a generated exercise](#changing-a-generated-exercise)
|
75
|
+
* [Implement a new generator](#implementing-a-generator)
|
76
|
+
|
77
|
+
Generated exercises depend on the [the shared metadata][x-common], which must be
|
78
|
+
cloned to the same directory that contains your clone of the xruby repository:
|
73
79
|
|
74
80
|
```
|
75
81
|
tree -L 1 ~/code/exercism
|
@@ -77,38 +83,145 @@ tree -L 1 ~/code/exercism
|
|
77
83
|
└── xruby
|
78
84
|
```
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
86
|
+
#### Regenerating an Exercise
|
87
|
+
|
88
|
+
From within the xruby directory, run the following command, where $PROBLEM is the slug
|
89
|
+
of the exercise, e.g. `clock` or `atbash-cipher`:
|
90
|
+
|
91
|
+
```
|
92
|
+
bin/generate $PROBLEM
|
93
|
+
```
|
94
|
+
|
95
|
+
#### Changing a Generated Exercise
|
96
|
+
|
97
|
+
The `$PROBLEM/$PROBLEM_test.rb` will never be edited directly.
|
98
|
+
|
99
|
+
There are two reasons why a test suite might change:
|
100
|
+
|
101
|
+
1. the tests are wrong (an incorrect expectation, a missing edge case, etc)
|
102
|
+
1. there might be issues with the style or boilerplate
|
103
|
+
|
104
|
+
In the first case, the changes need to be made to the `canonical-data.json` file for
|
105
|
+
the exercise, which lives in the x-common repository.
|
106
|
+
|
107
|
+
```
|
108
|
+
../x-common/exercises/$PROBLEM/
|
109
|
+
├── canonical-data.json
|
110
|
+
├── description.md
|
111
|
+
└── metadata.yml
|
112
|
+
```
|
113
|
+
|
114
|
+
This change will need to be submitted as a pull request to the x-common repository. This pull
|
115
|
+
request needs to be merged before you can regenerate the exercise.
|
85
116
|
|
86
|
-
|
117
|
+
Changes that don't have to do directly with the test inputs and outputs, will either need to be
|
118
|
+
made to `exercises/$PROBLEM/example.tt` or `lib/$PROBLEM_cases.rb`. Then you can regenerate the
|
119
|
+
exercise with `bin/generate $PROBLEM`.
|
87
120
|
|
88
|
-
|
89
|
-
as the following files in the xruby repository:
|
121
|
+
#### Implementing a Generator
|
90
122
|
|
91
|
-
|
92
|
-
1. `bin/generate hamming`
|
93
|
-
1. `lib/hamming.rb`
|
94
|
-
1. `lib/generator.rb`
|
123
|
+
You will need to implement three files to create a generator:
|
95
124
|
|
96
|
-
|
97
|
-
|
125
|
+
1. `exercises/$PROBLEM/example.tt` - the Erb template for the test file, `$PROBLEM_test.rb`.
|
126
|
+
1. `exercises/$PROBLEM/.meta/.version` - used to keep track of the version of the test files as the data changes.
|
127
|
+
1. `lib/$PROBLEM_cases.rb` - the logic for turning the data into tests.
|
98
128
|
|
99
|
-
|
100
|
-
`example.tt`.
|
129
|
+
You will not need to touch the top-level script, `bin/generate`.
|
101
130
|
|
102
|
-
|
131
|
+
The `bin/generate` command relies on some common logic implemented in `lib/generator.rb`.
|
132
|
+
You probably won't need to touch that, either.
|
103
133
|
|
104
|
-
|
134
|
+
The `lib/$PROBLEM_cases.rb` file should contain a small class that wraps the JSON for a single test case:
|
105
135
|
|
106
|
-
|
136
|
+
```
|
137
|
+
require 'exercise_cases'
|
138
|
+
|
139
|
+
class ProblemNameCase < OpenStruct
|
140
|
+
def test_name
|
141
|
+
'test_%s' % description.gsub(/[ -]/, '_')
|
142
|
+
end
|
143
|
+
|
144
|
+
def workload
|
145
|
+
# implement main logic of test here
|
146
|
+
end
|
147
|
+
|
148
|
+
def skipped
|
149
|
+
index.zero? ? '# skip' : 'skip'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
Instead of `ProblemName` use the name of the actual problem. This is important, since
|
155
|
+
the generator script will infer the name of the class from the argument that is passed.
|
156
|
+
|
157
|
+
This class must implement the following methods:
|
107
158
|
|
108
159
|
- `test_name` - Returns the name of the test (i.e `test_one_equals_one`)
|
109
160
|
- `workload` - Returns the main syntax for the test. This will vary depending on the test generator and its underlying implementation
|
110
161
|
- `skipped` - Returns skip syntax (i.e. `skip` or `# skip`)
|
111
162
|
|
163
|
+
Beyond that, you can implement any helper methods that you need.
|
164
|
+
|
165
|
+
Below this class, implement a small loop that will generate all the test cases by reading the
|
166
|
+
`canonical-data.json` file, and looping through the test cases.
|
167
|
+
|
168
|
+
You will need to adjust the logic to match the structure of the canonical data.
|
169
|
+
|
170
|
+
For example, if there is a single top-level key named "cases", then you can loop through
|
171
|
+
them as follows:
|
172
|
+
|
173
|
+
```
|
174
|
+
ProblemNameCases = proc do |data|
|
175
|
+
JSON.parse(data)['cases'].map.with_index do |row, i|
|
176
|
+
ProblemNameCase.new(row.merge('index' => i))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
If there are multiple sections, then you will need to loop through the sections, and then
|
182
|
+
loop through each of the cases in an inner loop:
|
183
|
+
|
184
|
+
```
|
185
|
+
ProblemNameCases = proc do |data|
|
186
|
+
i = 0
|
187
|
+
json = JSON.parse(data)
|
188
|
+
cases = []
|
189
|
+
%w(section1 section2 etc).each do |section|
|
190
|
+
json[section]['cases'].each do |row|
|
191
|
+
row = row.merge(row.merge('index' => i, 'section' => section))
|
192
|
+
cases << ProblemNameCase.new(row)
|
193
|
+
i += 1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
cases
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
Finally, you need to create a text template, `example.tt`, as the bases for the test suite.
|
201
|
+
|
202
|
+
Start with the following boilerplate, and adjust as necessary:
|
203
|
+
|
204
|
+
```
|
205
|
+
#!/usr/bin/env ruby
|
206
|
+
gem 'minitest', '>= 5.0.0'
|
207
|
+
require 'minitest/autorun'
|
208
|
+
require_relative '$PROBLEM'
|
209
|
+
|
210
|
+
# Common test data version: <%= abbreviated_commit_hash %>
|
211
|
+
class ProblemNameTest < Minitest::Test<% test_cases.each do |test_case| %>
|
212
|
+
def <%= test_case.name %>
|
213
|
+
<%= test_case.skipped %>
|
214
|
+
assert_equal <%= test_case.expected %>, <%= test_case.work_load %>
|
215
|
+
end
|
216
|
+
<% end %>
|
217
|
+
<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
|
218
|
+
def test_bookkeeping
|
219
|
+
skip
|
220
|
+
assert_equal <%= version %>, BookKeeping::VERSION
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
112
225
|
## Pull Requests
|
113
226
|
|
114
227
|
We welcome pull requests that provide fixes to existing test suites (missing
|
@@ -168,3 +281,5 @@ Copyright (c) 2014 Katrina Owen, _@kytrinyx.com
|
|
168
281
|
|
169
282
|
## Ruby icon
|
170
283
|
The Ruby icon is the Vienna.rb logo, and is used with permission. Thanks Floor Dress :)
|
284
|
+
|
285
|
+
[x-common]: https://github.com/exercism/x-common
|