Skip to content

Commit 2da9692

Browse files
authored
add bookstore problem (#92)
* add bookstore problem * fix bookstore stub file, better readability on solution * book-store difficulty 3->5
1 parent 5e8f82a commit 2da9692

File tree

7 files changed

+355
-0
lines changed

7 files changed

+355
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@
179179
"prerequisites": [],
180180
"difficulty": 5
181181
},
182+
{
183+
"slug": "book-store",
184+
"name": "Book Store",
185+
"uuid": "590c6355-dbb4-44a0-a2d7-98490055c7e9",
186+
"practices": [],
187+
"prerequisites": [],
188+
"difficulty": 5
189+
},
182190
{
183191
"slug": "change",
184192
"name": "Change",
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+
"hucancode"
4+
],
5+
"files": {
6+
"solution": [
7+
"book_store.odin"
8+
],
9+
"test": [
10+
"book_store_test.odin"
11+
],
12+
"example": [
13+
".meta/example.odin"
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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package book_store
2+
3+
import "core:slice"
4+
5+
calculate :: proc(counts: []u32) -> u32 {
6+
grand_total: f32 = 0.0
7+
used: u32 = 0
8+
BASE_PRICE: f32 : 800.0
9+
n := len(counts)
10+
get_discount :: proc(book: int) -> f32 {
11+
switch book {
12+
case 2:
13+
return 0.95
14+
case 3:
15+
return 0.90
16+
case 4:
17+
return 0.80
18+
case 5:
19+
return 0.75
20+
case:
21+
return 1.0
22+
}
23+
}
24+
for i in 0 ..< n {
25+
m := n - i
26+
x := counts[i] - used
27+
used = counts[i]
28+
price := BASE_PRICE * f32(m) * f32(x)
29+
grand_total += price * get_discount(m)
30+
}
31+
return u32(grand_total)
32+
}
33+
34+
total :: proc(basket: []u32) -> u32 {
35+
counts: [5]u32
36+
for book in basket {
37+
counts[book - 1] += 1
38+
}
39+
slice.sort(counts[:])
40+
delta := min(counts[0], counts[2] - counts[1])
41+
counts[0] -= delta
42+
counts[1] += delta
43+
return calculate(counts[:])
44+
}
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package book_store
2+
3+
total :: proc(books: []u32) -> u32 {
4+
// Implement this procedure.
5+
return 0
6+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package book_store
2+
3+
import "core:testing"
4+
5+
@(test)
6+
test_only_a_single_book :: proc(t: ^testing.T) {
7+
basket := [?]u32{1}
8+
result := total(basket[:])
9+
expected: u32 = 800
10+
testing.expect_value(t, result, expected)
11+
}
12+
13+
@(test)
14+
test_two_of_the_same_book :: proc(t: ^testing.T) {
15+
basket := [?]u32{2, 2}
16+
result := total(basket[:])
17+
expected: u32 = 1600
18+
testing.expect_value(t, result, expected)
19+
}
20+
21+
@(test)
22+
test_empty_basket :: proc(t: ^testing.T) {
23+
basket := [?]u32{}
24+
result := total(basket[:])
25+
expected: u32 = 0
26+
testing.expect_value(t, result, expected)
27+
}
28+
29+
@(test)
30+
test_two_different_books :: proc(t: ^testing.T) {
31+
basket := [?]u32{1, 2}
32+
result := total(basket[:])
33+
expected: u32 = 1520
34+
testing.expect_value(t, result, expected)
35+
}
36+
37+
@(test)
38+
test_three_different_books :: proc(t: ^testing.T) {
39+
basket := [?]u32{1, 2, 3}
40+
result := total(basket[:])
41+
expected: u32 = 2160
42+
testing.expect_value(t, result, expected)
43+
}
44+
45+
@(test)
46+
test_four_different_books :: proc(t: ^testing.T) {
47+
basket := [?]u32{1, 2, 3, 4}
48+
result := total(basket[:])
49+
expected: u32 = 2560
50+
testing.expect_value(t, result, expected)
51+
}
52+
53+
@(test)
54+
test_five_different_books :: proc(t: ^testing.T) {
55+
basket := [?]u32{1, 2, 3, 4, 5}
56+
result := total(basket[:])
57+
expected: u32 = 3000
58+
testing.expect_value(t, result, expected)
59+
}
60+
61+
@(test)
62+
test_two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three :: proc(t: ^testing.T) {
63+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 5}
64+
result := total(basket[:])
65+
expected: u32 = 5120
66+
testing.expect_value(t, result, expected)
67+
}
68+
69+
@(test)
70+
test_two_groups_of_four_is_cheaper_than_groups_of_five_and_three :: proc(t: ^testing.T) {
71+
basket := [?]u32{1, 1, 2, 3, 4, 4, 5, 5}
72+
result := total(basket[:])
73+
expected: u32 = 5120
74+
testing.expect_value(t, result, expected)
75+
}
76+
77+
@(test)
78+
test_group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three :: proc(t: ^testing.T) {
79+
basket := [?]u32{1, 1, 2, 2, 3, 4}
80+
result := total(basket[:])
81+
expected: u32 = 4080
82+
testing.expect_value(t, result, expected)
83+
}
84+
85+
@(test)
86+
test_two_each_of_first_four_books_and_one_copy_each_of_rest :: proc(t: ^testing.T) {
87+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 4, 5}
88+
result := total(basket[:])
89+
expected: u32 = 5560
90+
testing.expect_value(t, result, expected)
91+
}
92+
93+
@(test)
94+
test_two_copies_of_each_book :: proc(t: ^testing.T) {
95+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 4, 5, 5}
96+
result := total(basket[:])
97+
expected: u32 = 6000
98+
testing.expect_value(t, result, expected)
99+
}
100+
101+
@(test)
102+
test_three_copies_of_first_book_and_two_each_of_remaining :: proc(t: ^testing.T) {
103+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1}
104+
result := total(basket[:])
105+
expected: u32 = 6800
106+
testing.expect_value(t, result, expected)
107+
}
108+
109+
@(test)
110+
test_three_each_of_first_two_books_and_two_each_of_remaining_books :: proc(t: ^testing.T) {
111+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2}
112+
result := total(basket[:])
113+
expected: u32 = 7520
114+
testing.expect_value(t, result, expected)
115+
}
116+
117+
@(test)
118+
test_four_groups_of_four_are_cheaper_than_two_groups_each_of_five_and_three :: proc(
119+
t: ^testing.T,
120+
) {
121+
basket := [?]u32{1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5}
122+
result := total(basket[:])
123+
expected: u32 = 10240
124+
testing.expect_value(t, result, expected)
125+
}
126+
127+
@(test)
128+
test_check_that_groups_of_four_are_created_properly_even_when_there_are_more_groups_of_three_than_groups_of_five :: proc(
129+
t: ^testing.T,
130+
) {
131+
basket := [?]u32{1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5}
132+
result := total(basket[:])
133+
expected: u32 = 14560
134+
testing.expect_value(t, result, expected)
135+
}
136+
137+
@(test)
138+
test_one_group_of_one_and_four_is_cheaper_than_one_group_of_two_and_three :: proc(t: ^testing.T) {
139+
basket := [?]u32{1, 1, 2, 3, 4}
140+
result := total(basket[:])
141+
expected: u32 = 3360
142+
testing.expect_value(t, result, expected)
143+
}
144+
145+
@(test)
146+
test_one_group_of_one_and_two_plus_three_groups_of_four_is_cheaper_than_one_group_of_each_size :: proc(
147+
t: ^testing.T,
148+
) {
149+
basket := [?]u32{1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5}
150+
result := total(basket[:])
151+
expected: u32 = 10000
152+
testing.expect_value(t, result, expected)
153+
}

0 commit comments

Comments
 (0)