Skip to content

Commit ec2843b

Browse files
Fix issues and speedup (#468)
* Update verify-exercises script * Fix issues and speedup
1 parent f69b866 commit ec2843b

6 files changed

Lines changed: 101 additions & 62 deletions

File tree

bin/verify-exercises

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,77 @@
11
#!/usr/bin/env bash
22

3-
set -euo pipefail
4-
5-
declare -i TEST_RESULT=0
6-
FAILED_EXERCISES=''
7-
8-
for example_file in $(find exercises/practice -type f -name '*.example.pl' | sort)
9-
do
10-
echo "${example_file}"
11-
example_dir=$(dirname "${example_file}")
12-
exercise_dir=$(dirname "${example_dir}")
13-
exercise=$(basename "${example_file}" .example.pl)
14-
echo '-------------------------------------------------------'
15-
echo "Testing ${exercise}"
16-
if ! swipl -f "${example_file}" \
17-
-s "${exercise_dir}/${exercise}_tests.plt" \
18-
-g run_tests,halt \
19-
-t 'halt(1)' \
20-
--on-error=status \
21-
-- --all; then
22-
TEST_RESULT=1
23-
FAILED_EXERCISES+="${example_file}\n"
24-
fi
25-
done
26-
27-
if [ "${TEST_RESULT}" -ne 0 ]; then
28-
echo "The following exercises failed:"
29-
printf "%b" "${FAILED_EXERCISES}"
30-
exit "${TEST_RESULT}"
31-
fi
3+
# Synopsis:
4+
# Verify that each exercise's example/exemplar solution passes the tests.
5+
# You can either verify all exercises or a single exercise.
6+
7+
# Example: verify all exercises
8+
# bin/verify-exercises
9+
10+
# Example: verify single exercise
11+
# bin/verify-exercises two-fer
12+
13+
set -eo pipefail
14+
15+
die() { echo "$*" >&2; exit 1; }
16+
17+
required_tool() {
18+
command -v "${1}" >/dev/null 2>&1 ||
19+
die "${1} is required but not installed. Please install it and make sure it's in your PATH."
20+
}
21+
22+
required_tool jq
23+
24+
copy_example_or_examplar_to_solution() {
25+
jq -c '[.files.solution, .files.exemplar // .files.example] | transpose | map({src: .[1], dst: .[0]}) | .[]' .meta/config.json \
26+
| while read -r src_and_dst; do
27+
cp "$(jq -r '.src' <<< "${src_and_dst}")" "$(jq -r '.dst' <<< "${src_and_dst}")"
28+
done
29+
}
30+
31+
run_tests() {
32+
local slug
33+
34+
slug="${1}"
35+
snake_name=$(echo "${slug}" | sed 's/-/_/g')
36+
swipl -f "${snake_name}.pl" -s "${snake_name}_tests.plt" -g run_tests,halt -t 'halt(1)' --on-error=status -- --all
37+
}
38+
39+
verify_exercise() {
40+
local dir
41+
local slug
42+
local tmp_dir
43+
44+
dir=$(realpath "${1}")
45+
slug=$(basename "${dir}")
46+
tmp_dir=$(mktemp -d -t "exercism-verify-${slug}-XXXXX")
47+
48+
echo "Verifying ${slug} exercise..."
49+
50+
(
51+
trap 'rm -rf "$tmp_dir"' EXIT # remove tempdir when subshell ends
52+
cp -r "${dir}/." "${tmp_dir}"
53+
cd "${tmp_dir}"
54+
55+
copy_example_or_examplar_to_solution
56+
run_tests "${slug}"
57+
)
58+
}
59+
60+
verify_exercises() {
61+
local exercise_slug
62+
63+
exercise_slug="${1}"
64+
65+
shopt -s nullglob
66+
count=0
67+
for exercise_dir in ./exercises/{concept,practice}/${exercise_slug}/; do
68+
if [[ -d "${exercise_dir}" ]]; then
69+
verify_exercise "${exercise_dir}"
70+
((++count))
71+
fi
72+
done
73+
((count > 0)) || die 'no matching exercises found!'
74+
}
75+
76+
exercise_slug="${1:-*}"
77+
verify_exercises "${exercise_slug}"
Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
1-
:- use_module(library(clpfd)).
2-
3-
generate_upper_constraint(_, [], []).
4-
generate_upper_constraint(Max, [Coin|Coins], [Amount|Amounts]) :-
5-
Amount in 0..Max,
6-
generate_upper_constraint(Coin, Coins, Amounts).
1+
fewest_coins(Coins, Target, Change) :-
2+
make_change(Target, Coins, [], Change).
73

8-
coins([], [], []).
9-
coins([Coin|Coins], [Amount|Amounts], Change) :-
10-
length(CoinChange, Amount),
11-
maplist(=(Coin), CoinChange),
12-
coins(Coins, Amounts, Rest),
13-
append(CoinChange, Rest, Change).
4+
make_change(Val, Coins, Wallet, Change) :-
5+
( Val=:=0
6+
-> sort(0, @=<, Wallet, Change)
7+
; include(choose(Val, Coins), Coins, Chosen),
8+
( max_list(Chosen, Coin)
9+
; min_list(Coins, Coin), Coin=<Val
10+
), !,
11+
append(Wallet, [Coin], Ching),
12+
make_change(Val-Coin, Coins, Ching, Change)
13+
).
1414

15-
fewest_coins(Coins, Target, Change) :-
16-
Target #>= 0,
17-
reverse(Coins, ReversedCoins),
18-
[Coin|_] = ReversedCoins,
19-
length(Coins, N),
20-
length(Amounts, N),
21-
Max #= div(Target, Coin),
22-
generate_upper_constraint(Max, ReversedCoins, ReversedAmounts),
23-
reverse(ReversedAmounts, Amounts),
24-
scalar_product(Coins, Amounts, #=, Target),
25-
sum(Amounts, #=, TotalCoins),
26-
labeling([min(TotalCoins)], Amounts),
27-
coins(Coins, Amounts, Change),
28-
!.
15+
choose(Val, Coins, Coin) :-
16+
Coin=<Val,
17+
Diff is Val-Coin,
18+
( Diff=:=0
19+
; \+ include({Diff}/[C]>>(C>1, 0 is Diff mod C), Coins, [])
20+
).

exercises/practice/dnd-character/dnd_character_tests.plt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pending :-
8484
between(3, 18, Wisdom),
8585
between(3, 18, Charisma),
8686
modifier(Constitution, Modifier),
87-
Hitpoints =:= 10 + Modifier.
87+
Hitpoints =:= 10 + Modifier,
88+
!.
8889

8990
:- end_tests(dnd_character).

exercises/practice/high-scores/.meta/high_scores.example.pl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
personal_top_three(Scores, TopThree) :-
88
sort(0, @>=, Scores, Sorted),
9-
top_three(Sorted, TopThree).
9+
top_three(Sorted, TopThree), !.
1010

1111
top_three([], []).
1212
top_three([X], [X]).

exercises/practice/triangle/.meta/triangle.example.pl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
triangle(Side1, Side2, Side3, "equilateral") :-
22
Side1 =:= Side2,
33
Side2 =:= Side3,
4-
valid_triangle(Side1, Side2, Side3).
4+
valid_triangle(Side1, Side2, Side3), !.
55

66
triangle(Side1, Side2, Side3, "isosceles") :-
77
(
88
Side1 =:= Side2
99
; Side1 =:= Side3
1010
; Side2 =:= Side3
1111
),
12-
valid_triangle(Side1, Side2, Side3).
12+
valid_triangle(Side1, Side2, Side3), !.
1313

1414
triangle(Side1, Side2, Side3, "scalene") :-
1515
\+ triangle(Side1, Side2, Side3, "equilateral"),

exercises/practice/wedding-woes/.meta/wedding_woes.example.pl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
likes(malena, esteban).
66
likes(gustavo, valeria).
77

8-
pairing(First, Second) :- likes(First, Second), likes(Second, First).
9-
pairing(First, Second) :- chatty(First); chatty(Second).
10-
seating(A, B, C, D, E) :- pairing(A, B), pairing(B, C), pairing(C, D), pairing(D, E), pairing(E, A).
8+
pairing(First, Second) :- likes(First, Second), likes(Second, First), !.
9+
pairing(First, Second) :- chatty(First), !; chatty(Second), !.
10+
seating(A, B, C, D, E) :- pairing(A, B), pairing(B, C), pairing(C, D), pairing(D, E), pairing(E, A), !.

0 commit comments

Comments
 (0)