Skip to content

Commit

Permalink
Add book-store exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode committed Sep 21, 2024
1 parent 240b298 commit 91a99cf
Show file tree
Hide file tree
Showing 13 changed files with 3,774 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,14 @@
"prerequisites": [],
"difficulty": 8
},
{
"slug": "book-store",
"name": "Book Store",
"uuid": "799fcb2c-939a-4e7d-b8df-c815acc545c7",
"practices": [],
"prerequisites": [],
"difficulty": 8
},
{
"slug": "knapsack",
"name": "Knapsack",
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.
2 changes: 2 additions & 0 deletions exercises/practice/book-store/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.o
tests
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.asm"
],
"test": [
"book_store_test.c"
],
"example": [
".meta/example.asm"
]
},
"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"
}
97 changes: 97 additions & 0 deletions exercises/practice/book-store/.meta/example.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
default rel

section .rodata
price: dq 0, 3000, 2560, 2160, 1520, 800

section .text
global total
total:
cld
sub rsp, 48 ; reserve stack space
xor rax, rax
mov rcx, 5

.zero:
mov [rsp + 8*rcx], rax ; set book tally to 0
loop .zero

mov rcx, rdi
jrcxz .sort ; skip read loop if there are no books

.read:
lodsw
inc qword [rsp + 8*rax] ; increment book tally
loop .read


.sort:
; Bubble sort
xor r8, r8 ; number of swaps during scan
mov rcx, 4

.scan:
mov r9, qword [rsp + 8*rcx]
mov r10, qword [rsp + 8*rcx + 8]
cmp r9, r10
jbe .next_pair

mov qword [rsp + 8*rcx], r10
mov qword [rsp + 8*rcx + 8], r9
inc r8 ; count swap

.next_pair:
loop .scan

test r8, r8
jnz .sort ; repeat until a scan has no swaps

.differences:
; Compute group counts
mov rdx, qword [rsp + 8] ; groups of 5
mov r8, qword [rsp + 16]
mov r9, qword [rsp + 24]
mov r10, qword [rsp + 32]
mov r11, qword [rsp + 40]
sub r11, r10
sub r10, r9
sub r9, r8
sub r8, rdx
mov qword [rsp + 16], r8 ; groups of 4
mov qword [rsp + 24], r9 ; groups of 3
mov qword [rsp + 32], r10 ; groups of 2
mov qword [rsp + 40], r11 ; groups of 1


; Two groups of 4 books are cheaper than a group of 5 books and a group of 4 books
mov r8, qword [rsp + 8] ; groups of 5 books
mov r9, qword [rsp + 16] ; groups of 4 books
mov r10, qword [rsp + 24] ; groups of 3 books
mov r11, r10
cmp r8, r10
cmovb r11, r8 ; r11 now has min(groups of 5 books, groups of 3 books)
sub r8, r11
sub r10, r11
shl r11, 1
add r9, r11
mov qword [rsp + 8], r8
mov qword [rsp + 16], r9
mov qword [rsp + 24], r10

xor r10, r10
lea r11, [price]
mov rcx, 5

.multiply:
mov rax, qword [rsp + rcx*8] ; number of groups
mov rdx, qword [r11 + rcx*8] ; price of each group
mul rdx
add r10, rax ; accumulate total price
loop .multiply

mov rax, r10
add rsp, 48 ; restore original stack pointer
ret

%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
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"
46 changes: 46 additions & 0 deletions exercises/practice/book-store/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
AS = nasm

CFLAGS = -g -Wall -Wextra -pedantic -Werror
LDFLAGS =
ASFLAGS = -g -F dwarf -Werror

ifeq ($(shell uname -s),Darwin)
ifeq ($(shell sysctl -n hw.optional.arm64 2>/dev/null),1)
ALL_CFLAGS = -target x86_64-apple-darwin
endif
ALL_LDFLAGS = -Wl,-pie
ALL_ASFLAGS = -f macho64 --prefix _
else
ALL_LDFLAGS = -pie -Wl,--fatal-warnings
ALL_ASFLAGS = -f elf64
endif

ALL_CFLAGS += -std=c99 -fPIE -m64 $(CFLAGS)
ALL_LDFLAGS += $(LDFLAGS)
ALL_ASFLAGS += $(ASFLAGS)

C_OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
AS_OBJS = $(patsubst %.asm,%.o,$(wildcard *.asm))
ALL_OBJS = $(filter-out example.o,$(C_OBJS) $(AS_OBJS) vendor/unity.o)

CC_CMD = $(CC) $(ALL_CFLAGS) -c -o $@ $<

all: tests
@./$<

tests: $(ALL_OBJS)
@$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $(ALL_OBJS)

%.o: %.asm
@$(AS) $(ALL_ASFLAGS) -o $@ $<

%.o: %.c
@$(CC_CMD)

vendor/unity.o: vendor/unity.c vendor/unity.h vendor/unity_internals.h
@$(CC_CMD)

clean:
@rm -f *.o vendor/*.o tests

.PHONY: all clean
9 changes: 9 additions & 0 deletions exercises/practice/book-store/book_store.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
section .text
global total
total:
; Provide your implementation here
ret

%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
Loading

0 comments on commit 91a99cf

Please sign in to comment.