trackler 2.1.0.31 → 2.1.0.32
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/fixtures/tracks/coins/config.json +3 -0
- data/fixtures/tracks/coins/docs/EXERCISE_README_INSERT.md +1 -0
- data/fixtures/tracks/fruit/SETUP.md +1 -1
- data/lib/trackler/track.rb +12 -12
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/config.json +9 -0
- data/tracks/csharp/exercises/Exercises.All.sln +14 -0
- data/tracks/csharp/exercises/Exercises.Default.sln +14 -0
- data/tracks/csharp/exercises/rotational-cipher/Example.cs +22 -0
- data/tracks/csharp/exercises/rotational-cipher/RotationalCipher.cs +9 -0
- data/tracks/csharp/exercises/rotational-cipher/RotationalCipher.csproj +18 -0
- data/tracks/csharp/exercises/rotational-cipher/RotationalCipherTest.cs +64 -0
- data/tracks/csharp/generators/Exercises/RotationalCipherExercise.cs +23 -0
- data/tracks/csharp/generators/Program.cs +5 -1
- data/tracks/java/exercises/collatz-conjecture/src/test/java/CollatzCalculatorTest.java +7 -1
- data/tracks/perl6/exercises/clock/clock.t +18 -9
- data/tracks/perl6/exercises/clock/example.yaml +18 -9
- data/tracks/python/config.json +8 -0
- data/tracks/python/exercises/scale-generator/example.py +33 -0
- data/tracks/python/exercises/scale-generator/scale_generator.py +3 -0
- data/tracks/python/exercises/scale-generator/scale_generator_test.py +120 -0
- data/tracks/ruby/exercises/isogram/.meta/solutions/isogram.rb +2 -2
- metadata +12 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5ad44a7e68d17060f904305997256916000928c
|
|
4
|
+
data.tar.gz: 262c51966c2eba2d2870792badd92f8f2f9ade76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 001fbb9c03793117bef7a7bad3c82ca75fe2ea41562b479a095dc505383e65edc6784a78de809a667ecd33c220f8789aa5f9a2889ca3c512682da5b0ff973545
|
|
7
|
+
data.tar.gz: 48e03ff57c34fa14be4bbef05786bcaf5928cc39feac666ce3c8139260bdbe9492e552f183c35a0829bd00f84b39d4d99629bf8196d014f72f768e269dc141c1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This is the content of the docs/EXERCISE_README_INSERT.md file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
The SETUP.md file is deprecated, and
|
|
1
|
+
The SETUP.md file is deprecated, and docs/EXERCISE_README_INSERT.md should be used.
|
data/lib/trackler/track.rb
CHANGED
|
@@ -119,15 +119,23 @@ module Trackler
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def hints
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
if File.exist?(track_hints_filename)
|
|
123
|
+
File.read(track_hints_filename)
|
|
124
|
+
else
|
|
125
|
+
""
|
|
125
126
|
end
|
|
126
|
-
read track_hints_filename
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
private
|
|
130
130
|
|
|
131
|
+
def track_hints_filename
|
|
132
|
+
current = [File.join('docs', 'EXERCISE_README_INSERT.md')]
|
|
133
|
+
deprecated = [File.join('exercises', 'TRACK_HINTS.md'), 'SETUP.md']
|
|
134
|
+
|
|
135
|
+
filepaths = (current + deprecated).map { |name| dir.join(name) }
|
|
136
|
+
filepaths.find(-> { '' }) { |filepath| File.exist? filepath }
|
|
137
|
+
end
|
|
138
|
+
|
|
131
139
|
# The slugs for the problems that are currently in the track.
|
|
132
140
|
# We deprecated the old array of problem slugs in favor of an array
|
|
133
141
|
# containing richer metadata about a given exercise.
|
|
@@ -202,13 +210,5 @@ module Trackler
|
|
|
202
210
|
def png_icon
|
|
203
211
|
@png_icon ||= Image.new(File.join(dir, "img/icon.png"))
|
|
204
212
|
end
|
|
205
|
-
|
|
206
|
-
def read(f)
|
|
207
|
-
if File.exist?(f)
|
|
208
|
-
File.read(f)
|
|
209
|
-
else
|
|
210
|
-
""
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
213
|
end
|
|
214
214
|
end
|
data/lib/trackler/version.rb
CHANGED
data/tracks/csharp/config.json
CHANGED
|
@@ -210,6 +210,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZebraPuzzle", "zebra-puzzle
|
|
|
210
210
|
EndProject
|
|
211
211
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zipper", "zipper\Zipper.csproj", "{83504141-FF2A-427E-8A51-9FA0E9037A78}"
|
|
212
212
|
EndProject
|
|
213
|
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RotationalCipher", "rotational-cipher\RotationalCipher.csproj", "{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}"
|
|
214
|
+
EndProject
|
|
213
215
|
Global
|
|
214
216
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
215
217
|
Debug|Any CPU = Debug|Any CPU
|
|
@@ -1468,6 +1470,18 @@ Global
|
|
|
1468
1470
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x64.Build.0 = Release|Any CPU
|
|
1469
1471
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x86.ActiveCfg = Release|Any CPU
|
|
1470
1472
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x86.Build.0 = Release|Any CPU
|
|
1473
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
1474
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
1475
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|x64.ActiveCfg = Debug|x64
|
|
1476
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|x64.Build.0 = Debug|x64
|
|
1477
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|x86.ActiveCfg = Debug|x86
|
|
1478
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Debug|x86.Build.0 = Debug|x86
|
|
1479
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
1480
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
1481
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|x64.ActiveCfg = Release|x64
|
|
1482
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|x64.Build.0 = Release|x64
|
|
1483
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|x86.ActiveCfg = Release|x86
|
|
1484
|
+
{DF67FD2D-4897-43C5-A93E-7CCADBB29E75}.Release|x86.Build.0 = Release|x86
|
|
1471
1485
|
EndGlobalSection
|
|
1472
1486
|
GlobalSection(SolutionProperties) = preSolution
|
|
1473
1487
|
HideSolutionNode = FALSE
|
|
@@ -204,6 +204,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZebraPuzzle", "zebra-puzzle
|
|
|
204
204
|
EndProject
|
|
205
205
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zipper", "zipper\Zipper.csproj", "{83504141-FF2A-427E-8A51-9FA0E9037A78}"
|
|
206
206
|
EndProject
|
|
207
|
+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RotationalCipher", "rotational-cipher\RotationalCipher.csproj", "{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}"
|
|
208
|
+
EndProject
|
|
207
209
|
Global
|
|
208
210
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
209
211
|
Debug|Any CPU = Debug|Any CPU
|
|
@@ -1426,6 +1428,18 @@ Global
|
|
|
1426
1428
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x64.Build.0 = Release|Any CPU
|
|
1427
1429
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x86.ActiveCfg = Release|Any CPU
|
|
1428
1430
|
{83504141-FF2A-427E-8A51-9FA0E9037A78}.Release|x86.Build.0 = Release|Any CPU
|
|
1431
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
1432
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
1433
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|x64.ActiveCfg = Debug|x64
|
|
1434
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|x64.Build.0 = Debug|x64
|
|
1435
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|x86.ActiveCfg = Debug|x86
|
|
1436
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Debug|x86.Build.0 = Debug|x86
|
|
1437
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
1438
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
1439
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|x64.ActiveCfg = Release|x64
|
|
1440
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|x64.Build.0 = Release|x64
|
|
1441
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|x86.ActiveCfg = Release|x86
|
|
1442
|
+
{F72CEE93-2F93-4408-8FCC-FBEB3AF0C285}.Release|x86.Build.0 = Release|x86
|
|
1429
1443
|
EndGlobalSection
|
|
1430
1444
|
GlobalSection(SolutionProperties) = preSolution
|
|
1431
1445
|
HideSolutionNode = FALSE
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Linq;
|
|
3
|
+
|
|
4
|
+
public static class RotationalCipher
|
|
5
|
+
{
|
|
6
|
+
private const string LowerCaseLetters = "abcdefghijklmnopqrstuvwxyz";
|
|
7
|
+
private const string UpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
8
|
+
|
|
9
|
+
public static string Rotate(string text, int shiftKey)
|
|
10
|
+
=> new string(text.Select(letter => Rotate(letter, shiftKey)).ToArray());
|
|
11
|
+
|
|
12
|
+
private static char Rotate(char letter, int shiftKey)
|
|
13
|
+
{
|
|
14
|
+
if (!char.IsLetter(letter))
|
|
15
|
+
return letter;
|
|
16
|
+
|
|
17
|
+
return Rotate(letter, shiftKey, char.IsLower(letter) ? LowerCaseLetters : UpperCaseLetters);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static char Rotate(char letter, int shiftKey, string key)
|
|
21
|
+
=> key[(key.IndexOf(letter) + shiftKey) % key.Length];
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
|
|
3
|
+
<PropertyGroup>
|
|
4
|
+
<OutputType>Exe</OutputType>
|
|
5
|
+
<TargetFramework>netcoreapp1.0</TargetFramework>
|
|
6
|
+
</PropertyGroup>
|
|
7
|
+
|
|
8
|
+
<ItemGroup>
|
|
9
|
+
<Compile Remove="Example.cs" />
|
|
10
|
+
</ItemGroup>
|
|
11
|
+
|
|
12
|
+
<ItemGroup>
|
|
13
|
+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
|
14
|
+
<PackageReference Include="xunit" Version="2.2.0" />
|
|
15
|
+
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
|
16
|
+
</ItemGroup>
|
|
17
|
+
|
|
18
|
+
</Project>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
using Xunit;
|
|
2
|
+
|
|
3
|
+
public class RotationalCipherTest
|
|
4
|
+
{
|
|
5
|
+
[Fact]
|
|
6
|
+
public void Rotate_a_by_1()
|
|
7
|
+
{
|
|
8
|
+
Assert.Equal("b", RotationalCipher.Rotate("a", 1));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
[Fact(Skip = "Remove to run test")]
|
|
12
|
+
public void Rotate_a_by_26_same_output_as_input()
|
|
13
|
+
{
|
|
14
|
+
Assert.Equal("a", RotationalCipher.Rotate("a", 26));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
[Fact(Skip = "Remove to run test")]
|
|
18
|
+
public void Rotate_a_by_0_same_output_as_input()
|
|
19
|
+
{
|
|
20
|
+
Assert.Equal("a", RotationalCipher.Rotate("a", 0));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
[Fact(Skip = "Remove to run test")]
|
|
24
|
+
public void Rotate_m_by_13()
|
|
25
|
+
{
|
|
26
|
+
Assert.Equal("z", RotationalCipher.Rotate("m", 13));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[Fact(Skip = "Remove to run test")]
|
|
30
|
+
public void Rotate_n_by_13_with_wrap_around_alphabet()
|
|
31
|
+
{
|
|
32
|
+
Assert.Equal("a", RotationalCipher.Rotate("n", 13));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
[Fact(Skip = "Remove to run test")]
|
|
36
|
+
public void Rotate_capital_letters()
|
|
37
|
+
{
|
|
38
|
+
Assert.Equal("TRL", RotationalCipher.Rotate("OMG", 5));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[Fact(Skip = "Remove to run test")]
|
|
42
|
+
public void Rotate_spaces()
|
|
43
|
+
{
|
|
44
|
+
Assert.Equal("T R L", RotationalCipher.Rotate("O M G", 5));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
[Fact(Skip = "Remove to run test")]
|
|
48
|
+
public void Rotate_numbers()
|
|
49
|
+
{
|
|
50
|
+
Assert.Equal("Xiwxmrk 1 2 3 xiwxmrk", RotationalCipher.Rotate("Testing 1 2 3 testing", 4));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
[Fact(Skip = "Remove to run test")]
|
|
54
|
+
public void Rotate_punctuation()
|
|
55
|
+
{
|
|
56
|
+
Assert.Equal("Gzo'n zvo, Bmviyhv!", RotationalCipher.Rotate("Let's eat, Grandma!", 21));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[Fact(Skip = "Remove to run test")]
|
|
60
|
+
public void Rotate_all_letters()
|
|
61
|
+
{
|
|
62
|
+
Assert.Equal("Gur dhvpx oebja sbk whzcf bire gur ynml qbt.", RotationalCipher.Rotate("The quick brown fox jumps over the lazy dog.", 13));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
using Generators.Data;
|
|
2
|
+
using Generators.Methods;
|
|
3
|
+
|
|
4
|
+
namespace Generators.Exercises
|
|
5
|
+
{
|
|
6
|
+
public class RotationalCipherExercise : EqualityExercise
|
|
7
|
+
{
|
|
8
|
+
public RotationalCipherExercise() : base("rotational-cipher")
|
|
9
|
+
{
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected override TestMethodData CreateTestMethodData(CanonicalData canonicalData, CanonicalDataCase canonicalDataCase, int index)
|
|
13
|
+
{
|
|
14
|
+
var testMethodData = base.CreateTestMethodData(canonicalData, canonicalDataCase, index);
|
|
15
|
+
testMethodData.CanonicalDataCase.Input = new[]
|
|
16
|
+
{
|
|
17
|
+
testMethodData.CanonicalDataCase.Data["text"],
|
|
18
|
+
testMethodData.CanonicalDataCase.Data["shiftKey"]
|
|
19
|
+
};
|
|
20
|
+
return testMethodData;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -52,8 +52,12 @@ private static TestClass CreateTestClass(Exercise exercise)
|
|
|
52
52
|
return exercise.CreateTestClass(canonicalData);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
private static void SaveTestClassContentsToFile(string testClassFilePath, string testClassContents)
|
|
55
|
+
private static void SaveTestClassContentsToFile(string testClassFilePath, string testClassContents)
|
|
56
|
+
{
|
|
57
|
+
Directory.CreateDirectory(Path.GetDirectoryName(testClassFilePath));
|
|
56
58
|
File.WriteAllText(testClassFilePath, testClassContents);
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
|
|
58
62
|
private static string TestFilePath(Exercise exercise, TestClass testClass) => Path.Combine("..", "exercises", exercise.Name, TestFileName(testClass));
|
|
59
63
|
|
|
@@ -7,7 +7,7 @@ import org.junit.rules.ExpectedException;
|
|
|
7
7
|
import static org.junit.Assert.assertEquals;
|
|
8
8
|
|
|
9
9
|
/*
|
|
10
|
-
* version: 1.
|
|
10
|
+
* version: 1.1.0
|
|
11
11
|
*/
|
|
12
12
|
public class CollatzCalculatorTest {
|
|
13
13
|
|
|
@@ -38,6 +38,12 @@ public class CollatzCalculatorTest {
|
|
|
38
38
|
assertEquals(9, collatzCalculator.computeStepCount(12));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
@Ignore("Remove to run test")
|
|
42
|
+
@Test
|
|
43
|
+
public void testAVeryLargeInput() {
|
|
44
|
+
assertEquals(152, collatzCalculator.computeStepCount(1000000));
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
@Ignore("Remove to run test")
|
|
42
48
|
@Test
|
|
43
49
|
public void testZeroIsConsideredInvalidInput() {
|
|
@@ -24,17 +24,26 @@ subtest 'Class methods', {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
my $c-data;
|
|
27
|
-
|
|
28
|
-
for @(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
for @($c-data<cases>) {
|
|
28
|
+
for @(.<cases>) -> $case {
|
|
29
|
+
given $case<property> {
|
|
30
|
+
when 'create' {
|
|
31
|
+
is ::($exercise).new(hour => $case<hour>, minute => $case<minute>).?time, |$case<expected description>;
|
|
32
|
+
}
|
|
33
|
+
when 'add' {
|
|
34
|
+
my $clock = ::($exercise).new(hour => $case<hour>, minute => $case<minute>);
|
|
35
|
+
$clock.?add-minutes($case<add>);
|
|
36
|
+
is $clock.?time, |$case<expected description>;
|
|
37
|
+
}
|
|
38
|
+
when 'equal' {
|
|
39
|
+
is ::($exercise).new(hour => $case<clock1><hour>, minute => $case<clock1><minute>).?time eq
|
|
40
|
+
::($exercise).new(hour => $case<clock2><hour>, minute => $case<clock2><minute>).?time,
|
|
41
|
+
|$case<expected description>;
|
|
42
|
+
}
|
|
43
|
+
when %*ENV<EXERCISM>.so { bail-out "no case for property '$case<property>'" }
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
|
-
is ::($exercise).new(hour => .<clock1><hour>, minute => .<clock1><minute>).?time eq
|
|
36
|
-
::($exercise).new(hour => .<clock2><hour>, minute => .<clock2><minute>).?time,
|
|
37
|
-
|.<expected description> for @($c-data<cases>[3]<cases>);
|
|
38
47
|
todo 'optional test' unless %*ENV<EXERCISM>;
|
|
39
48
|
is ::($exercise).new(:0hour,:0minute).?add-minutes(65).?time, '01:05', 'add-minutes method can be chained';
|
|
40
49
|
|
|
@@ -3,16 +3,25 @@ version: 1
|
|
|
3
3
|
methods: time add-minutes
|
|
4
4
|
plan: 54
|
|
5
5
|
tests: |
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
for @($c-data<cases>) {
|
|
7
|
+
for @(.<cases>) -> $case {
|
|
8
|
+
given $case<property> {
|
|
9
|
+
when 'create' {
|
|
10
|
+
is ::($exercise).new(hour => $case<hour>, minute => $case<minute>).?time, |$case<expected description>;
|
|
11
|
+
}
|
|
12
|
+
when 'add' {
|
|
13
|
+
my $clock = ::($exercise).new(hour => $case<hour>, minute => $case<minute>);
|
|
14
|
+
$clock.?add-minutes($case<add>);
|
|
15
|
+
is $clock.?time, |$case<expected description>;
|
|
16
|
+
}
|
|
17
|
+
when 'equal' {
|
|
18
|
+
is ::($exercise).new(hour => $case<clock1><hour>, minute => $case<clock1><minute>).?time eq
|
|
19
|
+
::($exercise).new(hour => $case<clock2><hour>, minute => $case<clock2><minute>).?time,
|
|
20
|
+
|$case<expected description>;
|
|
21
|
+
}
|
|
22
|
+
when %*ENV<EXERCISM>.so { bail-out "no case for property '$case<property>'" }
|
|
23
|
+
}
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
|
-
is ::($exercise).new(hour => .<clock1><hour>, minute => .<clock1><minute>).?time eq
|
|
15
|
-
::($exercise).new(hour => .<clock2><hour>, minute => .<clock2><minute>).?time,
|
|
16
|
-
|.<expected description> for @($c-data<cases>[3]<cases>);
|
|
17
26
|
todo 'optional test' unless %*ENV<EXERCISM>;
|
|
18
27
|
is ::($exercise).new(:0hour,:0minute).?add-minutes(65).?time, '01:05', 'add-minutes method can be chained';
|
data/tracks/python/config.json
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class Scale(object):
|
|
2
|
+
|
|
3
|
+
ASCENDING_INTERVALS = ['m', 'M', 'A']
|
|
4
|
+
CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A',
|
|
5
|
+
'A#', 'B']
|
|
6
|
+
FLAT_CHROMATIC_SCALE = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab',
|
|
7
|
+
'A', 'Bb', 'B']
|
|
8
|
+
FLAT_KEYS = ['F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'd', 'g', 'c', 'f', 'bb',
|
|
9
|
+
'eb']
|
|
10
|
+
|
|
11
|
+
def __init__(self, tonic, scale_name, pattern=None):
|
|
12
|
+
self.tonic = tonic.capitalize()
|
|
13
|
+
self.name = self.tonic + ' ' + scale_name
|
|
14
|
+
self.pattern = pattern
|
|
15
|
+
self.chromatic_scale = (self.FLAT_CHROMATIC_SCALE
|
|
16
|
+
if tonic in self.FLAT_KEYS else
|
|
17
|
+
self.CHROMATIC_SCALE)
|
|
18
|
+
self.pitches = self._assign_pitches()
|
|
19
|
+
|
|
20
|
+
def _assign_pitches(self):
|
|
21
|
+
if self.pattern is None:
|
|
22
|
+
return self._reorder_chromatic_scale()
|
|
23
|
+
last_index = 0
|
|
24
|
+
pitches = []
|
|
25
|
+
scale = self._reorder_chromatic_scale()
|
|
26
|
+
for i, interval in enumerate(self.pattern):
|
|
27
|
+
pitches.append(scale[last_index])
|
|
28
|
+
last_index += self.ASCENDING_INTERVALS.index(interval) + 1
|
|
29
|
+
return pitches
|
|
30
|
+
|
|
31
|
+
def _reorder_chromatic_scale(self):
|
|
32
|
+
index = self.chromatic_scale.index(self.tonic)
|
|
33
|
+
return self.chromatic_scale[index:] + self.chromatic_scale[:index]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from scale_generator import Scale
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ScaleGeneratorTest(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def test_naming_scale(self):
|
|
9
|
+
chromatic = Scale('c', 'chromatic')
|
|
10
|
+
expected = 'C chromatic'
|
|
11
|
+
actual = chromatic.name
|
|
12
|
+
self.assertEqual(expected, actual)
|
|
13
|
+
|
|
14
|
+
def test_chromatic_scale(self):
|
|
15
|
+
chromatic = Scale('C', 'chromatic')
|
|
16
|
+
expected = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#',
|
|
17
|
+
'B']
|
|
18
|
+
actual = chromatic.pitches
|
|
19
|
+
self.assertEqual(expected, actual)
|
|
20
|
+
|
|
21
|
+
def test_another_chromatic_scale(self):
|
|
22
|
+
chromatic = Scale('F', 'chromatic')
|
|
23
|
+
expected = ['F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb',
|
|
24
|
+
'E']
|
|
25
|
+
actual = chromatic.pitches
|
|
26
|
+
self.assertEqual(expected, actual)
|
|
27
|
+
|
|
28
|
+
def test_naming_major_scale(self):
|
|
29
|
+
major = Scale('G', 'major', 'MMmMMMm')
|
|
30
|
+
expected = 'G major'
|
|
31
|
+
actual = major.name
|
|
32
|
+
self.assertEqual(expected, actual)
|
|
33
|
+
|
|
34
|
+
def test_major_scale(self):
|
|
35
|
+
major = Scale('C', 'major', 'MMmMMMm')
|
|
36
|
+
expected = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
|
|
37
|
+
actual = major.pitches
|
|
38
|
+
self.assertEqual(expected, actual)
|
|
39
|
+
|
|
40
|
+
def test_another_major_scale(self):
|
|
41
|
+
major = Scale('G', 'major', 'MMmMMMm')
|
|
42
|
+
expected = ['G', 'A', 'B', 'C', 'D', 'E', 'F#']
|
|
43
|
+
actual = major.pitches
|
|
44
|
+
self.assertEqual(expected, actual)
|
|
45
|
+
|
|
46
|
+
def test_minor_scale(self):
|
|
47
|
+
minor = Scale('f#', 'minor', 'MmMMmMM')
|
|
48
|
+
expected = ['F#', 'G#', 'A', 'B', 'C#', 'D', 'E']
|
|
49
|
+
actual = minor.pitches
|
|
50
|
+
self.assertEqual(expected, actual)
|
|
51
|
+
|
|
52
|
+
def test_another_minor_scale(self):
|
|
53
|
+
minor = Scale('bb', 'minor', 'MmMMmMM')
|
|
54
|
+
expected = ['Bb', 'C', 'Db', 'Eb', 'F', 'Gb', 'Ab']
|
|
55
|
+
actual = minor.pitches
|
|
56
|
+
self.assertEqual(expected, actual)
|
|
57
|
+
|
|
58
|
+
def test_dorian_mode(self):
|
|
59
|
+
dorian = Scale('d', 'dorian', 'MmMMMmM')
|
|
60
|
+
expected = ['D', 'E', 'F', 'G', 'A', 'B', 'C']
|
|
61
|
+
actual = dorian.pitches
|
|
62
|
+
self.assertEqual(expected, actual)
|
|
63
|
+
|
|
64
|
+
def test_mixolydian_mode(self):
|
|
65
|
+
mixolydian = Scale('Eb', 'mixolydian', 'MMmMMmM')
|
|
66
|
+
expected = ['Eb', 'F', 'G', 'Ab', 'Bb', 'C', 'Db']
|
|
67
|
+
actual = mixolydian.pitches
|
|
68
|
+
self.assertEqual(expected, actual)
|
|
69
|
+
|
|
70
|
+
def test_lydian_mode(self):
|
|
71
|
+
lydian = Scale('a', 'lydian', 'MMMmMMm')
|
|
72
|
+
expected = ['A', 'B', 'C#', 'D#', 'E', 'F#', 'G#']
|
|
73
|
+
actual = lydian.pitches
|
|
74
|
+
self.assertEqual(expected, actual)
|
|
75
|
+
|
|
76
|
+
def test_phrygian_mode(self):
|
|
77
|
+
phrygian = Scale('e', 'phrygian', 'mMMMmMM')
|
|
78
|
+
expected = ['E', 'F', 'G', 'A', 'B', 'C', 'D']
|
|
79
|
+
actual = phrygian.pitches
|
|
80
|
+
self.assertEqual(expected, actual)
|
|
81
|
+
|
|
82
|
+
def test_locrian_mode(self):
|
|
83
|
+
locrian = Scale('g', 'locrian', 'mMMmMMM')
|
|
84
|
+
expected = ['G', 'Ab', 'Bb', 'C', 'Db', 'Eb', 'F']
|
|
85
|
+
actual = locrian.pitches
|
|
86
|
+
self.assertEqual(expected, actual)
|
|
87
|
+
|
|
88
|
+
def test_harmonic_minor(self):
|
|
89
|
+
harmonic_minor = Scale('d', 'harmonic_minor', 'MmMMmAm')
|
|
90
|
+
expected = ['D', 'E', 'F', 'G', 'A', 'Bb', 'Db']
|
|
91
|
+
actual = harmonic_minor.pitches
|
|
92
|
+
self.assertEqual(expected, actual)
|
|
93
|
+
|
|
94
|
+
def test_octatonic(self):
|
|
95
|
+
octatonic = Scale('C', 'octatonic', 'MmMmMmMm')
|
|
96
|
+
expected = ['C', 'D', 'D#', 'F', 'F#', 'G#', 'A', 'B']
|
|
97
|
+
actual = octatonic.pitches
|
|
98
|
+
self.assertEqual(expected, actual)
|
|
99
|
+
|
|
100
|
+
def test_hexatonic(self):
|
|
101
|
+
hexatonic = Scale('Db', 'hexatonic', 'MMMMMM')
|
|
102
|
+
expected = ['Db', 'Eb', 'F', 'G', 'A', 'B']
|
|
103
|
+
actual = hexatonic.pitches
|
|
104
|
+
self.assertEqual(expected, actual)
|
|
105
|
+
|
|
106
|
+
def test_pentatonic(self):
|
|
107
|
+
pentatonic = Scale('A', 'pentatonic', 'MMAMA')
|
|
108
|
+
expected = ['A', 'B', 'C#', 'E', 'F#']
|
|
109
|
+
actual = pentatonic.pitches
|
|
110
|
+
self.assertEqual(expected, actual)
|
|
111
|
+
|
|
112
|
+
def test_enigmatic(self):
|
|
113
|
+
enigmatic = Scale('G', 'enigmatic', 'mAMMMmM')
|
|
114
|
+
expected = ['G', 'G#', 'B', 'C#', 'D#', 'F', 'F#']
|
|
115
|
+
actual = enigmatic.pitches
|
|
116
|
+
self.assertEqual(expected, actual)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == '__main__':
|
|
120
|
+
unittest.main()
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trackler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.0.
|
|
4
|
+
version: 2.1.0.32
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Katrina Owen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-06-
|
|
11
|
+
date: 2017-06-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubyzip
|
|
@@ -475,6 +475,8 @@ files:
|
|
|
475
475
|
- fixtures/tracks/animal/exercises/fish/included-via-symlink.txt
|
|
476
476
|
- fixtures/tracks/animal/global/some-vendored-library
|
|
477
477
|
- fixtures/tracks/animal/global/sub-global/other-some-vendor
|
|
478
|
+
- fixtures/tracks/coins/config.json
|
|
479
|
+
- fixtures/tracks/coins/docs/EXERCISE_README_INSERT.md
|
|
478
480
|
- fixtures/tracks/fake/config.json
|
|
479
481
|
- fixtures/tracks/fake/docs/ABOUT.org
|
|
480
482
|
- fixtures/tracks/fake/docs/INSTALLATION.org
|
|
@@ -1807,6 +1809,10 @@ files:
|
|
|
1807
1809
|
- tracks/csharp/exercises/roman-numerals/RomanNumerals.cs
|
|
1808
1810
|
- tracks/csharp/exercises/roman-numerals/RomanNumerals.csproj
|
|
1809
1811
|
- tracks/csharp/exercises/roman-numerals/RomanNumeralsTest.cs
|
|
1812
|
+
- tracks/csharp/exercises/rotational-cipher/Example.cs
|
|
1813
|
+
- tracks/csharp/exercises/rotational-cipher/RotationalCipher.cs
|
|
1814
|
+
- tracks/csharp/exercises/rotational-cipher/RotationalCipher.csproj
|
|
1815
|
+
- tracks/csharp/exercises/rotational-cipher/RotationalCipherTest.cs
|
|
1810
1816
|
- tracks/csharp/exercises/run-length-encoding/Example.cs
|
|
1811
1817
|
- tracks/csharp/exercises/run-length-encoding/RunLengthEncoding.cs
|
|
1812
1818
|
- tracks/csharp/exercises/run-length-encoding/RunLengthEncoding.csproj
|
|
@@ -1953,6 +1959,7 @@ files:
|
|
|
1953
1959
|
- tracks/csharp/generators/Exercises/PigLatinExercise.cs
|
|
1954
1960
|
- tracks/csharp/generators/Exercises/RaindropsExercise.cs
|
|
1955
1961
|
- tracks/csharp/generators/Exercises/RomanNumeralsExercise.cs
|
|
1962
|
+
- tracks/csharp/generators/Exercises/RotationalCipherExercise.cs
|
|
1956
1963
|
- tracks/csharp/generators/Exercises/WordyExercise.cs
|
|
1957
1964
|
- tracks/csharp/generators/Generators.csproj
|
|
1958
1965
|
- tracks/csharp/generators/Generators.csproj.user
|
|
@@ -7164,6 +7171,9 @@ files:
|
|
|
7164
7171
|
- tracks/python/exercises/say/example.py
|
|
7165
7172
|
- tracks/python/exercises/say/say.py
|
|
7166
7173
|
- tracks/python/exercises/say/say_test.py
|
|
7174
|
+
- tracks/python/exercises/scale-generator/example.py
|
|
7175
|
+
- tracks/python/exercises/scale-generator/scale_generator.py
|
|
7176
|
+
- tracks/python/exercises/scale-generator/scale_generator_test.py
|
|
7167
7177
|
- tracks/python/exercises/scrabble-score/example.py
|
|
7168
7178
|
- tracks/python/exercises/scrabble-score/scrabble_score.py
|
|
7169
7179
|
- tracks/python/exercises/scrabble-score/scrabble_score_test.py
|