Skip to content

Commit

Permalink
Add change exercise (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom authored Jan 23, 2025
1 parent a1d1257 commit 1733535
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "change",
"name": "Change",
"uuid": "a625e5e9-43cf-4765-bd9a-e41135132258",
"practices": [],
"prerequisites": [],
"difficulty": 6
}
]
},
Expand Down
8 changes: 8 additions & 0 deletions exercises/practice/change/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Instructions

Determine the fewest number of coins to give a customer so that the sum of their values equals the correct amount of change.

## Examples

- 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].
- 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].
26 changes: 26 additions & 0 deletions exercises/practice/change/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Introduction

In the mystical village of Coinholt, you stand behind the counter of your bakery, arranging a fresh batch of pastries.
The door creaks open, and in walks Denara, a skilled merchant with a keen eye for quality goods.
After a quick meal, she slides a shimmering coin across the counter, representing a value of 100 units.

You smile, taking the coin, and glance at the total cost of the meal: 88 units.
That means you need to return 12 units in change.

Denara holds out her hand expectantly.
"Just give me the fewest coins," she says with a smile.
"My pouch is already full, and I don't want to risk losing them on the road."

You know you have a few options.
"We have Lumis (worth 10 units), Viras (worth 5 units), and Zenth (worth 2 units) available for change."

You quickly calculate the possibilities in your head:

- one Lumis (1 × 10 units) + one Zenth (1 × 2 units) = 2 coins total
- two Viras (2 × 5 units) + one Zenth (1 × 2 units) = 3 coins total
- six Zenth (6 × 2 units) = 6 coins total

"The best choice is two coins: one Lumis and one Zenth," you say, handing her the change.

Denara smiles, clearly impressed.
"As always, you've got it right."
19 changes: 19 additions & 0 deletions exercises/practice/change/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"erikschierboom"
],
"files": {
"solution": [
"change.R"
],
"test": [
"test_change.R"
],
"example": [
".meta/example.R"
]
},
"blurb": "Correctly determine change to be given using the least number of coins.",
"source": "Software Craftsmanship - Coin Change Kata",
"source_url": "https://web.archive.org/web/20130115115225/http://craftsmanship.sv.cmu.edu:80/exercises/coin-change-kata"
}
32 changes: 32 additions & 0 deletions exercises/practice/change/.meta/example.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
find_fewest_coins <- function(coins, target) {
if (target == 0) {
return(c())
}

stopifnot(target > 0)
stopifnot(min(coins) <= target)

min_amount_coins <- rep(NA, target) |> as.list()

for (amount in 1:target) {
for (coin in coins[coins <= amount]) {
if (amount - coin == 0) {
sub_amount_coins <- c()
} else {
sub_amount_coins <- min_amount_coins[[amount - coin]]
}
if (any(is.na(sub_amount_coins))) next

amount_coins <- min_amount_coins[[amount]]
if (!any(is.na(amount_coins)) &&
length(amount_coins) <= length(sub_amount_coins) + 1) {
next
}

min_amount_coins[[amount]] <- c(coin, sub_amount_coins)
}
}

stopifnot(!any(is.na(min_amount_coins[[target]])))
min_amount_coins[[target]]
}
49 changes: 49 additions & 0 deletions exercises/practice/change/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[d0ebd0e1-9d27-4609-a654-df5c0ba1d83a]
description = "change for 1 cent"

[36887bea-7f92-4a9c-b0cc-c0e886b3ecc8]
description = "single coin change"

[cef21ccc-0811-4e6e-af44-f011e7eab6c6]
description = "multiple coin change"

[d60952bc-0c1a-4571-bf0c-41be72690cb3]
description = "change with Lilliputian Coins"

[408390b9-fafa-4bb9-b608-ffe6036edb6c]
description = "change with Lower Elbonia Coins"

[7421a4cb-1c48-4bf9-99c7-7f049689132f]
description = "large target values"

[f79d2e9b-0ae3-4d6a-bb58-dc978b0dba28]
description = "possible change without unit coins available"

[9a166411-d35d-4f7f-a007-6724ac266178]
description = "another possible change without unit coins available"

[ce0f80d5-51c3-469d-818c-3e69dbd25f75]
description = "a greedy approach is not optimal"

[bbbcc154-e9e9-4209-a4db-dd6d81ec26bb]
description = "no coins make 0 change"

[c8b81d5a-49bd-4b61-af73-8ee5383a2ce1]
description = "error testing for change smaller than the smallest of coins"

[3c43e3e4-63f9-46ac-9476-a67516e98f68]
description = "error if no combination can add up to target"

[8fe1f076-9b2d-4f44-89fe-8a6ccd63c8f3]
description = "cannot find negative change values"
3 changes: 3 additions & 0 deletions exercises/practice/change/change.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
find_fewest_coins <- function(coins, target) {

}
85 changes: 85 additions & 0 deletions exercises/practice/change/test_change.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
source("./change.R")
library(testthat)

test_that("Change for 1 cent", {
coins <- c(1, 5, 10, 25)
target <- 1
expected <- c(1)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Single coin change", {
coins <- c(1, 5, 10, 25, 100)
target <- 25
expected <- c(25)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Multiple coin change", {
coins <- c(1, 5, 10, 25, 100)
target <- 15
expected <- c(5, 10)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Change with Lilliputian Coins", {
coins <- c(1, 4, 15, 20, 50)
target <- 23
expected <- c(4, 4, 15)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Change with Lower Elbonia Coins", {
coins <- c(1, 5, 10, 21, 25)
target <- 63
expected <- c(21, 21, 21)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Large target values", {
coins <- c(1, 2, 5, 10, 20, 50, 100)
target <- 999
expected <- c(
2, 2, 5, 20, 20, 50, 100, 100, 100, 100, 100, 100, 100, 100, 100
)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Possible change without unit coins available", {
coins <- c(2, 5, 10, 20, 50)
target <- 21
expected <- c(2, 2, 2, 5, 10)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Another possible change without unit coins available", {
coins <- c(4, 5)
target <- 27
expected <- c(4, 4, 4, 5, 5, 5)
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("No coins make 0 change", {
coins <- c(1, 5, 10, 21, 25)
target <- 0
expected <- c()
expect_equal(find_fewest_coins(coins, target), expected)
})

test_that("Error testing for change smaller than the smallest of coins", {
coins <- c(5, 10)
target <- 3
expect_error(find_fewest_coins(coins, target))
})

test_that("Error if no combination can add up to target", {
coins <- c(5, 10)
target <- 94
expect_error(find_fewest_coins(coins, target))
})

test_that("Cannot find negative change values", {
coins <- c(1, 2, 5)
target <- -5
expect_error(find_fewest_coins(coins, target))
})

0 comments on commit 1733535

Please sign in to comment.