diff --git a/config.json b/config.json index 3de0ec0..8cdfbd3 100644 --- a/config.json +++ b/config.json @@ -421,6 +421,17 @@ "blocks" ], "difficulty": 9 + }, + { + "slug": "grains", + "name": "Grains", + "uuid": "668915db-bc3d-4ff4-a339-174ed2f35940", + "practices": [ + "errors", + "loops" + ], + "prerequisites": [], + "difficulty": 3 } ] } diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md new file mode 100644 index 0000000..df479fc --- /dev/null +++ b/exercises/practice/grains/.docs/instructions.md @@ -0,0 +1,15 @@ +# Instructions + +Calculate the number of grains of wheat on a chessboard given that the number on each square doubles. + +There once was a wise servant who saved the life of a prince. +The king promised to pay whatever the servant could dream up. +Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. +One grain on the first square of a chess board, with the number of grains doubling on each successive square. + +There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). + +Write code that shows: + +- how many grains were on a given square, and +- the total number of grains on the chessboard diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json new file mode 100644 index 0000000..2fb1181 --- /dev/null +++ b/exercises/practice/grains/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "grains.red" + ], + "test": [ + "grains-test.red" + ], + "example": [ + ".meta/example.red" + ] + }, + "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.", + "source": "The CodeRanch Cattle Drive, Assignment 6", + "source_url": "https://coderanch.com/wiki/718824/Grains" +} diff --git a/exercises/practice/grains/.meta/example.red b/exercises/practice/grains/.meta/example.red new file mode 100644 index 0000000..6dcf2b9 --- /dev/null +++ b/exercises/practice/grains/.meta/example.red @@ -0,0 +1,24 @@ +Red [ + description: {"Grains" exercise solution for exercism platform} + author: "BNAndras" +] + +square: function [ + "Returns the number of grains on a given square" + square [integer!] + return: [integer!] +] [ + if (square < 1) or (square > 64) [ + cause-error 'user 'message "square must be between 1 and 64" + ] + + power 2 (square - 1) +] + +total: function [ + "Returns the total number of grains on a chessboard" + return: [integer!] +] [ + (power 2 64) - 1 +] + diff --git a/exercises/practice/grains/.meta/tests.toml b/exercises/practice/grains/.meta/tests.toml new file mode 100644 index 0000000..6ea68bc --- /dev/null +++ b/exercises/practice/grains/.meta/tests.toml @@ -0,0 +1,43 @@ +# 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. + +[9fbde8de-36b2-49de-baf2-cd42d6f28405] +description = "returns the number of grains on the square -> grains on square 1" + +[ee1f30c2-01d8-4298-b25d-c677331b5e6d] +description = "returns the number of grains on the square -> grains on square 2" + +[10f45584-2fc3-4875-8ec6-666065d1163b] +description = "returns the number of grains on the square -> grains on square 3" + +[a7cbe01b-36f4-4601-b053-c5f6ae055170] +description = "returns the number of grains on the square -> grains on square 4" + +[c50acc89-8535-44e4-918f-b848ad2817d4] +description = "returns the number of grains on the square -> grains on square 16" + +[acd81b46-c2ad-4951-b848-80d15ed5a04f] +description = "returns the number of grains on the square -> grains on square 32" + +[c73b470a-5efb-4d53-9ac6-c5f6487f227b] +description = "returns the number of grains on the square -> grains on square 64" + +[1d47d832-3e85-4974-9466-5bd35af484e3] +description = "returns the number of grains on the square -> square 0 is invalid" + +[61974483-eeb2-465e-be54-ca5dde366453] +description = "returns the number of grains on the square -> negative square is invalid" + +[a95e4374-f32c-45a7-a10d-ffec475c012f] +description = "returns the number of grains on the square -> square greater than 64 is invalid" + +[6eb07385-3659-4b45-a6be-9dc474222750] +description = "returns the total number of grains on the board" diff --git a/exercises/practice/grains/grains-test.red b/exercises/practice/grains/grains-test.red new file mode 100644 index 0000000..445a2ef --- /dev/null +++ b/exercises/practice/grains/grains-test.red @@ -0,0 +1,123 @@ +Red [ + description: {Tests for "Grains" Exercism exercise} + author: "loziniak" +] + +#include %testlib.red + + test-init/limit %grains.red 1 +; test-init/limit %.meta/example.red 1 ; test example solution + +canonical-cases: [#( + description: "grains on square 1" + input: #( + square: 1 + ) + expected: 1 + function: "square" + uuid: "9fbde8de-36b2-49de-baf2-cd42d6f28405" +) #( + description: "grains on square 2" + input: #( + square: 2 + ) + expected: 2 + function: "square" + uuid: "ee1f30c2-01d8-4298-b25d-c677331b5e6d" +) #( + description: "grains on square 3" + input: #( + square: 3 + ) + expected: 4 + function: "square" + uuid: "10f45584-2fc3-4875-8ec6-666065d1163b" +) #( + description: "grains on square 4" + input: #( + square: 4 + ) + expected: 8 + function: "square" + uuid: "a7cbe01b-36f4-4601-b053-c5f6ae055170" +) #( + description: "grains on square 16" + input: #( + square: 16 + ) + expected: 32768 + function: "square" + uuid: "c50acc89-8535-44e4-918f-b848ad2817d4" +) #( + description: "grains on square 32" + input: #( + square: 32 + ) + expected: 2147483648.0 + function: "square" + uuid: "acd81b46-c2ad-4951-b848-80d15ed5a04f" +) #( + description: "grains on square 64" + input: #( + square: 64 + ) + expected: 9.223372036854776e18 + function: "square" + uuid: "c73b470a-5efb-4d53-9ac6-c5f6487f227b" +) #( + description: "square 0 is invalid" + input: #( + square: 0 + ) + expected: #( + error: "square must be between 1 and 64" + ) + function: "square" + uuid: "1d47d832-3e85-4974-9466-5bd35af484e3" +) #( + description: "negative square is invalid" + input: #( + square: -1 + ) + expected: #( + error: "square must be between 1 and 64" + ) + function: "square" + uuid: "61974483-eeb2-465e-be54-ca5dde366453" +) #( + description: "square greater than 64 is invalid" + input: #( + square: 65 + ) + expected: #( + error: "square must be between 1 and 64" + ) + function: "square" + uuid: "a95e4374-f32c-45a7-a10d-ffec475c012f" +) #( + description: "returns the total number of grains on the board" + input: #() + expected: 18446744073709551615 + function: "total" + uuid: "6eb07385-3659-4b45-a6be-9dc474222750" +)] + + +foreach c-case canonical-cases [ + expect-code: compose [ + (to word! c-case/function) (values-of c-case/input) + ] + case-code: reduce + either all [ + map? c-case/expected + string? c-case/expected/error + ] [ + ['expect-error/message quote 'user expect-code c-case/expected/error] + ] [ + ['expect c-case/expected expect-code] + ] + + test c-case/description case-code +] + +test-results/print diff --git a/exercises/practice/grains/grains.red b/exercises/practice/grains/grains.red new file mode 100644 index 0000000..e95d3f8 --- /dev/null +++ b/exercises/practice/grains/grains.red @@ -0,0 +1,15 @@ +Red [ + description: {"Grains" exercise solution for exercism platform} + author: "" ; you can write your name here, in quotes +] + +square: function [ + square +] [ + cause-error 'user 'message "You need to implement square function." +] + +total: function [] [ + cause-error 'user 'message "You need to implement total function." +] + diff --git a/exercises/practice/grains/testlib.red b/exercises/practice/grains/testlib.red new file mode 100644 index 0000000..9b0b79e --- /dev/null +++ b/exercises/practice/grains/testlib.red @@ -0,0 +1,217 @@ +Red [ + description: {Unit testing library} + author: "loziniak" +] + + +context [ + tested: ignore-after: test-file: results: output: none + + set 'test-init function [ + file [file!] + /limit + ia [integer!] + ] [ + self/tested: 0 + self/ignore-after: either limit [ia] [none] + self/test-file: file + self/results: copy [] + self/output: copy "" + ] + + sandbox!: context [ + + assert: function [ + code [block!] + /local result + ] [ + res: last results + + set/any 'result do code + either :result = true [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result + ] + + expect: function [ + expectation [any-type!] + code [block!] + /local result + ] [ + res: last results + res/expected: :expectation + + set/any 'result do code + res/actual: :result + + either :result = :expectation [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result + ] + + expect-error: function [ + type [word!] + code [block!] + /message + msg [string!] + /local result result-or-error + ] [ + returned-error?: no + set/any 'result-or-error try [ + set/any 'result do code + returned-error?: yes + :result + ] + + res: last results + res/actual: :result-or-error + res/expected: compose [type: (type)] + if message [append res/expected compose [id: 'message arg1: (msg)]] + + either all [ + error? :result-or-error + not returned-error? + result-or-error/type = type + any [ + not message + all [ + result-or-error/id = 'message + result-or-error/arg1 = msg + ] + ] + ] [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result-or-error + ] + ] + + set 'test function [ + summary [string!] + code [block!] + /extern + tested + ] [ + append results result: make map! compose/only [ + summary: (summary) ;@@ [string!] + test-code: (copy code) ;@@ [block!] + status: none ;@@ [word!] : 'pass | 'fail | 'error | 'ignored + ;-- expected (optional field) + ;-- actual (optional field) + ;-- output (optional field) + ] + + either any [ + none? ignore-after + tested < ignore-after + ] [ + clear output + old-functions: override-console + + exercise: make sandbox! load test-file + code: bind code exercise + uncaught?: yes + outcome: catch [ + outcome: try [ + catch/name [ + do code + ] 'expect-fail + none + ] + uncaught?: no + outcome + ] + + case [ + error? outcome [ + result/status: 'error + result/actual: outcome + ] + uncaught? [ + result/status: 'error + result/actual: make error! [type: 'throw id: 'throw arg1: outcome] + ] + ] + + restore-console old-functions + result/output: copy output + ] [ + result/status: 'ignored + ] + + tested: tested + 1 + ] + + set 'test-results function [ + /print + ] [ + either print [ + foreach result self/results [ + system/words/print rejoin [ + pad/with result/summary 40 #"." + "... " + switch result/status [ + pass ["✓"] + fail [rejoin [ + {FAILED.} + either find result 'expected [rejoin [ + { Expected: } result/expected + either find result 'actual [rejoin [ + {, but got } result/actual + ]] [] + ]] [] + newline + result/output + ]] + error [rejoin [ + newline + result/output + form result/actual + ]] + ignored ["(ignored)"] + ] + ] + ] + ] [ + self/results + ] + ] + + + override-console: function [] [ + old-functions: reduce [:prin :print :probe] + + system/words/prin: function [value [any-type!]] [ + append self/output form :value + return () + ] + system/words/print: function [value [any-type!]] [ + append self/output reduce [form :value #"^/"] + return () + ] + system/words/probe: function [value [any-type!]] [ + append self/output reduce [mold :value #"^/"] + return :value + ] + return old-functions + ] + + restore-console: function [old-functions [block!]] [ + set [prin print probe] old-functions + ] + +]