diff --git a/config.json b/config.json index b3ad6ab..538e0b6 100644 --- a/config.json +++ b/config.json @@ -722,6 +722,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "67cca2b4-9e4e-43f6-9e17-a3d7d291015f", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "matching-brackets", "name": "Matching Brackets", diff --git a/exercises/practice/knapsack/.busted b/exercises/practice/knapsack/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/knapsack/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 0000000..0ebf791 --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to determine which items to take so that the total value of her selection is maximized, taking into account the knapsack's carrying capacity. + +Items will be represented as a list of items. +Each item will have a weight and value. +All values given will be strictly positive. +Lhakpa can take only one of each item. + +For example: + +```text +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Maximum Weight: 10 +``` + +For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on. +In this example, Lhakpa should take the second and fourth item to maximize her value, which, in this case, is 90. +She cannot get more than 90 as her knapsack has a weight limit of 10. diff --git a/exercises/practice/knapsack/.docs/introduction.md b/exercises/practice/knapsack/.docs/introduction.md new file mode 100644 index 0000000..9ac9df5 --- /dev/null +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +Lhakpa is a [Sherpa][sherpa] mountain guide and porter. +After months of careful planning, the expedition Lhakpa works for is about to leave. +She will be paid the value she carried to the base camp. + +In front of her are many items, each with a value and weight. +Lhakpa would gladly take all of the items, but her knapsack can only hold so much weight. + +[sherpa]: https://en.wikipedia.org/wiki/Sherpa_people#Mountaineering diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 0000000..ea3d308 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "knapsack.moon" + ], + "test": [ + "knapsack_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Knapsack_problem" +} diff --git a/exercises/practice/knapsack/.meta/example.moon b/exercises/practice/knapsack/.meta/example.moon new file mode 100644 index 0000000..b4230bb --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.moon @@ -0,0 +1,16 @@ +-- following pseudocode under +-- https://en.wikipedia.org/wiki/Knapsack_problem#0-1_knapsack_problem +{ + maximumValue: (maxWt, items) -> + n = #items + -- using table comprehension syntax + -- because we need to have index 0 in both dimensions + m = {i, {j, 0 for j = 0, maxWt} for i = 0, n} + for i = 1, n + for j = 1, maxWt + if items[i].weight > j + m[i][j] = m[i - 1][j] + else + m[i][j] = math.max(m[i - 1][j], m[i - 1][j - items[i].weight] + items[i].value) + m[n][maxWt] +} diff --git a/exercises/practice/knapsack/.meta/spec_generator.moon b/exercises/practice/knapsack/.meta/spec_generator.moon new file mode 100644 index 0000000..d688204 --- /dev/null +++ b/exercises/practice/knapsack/.meta/spec_generator.moon @@ -0,0 +1,14 @@ +import table_list from require 'test_helpers' + +{ + module_imports: {'maximumValue'}, + + generate_test: (case, level) -> + lines = { + "items = #{table_list case.input.items, level}", + "maxWeight = #{case.input.maximumWeight}", + "result = maximumValue maxWeight, items", + "assert.are.equal #{case.expected}, result" + } + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 0000000..8e013ef --- /dev/null +++ b/exercises/practice/knapsack/.meta/tests.toml @@ -0,0 +1,36 @@ +# 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. + +[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7] +description = "no items" +include = false + +[3993a824-c20e-493d-b3c9-ee8a7753ee59] +description = "no items" +reimplements = "a4d7d2f0-ad8a-460c-86f3-88ba709d41a7" + +[1d39e98c-6249-4a8b-912f-87cb12e506b0] +description = "one item, too heavy" + +[833ea310-6323-44f2-9d27-a278740ffbd8] +description = "five items (cannot be greedy by weight)" + +[277cdc52-f835-4c7d-872b-bff17bab2456] +description = "five items (cannot be greedy by value)" + +[81d8e679-442b-4f7a-8a59-7278083916c9] +description = "example knapsack" + +[f23a2449-d67c-4c26-bf3e-cde020f27ecc] +description = "8 items" + +[7c682ae9-c385-4241-a197-d2fa02c81a11] +description = "15 items" diff --git a/exercises/practice/knapsack/knapsack.moon b/exercises/practice/knapsack/knapsack.moon new file mode 100644 index 0000000..e69de29 diff --git a/exercises/practice/knapsack/knapsack_spec.moon b/exercises/practice/knapsack/knapsack_spec.moon new file mode 100644 index 0000000..112c89a --- /dev/null +++ b/exercises/practice/knapsack/knapsack_spec.moon @@ -0,0 +1,86 @@ +import maximumValue from require 'knapsack' + +describe 'knapsack', -> + it 'no items', -> + items = {} + maxWeight = 100 + result = maximumValue maxWeight, items + assert.are.equal 0, result + + pending 'one item, too heavy', -> + items = {{value: 1, weight: 100}} + maxWeight = 10 + result = maximumValue maxWeight, items + assert.are.equal 0, result + + pending 'five items (cannot be greedy by weight)', -> + items = { + {value: 5, weight: 2}, + {value: 5, weight: 2}, + {value: 5, weight: 2}, + {value: 5, weight: 2}, + {value: 21, weight: 10}, + } + maxWeight = 10 + result = maximumValue maxWeight, items + assert.are.equal 21, result + + pending 'five items (cannot be greedy by value)', -> + items = { + {value: 20, weight: 2}, + {value: 20, weight: 2}, + {value: 20, weight: 2}, + {value: 20, weight: 2}, + {value: 50, weight: 10}, + } + maxWeight = 10 + result = maximumValue maxWeight, items + assert.are.equal 80, result + + pending 'example knapsack', -> + items = { + {value: 10, weight: 5}, + {value: 40, weight: 4}, + {value: 30, weight: 6}, + {value: 50, weight: 4}, + } + maxWeight = 10 + result = maximumValue maxWeight, items + assert.are.equal 90, result + + pending '8 items', -> + items = { + {value: 350, weight: 25}, + {value: 400, weight: 35}, + {value: 450, weight: 45}, + {value: 20, weight: 5}, + {value: 70, weight: 25}, + {value: 8, weight: 3}, + {value: 5, weight: 2}, + {value: 5, weight: 2}, + } + maxWeight = 104 + result = maximumValue maxWeight, items + assert.are.equal 900, result + + pending '15 items', -> + items = { + {value: 135, weight: 70}, + {value: 139, weight: 73}, + {value: 149, weight: 77}, + {value: 150, weight: 80}, + {value: 156, weight: 82}, + {value: 163, weight: 87}, + {value: 173, weight: 90}, + {value: 184, weight: 94}, + {value: 192, weight: 98}, + {value: 201, weight: 106}, + {value: 210, weight: 110}, + {value: 214, weight: 113}, + {value: 221, weight: 115}, + {value: 229, weight: 118}, + {value: 240, weight: 120}, + } + maxWeight = 750 + result = maximumValue maxWeight, items + assert.are.equal 1458, result diff --git a/lib/test_helpers.moon b/lib/test_helpers.moon index 075e1d9..bd5c054 100644 --- a/lib/test_helpers.moon +++ b/lib/test_helpers.moon @@ -95,6 +95,17 @@ table_tostring_ordered = (t, keys) -> s = [string.format '%s: %q', k, t[k] for k in *keys] "{#{table.concat s, ', '}}" +--- list of one-line key-value tables +table_list = (list, level) -> + error 'Provide a level for `table_list`', 2 if not level + if #list <= 1 + "{#{table.concat [table_tostring elem for elem in *list], ', '}}" + else + lines = [indent table_tostring(elem) .. ',', level + 1 for elem in *list] + table.insert lines, 1, '{' + table.insert lines, indent('}', level) + table.concat lines, '\n' + --- Table contains an element table_contains = (list, target) -> for elem in *list @@ -154,6 +165,7 @@ table_dump = (what, level = 0) -> :kv_table :table_tostring :table_tostring_ordered + :table_list :json_string :table_contains :table_dump