From f00dfa5e66dea14b442517672c65b5781899d02c Mon Sep 17 00:00:00 2001 From: bau-mann <17111602+bau-mann@users.noreply.github.com> Date: Thu, 6 Oct 2022 19:56:53 +0200 Subject: [PATCH 1/3] Initial Implementation Exercise "Book Store" --- config.json | 8 + .../practice/book-store/.docs/instructions.md | 68 ++++++ .../practice/book-store/.meta/config.json | 20 ++ .../book-store/.meta/zcl_book_store.clas.abap | 81 +++++++ .../practice/book-store/package.devc.xml | 10 + .../book-store/zcl_book_store.clas.abap | 34 +++ .../zcl_book_store.clas.testclasses.abap | 207 ++++++++++++++++++ .../book-store/zcl_book_store.clas.xml | 17 ++ 8 files changed, 445 insertions(+) create mode 100644 exercises/practice/book-store/.docs/instructions.md create mode 100644 exercises/practice/book-store/.meta/config.json create mode 100644 exercises/practice/book-store/.meta/zcl_book_store.clas.abap create mode 100644 exercises/practice/book-store/package.devc.xml create mode 100644 exercises/practice/book-store/zcl_book_store.clas.abap create mode 100644 exercises/practice/book-store/zcl_book_store.clas.testclasses.abap create mode 100644 exercises/practice/book-store/zcl_book_store.clas.xml diff --git a/config.json b/config.json index 8fe85f3c..87e09b09 100644 --- a/config.json +++ b/config.json @@ -280,6 +280,14 @@ "practices": [], "prerequisites": [], "difficulty": 4 + }, + { + "slug": "book-store", + "name": "Book Store", + "uuid": "0ceab982-40cd-48b3-be49-0fc1128708fd", + "practices": [], + "prerequisites": [], + "difficulty": 7 } ] }, diff --git a/exercises/practice/book-store/.docs/instructions.md b/exercises/practice/book-store/.docs/instructions.md new file mode 100644 index 00000000..cce9deea --- /dev/null +++ b/exercises/practice/book-store/.docs/instructions.md @@ -0,0 +1,68 @@ +# 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 a piece of 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 --> 25% discount (1st,2nd,3rd,4th,5th) +- +1 group of 3 --> 10% discount (1st,2nd,3rd) + +This would give a total of: + +- 5 books at a 25% discount +- +3 books at a 10% discount + +Resulting in: + +- 5 × (8 - 2.00) = 5 × 6.00 = $30.00 +- +3 × (8 - 0.80) = 3 × 7.20 = $21.60 + +For a total of $51.60 + +However, a different way to group these 8 books is: + +- 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th) +- +1 group of 4 books --> 20% discount (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 × (8 - 1.60) = 4 × 6.40 = $25.60 +- +4 × (8 - 1.60) = 4 × 6.40 = $25.60 + +For a total of $51.20 + +And $51.20 is the price with the biggest discount. diff --git a/exercises/practice/book-store/.meta/config.json b/exercises/practice/book-store/.meta/config.json new file mode 100644 index 00000000..e1f0e645 --- /dev/null +++ b/exercises/practice/book-store/.meta/config.json @@ -0,0 +1,20 @@ +{ + "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.", + "authors": [ + "bau-mann" + ], + "contributors": [], + "files": { + "solution": [ + "zcl_book_store.clas.abap" + ], + "test": [ + "zcl_book_store.clas.testclasses.abap" + ], + "example": [ + ".meta/zcl_book_store.clas.abap" + ] + }, + "source": "Inspired by the harry potter kata from Cyber-Dojo.", + "source_url": "http://cyber-dojo.org" +} diff --git a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap new file mode 100644 index 00000000..451acde5 --- /dev/null +++ b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap @@ -0,0 +1,81 @@ +CLASS zcl_book_store DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + + "! ID of book to buy from 1 to 5 + TYPES book_id TYPE i. + + TYPES basket_type TYPE SORTED TABLE OF book_id + WITH NON-UNIQUE KEY table_line. + + TYPES total TYPE p LENGTH 3 DECIMALS 2. + + "! @parameter basket | E.g., buying two copies of the first book and one copy of the second book + "! is equivalent to ( ( 1 ) ( 1 ) ( 2 ) ) + METHODS calculate_total + IMPORTING basket TYPE basket_type + RETURNING VALUE(total) TYPE total. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_book_store IMPLEMENTATION. + + METHOD calculate_total. + + TYPES: BEGIN OF basket_aggr_line, + book_id TYPE book_id, + quantity TYPE i, + END OF basket_aggr_line. + DATA basket_aggr TYPE TABLE OF basket_aggr_line + WITH NON-UNIQUE SORTED KEY quantity COMPONENTS quantity. + + TYPES: BEGIN OF discount_grp, + unique_books TYPE i, + discount TYPE total, + END OF discount_grp. + DATA grp TYPE REF TO discount_grp. + DATA discount_grps TYPE TABLE OF discount_grp. + discount_grps = VALUE #( ( unique_books = 5 discount = '.25' ) + ( unique_books = 4 discount = '.20' ) + ( unique_books = 3 discount = '.10' ) + ( unique_books = 2 discount = '.05' ) + ( unique_books = 1 discount = 0 ) ). + + DATA new_total TYPE total. + total = 999. + DATA(max_grp) = 5. + + DO 5 TIMES. + + new_total = 0. + basket_aggr = VALUE #( FOR i = 1 UNTIL i > 5 + ( book_id = i + quantity = lines( FILTER #( basket WHERE table_line = i ) ) ) ). + + LOOP AT discount_grps REFERENCE INTO grp. + IF max_grp >= grp->unique_books. + WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books. + SORT basket_aggr BY quantity DESCENDING. + DO grp->unique_books TIMES. + basket_aggr[ sy-index ]-quantity -= 1. + ENDDO. + new_total += grp->unique_books * 8 * ( 1 - grp->discount ). + ENDWHILE. + ENDIF. + ENDLOOP. + + total = nmin( val1 = total val2 = new_total ). + max_grp -= 1. + + ENDDO. + + ENDMETHOD. + +ENDCLASS. diff --git a/exercises/practice/book-store/package.devc.xml b/exercises/practice/book-store/package.devc.xml new file mode 100644 index 00000000..b648ead0 --- /dev/null +++ b/exercises/practice/book-store/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + Exercism: Book Store + + + + diff --git a/exercises/practice/book-store/zcl_book_store.clas.abap b/exercises/practice/book-store/zcl_book_store.clas.abap new file mode 100644 index 00000000..7b70218f --- /dev/null +++ b/exercises/practice/book-store/zcl_book_store.clas.abap @@ -0,0 +1,34 @@ +CLASS zcl_book_store DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + + "! ID of book to buy from 1 to 5 + TYPES book_id TYPE i. + + TYPES basket_type TYPE SORTED TABLE OF book_id + WITH NON-UNIQUE KEY table_line. + + TYPES total TYPE p LENGTH 3 DECIMALS 2. + + "! @parameter basket | E.g., buying two copies of the first book and one copy of the second book + "! is equivalent to ( ( 1 ) ( 1 ) ( 2 ) ) + METHODS calculate_total + IMPORTING basket TYPE basket_type + RETURNING VALUE(total) TYPE total. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_book_store IMPLEMENTATION. + + METHOD calculate_total. + " add solution here + ENDMETHOD. + +ENDCLASS. diff --git a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap new file mode 100644 index 00000000..93dbc6d3 --- /dev/null +++ b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap @@ -0,0 +1,207 @@ +CLASS ltcl_book_store DEFINITION FINAL FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + + DATA cut TYPE REF TO zcl_book_store. + + METHODS setup. + + METHODS only_a_single_book FOR TESTING RAISING cx_static_check. + METHODS two_of_the_same_book FOR TESTING RAISING cx_static_check. + METHODS empty_basket FOR TESTING RAISING cx_static_check. + METHODS two_different_books FOR TESTING RAISING cx_static_check. + METHODS three_different_books FOR TESTING RAISING cx_static_check. + METHODS four_different_books FOR TESTING RAISING cx_static_check. + METHODS five_different_books FOR TESTING RAISING cx_static_check. + "! Two groups of four is cheaper than group of five plus group of three + METHODS two_groups_of_four_1 FOR TESTING RAISING cx_static_check. + "! Two groups of four is cheaper than groups of five and three + METHODS two_groups_of_four_2 FOR TESTING RAISING cx_static_check. + "! Group of four plus group of two is cheaper than two groups of three + METHODS group_of_four_and_group_of_two FOR TESTING RAISING cx_static_check. + "! Two each of first four books and one copy each of rest + METHODS two_copies_of_each_except_one FOR TESTING RAISING cx_static_check. + METHODS two_copies_of_each_book FOR TESTING RAISING cx_static_check. + "! Three copies of first book and two each of remaining + METHODS three_of_first_two_each_remain FOR TESTING RAISING cx_static_check. + "! Three each of first two books and two each of remaining books + METHODS three_of_first_two_two_of_rem FOR TESTING RAISING cx_static_check. + "! Four groups of four are cheaper than two groups each of five and three + METHODS four_groups_of_four FOR TESTING RAISING cx_static_check. + "! Check that groups of four are created properly even when there are more groups of three than groups of five + METHODS groups_of_four FOR TESTING RAISING cx_static_check. + "!One group of one and four is cheaper than one group of two and three + METHODS one_group_of_one_and_four FOR TESTING RAISING cx_static_check. + "! One group of one and two plus three groups of four is cheaper than one group of each size" + METHODS mixed_1 FOR TESTING RAISING cx_static_check. + +ENDCLASS. + + +CLASS ltcl_book_store IMPLEMENTATION. + + METHOD setup. + cut = NEW #( ). + ENDMETHOD. + + METHOD only_a_single_book. + cl_abap_unit_assert=>assert_equals( + exp = 8 + act = cut->calculate_total( VALUE #( ( 1 ) ) ) ). + ENDMETHOD. + + METHOD two_of_the_same_book. + cl_abap_unit_assert=>assert_equals( + exp = 16 + act = cut->calculate_total( VALUE #( ( 2 ) ( 2 ) ) ) ). + ENDMETHOD. + + METHOD empty_basket. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = cut->calculate_total( VALUE #( ) ) ). + ENDMETHOD. + + METHOD two_different_books. + cl_abap_unit_assert=>assert_equals( + exp = '15.2' + act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ) ) ). + ENDMETHOD. + + METHOD three_different_books. + cl_abap_unit_assert=>assert_equals( + exp = '21.6' + act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ) ) ). + ENDMETHOD. + + METHOD four_different_books. + cl_abap_unit_assert=>assert_equals( + exp = '25.6' + act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ) ) ). + ENDMETHOD. + + METHOD five_different_books. + cl_abap_unit_assert=>assert_equals( + exp = '30' + act = cut->calculate_total( VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ) ) ). + ENDMETHOD. + + METHOD two_groups_of_four_1. + cl_abap_unit_assert=>assert_equals( + exp = '51.2' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) + ( 5 ) ) ) ). + ENDMETHOD. + + METHOD two_groups_of_four_2. + cl_abap_unit_assert=>assert_equals( + exp = '51.2' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) + ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ( 5 ) ) ) ). + ENDMETHOD. + + METHOD group_of_four_and_group_of_two. + cl_abap_unit_assert=>assert_equals( + exp = '40.8' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) + ( 4 ) ) ) ). + ENDMETHOD. + + METHOD two_copies_of_each_except_one. + cl_abap_unit_assert=>assert_equals( + exp = '55.6' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ) ) ). + ENDMETHOD. + + METHOD two_copies_of_each_book. + cl_abap_unit_assert=>assert_equals( + exp = 60 + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ( 5 ) ) ) ). + ENDMETHOD. + + METHOD three_of_first_two_each_remain. + cl_abap_unit_assert=>assert_equals( + exp = 68 + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ( 5 ) + ( 1 ) ) ) ). + ENDMETHOD. + + METHOD three_of_first_two_two_of_rem. + cl_abap_unit_assert=>assert_equals( + exp = '75.2' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ( 5 ) + ( 1 ) + ( 2 ) ) ) ). + ENDMETHOD. + + METHOD four_groups_of_four. + cl_abap_unit_assert=>assert_equals( + exp = '102.4' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) + ( 5 ) + ( 1 ) ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) + ( 4 ) + ( 5 ) ) ) ). + ENDMETHOD. + + METHOD groups_of_four. + cl_abap_unit_assert=>assert_equals( + exp = '145.6' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) ( 1 ) ( 1 ) ( 1 ) ( 1 ) + ( 2 ) ( 2 ) ( 2 ) ( 2 ) ( 2 ) ( 2 ) + ( 3 ) ( 3 ) ( 3 ) ( 3 ) ( 3 ) ( 3 ) + ( 4 ) ( 4 ) + ( 5 ) ( 5 ) ) ) ). + ENDMETHOD. + + METHOD one_group_of_one_and_four. + cl_abap_unit_assert=>assert_equals( + exp = '33.6' + act = cut->calculate_total( VALUE #( ( 1 ) ( 1 ) + ( 2 ) + ( 3 ) + ( 4 ) ) ) ). + ENDMETHOD. + + METHOD mixed_1. + cl_abap_unit_assert=>assert_equals( + exp = 100 + act = cut->calculate_total( VALUE #( ( 1 ) + ( 2 ) ( 2 ) + ( 3 ) ( 3 ) ( 3 ) + ( 4 ) ( 4 ) ( 4 ) ( 4 ) + ( 5 ) ( 5 ) ( 5 ) ( 5 ) ( 5 ) ) ) ). + ENDMETHOD. + +ENDCLASS. diff --git a/exercises/practice/book-store/zcl_book_store.clas.xml b/exercises/practice/book-store/zcl_book_store.clas.xml new file mode 100644 index 00000000..1dad2a61 --- /dev/null +++ b/exercises/practice/book-store/zcl_book_store.clas.xml @@ -0,0 +1,17 @@ + + + + + + ZCL_BOOK_STORE + E + Exercism: Book Store + 1 + X + X + X + X + + + + From 90bb02fbc44d3858190ba5cb0fb7d6b151acc6fc Mon Sep 17 00:00:00 2001 From: bau-mann <17111602+bau-mann@users.noreply.github.com> Date: Thu, 6 Oct 2022 20:02:59 +0200 Subject: [PATCH 2/3] Fix typo in test class --- .../practice/book-store/zcl_book_store.clas.testclasses.abap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap index 93dbc6d3..14dcbc17 100644 --- a/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap +++ b/exercises/practice/book-store/zcl_book_store.clas.testclasses.abap @@ -34,7 +34,7 @@ CLASS ltcl_book_store DEFINITION FINAL FOR TESTING METHODS groups_of_four FOR TESTING RAISING cx_static_check. "!One group of one and four is cheaper than one group of two and three METHODS one_group_of_one_and_four FOR TESTING RAISING cx_static_check. - "! One group of one and two plus three groups of four is cheaper than one group of each size" + "! One group of one and two plus three groups of four is cheaper than one group of each size METHODS mixed_1 FOR TESTING RAISING cx_static_check. ENDCLASS. From c47d891a089cc14dc48359722b47f5db598072c7 Mon Sep 17 00:00:00 2001 From: bau-mann <17111602+bau-mann@users.noreply.github.com> Date: Wed, 12 Oct 2022 20:32:29 +0200 Subject: [PATCH 3/3] Small Change to Reference Solution --- .../book-store/.meta/zcl_book_store.clas.abap | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap index 451acde5..7266a6e5 100644 --- a/exercises/practice/book-store/.meta/zcl_book_store.clas.abap +++ b/exercises/practice/book-store/.meta/zcl_book_store.clas.abap @@ -59,16 +59,14 @@ CLASS zcl_book_store IMPLEMENTATION. ( book_id = i quantity = lines( FILTER #( basket WHERE table_line = i ) ) ) ). - LOOP AT discount_grps REFERENCE INTO grp. - IF max_grp >= grp->unique_books. - WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books. - SORT basket_aggr BY quantity DESCENDING. - DO grp->unique_books TIMES. - basket_aggr[ sy-index ]-quantity -= 1. - ENDDO. - new_total += grp->unique_books * 8 * ( 1 - grp->discount ). - ENDWHILE. - ENDIF. + LOOP AT discount_grps REFERENCE INTO grp WHERE unique_books <= max_grp. + WHILE lines( FILTER #( basket_aggr USING KEY quantity WHERE quantity > 0 ) ) >= grp->unique_books. + SORT basket_aggr BY quantity DESCENDING. + DO grp->unique_books TIMES. + basket_aggr[ sy-index ]-quantity -= 1. + ENDDO. + new_total += grp->unique_books * 8 * ( 1 - grp->discount ). + ENDWHILE. ENDLOOP. total = nmin( val1 = total val2 = new_total ).