trackler 2.0.1.0 → 2.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/isogram/canonical-data.json +42 -31
- data/common/exercises/leap/canonical-data.json +7 -7
- data/common/exercises/matrix/description.md +3 -3
- data/common/exercises/minesweeper/canonical-data.json +54 -2
- data/common/exercises/pangram/canonical-data.json +5 -0
- data/lib/trackler/version.rb +1 -1
- data/tracks/clojure/config.json +5 -0
- data/tracks/clojure/exercises/acronym/project.clj +4 -0
- data/tracks/clojure/exercises/acronym/src/example.clj +8 -0
- data/tracks/clojure/exercises/acronym/test/acronym_test.clj +12 -0
- data/tracks/crystal/config.json +6 -0
- data/tracks/crystal/exercises/binary/spec/binary_spec.cr +76 -0
- data/tracks/crystal/exercises/binary/src/example.cr +18 -0
- data/tracks/crystal/src/generator/exercises/binary.cr +42 -0
- data/tracks/csharp/docs/TESTS.md +6 -2
- data/tracks/go/config.json +5 -0
- data/tracks/go/exercises/perfect-numbers/example.go +41 -0
- data/tracks/go/exercises/perfect-numbers/perfect_numbers_test.go +41 -0
- data/tracks/groovy/.gitignore +1 -0
- data/tracks/groovy/SETUP.md +6 -54
- data/tracks/groovy/exercises/difference-of-squares/Example.groovy +17 -15
- data/tracks/groovy/exercises/difference-of-squares/SquaresSpec.groovy +42 -0
- data/tracks/groovy/exercises/gigasecond/Example.groovy +9 -7
- data/tracks/groovy/exercises/gigasecond/Gigasecond.groovy +5 -2
- data/tracks/groovy/exercises/gigasecond/GigasecondSpec.groovy +38 -0
- data/tracks/groovy/exercises/hamming/Example.groovy +7 -5
- data/tracks/groovy/exercises/hamming/HammingSpec.groovy +50 -0
- data/tracks/groovy/exercises/hello-world/Example.groovy +5 -3
- data/tracks/groovy/exercises/hello-world/HelloWorld.groovy +5 -3
- data/tracks/groovy/exercises/hello-world/HelloWorldSpec.groovy +23 -0
- data/tracks/groovy/exercises/raindrops/Example.groovy +8 -8
- data/tracks/groovy/exercises/raindrops/RaindropsSpec.groovy +98 -0
- data/tracks/groovy/exercises/rna-transcription/ComplementSpec.groovy +51 -0
- data/tracks/groovy/exercises/rna-transcription/Example.groovy +19 -14
- data/tracks/haskell/.travis.yml +1 -1
- data/tracks/haskell/common/stack.yaml +1 -0
- data/tracks/haskell/config.json +1 -0
- data/tracks/haskell/exercises/pov/stack.yaml +1 -0
- data/tracks/haskell/exercises/robot-name/examples/success-standard/src/Robot.hs +1 -1
- data/tracks/java/bin/journey-test.sh +9 -4
- data/tracks/objective-c/Dangerfile +0 -2
- data/tracks/ocaml/exercises/hello-world/test.ml +6 -9
- data/tracks/ocaml/tools/test-generator/.merlin +1 -0
- data/tracks/ocaml/tools/test-generator/Makefile +4 -4
- data/tracks/ocaml/tools/test-generator/{src → interfaces}/codegen.mli +5 -1
- data/tracks/ocaml/tools/test-generator/{src → interfaces}/parser.mli +0 -0
- data/tracks/ocaml/tools/test-generator/interfaces/special_cases.mli +7 -0
- data/tracks/ocaml/tools/test-generator/interfaces/template.mli +11 -0
- data/tracks/ocaml/tools/test-generator/interfaces/test_generator.mli +3 -0
- data/tracks/ocaml/tools/test-generator/{src → interfaces}/utils.mli +0 -0
- data/tracks/ocaml/tools/test-generator/src/codegen.ml +6 -6
- data/tracks/ocaml/tools/test-generator/src/template.ml +24 -0
- data/tracks/ocaml/tools/test-generator/src/test_generator.ml +10 -26
- data/tracks/ocaml/tools/test-generator/test/all_tests.ml +2 -2
- data/tracks/ocaml/tools/test-generator/test/codegen_test.ml +1 -1
- data/tracks/ocaml/tools/test-generator/test/sample_template.txt +14 -0
- data/tracks/ocaml/tools/test-generator/test/template_test.ml +26 -0
- data/tracks/perl6/exercises/binary/binary.t +1 -1
- data/tracks/perl6/exercises/raindrops/Example.pm +2 -2
- data/tracks/perl6/exercises/raindrops/raindrops.t +19 -24
- data/tracks/php/.travis.yml +1 -0
- data/tracks/php/Makefile +26 -15
- data/tracks/php/README.md +19 -0
- data/tracks/ruby/README.md +10 -0
- data/tracks/rust/config.json +1 -0
- data/tracks/rust/exercises/alphametics/Cargo-example.toml +7 -0
- data/tracks/rust/exercises/alphametics/Cargo.lock +4 -0
- data/tracks/rust/exercises/alphametics/Cargo.toml +3 -0
- data/tracks/rust/exercises/alphametics/example.rs +76 -0
- data/tracks/rust/exercises/alphametics/src/lib.rs +5 -0
- data/tracks/rust/exercises/alphametics/tests/alphametics.rs +62 -0
- data/tracks/rust/problems.md +1 -0
- data/tracks/scala/exercises/dominoes/src/test/scala/DominoesSuite.scala +1 -1
- data/tracks/swift/Dangerfile +0 -2
- metadata +32 -39
- data/tracks/groovy/exercises/difference-of-squares/SquaresTest.groovy +0 -49
- data/tracks/groovy/exercises/gigasecond/GigasecondTest.groovy +0 -29
- data/tracks/groovy/exercises/hamming/HammingTest.groovy +0 -56
- data/tracks/groovy/exercises/hello-world/HelloWorldTest.groovy +0 -19
- data/tracks/groovy/exercises/raindrops/RaindropsTest.groovy +0 -21
- data/tracks/groovy/exercises/rna-transcription/ComplementTest.groovy +0 -55
- data/tracks/haskell/exercises/pov/stack.yaml +0 -1
- data/tracks/java/exercises/anagram/src/main/java/.keep +0 -0
- data/tracks/java/exercises/beer-song/src/example/java/.keep +0 -0
- data/tracks/java/exercises/beer-song/src/main/java/.keep +0 -0
- data/tracks/java/exercises/beer-song/src/test/java/.keep +0 -0
- data/tracks/java/exercises/difference-of-squares/src/example/java/.keep +0 -0
- data/tracks/java/exercises/difference-of-squares/src/main/java/.keep +0 -0
- data/tracks/java/exercises/etl/src/main/java/.keep +0 -0
- data/tracks/java/exercises/etl/src/test/java/.keep +0 -0
- data/tracks/java/exercises/grade-school/src/test/java/.keep +0 -0
- data/tracks/java/exercises/hamming/src/main/java/.keep +0 -0
- data/tracks/java/exercises/hello-world/src/main/java/.keep +0 -0
- data/tracks/java/exercises/hexadecimal/src/test/java/.keep +0 -0
- data/tracks/java/exercises/linked-list/src/example/java/.keep +0 -0
- data/tracks/java/exercises/linked-list/src/test/java/.keep +0 -0
- data/tracks/java/exercises/meetup/src/main/java/.keep +0 -0
- data/tracks/java/exercises/meetup/src/test/java/.keep +0 -0
- data/tracks/java/exercises/nth-prime/src/example/java/.keep +0 -0
- data/tracks/java/exercises/nth-prime/src/test/java/.keep +0 -0
- data/tracks/java/exercises/nucleotide-count/src/main/java/.keep +0 -0
- data/tracks/java/exercises/nucleotide-count/src/test/java/.keep +0 -0
- data/tracks/java/exercises/pangram/src/test/java/.keep +0 -0
- data/tracks/java/exercises/pascals-triangle/src/test/java/.keep +0 -0
- data/tracks/java/exercises/series/src/main/java/.keep +0 -0
- data/tracks/java/exercises/series/src/test/java/.keep +0 -0
- data/tracks/java/exercises/triangle/src/main/java/.keep +0 -0
- data/tracks/ocaml/tools/test-generator/src/special_cases.mli +0 -13
- data/tracks/ocaml/tools/test-generator/src/test_generator.mli +0 -6
- data/tracks/ocaml/tools/test-generator/test/test_generator_test.ml +0 -11
@@ -1,11 +1,11 @@
|
|
1
1
|
test: test_gen.native
|
2
2
|
@./all_tests.native
|
3
3
|
|
4
|
-
test_gen.native: all_tests.native
|
5
|
-
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg yojson -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src" test_gen.native
|
4
|
+
test_gen.native: all_tests.native src/*.ml interfaces/*.mli test/*.ml
|
5
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src,interfaces" test_gen.native
|
6
6
|
|
7
|
-
all_tests.native: src/*.ml
|
8
|
-
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg oUnit -pkg yojson -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src,test" all_tests.native
|
7
|
+
all_tests.native: src/*.ml test/*.ml
|
8
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg oUnit -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src,test" all_tests.native
|
9
9
|
|
10
10
|
clean:
|
11
11
|
rm -rf _build
|
@@ -6,4 +6,8 @@ type fixup_parameter_function = key: string -> value: parameter -> string
|
|
6
6
|
|
7
7
|
type edit_parameters_function = (string * string) list -> (string * string) list
|
8
8
|
|
9
|
-
|
9
|
+
type subst = Subst of string
|
10
|
+
|
11
|
+
val subst_to_string : subst -> string
|
12
|
+
|
13
|
+
val generate_code : fixup_parameter_function -> edit_parameters_function -> string -> case list -> (subst list, string) Result.t
|
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
open Codegen
|
4
|
+
|
5
|
+
type t = {start: int; finish: int; file_text: string; template: string}
|
6
|
+
|
7
|
+
val fill : int -> int -> template: string -> subst list -> string
|
8
|
+
|
9
|
+
val find_template : template_text: string -> (int * int * string) option
|
10
|
+
|
11
|
+
val show : t -> string
|
File without changes
|
@@ -6,19 +6,19 @@ type fixup_parameter_function = key: string -> value: parameter -> string
|
|
6
6
|
|
7
7
|
type edit_parameters_function = (string * string) list -> (string * string) list
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
type subst = Subst of string [@@deriving eq, show]
|
10
|
+
|
11
|
+
let subst_to_string (Subst s) = s
|
12
12
|
|
13
13
|
let replace_key (key: string) (value: string) (target: string): string =
|
14
|
-
String.substr_replace_all
|
14
|
+
String.substr_replace_all ~pattern:("$" ^ key) ~with_:value target
|
15
15
|
|
16
|
-
let rec replace_keys (f: fixup_parameter_function) (ed: edit_parameters_function) (s: string) (c: case):
|
16
|
+
let rec replace_keys (f: fixup_parameter_function) (ed: edit_parameters_function) (s: string) (c: case): subst =
|
17
17
|
let s = replace_key "description" c.description s in
|
18
18
|
let expected = f ~key:"expected" ~value:c.expected in
|
19
19
|
let s = replace_key "expected" expected s in
|
20
20
|
let parameter_strings = ed @@ List.map ~f:(fun (k,p) -> (k,parameter_to_string p)) c.parameters in
|
21
|
-
List.fold parameter_strings ~init:s ~f:(fun s (k,v) -> replace_key k v s)
|
21
|
+
List.fold parameter_strings ~init:(Subst s) ~f:(fun (Subst s) (k,v) -> Subst (replace_key k v s))
|
22
22
|
|
23
23
|
let generate_code (f: fixup_parameter_function) (ed: edit_parameters_function) template cases =
|
24
24
|
Ok (List.map cases ~f:(replace_keys f ed template))
|
@@ -0,0 +1,24 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
open Codegen
|
4
|
+
open Utils
|
5
|
+
|
6
|
+
(* start and finish are the lines where the generated code is *)
|
7
|
+
type t = {start: int; finish: int; file_text: string; template: string} [@@deriving eq, show]
|
8
|
+
|
9
|
+
let find_template ~(template_text: string): t option =
|
10
|
+
let open Option.Monad_infix in
|
11
|
+
let str_contains pattern s = String.substr_index s ~pattern |> Option.is_some in
|
12
|
+
let lines = String.split_lines template_text |> List.to_array in
|
13
|
+
let start_index = find_arrayi lines ~f:(str_contains "(* GENERATED-CODE") |> Option.map ~f:fst in
|
14
|
+
let finish_index = (start_index >>= (fun start -> find_arrayi ~start lines ~f:(str_contains "END GENERATED-CODE *"))) |> Option.map ~f:fst in
|
15
|
+
let template_lines = Option.map2 start_index finish_index (fun s -> Array.slice lines (s+1)) in
|
16
|
+
Option.map2 start_index template_lines ~f:(fun s l -> {start=s; finish=(s + 1 + Array.length l); file_text = template_text; template=String.concat_array l ~sep:"\n"})
|
17
|
+
|
18
|
+
let fill (template: t) (substs: subst list): string =
|
19
|
+
let lines = String.split_lines template.file_text |> List.to_array in
|
20
|
+
let before = Array.slice lines 0 template.start in
|
21
|
+
let subst = Array.of_list (List.map ~f:subst_to_string substs) in
|
22
|
+
let after = Array.slice lines (template.finish + 1) (Array.length lines) in
|
23
|
+
let join = String.concat_array ~sep:"\n" in
|
24
|
+
String.concat [join before; join subst; join after] ~sep:"\n"
|
@@ -4,23 +4,7 @@ open Model
|
|
4
4
|
open Utils
|
5
5
|
open Codegen
|
6
6
|
open Special_cases
|
7
|
-
|
8
|
-
let find_template ~(template_text: string): (int * int * string) option =
|
9
|
-
let open Option.Monad_infix in
|
10
|
-
let str_contains pattern s = String.substr_index s ~pattern |> Option.is_some in
|
11
|
-
let lines = String.split_lines template_text |> List.to_array in
|
12
|
-
let start_index = find_arrayi lines ~f:(str_contains "(* GENERATED-CODE") |> Option.map ~f:fst in
|
13
|
-
let finish_index = (start_index >>= (fun start -> find_arrayi ~start lines ~f:(str_contains "END GENERATED-CODE *"))) |> Option.map ~f:fst in
|
14
|
-
let template_lines = Option.map2 start_index finish_index (fun s -> Array.slice lines (s+1)) in
|
15
|
-
Option.map2 start_index template_lines ~f:(fun s l -> (s, s + 1 + Array.length l, String.concat_array l ~sep:"\n"))
|
16
|
-
|
17
|
-
let splice_in_filled_in_code (start: int) (finish: int) ~(template: string) (substs: string list): string =
|
18
|
-
let lines = String.split_lines template |> List.to_array in
|
19
|
-
let before = Array.slice lines 0 start in
|
20
|
-
let subst = Array.of_list substs in
|
21
|
-
let after = Array.slice lines (finish + 1) (Array.length lines) in
|
22
|
-
let join = String.concat_array ~sep:"\n" in
|
23
|
-
String.concat [join before; join subst; join after] ~sep:"\n"
|
7
|
+
open Template
|
24
8
|
|
25
9
|
type content = string
|
26
10
|
|
@@ -30,23 +14,23 @@ let find_nested_files (name: string) (base: string): (string * content) list =
|
|
30
14
|
|> List.filter ~f:(fun slug -> Sys.file_exists_exn (base ^ "/" ^ slug ^ "/" ^ name))
|
31
15
|
|> List.map ~f:(fun slug -> (slug, In_channel.read_all (base ^ "/" ^ slug ^ "/" ^ name)))
|
32
16
|
|
33
|
-
let
|
17
|
+
let find_template_files = find_nested_files "template.ml"
|
34
18
|
|
35
19
|
let find_canonical_data_files = find_nested_files "canonical-data.json"
|
36
20
|
|
37
|
-
let combine_files (
|
38
|
-
List.filter_map
|
21
|
+
let combine_files (template_files: (string * content) list) (canonical_data_files: (string * content) list): (string * content * content) list =
|
22
|
+
List.filter_map template_files ~f:(fun (n,t) -> (List.Assoc.find canonical_data_files ~equal:String.equal n |> Option.map ~f:(fun c -> (n,t,c))))
|
39
23
|
|
40
24
|
let generate_code ~slug ~template_file ~canonical_data_file =
|
41
25
|
let template = find_template template_file in
|
42
|
-
let template = Result.of_option template "cannot
|
26
|
+
let template = Result.of_option template ("cannot recognize file for " ^ slug ^ " as a template") in
|
43
27
|
let cases = parse_json_text canonical_data_file in
|
44
28
|
let cases = Result.map_error cases show_error in
|
45
29
|
let open Result.Monad_infix in
|
46
|
-
template >>= fun
|
30
|
+
template >>= fun template ->
|
47
31
|
cases >>= fun cs ->
|
48
|
-
let substs = Result.ok_or_failwith @@ generate_code (fixup ~stringify:parameter_to_string ~slug) (edit ~slug) template cs in
|
49
|
-
Result.return (
|
32
|
+
let substs = Result.ok_or_failwith @@ generate_code (fixup ~stringify:parameter_to_string ~slug) (edit ~slug) template.template cs in
|
33
|
+
Result.return (fill template substs)
|
50
34
|
|
51
35
|
let output_tests (files: (string * content * content) list) (output_folder: string): unit =
|
52
36
|
let output_filepath name = output_folder ^ "/" ^ name ^ "/test.ml" in
|
@@ -56,7 +40,7 @@ let output_tests (files: (string * content * content) list) (output_folder: stri
|
|
56
40
|
List.iter files ~f:output1
|
57
41
|
|
58
42
|
let run ~(templates_folder: string) ~(canonical_data_folder: string) ~(output_folder: string) =
|
59
|
-
let
|
43
|
+
let template_files = find_template_files templates_folder in
|
60
44
|
let canonical_data_files = find_canonical_data_files canonical_data_folder in
|
61
|
-
let combined = combine_files
|
45
|
+
let combined = combine_files template_files canonical_data_files in
|
62
46
|
output_tests combined output_folder
|
@@ -4,8 +4,8 @@ open Codegen_test
|
|
4
4
|
open Model_test
|
5
5
|
open Parser_test
|
6
6
|
open Special_cases_test
|
7
|
-
open
|
7
|
+
open Template_test
|
8
8
|
open Utils_test
|
9
9
|
|
10
10
|
let () =
|
11
|
-
run_test_tt_main ("tests" >:::List.concat [codegen_tests; model_tests; parser_tests; special_cases_tests;
|
11
|
+
run_test_tt_main ("tests" >:::List.concat [codegen_tests; model_tests; parser_tests; special_cases_tests; template_tests; utils_tests])
|
@@ -9,7 +9,7 @@ let fixup ~key ~value = parameter_to_string value
|
|
9
9
|
let edit = Fn.id
|
10
10
|
let assert_gen exp cases = assert_equal exp
|
11
11
|
~printer:(fun xs -> "[" ^ (String.concat ~sep:";" xs) ^ "]")
|
12
|
-
(Result.ok_or_failwith @@ generate_code fixup edit leap_template cases)
|
12
|
+
(Result.ok_or_failwith @@ generate_code fixup edit leap_template cases |> List.map ~f:subst_to_string)
|
13
13
|
let ae exp cases _test_ctxt = assert_gen exp cases
|
14
14
|
|
15
15
|
let codegen_tests = [
|
@@ -0,0 +1,14 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Hello_world
|
4
|
+
|
5
|
+
let ae exp got _test_ctxt = assert_equal ~printer:String.to_string exp got
|
6
|
+
|
7
|
+
let tests = [
|
8
|
+
(* GENERATED-CODE
|
9
|
+
code
|
10
|
+
END GENERATED-CODE *)
|
11
|
+
]
|
12
|
+
|
13
|
+
let () =
|
14
|
+
run_test_tt_main ("Hello World tests" >::: tests)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Codegen
|
4
|
+
open Template
|
5
|
+
|
6
|
+
let printer = Option.value_map ~f:show ~default:"None"
|
7
|
+
|
8
|
+
let template_tests = [
|
9
|
+
"if there is no generated code marker in a string then find_template returns None" >:: (fun _ctx ->
|
10
|
+
assert_equal None @@ find_template ~template_text:"some code block"
|
11
|
+
);
|
12
|
+
|
13
|
+
"if there is generated code marker in a string then find_template returns where it is" >:: (fun _ctx ->
|
14
|
+
let template_text = In_channel.read_all "test/sample_template.txt" in
|
15
|
+
assert_equal ~printer (Some {start=7; finish=9; file_text=template_text; template="code"}) @@ find_template ~template_text
|
16
|
+
);
|
17
|
+
|
18
|
+
"fills in lines of a template" >:: (fun _ctx ->
|
19
|
+
let template_text = In_channel.read_all "test/sample_template.txt" in
|
20
|
+
let template = {start=7; finish=9; file_text=template_text;template="code"} in
|
21
|
+
let filled = fill template [Subst "line1"; Subst "line2"] in
|
22
|
+
let pattern = " (* GENERATED-CODE\ncode\n END GENERATED-CODE *)" in
|
23
|
+
let expected = (String.substr_replace_all ~pattern ~with_:"line1\nline2" template_text) in
|
24
|
+
assert_equal ~printer:Fn.id expected (filled ^ "\n")
|
25
|
+
);
|
26
|
+
]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class Raindrops {
|
2
|
-
method convert ($num) {
|
2
|
+
method convert (Int:D $num --> Str:D) {
|
3
3
|
my $str = '';
|
4
4
|
$str ~= "Pling" if $num % 3 == 0;
|
5
5
|
$str ~= "Plang" if $num % 5 == 0;
|
6
6
|
$str ~= "Plong" if $num % 7 == 0;
|
7
|
-
return $str ?? $str !! $num;
|
7
|
+
return $str ?? $str !! $num.Str;
|
8
8
|
}
|
9
9
|
}
|
@@ -3,31 +3,26 @@ use v6;
|
|
3
3
|
use Test;
|
4
4
|
use lib IO::Path.new($?FILE).parent.path;
|
5
5
|
|
6
|
-
plan 18;
|
7
|
-
|
8
6
|
BEGIN {
|
9
|
-
|
10
|
-
|
11
|
-
}
|
12
|
-
|
13
|
-
pass 'Load module';
|
14
|
-
|
7
|
+
plan 23;
|
8
|
+
eval-lives-ok %*ENV<EXERCISM>.so ?? 'use Example' !! 'use Raindrops', 'Module loaded';
|
9
|
+
}
|
15
10
|
|
16
11
|
ok Raindrops.can('convert'), 'Class Raindrops has convert method';
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
is Raindrops.convert(
|
25
|
-
|
26
|
-
|
27
|
-
is Raindrops.convert(
|
28
|
-
is Raindrops.convert(
|
29
|
-
is Raindrops.convert(
|
30
|
-
is Raindrops.convert(
|
31
|
-
is Raindrops.convert(
|
32
|
-
is Raindrops.convert(
|
33
|
-
is Raindrops.convert(
|
13
|
+
my @ints = 1, 8, 52;
|
14
|
+
my @plings = 3, 6, 9, 27;
|
15
|
+
my @plangs = 5, 10, 25, 3125;
|
16
|
+
my @plongs = 7, 14, 49;
|
17
|
+
|
18
|
+
for @ints {
|
19
|
+
is Raindrops.convert($_), $_, "$_ is not a factor of 3, 5 or 7";
|
20
|
+
isa-ok Raindrops.convert($_), Str, "$_ gives a string";
|
21
|
+
}
|
22
|
+
is Raindrops.convert($_), 'Pling', "$_ is a factor of 3, not 5 or 7" for @plings;
|
23
|
+
is Raindrops.convert($_), 'Plang', "$_ is a factor of 5, not 3 or 7" for @plangs;
|
24
|
+
is Raindrops.convert($_), 'Plong', "$_ is a factor of 7, not 3 or 5" for @plongs;
|
25
|
+
is Raindrops.convert(15), 'PlingPlang', '15 is a factor of 3 and 5, not 7';
|
26
|
+
is Raindrops.convert(21), 'PlingPlong', '21 is a factor of 3 and 7, not 5';
|
27
|
+
is Raindrops.convert(35), 'PlangPlong', '35 is a factor of 5 and 7, not 3';
|
28
|
+
is Raindrops.convert(105), 'PlingPlangPlong', '105 is a factor of 3, 5 and 7';
|
data/tracks/php/.travis.yml
CHANGED
data/tracks/php/Makefile
CHANGED
@@ -12,32 +12,43 @@ FILEEXT := "php"
|
|
12
12
|
EXAMPLE := "example.$(FILEEXT)"
|
13
13
|
TSTFILE := "$(ASSIGNMENT)_test.$(FILEEXT)"
|
14
14
|
|
15
|
-
|
16
|
-
bin/phpunit.phar
|
17
|
-
|
18
|
-
|
15
|
+
default:
|
16
|
+
wget --no-check-certificate https://phar.phpunit.de/phpunit.phar -O bin/phpunit.phar
|
17
|
+
chmod +x bin/phpunit.phar
|
18
|
+
@wget --no-check-certificate https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.0.0a2/phpcs.phar -O bin/phpcs.phar
|
19
|
+
chmod +x bin/phpcs.phar
|
19
20
|
|
20
|
-
|
21
|
-
@
|
22
|
-
chmod +x $@
|
21
|
+
help: ## Prints this help
|
22
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
install: ## install development dependencies
|
25
|
+
wget --no-check-certificate https://phar.phpunit.de/phpunit.phar -O bin/phpunit.phar
|
26
|
+
chmod +x bin/phpunit.phar
|
27
|
+
|
28
|
+
@wget --no-check-certificate https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.0.0a2/phpcs.phar -O bin/phpcs.phar
|
29
|
+
chmod +x bin/phpcs.phar
|
30
|
+
|
31
|
+
install-test: ## install test dependency: phpunit.phar
|
32
|
+
@wget --no-check-certificate https://phar.phpunit.de/phpunit.phar -O bin/phpunit.phar
|
33
|
+
chmod +x bin/phpunit.phar
|
34
|
+
|
35
|
+
install-style: ## install style checker dependency: phpcs.phar
|
36
|
+
@wget --no-check-certificate https://phar.phpunit.de/phpunit.phar -O bin/phpcs.phar
|
37
|
+
chmod +x bin/phpcs.phar
|
38
|
+
|
39
|
+
test-assignment: bin/phpunit.phar ## run single test using ASSIGNMENTS: test-assignment ASSIGNMENT=wordy
|
26
40
|
@echo "running tests for: $(ASSIGNMENT)"
|
27
41
|
@cat ./exercises/$(ASSIGNMENT)/$(TSTFILE) | sed '/markTestSkipped()/d' > $(OUTDIR)/$(TSTFILE)
|
28
42
|
@cp ./exercises/$(ASSIGNMENT)/$(EXAMPLE) $(OUTDIR)/$(ASSIGNMENT).$(FILEEXT)
|
29
43
|
@bin/phpunit.phar --no-configuration $(OUTDIR)/$(TSTFILE)
|
30
44
|
|
31
|
-
|
32
|
-
test:
|
45
|
+
test: ## run all tests
|
33
46
|
@for assignment in $(ASSIGNMENTS); do ASSIGNMENT=$$assignment $(MAKE) -s test-assignment || exit 1; done
|
34
47
|
|
35
|
-
|
36
|
-
style-check-assignment: bin/phpcs.phar
|
48
|
+
style-check-assignment: bin/phpcs.phar ## run style check single test using ASSIGNMENTS: style-check-assignment ASSIGNMENT=wordy
|
37
49
|
@echo "checking $(ASSIGNMENT) against xPHP code standards"
|
38
50
|
@bin/phpcs.phar -sp --standard=phpcs-xphp.xml ./exercises/$(ASSIGNMENT)
|
39
51
|
|
40
|
-
|
41
|
-
style-check:
|
52
|
+
style-check: ## run style check all tests
|
42
53
|
@for assignment in $(ASSIGNMENTS); do ASSIGNMENT=$$assignment $(MAKE) -s style-check-assignment || exit 1; done
|
43
54
|
|
data/tracks/php/README.md
CHANGED
@@ -4,6 +4,20 @@
|
|
4
4
|
|
5
5
|
Exercism exercises in PHP
|
6
6
|
|
7
|
+
## Install Dependencies
|
8
|
+
|
9
|
+
### All dependencies
|
10
|
+
|
11
|
+
% make install
|
12
|
+
|
13
|
+
### Only tests dependencies
|
14
|
+
|
15
|
+
% make install-test
|
16
|
+
|
17
|
+
### Only style-check dependencies
|
18
|
+
|
19
|
+
% make install-style
|
20
|
+
|
7
21
|
## Running Unit Test Suite
|
8
22
|
|
9
23
|
### All Assignments
|
@@ -24,6 +38,11 @@ Exercism exercises in PHP
|
|
24
38
|
|
25
39
|
% make style-check-assignment ASSIGNMENT=wordy
|
26
40
|
|
41
|
+
## Help
|
42
|
+
If you need command line help for run make commands
|
43
|
+
|
44
|
+
% make
|
45
|
+
|
27
46
|
## Contributing
|
28
47
|
|
29
48
|
- Follow the [PSR-2] coding style (xPHP uses a slightly [modified] version of [PSR-2]).
|
data/tracks/ruby/README.md
CHANGED
@@ -108,6 +108,16 @@ then additional inputs/outputs should be submitted to the x-common repository.
|
|
108
108
|
Changes to the test suite (style, boilerplate, etc) will probably have to be made to
|
109
109
|
`example.tt`.
|
110
110
|
|
111
|
+
### Exercise Generators
|
112
|
+
|
113
|
+
If you wish to create a new generator, or edit an existing one, the generators currently live in the lib directory and are named `$PROBLEM_cases.rb`. For example, the hamming generator is `lib/hamming_cases.rb`.
|
114
|
+
|
115
|
+
All generators currently adhere to a common public interface, and must define the following three methods:
|
116
|
+
|
117
|
+
- `test_name` - Returns the name of the test (i.e `test_one_equals_one`)
|
118
|
+
- `workload` - Returns the main syntax for the test. This will vary depending on the test generator and its underlying implementation
|
119
|
+
- `skipped` - Returns skip syntax (i.e. `skip` or `# skip`)
|
120
|
+
|
111
121
|
## Pull Requests
|
112
122
|
|
113
123
|
We welcome pull requests that provide fixes to existing test suites (missing
|
data/tracks/rust/config.json
CHANGED
@@ -0,0 +1,76 @@
|
|
1
|
+
// This is a brute-force solution, use `cargo test --release` for faster testing
|
2
|
+
|
3
|
+
extern crate itertools;
|
4
|
+
extern crate permutohedron;
|
5
|
+
|
6
|
+
use itertools::Itertools;
|
7
|
+
use permutohedron::Heap as Permutations;
|
8
|
+
|
9
|
+
|
10
|
+
use std::collections::HashMap;
|
11
|
+
use std::collections::HashSet;
|
12
|
+
use std::char;
|
13
|
+
|
14
|
+
fn test_equation(puzzle: &str, substitutions: &HashMap<char, u8>) -> bool {
|
15
|
+
// Create a new String with characters changed to numbers
|
16
|
+
let puzzle: String = puzzle.chars()
|
17
|
+
.map(|c| {
|
18
|
+
if let Some(&n) = substitutions.get(&c) {
|
19
|
+
// If the character is in the substitutions, get the number and
|
20
|
+
// convert it to a char
|
21
|
+
char::from_digit(n as u32, 10).unwrap()
|
22
|
+
} else {
|
23
|
+
// Otherwise just copy over the character
|
24
|
+
c
|
25
|
+
}
|
26
|
+
})
|
27
|
+
.collect();
|
28
|
+
|
29
|
+
// Split the puzzle into left and right side
|
30
|
+
let equation: Vec<&str> = puzzle.split("==").collect();
|
31
|
+
|
32
|
+
// Parse the number on the right side
|
33
|
+
let right = equation[1].trim().parse::<u32>().unwrap();
|
34
|
+
|
35
|
+
// Sum the parts on the left side
|
36
|
+
let left: u32 = equation[0].split('+').map(str::trim).map(|n| n.parse::<u32>().unwrap()).sum();
|
37
|
+
|
38
|
+
// Create a String with just the numbers and spaces
|
39
|
+
let just_numbers =
|
40
|
+
puzzle.chars().filter(|c| c.is_digit(10) || c.is_whitespace()).collect::<String>();
|
41
|
+
// Split this into the numbers and check every number's first character
|
42
|
+
let no_leading_zeroes = just_numbers.split_whitespace()
|
43
|
+
.all(|number| number.chars().next().unwrap() != '0');
|
44
|
+
|
45
|
+
// Return true if left and right side is equal and the equation doesnt
|
46
|
+
// contain leading zeroes.
|
47
|
+
left == right && no_leading_zeroes
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
pub fn solve(puzzle: &str) -> Option<HashMap<char, u8>> {
|
52
|
+
// Get unique letters from the puzzle
|
53
|
+
let letters: HashSet<char> =
|
54
|
+
puzzle.chars().filter(|&c| c.is_alphabetic() && c.is_uppercase()).collect();
|
55
|
+
let letters: Vec<char> = letters.into_iter().collect();
|
56
|
+
|
57
|
+
// All available numbers for substitution
|
58
|
+
let numbers: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
59
|
+
|
60
|
+
// Iterate every combination with the length of unique letters in the puzzle
|
61
|
+
for combinations in numbers.iter().combinations(letters.len()) {
|
62
|
+
let mut c = combinations;
|
63
|
+
let permutations = Permutations::new(&mut c);
|
64
|
+
// Iterate every permutation of a letter combination
|
65
|
+
for p in permutations {
|
66
|
+
let substitution: HashMap<char, u8> =
|
67
|
+
letters.iter().zip(p).map(|(&c, &n)| (c, n)).collect();
|
68
|
+
if test_equation(puzzle, &substitution) {
|
69
|
+
// We found a good substitution
|
70
|
+
return Some(substitution);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
// If we tested every combination and did not found a solution then return None
|
75
|
+
None
|
76
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
extern crate alphametics;
|
2
|
+
use std::collections::HashMap;
|
3
|
+
|
4
|
+
fn assert_alphametic_solution_eq(puzzle: &str, solution: &[(char, u8)]) {
|
5
|
+
let answer = alphametics::solve(puzzle).unwrap();
|
6
|
+
let solution: HashMap<char, u8> = solution.iter().cloned().collect();
|
7
|
+
assert_eq!(answer, solution);
|
8
|
+
}
|
9
|
+
|
10
|
+
#[test]
|
11
|
+
fn test_with_three_letters() {
|
12
|
+
assert_alphametic_solution_eq("I + BB == ILL", &[('I', 1), ('B', 9), ('L', 0)]);
|
13
|
+
}
|
14
|
+
|
15
|
+
#[test]
|
16
|
+
#[ignore]
|
17
|
+
fn test_must_have_unique_value_for_each_letter() {
|
18
|
+
let answer = alphametics::solve("A == B");
|
19
|
+
assert_eq!(answer, None);
|
20
|
+
}
|
21
|
+
|
22
|
+
#[test]
|
23
|
+
#[ignore]
|
24
|
+
fn test_leading_zero_solution_is_invalid() {
|
25
|
+
let answer = alphametics::solve("ACA + DD == BD");
|
26
|
+
assert_eq!(answer, None);
|
27
|
+
}
|
28
|
+
|
29
|
+
#[test]
|
30
|
+
#[ignore]
|
31
|
+
fn test_puzzle_with_four_letters() {
|
32
|
+
assert_alphametic_solution_eq("AS + A == MOM", &[('A', 9), ('S', 2), ('M', 1), ('O', 0)]);
|
33
|
+
}
|
34
|
+
|
35
|
+
#[test]
|
36
|
+
#[ignore]
|
37
|
+
fn test_puzzle_with_six_letters() {
|
38
|
+
assert_alphametic_solution_eq("NO + NO + TOO == LATE",
|
39
|
+
&[('N', 7), ('O', 4), ('T', 9), ('L', 1), ('A', 0), ('E', 2)]);
|
40
|
+
}
|
41
|
+
|
42
|
+
#[test]
|
43
|
+
#[ignore]
|
44
|
+
fn test_puzzle_with_seven_letters() {
|
45
|
+
assert_alphametic_solution_eq("HE + SEES + THE == LIGHT",
|
46
|
+
&[('E', 4), ('G', 2), ('H', 5), ('I', 0), ('L', 1), ('S', 9), ('T', 7)]);
|
47
|
+
}
|
48
|
+
|
49
|
+
#[test]
|
50
|
+
#[ignore]
|
51
|
+
fn test_puzzle_with_eight_letters() {
|
52
|
+
assert_alphametic_solution_eq("SEND + MORE == MONEY",
|
53
|
+
&[('S', 9), ('E', 5), ('N', 6), ('D', 7), ('M', 1), ('O', 0), ('R', 8), ('Y', 2)]);
|
54
|
+
}
|
55
|
+
|
56
|
+
#[test]
|
57
|
+
#[ignore]
|
58
|
+
fn test_puzzle_with_ten_letters() {
|
59
|
+
assert_alphametic_solution_eq("AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE",
|
60
|
+
&[('A', 5), ('D', 3), ('E', 4), ('F', 7), ('G', 8), ('N', 0), ('O', 2), ('R', 1),
|
61
|
+
('S', 6), ('T', 9)]);
|
62
|
+
}
|
data/tracks/rust/problems.md
CHANGED
@@ -58,6 +58,7 @@ phone-number | option, format, unwrap_or, iters, match
|
|
58
58
|
wordy | Result, string parsing, operators (optional)
|
59
59
|
tournament | enum, sorting, hashmap, structs
|
60
60
|
custom-set | generic over type, vector, equality, struct
|
61
|
+
alphametics | string parsing, combinations, math, external crates (optional)
|
61
62
|
|
62
63
|
## Rust Gets Strange
|
63
64
|
|
@@ -71,7 +71,7 @@ class DominoesSuite extends FunSuite with Matchers {
|
|
71
71
|
|
72
72
|
private def checkChain(result: List[(Int, Int)], input: List[(Int, Int)]): Unit = {
|
73
73
|
def sortDomino(ab: (Int, Int)): (Int, Int) =
|
74
|
-
if (
|
74
|
+
if (ab._1 > ab._2) ab.swap else ab
|
75
75
|
def consecutivesShouldMatch(dominoes: List[((Int, Int), Int)]): Unit =
|
76
76
|
dominoes.tails foreach {
|
77
77
|
case (a@(_,x), i1)::(b@(y,_), i2)::_ =>
|
data/tracks/swift/Dangerfile
CHANGED