From 9f2502e0fe9c05746e93c36e5123dffce77f72c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Geron?= Date: Wed, 9 Oct 2024 09:19:12 +1300 Subject: [PATCH] Add knapsack exercise (#132) --- config.json | 8 ++ .../practice/knapsack/.docs/instructions.md | 25 +++++ .../practice/knapsack/.docs/introduction.md | 8 ++ exercises/practice/knapsack/.meta/Example.roc | 15 +++ exercises/practice/knapsack/.meta/config.json | 19 ++++ exercises/practice/knapsack/.meta/template.j2 | 22 +++++ exercises/practice/knapsack/.meta/tests.toml | 36 +++++++ exercises/practice/knapsack/Knapsack.roc | 7 ++ exercises/practice/knapsack/knapsack-test.roc | 98 +++++++++++++++++++ 9 files changed, 238 insertions(+) create mode 100644 exercises/practice/knapsack/.docs/instructions.md create mode 100644 exercises/practice/knapsack/.docs/introduction.md create mode 100644 exercises/practice/knapsack/.meta/Example.roc create mode 100644 exercises/practice/knapsack/.meta/config.json create mode 100644 exercises/practice/knapsack/.meta/template.j2 create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/Knapsack.roc create mode 100644 exercises/practice/knapsack/knapsack-test.roc diff --git a/config.json b/config.json index 4c1b4f9..f5d7f97 100644 --- a/config.json +++ b/config.json @@ -559,6 +559,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "07b3e979-9c13-4075-8608-95398a67a4dc", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "acronym", "name": "Acronym", diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 0000000..3411db9 --- /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 his 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. +Bob 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, Bob should take the second and fourth item to maximize his value, which, in this case, is 90. +He cannot get more than 90 as his 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..9b2bed8 --- /dev/null +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +Bob is a thief. +After months of careful planning, he finally manages to crack the security systems of a fancy store. + +In front of him are many items, each with a value and weight. +Bob would gladly take all of the items, but his knapsack can only hold so much weight. +Bob has to carefully consider which items to take so that the total value of his selection is maximized. diff --git a/exercises/practice/knapsack/.meta/Example.roc b/exercises/practice/knapsack/.meta/Example.roc new file mode 100644 index 0000000..2ea25ff --- /dev/null +++ b/exercises/practice/knapsack/.meta/Example.roc @@ -0,0 +1,15 @@ +module [maximumValue] + +Item : { weight : U64, value : U64 } + +maximumValue : { items : List Item, maximumWeight : U64 } -> U64 +maximumValue = \{ items, maximumWeight } -> + when items is + [] -> 0 + [item, .. as rest] -> + maxValueWithoutItem = maximumValue { items: rest, maximumWeight } + if item.weight > maximumWeight then + maxValueWithoutItem + else + maxValueWithItem = item.value + maximumValue { items: rest, maximumWeight: maximumWeight - item.weight } + Num.max maxValueWithoutItem maxValueWithItem diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 0000000..0406e6a --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "ageron" + ], + "files": { + "solution": [ + "Knapsack.roc" + ], + "test": [ + "knapsack-test.roc" + ], + "example": [ + ".meta/Example.roc" + ] + }, + "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/template.j2 b/exercises/practice/knapsack/.meta/template.j2 new file mode 100644 index 0000000..58fe5ea --- /dev/null +++ b/exercises/practice/knapsack/.meta/template.j2 @@ -0,0 +1,22 @@ +{%- import "generator_macros.j2" as macros with context -%} +{{ macros.canonical_ref() }} +{{ macros.header() }} + +import {{ exercise | to_pascal }} exposing [{{ cases[0]["property"] | to_camel }}] + +{% for case in cases -%} +# {{ case["description"] }} +expect + {%- if case["input"]["items"] == [] %} + items = [] + {%- else %} + items = [ + {%- for item in case["input"]["items"] %} + {{ item | to_roc }}, + {%- endfor %} + ] + {%- endif %} + result = {{ case["property"] | to_camel }} { items, maximumWeight: {{ case["input"]["maximumWeight"] }} } + result == {{ case["expected"] | to_roc }} + +{% endfor %} 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.roc b/exercises/practice/knapsack/Knapsack.roc new file mode 100644 index 0000000..c295bac --- /dev/null +++ b/exercises/practice/knapsack/Knapsack.roc @@ -0,0 +1,7 @@ +module [maximumValue] + +Item : { weight : U64, value : U64 } + +maximumValue : { items : List Item, maximumWeight : U64 } -> U64 +maximumValue = \{ items, maximumWeight } -> + crash "Please implement the 'maximumValue' function" diff --git a/exercises/practice/knapsack/knapsack-test.roc b/exercises/practice/knapsack/knapsack-test.roc new file mode 100644 index 0000000..a85b7de --- /dev/null +++ b/exercises/practice/knapsack/knapsack-test.roc @@ -0,0 +1,98 @@ +# These tests are auto-generated with test data from: +# https://github.com/exercism/problem-specifications/tree/main/exercises/knapsack/canonical-data.json +# File last updated on 2024-10-06 +app [main] { + pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br", +} + +main = + Task.ok {} + +import Knapsack exposing [maximumValue] + +# no items +expect + items = [] + result = maximumValue { items, maximumWeight: 100 } + result == 0 + +# one item, too heavy +expect + items = [ + { weight: 100, value: 1 }, + ] + result = maximumValue { items, maximumWeight: 10 } + result == 0 + +# five items (cannot be greedy by weight) +expect + items = [ + { weight: 2, value: 5 }, + { weight: 2, value: 5 }, + { weight: 2, value: 5 }, + { weight: 2, value: 5 }, + { weight: 10, value: 21 }, + ] + result = maximumValue { items, maximumWeight: 10 } + result == 21 + +# five items (cannot be greedy by value) +expect + items = [ + { weight: 2, value: 20 }, + { weight: 2, value: 20 }, + { weight: 2, value: 20 }, + { weight: 2, value: 20 }, + { weight: 10, value: 50 }, + ] + result = maximumValue { items, maximumWeight: 10 } + result == 80 + +# example knapsack +expect + items = [ + { weight: 5, value: 10 }, + { weight: 4, value: 40 }, + { weight: 6, value: 30 }, + { weight: 4, value: 50 }, + ] + result = maximumValue { items, maximumWeight: 10 } + result == 90 + +# 8 items +expect + items = [ + { weight: 25, value: 350 }, + { weight: 35, value: 400 }, + { weight: 45, value: 450 }, + { weight: 5, value: 20 }, + { weight: 25, value: 70 }, + { weight: 3, value: 8 }, + { weight: 2, value: 5 }, + { weight: 2, value: 5 }, + ] + result = maximumValue { items, maximumWeight: 104 } + result == 900 + +# 15 items +expect + items = [ + { weight: 70, value: 135 }, + { weight: 73, value: 139 }, + { weight: 77, value: 149 }, + { weight: 80, value: 150 }, + { weight: 82, value: 156 }, + { weight: 87, value: 163 }, + { weight: 90, value: 173 }, + { weight: 94, value: 184 }, + { weight: 98, value: 192 }, + { weight: 106, value: 201 }, + { weight: 110, value: 210 }, + { weight: 113, value: 214 }, + { weight: 115, value: 221 }, + { weight: 118, value: 229 }, + { weight: 120, value: 240 }, + ] + result = maximumValue { items, maximumWeight: 750 } + result == 1458 +