Skip to content

Commit faf0aee

Browse files
Add change exercise
1 parent 483fc2f commit faf0aee

File tree

12 files changed

+1107
-0
lines changed

12 files changed

+1107
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,14 @@
580580
"prerequisites": [],
581581
"difficulty": 5
582582
},
583+
{
584+
"slug": "change",
585+
"name": "Change",
586+
"uuid": "12729eeb-5129-4c4e-99be-fb9ad623d21a",
587+
"practices": [],
588+
"prerequisites": [],
589+
"difficulty": 6
590+
},
583591
{
584592
"slug": "intergalactic-transmission",
585593
"name": "Intergalactic Transmission",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Instructions
2+
3+
Determine the fewest number of coins to give a customer so that the sum of their values equals the correct amount of change.
4+
5+
## Examples
6+
7+
- An amount of 15 with available coin values [1, 5, 10, 25, 100] should return one coin of value 5 and one coin of value 10, or [5, 10].
8+
- An amount of 40 with available coin values [1, 5, 10, 25, 100] should return one coin of value 5, one coin of value 10, and one coin of value 25, or [5, 10, 25].
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Introduction
2+
3+
In the mystical village of Coinholt, you stand behind the counter of your bakery, arranging a fresh batch of pastries.
4+
The door creaks open, and in walks Denara, a skilled merchant with a keen eye for quality goods.
5+
After a quick meal, she slides a shimmering coin across the counter, representing a value of 100 units.
6+
7+
You smile, taking the coin, and glance at the total cost of the meal: 88 units.
8+
That means you need to return 12 units in change.
9+
10+
Denara holds out her hand expectantly.
11+
"Just give me the fewest coins," she says with a smile.
12+
"My pouch is already full, and I don't want to risk losing them on the road."
13+
14+
You know you have a few options.
15+
"We have Lumis (worth 10 units), Viras (worth 5 units), and Zenth (worth 2 units) available for change."
16+
17+
You quickly calculate the possibilities in your head:
18+
19+
- one Lumis (1 × 10 units) + one Zenth (1 × 2 units) = 2 coins total
20+
- two Viras (2 × 5 units) + one Zenth (1 × 2 units) = 3 coins total
21+
- six Zenth (6 × 2 units) = 6 coins total
22+
23+
"The best choice is two coins: one Lumis and one Zenth," you say, handing her the change.
24+
25+
Denara smiles, clearly impressed.
26+
"As always, you've got it right."
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"keiravillekode"
4+
],
5+
"files": {
6+
"solution": [
7+
"Change.pas"
8+
],
9+
"test": [
10+
"TestCases.pas"
11+
],
12+
"example": [
13+
".meta/example.pas"
14+
]
15+
},
16+
"blurb": "Correctly determine change to be given using the least number of coins.",
17+
"source": "Software Craftsmanship - Coin Change Kata",
18+
"source_url": "https://web.archive.org/web/20130115115225/http://craftsmanship.sv.cmu.edu:80/exercises/coin-change-kata"
19+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
unit Change;
2+
3+
{$mode ObjFPC}{$H+}
4+
5+
interface
6+
7+
type
8+
TIntArray = Array Of Integer;
9+
10+
function findFewestCoins(
11+
const coins : TIntArray;
12+
target : Integer
13+
) : TIntArray;
14+
15+
implementation
16+
17+
uses SysUtils;
18+
19+
function findFewestCoins(
20+
const coins : TIntArray;
21+
target : Integer
22+
) : TIntArray;
23+
var
24+
table : TIntArray = ();
25+
i : Integer;
26+
j : Integer;
27+
coin : Integer;
28+
best : Integer;
29+
begin
30+
if target < 0 then
31+
raise ENotImplemented.Create('target cannot be negative');
32+
33+
SetLength(table, target + 1);
34+
table[0] := 0;
35+
for i := 1 to target do
36+
begin
37+
table[i] := target + 1;
38+
for j := low(coins) to high(coins) do
39+
begin
40+
coin := coins[j];
41+
if (coin <= i) and (table[i] > table[i - coin]) then
42+
table[i] := table[i - coin] + 1;
43+
end;
44+
end;
45+
46+
if table[target] = target + 1 then
47+
raise ENotImplemented.Create('cannot make target with given coins');
48+
49+
result := [];
50+
while target > 0 do
51+
begin
52+
best := -1;
53+
for j := low(coins) to high(coins) do
54+
begin
55+
coin := coins[j];
56+
if (coin <= target) and ((best = -1) or (table[target - coin] < table[target - best])) then
57+
best := coin;
58+
end;
59+
insert(best, result, length(result));
60+
target := target - best;
61+
end;
62+
end;
63+
64+
end.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[d0ebd0e1-9d27-4609-a654-df5c0ba1d83a]
13+
description = "change for 1 cent"
14+
15+
[36887bea-7f92-4a9c-b0cc-c0e886b3ecc8]
16+
description = "single coin change"
17+
18+
[cef21ccc-0811-4e6e-af44-f011e7eab6c6]
19+
description = "multiple coin change"
20+
21+
[d60952bc-0c1a-4571-bf0c-41be72690cb3]
22+
description = "change with Lilliputian Coins"
23+
24+
[408390b9-fafa-4bb9-b608-ffe6036edb6c]
25+
description = "change with Lower Elbonia Coins"
26+
27+
[7421a4cb-1c48-4bf9-99c7-7f049689132f]
28+
description = "large target values"
29+
30+
[f79d2e9b-0ae3-4d6a-bb58-dc978b0dba28]
31+
description = "possible change without unit coins available"
32+
33+
[9a166411-d35d-4f7f-a007-6724ac266178]
34+
description = "another possible change without unit coins available"
35+
36+
[ce0f80d5-51c3-469d-818c-3e69dbd25f75]
37+
description = "a greedy approach is not optimal"
38+
39+
[bbbcc154-e9e9-4209-a4db-dd6d81ec26bb]
40+
description = "no coins make 0 change"
41+
42+
[c8b81d5a-49bd-4b61-af73-8ee5383a2ce1]
43+
description = "error testing for change smaller than the smallest of coins"
44+
45+
[3c43e3e4-63f9-46ac-9476-a67516e98f68]
46+
description = "error if no combination can add up to target"
47+
48+
[8fe1f076-9b2d-4f44-89fe-8a6ccd63c8f3]
49+
description = "cannot find negative change values"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
unit Change;
2+
3+
{$mode ObjFPC}{$H+}
4+
5+
interface
6+
7+
type
8+
TIntArray = Array Of Integer;
9+
10+
function findFewestCoins(
11+
const coins : TIntArray;
12+
const target : Integer
13+
) : TIntArray;
14+
15+
implementation
16+
17+
uses SysUtils;
18+
19+
function findFewestCoins(
20+
const coins : TIntArray;
21+
const target : Integer
22+
) : TIntArray;
23+
begin
24+
25+
raise ENotImplemented.Create('Please implement your solution.'); result := [length(coins), target];
26+
27+
end;
28+
29+
end.

exercises/practice/change/Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
SHELL = /bin/bash
2+
MAKEFLAGS += --no-print-directory
3+
DESTDIR = build
4+
EXECUTABLE = $(DESTDIR)/test
5+
COMMAND = fpc -l- -v0 -g -gl -Sa -Cr -Sehnw -Fu./lib test.pas -FE"./$(DESTDIR)"
6+
7+
.ONESHELL:
8+
9+
test:
10+
@mkdir -p "./$(DESTDIR)"
11+
@cp -r ./lib "./$(DESTDIR)"
12+
@$(COMMAND) && ./$(EXECUTABLE) $(test)
13+
14+
clean:
15+
@rm -fr "./$(DESTDIR)"
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
unit TestCases;
2+
3+
{$mode ObjFPC}{$H+}
4+
5+
interface
6+
7+
uses Classes, SysUtils, FPCUnit, TestRegistry, FPCUnitTestUtils;
8+
9+
type
10+
ChangeTest = class(TTestCase)
11+
published
12+
procedure change_for_1_cent;
13+
procedure single_coin_change;
14+
procedure multiple_coin_change;
15+
procedure change_with_lilliputian_coins;
16+
procedure change_with_lower_elbonia_coins;
17+
procedure large_target_values;
18+
procedure possible_change_without_unit_coins_available;
19+
procedure another_possible_change_without_unit_coins_available;
20+
procedure a_greedy_approach_is_not_optimal;
21+
procedure no_coins_make_0_change;
22+
procedure error_testing_for_change_smaller_than_the_smallest_of_coins;
23+
procedure error_if_no_combination_can_add_up_to_target;
24+
procedure cannot_find_negative_change_values;
25+
end;
26+
27+
implementation
28+
29+
uses Change;
30+
31+
procedure TapAssertExpectionMessage(
32+
ACase : TTestCase ;
33+
const AMessage : string ;
34+
const Expected : string ;
35+
const coins : TIntArray ;
36+
const target : Integer
37+
);
38+
var
39+
JsonMsg : string;
40+
actual : string;
41+
begin
42+
actual := '';
43+
try
44+
findFewestCoins(coins, target);
45+
except
46+
on E: Exception do actual := E.Message;
47+
end;
48+
JsonMsg := EncodeJsonMessage(AMessage, expected, actual);
49+
ACase.AssertTrue(JsonMsg, expected = actual);
50+
end;
51+
52+
// d0ebd0e1-9d27-4609-a654-df5c0ba1d83a
53+
procedure ChangeTest.change_for_1_cent;
54+
begin
55+
TapAssertTrue(Self, 'change for 1 cent', [1], Change.findFewestCoins([1,5,10,25], 1));
56+
end;
57+
58+
// 36887bea-7f92-4a9c-b0cc-c0e886b3ecc8
59+
procedure ChangeTest.single_coin_change;
60+
begin
61+
TapAssertTrue(Self, 'single coin change', [25], Change.findFewestCoins([1,5,10,25,100], 25));
62+
end;
63+
64+
// cef21ccc-0811-4e6e-af44-f011e7eab6c6
65+
procedure ChangeTest.multiple_coin_change;
66+
begin
67+
TapAssertTrue(Self, 'multiple coin change', [5,10], Change.findFewestCoins([1,5,10,25,100], 15));
68+
end;
69+
70+
// d60952bc-0c1a-4571-bf0c-41be72690cb3
71+
procedure ChangeTest.change_with_lilliputian_coins;
72+
begin
73+
TapAssertTrue(Self, 'change with Lilliputian Coins', [4,4,15], Change.findFewestCoins([1,4,15,20,50], 23));
74+
end;
75+
76+
// 408390b9-fafa-4bb9-b608-ffe6036edb6c
77+
procedure ChangeTest.change_with_lower_elbonia_coins;
78+
begin
79+
TapAssertTrue(Self, 'change with Lower Elbonia Coins', [21,21,21], Change.findFewestCoins([1,5,10,21,25], 63));
80+
end;
81+
82+
// 7421a4cb-1c48-4bf9-99c7-7f049689132f
83+
procedure ChangeTest.large_target_values;
84+
begin
85+
TapAssertTrue(Self, 'large target values', [2,2,5,20,20,50,100,100,100,100,100,100,100,100,100], Change.findFewestCoins([1,2,5,10,20,50,100], 999));
86+
end;
87+
88+
// f79d2e9b-0ae3-4d6a-bb58-dc978b0dba28
89+
procedure ChangeTest.possible_change_without_unit_coins_available;
90+
begin
91+
TapAssertTrue(Self, 'possible change without unit coins available', [2,2,2,5,10], Change.findFewestCoins([2,5,10,20,50], 21));
92+
end;
93+
94+
// 9a166411-d35d-4f7f-a007-6724ac266178
95+
procedure ChangeTest.another_possible_change_without_unit_coins_available;
96+
begin
97+
TapAssertTrue(Self, 'another possible change without unit coins available', [4,4,4,5,5,5], Change.findFewestCoins([4,5], 27));
98+
end;
99+
100+
// ce0f80d5-51c3-469d-818c-3e69dbd25f75
101+
procedure ChangeTest.a_greedy_approach_is_not_optimal;
102+
begin
103+
TapAssertTrue(Self, 'a greedy approach is not optimal', [10,10], Change.findFewestCoins([1,10,11], 20));
104+
end;
105+
106+
// bbbcc154-e9e9-4209-a4db-dd6d81ec26bb
107+
procedure ChangeTest.no_coins_make_0_change;
108+
begin
109+
TapAssertTrue(Self, 'no coins make 0 change', [], Change.findFewestCoins([1,5,10,21,25], 0));
110+
end;
111+
112+
// c8b81d5a-49bd-4b61-af73-8ee5383a2ce1
113+
procedure ChangeTest.error_testing_for_change_smaller_than_the_smallest_of_coins;
114+
begin
115+
TapAssertExpectionMessage(Self, 'error testing for change smaller than the smallest of coins', 'cannot make target with given coins', [5,10], 3);
116+
end;
117+
118+
// 3c43e3e4-63f9-46ac-9476-a67516e98f68
119+
procedure ChangeTest.error_if_no_combination_can_add_up_to_target;
120+
begin
121+
TapAssertExpectionMessage(Self, 'error if no combination can add up to target', 'cannot make target with given coins', [5,10], 94);
122+
end;
123+
124+
// 8fe1f076-9b2d-4f44-89fe-8a6ccd63c8f3
125+
procedure ChangeTest.cannot_find_negative_change_values;
126+
begin
127+
TapAssertExpectionMessage(Self, 'cannot find negative change values', 'target cannot be negative', [1,2,5], -5);
128+
end;
129+
130+
initialization
131+
RegisterTest(ChangeTest);
132+
133+
end.

0 commit comments

Comments
 (0)