trackler 1.0.4.0 → 1.0.4.1

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/common/CONTRIBUTING.md +17 -2
  3. data/common/exercises/anagram/canonical-data.json +0 -14
  4. data/common/exercises/pig-latin/canonical-data.json +6 -1
  5. data/common/exercises/raindrops/description.md +4 -4
  6. data/common/exercises/raindrops/metadata.yml +1 -1
  7. data/common/exercises/roman-numerals/metadata.yml +1 -1
  8. data/lib/trackler/version.rb +1 -1
  9. data/tracks/c/config.json +12 -1
  10. data/tracks/c/exercises/roman-numerals/makefile +16 -0
  11. data/tracks/c/exercises/roman-numerals/src/example.c +43 -0
  12. data/tracks/c/exercises/roman-numerals/src/example.h +6 -0
  13. data/tracks/c/exercises/roman-numerals/test/test_roman_numerals.c +126 -0
  14. data/tracks/c/exercises/roman-numerals/test/vendor/unity.c +1300 -0
  15. data/tracks/c/exercises/roman-numerals/test/vendor/unity.h +274 -0
  16. data/tracks/c/exercises/roman-numerals/test/vendor/unity_internals.h +701 -0
  17. data/tracks/clojure/exercises/flatten-array/project.clj +4 -0
  18. data/tracks/csharp/exercises/protein-translation/ProteinTranslationTest.cs +1 -1
  19. data/tracks/elixir/exercises/bowling/bowling_test.exs +157 -62
  20. data/tracks/elixir/exercises/bowling/example.exs +33 -4
  21. data/tracks/erlang/config.json +176 -36
  22. data/tracks/go/.travis.yml +7 -1
  23. data/tracks/go/bin/test-without-stubs +29 -0
  24. data/tracks/go/exercises/robot-simulator/defs.go +1 -1
  25. data/tracks/go/exercises/variable-length-quantity/variable_length_quantity_test.go +31 -25
  26. data/tracks/haskell/.travis.yml +9 -5
  27. data/tracks/haskell/config.json +127 -121
  28. data/tracks/haskell/exercises/leap/src/LeapYear.hs +1 -1
  29. data/tracks/haskell/exercises/space-age/HINTS.md +14 -2
  30. data/tracks/haskell/exercises/space-age/{src/Example.hs → examples/success-double/SpaceAge.hs} +0 -0
  31. data/tracks/haskell/exercises/space-age/examples/success-double/package.yaml +19 -0
  32. data/tracks/haskell/exercises/space-age/examples/success-rational/SpaceAge.hs +24 -0
  33. data/tracks/haskell/exercises/space-age/examples/success-rational/package.yaml +19 -0
  34. data/tracks/haskell/exercises/space-age/src/SpaceAge.hs +4 -1
  35. data/tracks/haskell/exercises/space-age/test/Tests.hs +12 -5
  36. data/tracks/haskell/exercises/word-count/HINTS.md +21 -0
  37. data/tracks/haskell/exercises/word-count/examples/success-newtype/WordCount.hs +31 -0
  38. data/tracks/haskell/exercises/word-count/examples/success-newtype/package.yaml +21 -0
  39. data/tracks/haskell/exercises/word-count/examples/success-simple/WordCount.hs +13 -0
  40. data/tracks/haskell/exercises/word-count/examples/success-simple/package.yaml +20 -0
  41. data/tracks/haskell/exercises/word-count/package.yaml +0 -2
  42. data/tracks/haskell/exercises/word-count/src/WordCount.hs +2 -1
  43. data/tracks/haskell/exercises/word-count/test/Tests.hs +15 -6
  44. data/tracks/haskell/exercises/zebra-puzzle/package.yaml +19 -0
  45. data/tracks/haskell/exercises/zebra-puzzle/src/Example.hs +113 -0
  46. data/tracks/haskell/exercises/zebra-puzzle/src/ZebraPuzzle.hs +12 -0
  47. data/tracks/haskell/exercises/zebra-puzzle/stack.yaml +1 -0
  48. data/tracks/haskell/exercises/zebra-puzzle/test/Tests.hs +13 -0
  49. data/tracks/javascript/exercises/beer-song/beer-song.spec.js +5 -0
  50. data/tracks/lisp/config.json +147 -0
  51. data/tracks/ocaml/config.json +12 -0
  52. data/tracks/ocaml/exercises/atbash-cipher/.merlin +5 -0
  53. data/tracks/ocaml/exercises/atbash-cipher/HINTS.md +17 -0
  54. data/tracks/ocaml/exercises/atbash-cipher/Makefile +11 -0
  55. data/tracks/ocaml/exercises/atbash-cipher/atbash_cipher.mli +5 -0
  56. data/tracks/ocaml/exercises/atbash-cipher/example.ml +47 -0
  57. data/tracks/ocaml/exercises/atbash-cipher/test.ml +37 -0
  58. data/tracks/ocaml/exercises/meetup/.merlin +5 -0
  59. data/tracks/ocaml/exercises/meetup/Makefile +11 -0
  60. data/tracks/ocaml/exercises/meetup/example.ml +63 -0
  61. data/tracks/ocaml/exercises/meetup/meetup.mli +7 -0
  62. data/tracks/ocaml/exercises/meetup/test.ml +786 -0
  63. data/tracks/php/exercises/bowling/bowling_test.php +233 -10
  64. data/tracks/php/exercises/bowling/example.php +49 -6
  65. data/tracks/python/.travis.yml +5 -0
  66. data/tracks/python/config.json +33 -1
  67. data/tracks/python/exercises/flatten-array/example.py +18 -0
  68. data/tracks/python/exercises/flatten-array/flatten_array_test.py +40 -0
  69. data/tracks/python/exercises/robot-name/robot_name_test.py +8 -2
  70. data/tracks/r/config.json +32 -8
  71. data/tracks/ruby/bin/enable-executable +9 -5
  72. data/tracks/ruby/bin/executable-tests-check +7 -3
  73. data/tracks/ruby/bin/local-status-check +4 -4
  74. data/tracks/ruby/config.json +8 -1
  75. data/tracks/ruby/exercises/acronym/acronym_test.rb +0 -1
  76. data/tracks/ruby/exercises/anagram/.version +1 -0
  77. data/tracks/ruby/exercises/anagram/anagram_test.rb +101 -31
  78. data/tracks/ruby/exercises/anagram/example.rb +4 -0
  79. data/tracks/ruby/exercises/anagram/example.tt +19 -0
  80. data/tracks/ruby/exercises/gigasecond/.version +1 -1
  81. data/tracks/ruby/exercises/gigasecond/example.rb +1 -1
  82. data/tracks/ruby/exercises/gigasecond/gigasecond_test.rb +2 -2
  83. data/tracks/ruby/exercises/isogram/.version +1 -0
  84. data/tracks/ruby/exercises/isogram/example.rb +10 -0
  85. data/tracks/ruby/exercises/isogram/example.tt +20 -0
  86. data/tracks/ruby/exercises/isogram/isogram_test.rb +90 -0
  87. data/tracks/ruby/exercises/poker/example.rb +97 -39
  88. data/tracks/ruby/exercises/poker/poker_test.rb +67 -36
  89. data/tracks/ruby/exercises/sieve/.version +1 -0
  90. data/tracks/ruby/exercises/sieve/example.rb +4 -0
  91. data/tracks/ruby/exercises/sieve/example.tt +21 -0
  92. data/tracks/ruby/exercises/sieve/sieve_test.rb +36 -13
  93. data/tracks/ruby/exercises/word-count/example.tt +4 -3
  94. data/tracks/ruby/exercises/word-count/word_count_test.rb +38 -48
  95. data/tracks/ruby/lib/anagram_cases.rb +42 -0
  96. data/tracks/ruby/lib/isogram_cases.rb +24 -0
  97. data/tracks/ruby/lib/sieve_cases.rb +33 -0
  98. data/tracks/ruby/lib/word_count_cases.rb +19 -0
  99. data/tracks/scala/config.json +21 -0
  100. data/tracks/scala/docs/RESOURCES.md +1 -0
  101. data/tracks/scala/exercises/bracket-push/build.sbt +4 -0
  102. data/tracks/scala/exercises/bracket-push/example.scala +23 -0
  103. data/tracks/scala/exercises/bracket-push/src/main/scala/.keep +0 -0
  104. data/tracks/scala/exercises/bracket-push/src/test/scala/BracketsTest.scala +68 -0
  105. data/tracks/scala/exercises/change/src/test/scala/ChangeTest.scala +7 -0
  106. data/tracks/scala/exercises/sgf-parsing/build.sbt +7 -0
  107. data/tracks/scala/exercises/sgf-parsing/example.scala +66 -0
  108. data/tracks/scala/exercises/sgf-parsing/src/main/scala/.keep +0 -0
  109. data/tracks/scala/exercises/sgf-parsing/src/main/scala/Sgf.scala +17 -0
  110. data/tracks/scala/exercises/sgf-parsing/src/test/scala/SgfTest.scala +66 -0
  111. data/tracks/scala/exercises/zebra-puzzle/build.sbt +3 -0
  112. data/tracks/scala/exercises/zebra-puzzle/example.scala +152 -0
  113. data/tracks/scala/exercises/zebra-puzzle/src/main/scala/.keep +0 -0
  114. data/tracks/scala/exercises/zebra-puzzle/src/main/scala/ZebraPuzzle.scala +14 -0
  115. data/tracks/scala/exercises/zebra-puzzle/src/test/scala/ZebraPuzzleTest.scala +11 -0
  116. data/tracks/scala/testgen/src/main/scala/BracketPushTestGenerator.scala +44 -0
  117. metadata +63 -3
  118. data/tracks/haskell/exercises/word-count/src/Example.hs +0 -8
@@ -16,43 +16,163 @@ class GameTest extends \PHPUnit_Framework_TestCase
16
16
  $this->game = new Game();
17
17
  }
18
18
 
19
- public function testGutterGame()
19
+ public function testShouldBeAbleToScoreAGameWithAllZeros()
20
20
  {
21
21
  $this->rollMany(20, 0);
22
22
 
23
23
  $this->assertEquals(0, $this->game->score());
24
24
  }
25
25
 
26
- public function testAllOnes()
26
+ public function testShouldBeAbleToScoreAGameWithNoStrikesOrSpares()
27
27
  {
28
28
  $this->markTestSkipped();
29
- $this->rollMany(20, 1);
29
+ $this->game->roll(3);
30
+ $this->game->roll(6);
31
+ $this->game->roll(3);
32
+ $this->game->roll(6);
33
+ $this->game->roll(3);
34
+ $this->game->roll(6);
35
+ $this->game->roll(3);
36
+ $this->game->roll(6);
37
+ $this->game->roll(3);
38
+ $this->game->roll(6);
39
+ $this->game->roll(3);
40
+ $this->game->roll(6);
41
+ $this->game->roll(3);
42
+ $this->game->roll(6);
43
+ $this->game->roll(3);
44
+ $this->game->roll(6);
45
+ $this->game->roll(3);
46
+ $this->game->roll(6);
47
+ $this->game->roll(3);
48
+ $this->game->roll(6);
30
49
 
31
- $this->assertEquals(20, $this->game->score());
50
+ $this->assertEquals(90, $this->game->score());
51
+ }
52
+
53
+ public function testASpareFollowedByZerosIsWorthTenPoints()
54
+ {
55
+ $this->markTestSkipped();
56
+ $this->game->roll(6);
57
+ $this->game->roll(4);
58
+ $this->rollMany(18, 0);
59
+
60
+ $this->assertEquals(10, $this->game->score());
32
61
  }
33
62
 
34
- public function testOneSpare()
63
+ public function testPointsScoredInTheRollAfterASpareAreCountedTwice()
35
64
  {
36
65
  $this->markTestSkipped();
37
- $this->rollSpare();
66
+ $this->game->roll(6);
67
+ $this->game->roll(4);
38
68
  $this->game->roll(3);
39
69
  $this->rollMany(17, 0);
40
70
 
41
71
  $this->assertEquals(16, $this->game->score());
42
72
  }
43
73
 
44
- public function testOneStrike()
74
+ public function testConsecutiveSparesEachGetAOneRollBonus()
45
75
  {
46
76
  $this->markTestSkipped();
47
- $this->rollStrike();
77
+ $this->game->roll(5);
78
+ $this->game->roll(5);
48
79
  $this->game->roll(3);
80
+ $this->game->roll(7);
49
81
  $this->game->roll(4);
82
+ $this->rollMany(15, 0);
83
+
84
+ $this->assertEquals(31, $this->game->score());
85
+ }
86
+
87
+ public function testASpareInTheLastFrameGetsAOneRollBonusThatIsCountedOnce()
88
+ {
89
+ $this->markTestSkipped();
90
+ $this->rollMany(18, 0);
91
+ $this->game->roll(7);
92
+ $this->game->roll(3);
93
+ $this->game->roll(7);
94
+
95
+ $this->assertEquals(17, $this->game->score());
96
+ }
97
+
98
+ public function testAStrikeEarnsTenPointsInFrameWithASingleRoll()
99
+ {
100
+ $this->markTestSkipped();
101
+ $this->game->roll(10);
102
+ $this->rollMany(18, 0);
103
+
104
+ $this->assertEquals(10, $this->game->score());
105
+ }
106
+
107
+ public function testPointsScoredInTheTwoRollsAfterAStrikeAreCountedTwiceAsABonus()
108
+ {
109
+ $this->markTestSkipped();
110
+ $this->game->roll(10);
111
+ $this->game->roll(5);
112
+ $this->game->roll(3);
50
113
  $this->rollMany(16, 0);
51
114
 
52
- $this->assertEquals(24, $this->game->score());
115
+ $this->assertEquals(26, $this->game->score());
116
+ }
117
+
118
+ public function testConsecutiveStrikesEachGetTheTwoRollBonus()
119
+ {
120
+ $this->markTestSkipped();
121
+ $this->game->roll(10);
122
+ $this->game->roll(10);
123
+ $this->game->roll(10);
124
+ $this->game->roll(5);
125
+ $this->game->roll(3);
126
+ $this->rollMany(12, 0);
127
+
128
+ $this->assertEquals(81, $this->game->score());
129
+ }
130
+
131
+ public function testAStrikeInTheLastFrameGetsATwoRollBonusThatIsCountedOnce()
132
+ {
133
+ $this->markTestSkipped();
134
+ $this->rollMany(18, 0);
135
+ $this->game->roll(10);
136
+ $this->game->roll(7);
137
+ $this->game->roll(1);
138
+
139
+ $this->assertEquals(18, $this->game->score());
140
+ }
141
+
142
+ public function testRollingASpareWithTheTwoRollBonusDoesNotGetABonusRoll()
143
+ {
144
+ $this->markTestSkipped();
145
+ $this->rollMany(18, 0);
146
+ $this->game->roll(10);
147
+ $this->game->roll(10);
148
+ $this->game->roll(10);
149
+
150
+ $this->assertEquals(30, $this->game->score());
151
+ }
152
+
153
+ public function testAStrikeWithTheOneRollBonusAfterASpareInTheLastFrameDoesNotGetABonus()
154
+ {
155
+ $this->markTestSkipped();
156
+ $this->rollMany(18, 0);
157
+ $this->game->roll(7);
158
+ $this->game->roll(3);
159
+ $this->game->roll(10);
160
+
161
+ $this->assertEquals(20, $this->game->score());
162
+ }
163
+
164
+ public function testStrikesWithTheTwoRollBonusDoNotGetBonusRolls()
165
+ {
166
+ $this->markTestSkipped();
167
+ $this->rollMany(18, 0);
168
+ $this->game->roll(10);
169
+ $this->game->roll(7);
170
+ $this->game->roll(3);
171
+
172
+ $this->assertEquals(20, $this->game->score());
53
173
  }
54
174
 
55
- public function testPerfectGame()
175
+ public function testAllStrikesIsAPerfectGame()
56
176
  {
57
177
  $this->markTestSkipped();
58
178
  $this->rollMany(12, 10);
@@ -60,6 +180,109 @@ class GameTest extends \PHPUnit_Framework_TestCase
60
180
  $this->assertEquals(300, $this->game->score());
61
181
  }
62
182
 
183
+ public function testRollsCanNotScoreNegativePoints()
184
+ {
185
+ $this->markTestSkipped();
186
+ $this->expectException(Exception::class);
187
+
188
+ $this->game->roll(-1);
189
+ }
190
+
191
+ public function testARollCanNotScoreMoreThan10Points()
192
+ {
193
+ $this->markTestSkipped();
194
+ $this->expectException(Exception::class);
195
+ $this->game->roll(11);
196
+ $this->rollMany(19, 0);
197
+
198
+ $this->game->score();
199
+ }
200
+
201
+ public function testTwoRollsInAFrameCanNotScoreMoreThan10Points()
202
+ {
203
+ $this->markTestSkipped();
204
+
205
+ $this->expectException(Exception::class);
206
+ $this->game->roll(5);
207
+ $this->game->roll(6);
208
+ $this->rollMany(18, 0);
209
+
210
+ $this->game->score();
211
+ }
212
+
213
+ public function testTwoBonusRollsAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points()
214
+ {
215
+ $this->markTestSkipped();
216
+ $this->expectException(Exception::class);
217
+
218
+ $this->rollMany(18, 0);
219
+ $this->game->roll(10);
220
+ $this->game->roll(5);
221
+ $this->game->roll(6);
222
+
223
+ $this->game->score();
224
+ }
225
+
226
+ public function testAnUnstartedGameCanNotBeScored()
227
+ {
228
+ $this->markTestSkipped();
229
+
230
+ $this->expectException(Exception::class);
231
+
232
+ $this->game->score();
233
+ }
234
+
235
+ public function testAnIncompleteGameCanNotBeScored()
236
+ {
237
+ $this->markTestSkipped();
238
+ $this->expectException(Exception::class);
239
+ $this->game->roll(0);
240
+ $this->game->roll(0);
241
+
242
+ $this->game->score();
243
+ }
244
+
245
+ public function testAGameWithMoreThanTenFramesCanNotBeScored()
246
+ {
247
+ $this->markTestSkipped();
248
+ $this->expectException(Exception::class);
249
+ $this->rollMany(21, 0);
250
+
251
+ $this->game->score();
252
+ }
253
+
254
+ public function testBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
255
+ {
256
+ $this->markTestSkipped();
257
+ $this->expectException(Exception::class);
258
+ $this->rollMany(18, 0);
259
+ $this->game->roll(10);
260
+
261
+ $this->game->score();
262
+ }
263
+
264
+ public function testBothBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
265
+ {
266
+ $this->markTestSkipped();
267
+ $this->expectException(Exception::class);
268
+ $this->rollMany(18, 0);
269
+ $this->game->roll(10);
270
+ $this->game->roll(10);
271
+
272
+ $this->game->score();
273
+ }
274
+
275
+ public function testBonusRollForASpareInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
276
+ {
277
+ $this->markTestSkipped();
278
+ $this->expectException(Exception::class);
279
+ $this->rollMany(18, 0);
280
+ $this->game->roll(7);
281
+ $this->game->roll(3);
282
+
283
+ $this->game->score();
284
+ }
285
+
63
286
  private function rollStrike()
64
287
  {
65
288
  $this->game->roll(10);
@@ -6,19 +6,22 @@
6
6
  */
7
7
  class Game
8
8
  {
9
- private $rolls = array();
10
- private $currentRoll = 0;
9
+ private $rolls = [];
11
10
 
12
11
  public function roll($pins)
13
12
  {
14
- $this->rolls[$this->currentRoll++] = $pins;
13
+ if ($pins < 0 || $pins > 10) {
14
+ throw new Exception('Pins must be between 0 and 10');
15
+ }
16
+ $this->rolls[] = $pins;
15
17
  }
16
18
 
17
19
  public function score()
18
20
  {
19
21
  $score = 0;
20
22
  $frameIndex = 0;
21
- for ($frame = 0; $frame < 10; $frame++) {
23
+ foreach (range(1, 10) as $frame) {
24
+ $this->guardAgainstIncompleteGame($frameIndex);
22
25
  if ($this->isStrike($frameIndex)) {
23
26
  $score += 10 + $this->strikeBonus($frameIndex);
24
27
  $frameIndex++;
@@ -30,6 +33,7 @@ class Game
30
33
  $frameIndex += 2;
31
34
  }
32
35
  }
36
+ $this->guardAgainstTooManyFrames($frameIndex);
33
37
 
34
38
  return $score;
35
39
  }
@@ -41,7 +45,16 @@ class Game
41
45
 
42
46
  private function strikeBonus($frameIndex)
43
47
  {
44
- return $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2];
48
+ if (!isset($this->rolls[$frameIndex + 1]) || !isset($this->rolls[$frameIndex + 2])) {
49
+ throw new Exception('Incomplete game');
50
+ }
51
+ $bonus1 = $this->rolls[$frameIndex + 1];
52
+ $bonus2 = $this->rolls[$frameIndex + 2];
53
+ $sum = $bonus1 + $bonus2;
54
+ if ($sum > 10 && $frameIndex = 10 && $bonus1 != 10) {
55
+ throw new Exception('Cannot score more than 10');
56
+ }
57
+ return $sum;
45
58
  }
46
59
 
47
60
  private function isSpare($frameIndex)
@@ -51,11 +64,41 @@ class Game
51
64
 
52
65
  private function spareBonus($frameIndex)
53
66
  {
67
+ if (!isset($this->rolls[$frameIndex + 2])) {
68
+ throw new Exception('Incomplete game');
69
+ }
54
70
  return $this->rolls[$frameIndex + 2];
55
71
  }
56
72
 
57
73
  private function sumOfBallsInFrame($frameIndex)
58
74
  {
59
- return $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1];
75
+ $sum = $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1];
76
+ if ($sum > 10) {
77
+ throw new Exception('Cannot score more than ten in single frame');
78
+ }
79
+ return $sum;
80
+ }
81
+
82
+ private function guardAgainstIncompleteGame($frameIndex)
83
+ {
84
+ if (!isset($this->rolls[$frameIndex])) {
85
+ throw new Exception('Incomplete game');
86
+ }
87
+ }
88
+
89
+ private function guardAgainstTooManyFrames($frameIndex)
90
+ {
91
+ $rollsCount = count($this->rolls);
92
+ if ($this->isStrike($frameIndex - 1)) {
93
+ if ($rollsCount > $frameIndex + 2) {
94
+ throw new Exception('Too many frames in game');
95
+ }
96
+ } elseif ($this->isSpare($frameIndex - 2)) {
97
+ if ($rollsCount > $frameIndex + 1) {
98
+ throw new Exception('Too many frames in game');
99
+ }
100
+ } elseif ($rollsCount > $frameIndex) {
101
+ throw new Exception('Too many frames in game');
102
+ }
60
103
  }
61
104
  }
@@ -7,6 +7,11 @@ python:
7
7
  - 3.3
8
8
  - 3.4
9
9
  - 3.5
10
+ - nightly
11
+
12
+ matrix:
13
+ allow_failures:
14
+ - python: nightly
10
15
 
11
16
  install:
12
17
  - pip install -r requirements-travis.txt
@@ -28,6 +28,7 @@
28
28
  "largest-series-product",
29
29
  "kindergarten-garden",
30
30
  "grade-school",
31
+ "flatten-array",
31
32
  "roman-numerals",
32
33
  "space-age",
33
34
  "grains",
@@ -100,24 +101,49 @@
100
101
  "slug": "pangram",
101
102
  "difficulty": 1,
102
103
  "topics": [
104
+ "control-flow (loops)",
105
+ "control-flow (if-else statements)",
106
+ "strings",
107
+ "algorithms",
108
+ "filtering",
109
+ "logic"
103
110
  ]
104
111
  },
105
112
  {
106
113
  "slug": "rna-transcription",
107
114
  "difficulty": 1,
108
115
  "topics": [
116
+ "control-flow (if-else statements)",
117
+ "control-flow (loops)",
118
+ "maps",
119
+ "strings",
120
+ "logic",
121
+ "transforming"
109
122
  ]
110
123
  },
111
124
  {
112
125
  "slug": "hamming",
113
126
  "difficulty": 1,
114
127
  "topics": [
128
+ "control-flow (if-else statements)",
129
+ "control-flow (loops)",
130
+ "sequences",
131
+ "sets",
132
+ "strings",
133
+ "algorithms",
134
+ "filtering",
135
+ "logic"
115
136
  ]
116
137
  },
117
138
  {
118
139
  "slug": "word-count",
119
- "difficulty": 1,
140
+ "difficulty": 3,
120
141
  "topics": [
142
+ "strings",
143
+ "algorithms",
144
+ "logic",
145
+ "pattern recognition",
146
+ "text formatting"
121
147
  ]
122
148
  },
123
149
  {
@@ -222,6 +248,12 @@
222
248
  "topics": [
223
249
  ]
224
250
  },
251
+ {
252
+ "slug": "flatten-array",
253
+ "difficulty": 1,
254
+ "topics": [
255
+ ]
256
+ },
225
257
  {
226
258
  "slug": "roman-numerals",
227
259
  "difficulty": 1,