Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,14 @@
"prerequisites": [],
"difficulty": 6
},
{
"slug": "book-store",
"name": "Book Store",
"uuid": "5cda9fbb-709f-44c0-bddf-5cf2652eff01",
"practices": [],
"prerequisites": [],
"difficulty": 7
},
{
"slug": "satellite",
"name": "Satellite",
Expand Down
5 changes: 5 additions & 0 deletions exercises/practice/book-store/.busted
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
return {
default = {
ROOT = { '.' }
}
}
61 changes: 61 additions & 0 deletions exercises/practice/book-store/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Instructions

To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases.

One copy of any of the five books costs $8.

If, however, you buy two different books, you get a 5% discount on those two books.

If you buy 3 different books, you get a 10% discount.

If you buy 4 different books, you get a 20% discount.

If you buy all 5, you get a 25% discount.

Note that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8.

Your mission is to write code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.

For example, how much does this basket of books cost?

- 2 copies of the first book
- 2 copies of the second book
- 2 copies of the third book
- 1 copy of the fourth book
- 1 copy of the fifth book

One way of grouping these 8 books is:

- 1 group of 5 (1st, 2nd,3rd, 4th, 5th)
- 1 group of 3 (1st, 2nd, 3rd)

This would give a total of:

- 5 books at a 25% discount
- 3 books at a 10% discount

Resulting in:

- 5 × (100% - 25%) × $8 = 5 × $6.00 = $30.00, plus
- 3 × (100% - 10%) × $8 = 3 × $7.20 = $21.60

Which equals $51.60.

However, a different way to group these 8 books is:

- 1 group of 4 books (1st, 2nd, 3rd, 4th)
- 1 group of 4 books (1st, 2nd, 3rd, 5th)

This would give a total of:

- 4 books at a 20% discount
- 4 books at a 20% discount

Resulting in:

- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60, plus
- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60

Which equals $51.20.

And $51.20 is the price with the biggest discount.
19 changes: 19 additions & 0 deletions exercises/practice/book-store/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"glennj"
],
"files": {
"solution": [
"book_store.moon"
],
"test": [
"book_store_spec.moon"
],
"example": [
".meta/example.moon"
]
},
"blurb": "To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts of multiple-book purchases.",
"source": "Inspired by the harry potter kata from Cyber-Dojo.",
"source_url": "https://cyber-dojo.org"
}
65 changes: 65 additions & 0 deletions exercises/practice/book-store/.meta/example.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fold from require 'moon'

BOOK_PRICE = 800
DISCOUNTED = {[0]: 1.0, 1.0, 0.95, 0.90, 0.80, 0.75}


contains = (list, item) ->
for i, elem in ipairs list
return i if elem == item
nil


filter = (list, pred) ->
fold {{}, table.unpack list}, (filtered, elem) ->
table.insert filtered, elem if pred elem
filtered


group = (basket) ->
bundles = {{}}
for book in *basket
added = false
for bundle in *bundles
if not contains bundle, book
table.insert bundle, book
added = true
break
if not added
table.insert bundles, {book}

return bundles


optimize = (bundles) ->
-- Two bundles of 4 are cheaper than a bundle of 5 plus a bundle of 3.
-- Look for a book in a 5-bundle that can be moved into a 3-bundle.

bundle5 = filter bundles, (bundle) -> #bundle == 5
return bundles if #bundle5 == 0

bundle3 = filter bundles, (bundle) -> #bundle == 3
return bundles if #bundle3 == 0

b5 = bundle5[1]
b3 = bundle3[1]

for book in *b5
idx = contains b3, book
if not idx
table.insert b3, book
table.remove b5, idx
break

return optimize bundles



total_price = (basket) ->
bundles = optimize group basket

fold {0, table.unpack bundles}, (price, bundle) ->
price + #bundle * BOOK_PRICE * DISCOUNTED[#bundle]


{ total: total_price }
13 changes: 13 additions & 0 deletions exercises/practice/book-store/.meta/spec_generator.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
int_list = (list) -> "{#{table.concat list, ', '}}"

{
module_name: 'BookStore',

generate_test: (case, level) ->
lines = {
"result = BookStore.#{case.property} #{int_list case.input.basket}",
"expected = #{case.expected}",
"assert.are.equal expected, result"
}
table.concat [indent line, level for line in *lines], '\n'
}
64 changes: 64 additions & 0 deletions exercises/practice/book-store/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 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.

[17146bd5-2e80-4557-ab4c-05632b6b0d01]
description = "Only a single book"

[cc2de9ac-ff2a-4efd-b7c7-bfe0f43271ce]
description = "Two of the same book"

[5a86eac0-45d2-46aa-bbf0-266b94393a1a]
description = "Empty basket"

[158bd19a-3db4-4468-ae85-e0638a688990]
description = "Two different books"

[f3833f6b-9332-4a1f-ad98-6c3f8e30e163]
description = "Three different books"

[1951a1db-2fb6-4cd1-a69a-f691b6dd30a2]
description = "Four different books"

[d70f6682-3019-4c3f-aede-83c6a8c647a3]
description = "Five different books"

[78cacb57-911a-45f1-be52-2a5bd428c634]
description = "Two groups of four is cheaper than group of five plus group of three"

[f808b5a4-e01f-4c0d-881f-f7b90d9739da]
description = "Two groups of four is cheaper than groups of five and three"

[fe96401c-5268-4be2-9d9e-19b76478007c]
description = "Group of four plus group of two is cheaper than two groups of three"

[68ea9b78-10ad-420e-a766-836a501d3633]
description = "Two each of first four books and one copy each of rest"

[c0a779d5-a40c-47ae-9828-a340e936b866]
description = "Two copies of each book"

[18fd86fe-08f1-4b68-969b-392b8af20513]
description = "Three copies of first book and two each of remaining"

[0b19a24d-e4cf-4ec8-9db2-8899a41af0da]
description = "Three each of first two books and two each of remaining books"

[bb376344-4fb2-49ab-ab85-e38d8354a58d]
description = "Four groups of four are cheaper than two groups each of five and three"

[5260ddde-2703-4915-b45a-e54dbbac4303]
description = "Check that groups of four are created properly even when there are more groups of three than groups of five"

[b0478278-c551-4747-b0fc-7e0be3158b1f]
description = "One group of one and four is cheaper than one group of two and three"

[cf868453-6484-4ae1-9dfc-f8ee85bbde01]
description = "One group of one and two plus three groups of four is cheaper than one group of each size"
4 changes: 4 additions & 0 deletions exercises/practice/book-store/book_store.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
total: (basket) ->
error 'Implement me'
}
92 changes: 92 additions & 0 deletions exercises/practice/book-store/book_store_spec.moon
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
BookStore = require 'book_store'

describe 'book-store', ->
it 'Only a single book', ->
result = BookStore.total {1}
expected = 800
assert.are.equal expected, result

pending 'Two of the same book', ->
result = BookStore.total {2, 2}
expected = 1600
assert.are.equal expected, result

pending 'Empty basket', ->
result = BookStore.total {}
expected = 0
assert.are.equal expected, result

pending 'Two different books', ->
result = BookStore.total {1, 2}
expected = 1520
assert.are.equal expected, result

pending 'Three different books', ->
result = BookStore.total {1, 2, 3}
expected = 2160
assert.are.equal expected, result

pending 'Four different books', ->
result = BookStore.total {1, 2, 3, 4}
expected = 2560
assert.are.equal expected, result

pending 'Five different books', ->
result = BookStore.total {1, 2, 3, 4, 5}
expected = 3000
assert.are.equal expected, result

pending 'Two groups of four is cheaper than group of five plus group of three', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 5}
expected = 5120
assert.are.equal expected, result

pending 'Two groups of four is cheaper than groups of five and three', ->
result = BookStore.total {1, 1, 2, 3, 4, 4, 5, 5}
expected = 5120
assert.are.equal expected, result

pending 'Group of four plus group of two is cheaper than two groups of three', ->
result = BookStore.total {1, 1, 2, 2, 3, 4}
expected = 4080
assert.are.equal expected, result

pending 'Two each of first four books and one copy each of rest', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5}
expected = 5560
assert.are.equal expected, result

pending 'Two copies of each book', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5}
expected = 6000
assert.are.equal expected, result

pending 'Three copies of first book and two each of remaining', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1}
expected = 6800
assert.are.equal expected, result

pending 'Three each of first two books and two each of remaining books', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2}
expected = 7520
assert.are.equal expected, result

pending 'Four groups of four are cheaper than two groups each of five and three', ->
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5}
expected = 10240
assert.are.equal expected, result

pending 'Check that groups of four are created properly even when there are more groups of three than groups of five', ->
result = BookStore.total {1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5}
expected = 14560
assert.are.equal expected, result

pending 'One group of one and four is cheaper than one group of two and three', ->
result = BookStore.total {1, 1, 2, 3, 4}
expected = 3360
assert.are.equal expected, result

pending 'One group of one and two plus three groups of four is cheaper than one group of each size', ->
result = BookStore.total {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5}
expected = 10000
assert.are.equal expected, result
Loading
Loading