Skip to content

Commit

Permalink
Add book-store exercise (#442)
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode authored Jan 30, 2025
1 parent 3b73b60 commit aff38f2
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@
],
"difficulty": 1
},
{
"slug": "book-store",
"name": "Book Store",
"uuid": "cc192edb-a5d5-4f0d-a585-324641235cd5",
"practices": [],
"prerequisites": [],
"difficulty": 7
},
{
"slug": "pig-latin",
"name": "Pig Latin",
Expand Down
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": [
"keiravillekode"
],
"files": {
"solution": [
"book_store.zig"
],
"test": [
"test_book_store.zig"
],
"example": [
".meta/example.zig"
]
},
"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"
}
24 changes: 24 additions & 0 deletions exercises/practice/book-store/.meta/example.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const std = @import("std");

pub fn total(basket: []const u32) u32 {
var tally: [5]u32 = .{ 0, 0, 0, 0, 0 };
for (basket) |book| {
tally[book - 1] += 1;
}

std.mem.sort(u32, &tally, {}, comptime std.sort.desc(u32));

var five = tally[4];
var four = tally[3] - tally[4];
var three = tally[2] - tally[3];
const two = tally[1] - tally[2];
const one = tally[0] - tally[1];

// Two groups of four are cheaper than a group of five plus a group of three.
const adjustment = @min(three, five);
five -= adjustment;
three -= adjustment;
four += 2 * adjustment;

return 5 * five * 600 + 4 * four * 640 + 3 * three * 720 + 2 * two * 760 + 1 * one * 800;
}
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.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn total(basket: []const u32) u32 {
_ = basket;
@compileError("please implement the total function");
}
94 changes: 94 additions & 0 deletions exercises/practice/book-store/test_book_store.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const std = @import("std");
const testing = std.testing;

const book_store = @import("book_store.zig");

test "Only a single book" {
const basket = &[_]u32{1};
try testing.expectEqual(800, book_store.total(basket));
}

test "Two of the same book" {
const basket = &[_]u32{ 2, 2 };
try testing.expectEqual(1600, book_store.total(basket));
}

test "Empty basket" {
const basket = &[_]u32{};
try testing.expectEqual(0, book_store.total(basket));
}

test "Two different books" {
const basket = &[_]u32{ 1, 2 };
try testing.expectEqual(1520, book_store.total(basket));
}

test "Three different books" {
const basket = &[_]u32{ 1, 2, 3 };
try testing.expectEqual(2160, book_store.total(basket));
}

test "Four different books" {
const basket = &[_]u32{ 1, 2, 3, 4 };
try testing.expectEqual(2560, book_store.total(basket));
}

test "Five different books" {
const basket = &[_]u32{ 1, 2, 3, 4, 5 };
try testing.expectEqual(3000, book_store.total(basket));
}

test "Two groups of four is cheaper than group of five plus group of three" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 5 };
try testing.expectEqual(5120, book_store.total(basket));
}

test "Two groups of four is cheaper than groups of five and three" {
const basket = &[_]u32{ 1, 1, 2, 3, 4, 4, 5, 5 };
try testing.expectEqual(5120, book_store.total(basket));
}

test "Group of four plus group of two is cheaper than two groups of three" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 4 };
try testing.expectEqual(4080, book_store.total(basket));
}

test "Two each of first four books and one copy each of rest" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 4, 5 };
try testing.expectEqual(5560, book_store.total(basket));
}

test "Two copies of each book" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
try testing.expectEqual(6000, book_store.total(basket));
}

test "Three copies of first book and two each of remaining" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1 };
try testing.expectEqual(6800, book_store.total(basket));
}

test "Three each of first two books and two each of remaining books" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2 };
try testing.expectEqual(7520, book_store.total(basket));
}

test "Four groups of four are cheaper than two groups each of five and three" {
const basket = &[_]u32{ 1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5 };
try testing.expectEqual(10240, book_store.total(basket));
}

test "Check that groups of four are created properly even when there are more groups of three than groups of five" {
const basket = &[_]u32{ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5 };
try testing.expectEqual(14560, book_store.total(basket));
}

test "One group of one and four is cheaper than one group of two and three" {
const basket = &[_]u32{ 1, 1, 2, 3, 4 };
try testing.expectEqual(3360, book_store.total(basket));
}

test "One group of one and two plus three groups of four is cheaper than one group of each size" {
const basket = &[_]u32{ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5 };
try testing.expectEqual(10000, book_store.total(basket));
}

0 comments on commit aff38f2

Please sign in to comment.