diff --git a/config.json b/config.json index a798115..22e6952 100644 --- a/config.json +++ b/config.json @@ -417,6 +417,14 @@ "practices": [], "prerequisites": [], "difficulty": 8 + }, + { + "slug": "saddle-points", + "name": "Saddle Points", + "uuid": "d050e56b-9f86-416c-baa3-10a98f47b944", + "practices": [], + "prerequisites": [], + "difficulty": 8 }, { "slug": "scrabble-score", diff --git a/exercises/practice/saddle-points/.docs/instructions.append.md b/exercises/practice/saddle-points/.docs/instructions.append.md new file mode 100644 index 0000000..4c60a25 --- /dev/null +++ b/exercises/practice/saddle-points/.docs/instructions.append.md @@ -0,0 +1,12 @@ +# SQLite-specific instructions + +- The **matrix** column contains the input, a JSON-encoded array of arrays of integers. +- The **result** columns should contain the output, the list of saddle points. + The output should be a JSON-encoded array of objects, each object has two keys: row and column. + For example, `[{"row": 2, "column": 1}]`. + +## JSON documentation + +See [JSON Functions And Operators][json-docs] for SQLite JSON functions. + +[json-docs]: https://www.sqlite.org/json1.html diff --git a/exercises/practice/saddle-points/.docs/instructions.md b/exercises/practice/saddle-points/.docs/instructions.md new file mode 100644 index 0000000..f69cdab --- /dev/null +++ b/exercises/practice/saddle-points/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to find the potential trees where you could build your tree house. + +The data company provides the data as grids that show the heights of the trees. +The rows of the grid represent the east-west direction, and the columns represent the north-south direction. + +An acceptable tree will be the largest in its row, while being the smallest in its column. + +A grid might not have any good trees at all. +Or it might have one, or even several. + +Here is a grid that has exactly one candidate tree. + +```text + ↓ + 1 2 3 4 + |----------- + 1 | 9 8 7 8 +→ 2 |[5] 3 2 4 + 3 | 6 6 7 1 +``` + +- Row 2 has values 5, 3, 2, and 4. The largest value is 5. +- Column 1 has values 9, 5, and 6. The smallest value is 5. + +So the point at `[2, 1]` (row: 2, column: 1) is a great spot for a tree house. diff --git a/exercises/practice/saddle-points/.docs/introduction.md b/exercises/practice/saddle-points/.docs/introduction.md new file mode 100644 index 0000000..34b2c77 --- /dev/null +++ b/exercises/practice/saddle-points/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +You plan to build a tree house in the woods near your house so that you can watch the sun rise and set. + +You've obtained data from a local survey company that show the height of every tree in each rectangular section of the map. +You need to analyze each grid on the map to find good trees for your tree house. + +A good tree is both: + +- taller than every tree to the east and west, so that you have the best possible view of the sunrises and sunsets. +- shorter than every tree to the north and south, to minimize the amount of tree climbing. diff --git a/exercises/practice/saddle-points/.meta/config.json b/exercises/practice/saddle-points/.meta/config.json new file mode 100644 index 0000000..838aed2 --- /dev/null +++ b/exercises/practice/saddle-points/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "saddle-points.sql" + ], + "test": [ + "saddle-points_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "blurb": "Detect saddle points in a matrix.", + "source": "J Dalbey's Programming Practice problems", + "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html" +} diff --git a/exercises/practice/saddle-points/.meta/example.sql b/exercises/practice/saddle-points/.meta/example.sql new file mode 100644 index 0000000..dc5dc2b --- /dev/null +++ b/exercises/practice/saddle-points/.meta/example.sql @@ -0,0 +1,52 @@ +UPDATE "saddle-points" + SET result = ( + WITH + jtree AS ( + SELECT *, + SUBSTR(fullkey, 1, INSTR(fullkey, ']')) rowkey, + SUBSTR(fullkey, INSTR(fullkey, '][')) colkey + FROM JSON_TREE(matrix) j + ), + max_single_in_row AS ( + SELECT MAX(value) value, + fullkey, + rowkey + FROM jtree + WHERE type = 'integer' + GROUP BY rowkey + ), + max_mult_in_row AS ( + SELECT jtree.VALUE, jtree.fullkey, ms.rowkey + FROM max_single_in_row ms, jtree + WHERE ms.rowkey = jtree.rowkey + AND ms.value = jtree.value + ), + min_single_in_col AS ( + SELECT MIN(value) value, fullkey, colkey + FROM jtree + WHERE type = 'integer' + GROUP BY colkey + ), + min_mult_in_col AS ( + SELECT jtree.VALUE, jtree.fullkey, ms.colkey + FROM min_single_in_col ms, jtree + WHERE ms.colkey = jtree.colkey + AND ms.value = jtree.VALUE + ), + get_coords AS ( + SELECT JSON(REPLACE(LTRIM(mc.fullkey, '$'), '][', ',')) coords + FROM max_mult_in_row mr, + min_mult_in_col mc + WHERE mr.fullkey = mc.fullkey + ), + to_objects AS ( + SELECT JSON_OBJECT( + 'row', JSON_EXTRACT(coords, '$[0]') + 1, + 'column', JSON_EXTRACT(coords, '$[1]') + 1 + ) AS jobj + FROM get_coords + ) + SELECT JSON_GROUP_ARRAY(JSON(jobj)) + FROM to_objects + ) +; diff --git a/exercises/practice/saddle-points/.meta/tests.toml b/exercises/practice/saddle-points/.meta/tests.toml new file mode 100644 index 0000000..ca00852 --- /dev/null +++ b/exercises/practice/saddle-points/.meta/tests.toml @@ -0,0 +1,37 @@ +# 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. + +[3e374e63-a2e0-4530-a39a-d53c560382bd] +description = "Can identify single saddle point" + +[6b501e2b-6c1f-491f-b1bb-7f278f760534] +description = "Can identify that empty matrix has no saddle points" + +[8c27cc64-e573-4fcb-a099-f0ae863fb02f] +description = "Can identify lack of saddle points when there are none" + +[6d1399bd-e105-40fd-a2c9-c6609507d7a3] +description = "Can identify multiple saddle points in a column" + +[3e81dce9-53b3-44e6-bf26-e328885fd5d1] +description = "Can identify multiple saddle points in a row" + +[88868621-b6f4-4837-bb8b-3fad8b25d46b] +description = "Can identify saddle point in bottom right corner" + +[5b9499ca-fcea-4195-830a-9c4584a0ee79] +description = "Can identify saddle points in a non square matrix" + +[ee99ccd2-a1f1-4283-ad39-f8c70f0cf594] +description = "Can identify that saddle points in a single column matrix are those with the minimum value" + +[63abf709-a84b-407f-a1b3-456638689713] +description = "Can identify that saddle points in a single row matrix are those with the maximum value" diff --git a/exercises/practice/saddle-points/create_fixture.sql b/exercises/practice/saddle-points/create_fixture.sql new file mode 100644 index 0000000..6720fdd --- /dev/null +++ b/exercises/practice/saddle-points/create_fixture.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS "saddle-points"; +CREATE TABLE "saddle-points" ( + matrix TEXT NOT NULL, -- json array of arrays + result TEXT -- json array of objects +); + +.mode csv +.import ./data.csv "saddle-points" + +UPDATE "saddle-points" SET result = NULL; diff --git a/exercises/practice/saddle-points/create_test_table.sql b/exercises/practice/saddle-points/create_test_table.sql new file mode 100644 index 0000000..a6a0abb --- /dev/null +++ b/exercises/practice/saddle-points/create_test_table.sql @@ -0,0 +1,27 @@ +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and description are taken from the test.toml file + uuid TEXT PRIMARY KEY, + description TEXT NOT NULL, + -- The following section is needed by the online test-runner + status TEXT DEFAULT 'fail', + message TEXT, + output TEXT, + test_code TEXT, + task_id INTEGER DEFAULT NULL, + -- Here are columns for the actual tests + matrix TEXT NOT NULL, -- json array of arrays + expected TEXT NOT NULL -- json array of objects +); + +INSERT INTO tests (uuid, description, matrix, expected) +VALUES + ('3e374e63-a2e0-4530-a39a-d53c560382bd', 'Can identify single saddle point', '[[9,8,7],[5,3,2],[6,6,7]]', '[{"row":2,"column":1}]'), + ('6b501e2b-6c1f-491f-b1bb-7f278f760534', 'Can identify that empty matrix has no saddle points', '[[]]', '[]'), + ('8c27cc64-e573-4fcb-a099-f0ae863fb02f', 'Can identify lack of saddle points when there are none', '[[1,2,3],[3,1,2],[2,3,1]]', '[]'), + ('6d1399bd-e105-40fd-a2c9-c6609507d7a3', 'Can identify multiple saddle points in a column', '[[4,5,4],[3,5,5],[1,5,4]]', '[{"row":1,"column":2},{"row":2,"column":2},{"row":3,"column":2}]'), + ('3e81dce9-53b3-44e6-bf26-e328885fd5d1', 'Can identify multiple saddle points in a row', '[[6,7,8],[5,5,5],[7,5,6]]', '[{"row":2,"column":1},{"row":2,"column":2},{"row":2,"column":3}]'), + ('88868621-b6f4-4837-bb8b-3fad8b25d46b', 'Can identify saddle point in bottom right corner', '[[8,7,9],[6,7,6],[3,2,5]]', '[{"row":3,"column":3}]'), + ('5b9499ca-fcea-4195-830a-9c4584a0ee79', 'Can identify saddle points in a non square matrix', '[[3,1,3],[3,2,4]]', '[{"row":1,"column":3},{"row":1,"column":1}]'), + ('ee99ccd2-a1f1-4283-ad39-f8c70f0cf594', 'Can identify that saddle points in a single column matrix are those with the minimum value', '[[2],[1],[4],[1]]', '[{"row":2,"column":1},{"row":4,"column":1}]'), + ('63abf709-a84b-407f-a1b3-456638689713', 'Can identify that saddle points in a single row matrix are those with the maximum value', '[[2,5,3,5]]', '[{"row":1,"column":2},{"row":1,"column":4}]'); diff --git a/exercises/practice/saddle-points/data.csv b/exercises/practice/saddle-points/data.csv new file mode 100644 index 0000000..74d9a4c --- /dev/null +++ b/exercises/practice/saddle-points/data.csv @@ -0,0 +1,9 @@ +"[[9,8,7],[5,3,2],[6,6,7]]", +"[[]]", +"[[1,2,3],[3,1,2],[2,3,1]]", +"[[4,5,4],[3,5,5],[1,5,4]]", +"[[6,7,8],[5,5,5],[7,5,6]]", +"[[8,7,9],[6,7,6],[3,2,5]]", +"[[3,1,3],[3,2,4]]", +"[[2],[1],[4],[1]]", +"[[2,5,3,5]]", diff --git a/exercises/practice/saddle-points/saddle-points.sql b/exercises/practice/saddle-points/saddle-points.sql new file mode 100644 index 0000000..ffb9ea8 --- /dev/null +++ b/exercises/practice/saddle-points/saddle-points.sql @@ -0,0 +1,6 @@ +-- Schema: +-- CREATE TABLE "saddle-points" ( +-- matrix TEXT NOT NULL, -- json array of arrays +-- result TEXT -- json array of object +-- ); +-- Task: update the saddle-points table and set the result based on the matrix. diff --git a/exercises/practice/saddle-points/saddle-points_test.sql b/exercises/practice/saddle-points/saddle-points_test.sql new file mode 100644 index 0000000..16fb297 --- /dev/null +++ b/exercises/practice/saddle-points/saddle-points_test.sql @@ -0,0 +1,57 @@ +-- Create database: +.read ./create_fixture.sql + +-- Read user student solution and save any output as markdown in user_output.md: +.mode markdown +.output user_output.md +.read ./saddle-points.sql +.output + +-- Create a clean testing environment: +.read ./create_test_table.sql + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests + SET status = 'pass' + FROM (SELECT matrix, result FROM "saddle-points") AS actual + WHERE actual.matrix = tests.matrix + AND IIF( + actual.matrix ISNULL, ( + SELECT JSON_GROUP_ARRAY(JSON(value)) + FROM ( + SELECT j.value + FROM JSON_EACH(actual.result) j + ORDER BY j.VALUE + ) + ), + actual.result + ) = ( + SELECT JSON_GROUP_ARRAY(JSON(value)) + FROM ( + SELECT j.VALUE + FROM JSON_EACH(tests.expected) j + ORDER BY j.VALUE + ) + ) +; + +-- Update message for failed tests to give helpful information: +UPDATE tests + SET message = ( + 'Result for "' || tests.matrix || '"' + || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' + ) + FROM (SELECT matrix, result FROM "saddle-points") AS actual + WHERE actual.matrix = tests.matrix AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT description, status, message, output, test_code, task_id +FROM tests; + +-- Display test results in readable form for the student: +.mode table +SELECT description, status, message +FROM tests;