Skip to content

Commit a77d334

Browse files
authored
Add book-script (#69)
1 parent e040300 commit a77d334

File tree

10 files changed

+458
-0
lines changed

10 files changed

+458
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,14 @@
495495
"prerequisites": [],
496496
"difficulty": 6
497497
},
498+
{
499+
"slug": "book-store",
500+
"name": "Book Store",
501+
"uuid": "5cda9fbb-709f-44c0-bddf-5cf2652eff01",
502+
"practices": [],
503+
"prerequisites": [],
504+
"difficulty": 7
505+
},
498506
{
499507
"slug": "satellite",
500508
"name": "Satellite",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Instructions
2+
3+
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.
4+
5+
One copy of any of the five books costs $8.
6+
7+
If, however, you buy two different books, you get a 5% discount on those two books.
8+
9+
If you buy 3 different books, you get a 10% discount.
10+
11+
If you buy 4 different books, you get a 20% discount.
12+
13+
If you buy all 5, you get a 25% discount.
14+
15+
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.
16+
17+
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.
18+
19+
For example, how much does this basket of books cost?
20+
21+
- 2 copies of the first book
22+
- 2 copies of the second book
23+
- 2 copies of the third book
24+
- 1 copy of the fourth book
25+
- 1 copy of the fifth book
26+
27+
One way of grouping these 8 books is:
28+
29+
- 1 group of 5 (1st, 2nd,3rd, 4th, 5th)
30+
- 1 group of 3 (1st, 2nd, 3rd)
31+
32+
This would give a total of:
33+
34+
- 5 books at a 25% discount
35+
- 3 books at a 10% discount
36+
37+
Resulting in:
38+
39+
- 5 × (100% - 25%) × $8 = 5 × $6.00 = $30.00, plus
40+
- 3 × (100% - 10%) × $8 = 3 × $7.20 = $21.60
41+
42+
Which equals $51.60.
43+
44+
However, a different way to group these 8 books is:
45+
46+
- 1 group of 4 books (1st, 2nd, 3rd, 4th)
47+
- 1 group of 4 books (1st, 2nd, 3rd, 5th)
48+
49+
This would give a total of:
50+
51+
- 4 books at a 20% discount
52+
- 4 books at a 20% discount
53+
54+
Resulting in:
55+
56+
- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60, plus
57+
- 4 × (100% - 20%) × $8 = 4 × $6.40 = $25.60
58+
59+
Which equals $51.20.
60+
61+
And $51.20 is the price with the biggest discount.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"glennj"
4+
],
5+
"files": {
6+
"solution": [
7+
"book_store.moon"
8+
],
9+
"test": [
10+
"book_store_spec.moon"
11+
],
12+
"example": [
13+
".meta/example.moon"
14+
]
15+
},
16+
"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.",
17+
"source": "Inspired by the harry potter kata from Cyber-Dojo.",
18+
"source_url": "https://cyber-dojo.org"
19+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import fold from require 'moon'
2+
3+
BOOK_PRICE = 800
4+
DISCOUNTED = {[0]: 1.0, 1.0, 0.95, 0.90, 0.80, 0.75}
5+
6+
7+
contains = (list, item) ->
8+
for i, elem in ipairs list
9+
return i if elem == item
10+
nil
11+
12+
13+
filter = (list, pred) ->
14+
fold {{}, table.unpack list}, (filtered, elem) ->
15+
table.insert filtered, elem if pred elem
16+
filtered
17+
18+
19+
group = (basket) ->
20+
bundles = {{}}
21+
for book in *basket
22+
added = false
23+
for bundle in *bundles
24+
if not contains bundle, book
25+
table.insert bundle, book
26+
added = true
27+
break
28+
if not added
29+
table.insert bundles, {book}
30+
31+
return bundles
32+
33+
34+
optimize = (bundles) ->
35+
-- Two bundles of 4 are cheaper than a bundle of 5 plus a bundle of 3.
36+
-- Look for a book in a 5-bundle that can be moved into a 3-bundle.
37+
38+
bundle5 = filter bundles, (bundle) -> #bundle == 5
39+
return bundles if #bundle5 == 0
40+
41+
bundle3 = filter bundles, (bundle) -> #bundle == 3
42+
return bundles if #bundle3 == 0
43+
44+
b5 = bundle5[1]
45+
b3 = bundle3[1]
46+
47+
for book in *b5
48+
idx = contains b3, book
49+
if not idx
50+
table.insert b3, book
51+
table.remove b5, idx
52+
break
53+
54+
return optimize bundles
55+
56+
57+
58+
total_price = (basket) ->
59+
bundles = optimize group basket
60+
61+
fold {0, table.unpack bundles}, (price, bundle) ->
62+
price + #bundle * BOOK_PRICE * DISCOUNTED[#bundle]
63+
64+
65+
{ total: total_price }
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int_list = (list) -> "{#{table.concat list, ', '}}"
2+
3+
{
4+
module_name: 'BookStore',
5+
6+
generate_test: (case, level) ->
7+
lines = {
8+
"result = BookStore.#{case.property} #{int_list case.input.basket}",
9+
"expected = #{case.expected}",
10+
"assert.are.equal expected, result"
11+
}
12+
table.concat [indent line, level for line in *lines], '\n'
13+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
[17146bd5-2e80-4557-ab4c-05632b6b0d01]
13+
description = "Only a single book"
14+
15+
[cc2de9ac-ff2a-4efd-b7c7-bfe0f43271ce]
16+
description = "Two of the same book"
17+
18+
[5a86eac0-45d2-46aa-bbf0-266b94393a1a]
19+
description = "Empty basket"
20+
21+
[158bd19a-3db4-4468-ae85-e0638a688990]
22+
description = "Two different books"
23+
24+
[f3833f6b-9332-4a1f-ad98-6c3f8e30e163]
25+
description = "Three different books"
26+
27+
[1951a1db-2fb6-4cd1-a69a-f691b6dd30a2]
28+
description = "Four different books"
29+
30+
[d70f6682-3019-4c3f-aede-83c6a8c647a3]
31+
description = "Five different books"
32+
33+
[78cacb57-911a-45f1-be52-2a5bd428c634]
34+
description = "Two groups of four is cheaper than group of five plus group of three"
35+
36+
[f808b5a4-e01f-4c0d-881f-f7b90d9739da]
37+
description = "Two groups of four is cheaper than groups of five and three"
38+
39+
[fe96401c-5268-4be2-9d9e-19b76478007c]
40+
description = "Group of four plus group of two is cheaper than two groups of three"
41+
42+
[68ea9b78-10ad-420e-a766-836a501d3633]
43+
description = "Two each of first four books and one copy each of rest"
44+
45+
[c0a779d5-a40c-47ae-9828-a340e936b866]
46+
description = "Two copies of each book"
47+
48+
[18fd86fe-08f1-4b68-969b-392b8af20513]
49+
description = "Three copies of first book and two each of remaining"
50+
51+
[0b19a24d-e4cf-4ec8-9db2-8899a41af0da]
52+
description = "Three each of first two books and two each of remaining books"
53+
54+
[bb376344-4fb2-49ab-ab85-e38d8354a58d]
55+
description = "Four groups of four are cheaper than two groups each of five and three"
56+
57+
[5260ddde-2703-4915-b45a-e54dbbac4303]
58+
description = "Check that groups of four are created properly even when there are more groups of three than groups of five"
59+
60+
[b0478278-c551-4747-b0fc-7e0be3158b1f]
61+
description = "One group of one and four is cheaper than one group of two and three"
62+
63+
[cf868453-6484-4ae1-9dfc-f8ee85bbde01]
64+
description = "One group of one and two plus three groups of four is cheaper than one group of each size"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
total: (basket) ->
3+
error 'Implement me'
4+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
BookStore = require 'book_store'
2+
3+
describe 'book-store', ->
4+
it 'Only a single book', ->
5+
result = BookStore.total {1}
6+
expected = 800
7+
assert.are.equal expected, result
8+
9+
pending 'Two of the same book', ->
10+
result = BookStore.total {2, 2}
11+
expected = 1600
12+
assert.are.equal expected, result
13+
14+
pending 'Empty basket', ->
15+
result = BookStore.total {}
16+
expected = 0
17+
assert.are.equal expected, result
18+
19+
pending 'Two different books', ->
20+
result = BookStore.total {1, 2}
21+
expected = 1520
22+
assert.are.equal expected, result
23+
24+
pending 'Three different books', ->
25+
result = BookStore.total {1, 2, 3}
26+
expected = 2160
27+
assert.are.equal expected, result
28+
29+
pending 'Four different books', ->
30+
result = BookStore.total {1, 2, 3, 4}
31+
expected = 2560
32+
assert.are.equal expected, result
33+
34+
pending 'Five different books', ->
35+
result = BookStore.total {1, 2, 3, 4, 5}
36+
expected = 3000
37+
assert.are.equal expected, result
38+
39+
pending 'Two groups of four is cheaper than group of five plus group of three', ->
40+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 5}
41+
expected = 5120
42+
assert.are.equal expected, result
43+
44+
pending 'Two groups of four is cheaper than groups of five and three', ->
45+
result = BookStore.total {1, 1, 2, 3, 4, 4, 5, 5}
46+
expected = 5120
47+
assert.are.equal expected, result
48+
49+
pending 'Group of four plus group of two is cheaper than two groups of three', ->
50+
result = BookStore.total {1, 1, 2, 2, 3, 4}
51+
expected = 4080
52+
assert.are.equal expected, result
53+
54+
pending 'Two each of first four books and one copy each of rest', ->
55+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5}
56+
expected = 5560
57+
assert.are.equal expected, result
58+
59+
pending 'Two copies of each book', ->
60+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5}
61+
expected = 6000
62+
assert.are.equal expected, result
63+
64+
pending 'Three copies of first book and two each of remaining', ->
65+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1}
66+
expected = 6800
67+
assert.are.equal expected, result
68+
69+
pending 'Three each of first two books and two each of remaining books', ->
70+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2}
71+
expected = 7520
72+
assert.are.equal expected, result
73+
74+
pending 'Four groups of four are cheaper than two groups each of five and three', ->
75+
result = BookStore.total {1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5}
76+
expected = 10240
77+
assert.are.equal expected, result
78+
79+
pending 'Check that groups of four are created properly even when there are more groups of three than groups of five', ->
80+
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}
81+
expected = 14560
82+
assert.are.equal expected, result
83+
84+
pending 'One group of one and four is cheaper than one group of two and three', ->
85+
result = BookStore.total {1, 1, 2, 3, 4}
86+
expected = 3360
87+
assert.are.equal expected, result
88+
89+
pending 'One group of one and two plus three groups of four is cheaper than one group of each size', ->
90+
result = BookStore.total {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5}
91+
expected = 10000
92+
assert.are.equal expected, result

0 commit comments

Comments
 (0)