Skip to content

Commit b44b4ba

Browse files
author
James Rucker
committed
Implement exercise book-store
1 parent 9fafb45 commit b44b4ba

File tree

6 files changed

+261
-0
lines changed

6 files changed

+261
-0
lines changed

config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,18 @@
923923
"strings",
924924
"logic"
925925
]
926+
},
927+
{
928+
"uuid": "0ec96460-08be-49a0-973a-4336f21b763c",
929+
"slug": "book-store",
930+
"core": false,
931+
"unlocked_by": null,
932+
"difficulty": 6,
933+
"topics": [
934+
"loops",
935+
"arrays",
936+
"logic"
937+
]
926938
}
927939
],
928940
"foregone": [
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require 'generator/exercise_case'
2+
3+
class BookStoreCase < Generator::ExerciseCase
4+
5+
def workload
6+
assert_equal { "BookStore.calculate_price(#{basket})" }
7+
end
8+
9+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module BookKeeping
2+
VERSION = 0
3+
end
4+
5+
class BookStore
6+
7+
GROUP_DISCOUNTS = [0, 0.05, 0.1, 0.2, 0.25]
8+
INDIVIDUAL_PRICE = 8
9+
10+
def self.calculate_price(basket)
11+
groups = []
12+
remaining_books = basket.dup
13+
14+
# Make as many groups of 4 as you can
15+
while (largest_group = remaining_books.uniq).length > 3
16+
group_of_four = largest_group[0..3]
17+
group_of_four.each {|book| remaining_books.delete_at(remaining_books.index(book)) }
18+
groups << group_of_four
19+
end
20+
21+
# Bump as many of them to groups of 5 as you can
22+
groups.each do |group|
23+
fifth_book = (remaining_books - group).first
24+
next unless fifth_book
25+
26+
group << fifth_book
27+
remaining_books.delete_at(remaining_books.index(fifth_book))
28+
end
29+
30+
# Make the largest groups you can with the remaining books
31+
while (new_group = remaining_books.uniq).any?
32+
new_group.each {|book| remaining_books.delete_at(remaining_books.index(book)) }
33+
groups << new_group
34+
end
35+
36+
groups.map {|group| group_price(group.length) }.sum
37+
end
38+
39+
private
40+
41+
def self.group_price(group_size)
42+
discount = GROUP_DISCOUNTS[group_size - 1]
43+
44+
group_size * INDIVIDUAL_PRICE * (1 - discount)
45+
end
46+
47+
end

exercises/book-store/README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Book Store
2+
3+
To try and encourage more sales of different books from a popular 5 book
4+
series, a bookshop has decided to offer discounts on multiple book purchases.
5+
6+
One copy of any of the five books costs $8.
7+
8+
If, however, you buy two different books, you get a 5%
9+
discount on those two books.
10+
11+
If you buy 3 different books, you get a 10% discount.
12+
13+
If you buy 4 different books, you get a 20% discount.
14+
15+
If you buy all 5, you get a 25% discount.
16+
17+
Note: that if you buy four books, of which 3 are
18+
different titles, you get a 10% discount on the 3 that
19+
form part of a set, but the fourth book still costs $8.
20+
21+
Your mission is to write a piece of code to calculate the
22+
price of any conceivable shopping basket (containing only
23+
books of the same series), giving as big a discount as
24+
possible.
25+
26+
For example, how much does this basket of books cost?
27+
28+
- 2 copies of the first book
29+
- 2 copies of the second book
30+
- 2 copies of the third book
31+
- 1 copy of the fourth book
32+
- 1 copy of the fifth book
33+
34+
One way of grouping these 8 books is:
35+
36+
- 1 group of 5 --> 25% discount (1st,2nd,3rd,4th,5th)
37+
- +1 group of 3 --> 10% discount (1st,2nd,3rd)
38+
39+
This would give a total of:
40+
41+
- 5 books at a 25% discount
42+
- +3 books at a 10% discount
43+
44+
Resulting in:
45+
46+
- 5 x (8 - 2.00) == 5 x 6.00 == $30.00
47+
- +3 x (8 - 0.80) == 3 x 7.20 == $21.60
48+
49+
For a total of $51.60
50+
51+
However, a different way to group these 8 books is:
52+
53+
- 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th)
54+
- +1 group of 4 books --> 20% discount (1st,2nd,3rd,5th)
55+
56+
This would give a total of:
57+
58+
- 4 books at a 20% discount
59+
- +4 books at a 20% discount
60+
61+
Resulting in:
62+
63+
- 4 x (8 - 1.60) == 4 x 6.40 == $25.60
64+
- +4 x (8 - 1.60) == 4 x 6.40 == $25.60
65+
66+
For a total of $51.20
67+
68+
And $51.20 is the price with the biggest discount.
69+
70+
* * * *
71+
72+
For installation and learning resources, refer to the
73+
[exercism help page](http://exercism.io/languages/ruby).
74+
75+
For running the tests provided, you will need the Minitest gem. Open a
76+
terminal window and run the following command to install minitest:
77+
78+
gem install minitest
79+
80+
If you would like color output, you can `require 'minitest/pride'` in
81+
the test file, or note the alternative instruction, below, for running
82+
the test file.
83+
84+
In order to run the test, you can run the test file from the exercise
85+
directory. For example, if the test suite is called
86+
`hello_world_test.rb`, you can run the following command:
87+
88+
ruby hello_world_test.rb
89+
90+
To include color from the command line:
91+
92+
ruby -r minitest/pride hello_world_test.rb
93+
94+
95+
## Source
96+
97+
Inspired by the harry potter kata from Cyber-Dojo. [http://cyber-dojo.org](http://cyber-dojo.org)
98+
99+
## Submitting Incomplete Solutions
100+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
require 'minitest/autorun'
2+
require_relative 'book_store'
3+
4+
# Common test data version: 1.0.1 087ad69
5+
class BookStoreTest < Minitest::Test
6+
def test_only_a_single_book
7+
# skip
8+
assert_equal 8.0, BookStore.calculate_price([1])
9+
end
10+
11+
def test_two_of_the_same_book
12+
skip
13+
assert_equal 16.0, BookStore.calculate_price([2, 2])
14+
end
15+
16+
def test_empty_basket
17+
skip
18+
assert_equal 0.0, BookStore.calculate_price([])
19+
end
20+
21+
def test_two_different_books
22+
skip
23+
assert_equal 15.2, BookStore.calculate_price([1, 2])
24+
end
25+
26+
def test_three_different_books
27+
skip
28+
assert_equal 21.6, BookStore.calculate_price([1, 2, 3])
29+
end
30+
31+
def test_four_different_books
32+
skip
33+
assert_equal 25.6, BookStore.calculate_price([1, 2, 3, 4])
34+
end
35+
36+
def test_five_different_books
37+
skip
38+
assert_equal 30.0, BookStore.calculate_price([1, 2, 3, 4, 5])
39+
end
40+
41+
def test_two_groups_of_four_is_cheaper_than_group_of_five_plus_group_of_three
42+
skip
43+
assert_equal 51.2, BookStore.calculate_price([1, 1, 2, 2, 3, 3, 4, 5])
44+
end
45+
46+
def test_group_of_four_plus_group_of_two_is_cheaper_than_two_groups_of_three
47+
skip
48+
assert_equal 40.8, BookStore.calculate_price([1, 1, 2, 2, 3, 4])
49+
end
50+
51+
def test_two_each_of_first_4_books_and_1_copy_each_of_rest
52+
skip
53+
assert_equal 55.6, BookStore.calculate_price([1, 1, 2, 2, 3, 3, 4, 4, 5])
54+
end
55+
56+
def test_two_copies_of_each_book
57+
skip
58+
assert_equal 60.0, BookStore.calculate_price([1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
59+
end
60+
61+
def test_three_copies_of_first_book_and_2_each_of_remaining
62+
skip
63+
assert_equal 68.0, BookStore.calculate_price([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1])
64+
end
65+
66+
def test_three_each_of_first_2_books_and_2_each_of_remaining_books
67+
skip
68+
assert_equal 75.2, BookStore.calculate_price([1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2])
69+
end
70+
71+
# Problems in exercism evolve over time, as we find better ways to ask
72+
# questions.
73+
# The version number refers to the version of the problem you solved,
74+
# not your solution.
75+
#
76+
# Define a constant named VERSION inside of the top level BookKeeping
77+
# module, which may be placed near the end of your file.
78+
#
79+
# In your file, it will look like this:
80+
#
81+
# module BookKeeping
82+
# VERSION = 1 # Where the version number matches the one in the test.
83+
# end
84+
#
85+
# If you are curious, read more about constants on RubyDoc:
86+
# http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html
87+
88+
def test_bookkeeping
89+
skip
90+
assert_equal 0, BookKeeping::VERSION
91+
end
92+
end

0 commit comments

Comments
 (0)