diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb2ead79..5c6cb594 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,6 @@ updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'daily' + interval: 'monthly' labels: - 'x:size/small' diff --git a/.github/workflows/no-important-files-changed.yml b/.github/workflows/no-important-files-changed.yml new file mode 100644 index 00000000..812e9129 --- /dev/null +++ b/.github/workflows/no-important-files-changed.yml @@ -0,0 +1,23 @@ +name: No important files changed + +on: + pull_request_target: + types: [opened] + branches: [main] + paths: + - "exercises/concept/**" + - "exercises/practice/**" + - "!exercises/*/*/.approaches/**" + - "!exercises/*/*/.articles/**" + - "!exercises/*/*/.docs/**" + - "!exercises/*/*/.meta/**" + +permissions: + pull-requests: write + +jobs: + check: + uses: exercism/github-actions/.github/workflows/check-no-important-files-changed.yml@main + with: + repository: ${{ github.event.pull_request.head.repo.owner.login }}/${{ github.event.pull_request.head.repo.name }} + ref: ${{ github.head_ref }} diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml new file mode 100644 index 00000000..b6ec9c56 --- /dev/null +++ b/.github/workflows/ping-cross-track-maintainers-team.yml @@ -0,0 +1,16 @@ +name: Ping cross-track maintainers team + +on: + pull_request_target: + types: + - opened + +permissions: + pull-requests: write + +jobs: + ping: + if: github.repository_owner == 'exercism' # Stops this job from running on forks + uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main + secrets: + github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }} diff --git a/.github/workflows/run-configlet-sync.yml b/.github/workflows/run-configlet-sync.yml new file mode 100644 index 00000000..b49cbffe --- /dev/null +++ b/.github/workflows/run-configlet-sync.yml @@ -0,0 +1,10 @@ +name: Run Configlet Sync + +on: + workflow_dispatch: + schedule: + - cron: '0 0 15 * *' + +jobs: + call-gha-workflow: + uses: exercism/github-actions/.github/workflows/configlet-sync.yml@main diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67a8ba00..0116cbf8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ # Requires scripts: # - bin/test -name: / Test +name: Test Exercises on: push: @@ -23,17 +23,19 @@ on: jobs: ci: - runs-on: + runs-on: ubuntu-24.04 steps: - name: Checkout repository - uses: actions/checkout@v3 - - - name: Use - uses: + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: Setup Node.js + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 + with: + node-version: 'node' + - name: Install project dependencies - run: - + run: npm ci + - name: Verify all exercises run: bin/verify-exercises diff --git a/.gitignore b/.gitignore index 6a513a06..8d916bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ .DS_Store bin/configlet bin/configlet.exe +.pyret/ +*.jarr +node_modules diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index df8e3676..3f7813de 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -90,4 +90,4 @@ This policy was initially adopted from the Front-end London Slack community and A version history can be seen on [GitHub](https://github.com/exercism/website-copy/edit/main/pages/code_of_conduct.md). _This policy is a "living" document, and subject to refinement and expansion in the future. -This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Slack, Twitter, email) and any other Exercism entity or event._ +This policy applies to the Exercism website, the Exercism GitHub organization, any other Exercism-related communication channels (e.g. Discord, Forum, Twitter, email) and any other Exercism entity or event._ diff --git a/LICENSE b/LICENSE index ef5d2119..90e73be0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Exercism +Copyright (c) 2021 Exercism Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index cc90a7e2..0745e6fc 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,57 @@ # Exercism Pyret Track -[![configlet](https://github.com/exercism/pyret/workflows/configlet/badge.svg)](https://github.com/exercism/pyret/actions?query=workflow%3Aconfiglet) [![tests](https://github.com/exercism/pyret/workflows/test/badge.svg)](https://github.com/exercism/pyret/actions?query=workflow%3Atest) +[![Configlet](https://github.com/exercism/pyret/actions/workflows/configlet.yml/badge.svg)](https://github.com/exercism/pyret/actions/workflows/configlet.yml) [![.github/workflows/test.yml](https://github.com/exercism/pyret/actions/workflows/test.yml/badge.svg)](https://github.com/exercism/pyret/actions/workflows/test.yml) Exercism exercises in Pyret. +## Setup + +If you're solving Exercism exercises offline, you'll need a recent copy of [pyret-npm](https://www.npmjs.com/package/pyret-npm) (0.0.27+). +Currently, pyret-npm works on Linux and MacOS platforms although Windows users can run it via the [WSL](https://learn.microsoft.com/en-us/windows/wsl/). +However, you can also use [Pyret's online IDE](https://code.pyret.org/). +In that case, you'll need to switch from the IDE's default `essentials2021` namespace to the older `starter2024` supported by pyret-npm. + +## Support + +For support with Pyret in Exercism, please visit [the Pyret subcategory](https://forum.exercism.org/c/programming/pyret/265) on the official [Exercism forum](https://forum.exercism.org). + +## Coding Style + +Please consult [the official Pyret style guide](https://pyret.org/docs/latest/Pyret_Style_Guide.html). + ## Testing -To test the exercises, run `./bin/test`. +To test the exercises, run `./bin/verify-exercises` on a Linux or MacOS platform. This command will iterate over all exercises and check to see if their exemplar/example implementation passes all the tests. -### Track linting +## Contributing Guide + +Please see [Exercism's contributing guide](https://exercism.org/docs/building). + +Here's the basic template for an `-test.arr`. +Each `check` block corresponds to a single test case, and its label is reported to the student. + +```pyret + +use context starter2024 + +include file(".arr") + +check "foo returns 1": + foo() is 1 +end + +check "bar returns 2": + bar() is 2 +end +``` + +## Track linting [`configlet`](https://exercism.org/docs/building/configlet) is an Exercism-wide tool for working with tracks. You can download it by running: ```shell -$ ./bin/fetch-configlet +./bin/fetch-configlet ``` Run its [`lint` command](https://exercism.org/docs/building/configlet/lint) to verify if all exercises have all the necessary files and if config files are correct: diff --git a/bin/fetch-configlet b/bin/fetch-configlet index 4800e150..6bef43ab 100755 --- a/bin/fetch-configlet +++ b/bin/fetch-configlet @@ -24,10 +24,11 @@ get_download_url() { local latest='https://api.github.com/repos/exercism/configlet/releases/latest' local arch case "$(uname -m)" in - x86_64) arch='x86-64' ;; - *686*) arch='i386' ;; - *386*) arch='i386' ;; - *) arch='x86-64' ;; + aarch64|arm64) arch='arm64' ;; + x86_64) arch='x86-64' ;; + *686*) arch='i386' ;; + *386*) arch='i386' ;; + *) arch='x86-64' ;; esac local suffix="${os}_${arch}.${ext}" curl "${curlopts[@]}" --header 'Accept: application/vnd.github.v3+json' "${latest}" | @@ -47,7 +48,7 @@ main() { fi local os - case "$(uname)" in + case "$(uname -s)" in Darwin*) os='macos' ;; Linux*) os='linux' ;; Windows*) os='windows' ;; @@ -58,8 +59,8 @@ main() { local ext case "${os}" in - windows*) ext='zip' ;; - *) ext='tar.gz' ;; + windows) ext='zip' ;; + *) ext='tar.gz' ;; esac echo "Fetching configlet..." >&2 @@ -69,16 +70,16 @@ main() { curl "${curlopts[@]}" --output "${output_path}" "${download_url}" case "${ext}" in - *zip) unzip "${output_path}" -d "${output_dir}" ;; - *) tar xzf "${output_path}" -C "${output_dir}" ;; + zip) unzip "${output_path}" -d "${output_dir}" ;; + *) tar xzf "${output_path}" -C "${output_dir}" ;; esac rm -f "${output_path}" local executable_ext case "${os}" in - windows*) executable_ext='.exe' ;; - *) executable_ext='' ;; + windows) executable_ext='.exe' ;; + *) executable_ext='' ;; esac local configlet_path="${output_dir}/configlet${executable_ext}" diff --git a/bin/fetch-configlet.ps1 b/bin/fetch-configlet.ps1 index ab8f1036..a7896b22 100644 --- a/bin/fetch-configlet.ps1 +++ b/bin/fetch-configlet.ps1 @@ -12,20 +12,31 @@ $requestOpts = @{ RetryIntervalSec = 1 } -$arch = If ([Environment]::Is64BitOperatingSystem) { "x86-64" } Else { "i386" } -$fileName = "configlet_.+_windows_$arch.zip" - Function Get-DownloadUrl { + $arch = If ([Environment]::Is64BitOperatingSystem) { "x86-64" } Else { "i386" } $latestUrl = "https://api.github.com/repos/exercism/configlet/releases/latest" - Invoke-RestMethod -Uri $latestUrl -PreserveAuthorizationOnRedirect @requestOpts - | Select-Object -ExpandProperty assets - | Where-Object { $_.browser_download_url -match $FileName } - | Select-Object -ExpandProperty browser_download_url + Invoke-RestMethod -Uri $latestUrl -PreserveAuthorizationOnRedirect @requestOpts ` + | Select-Object -ExpandProperty assets ` + | Where-Object { $_.name -match "^configlet_.+_windows_${arch}.zip$" } ` + | Select-Object -ExpandProperty browser_download_url -First 1 } -$downloadUrl = Get-DownloadUrl $outputDirectory = "bin" -$outputFile = Join-Path -Path $outputDirectory -ChildPath $fileName -Invoke-WebRequest -Uri $downloadUrl -OutFile $outputFile @requestOpts -Expand-Archive $outputFile -DestinationPath $outputDirectory -Force -Remove-Item -Path $outputFile +if (!(Test-Path -Path $outputDirectory)) { + Write-Output "Error: no ./bin directory found. This script should be ran from a repo root." + exit 1 +} + +Write-Output "Fetching configlet..." +$downloadUrl = Get-DownloadUrl +$outputFileName = "configlet.zip" +$outputPath = Join-Path -Path $outputDirectory -ChildPath $outputFileName +Invoke-WebRequest -Uri $downloadUrl -OutFile $outputPath @requestOpts + +$configletPath = Join-Path -Path $outputDirectory -ChildPath "configlet.exe" +if (Test-Path -Path $configletPath) { Remove-Item -Path $configletPath } +[System.IO.Compression.ZipFile]::ExtractToDirectory($outputPath, $outputDirectory) +Remove-Item -Path $outputPath + +$configletVersion = (Select-String -Pattern "/releases/download/(.+?)/" -InputObject $downloadUrl -AllMatches).Matches.Groups[1].Value +Write-Output "Downloaded configlet ${configletVersion} to ${configletPath}" diff --git a/bin/verify-exercises b/bin/verify-exercises index 8d313282..27cbaf00 100755 --- a/bin/verify-exercises +++ b/bin/verify-exercises @@ -16,7 +16,7 @@ # .meta/config.json file, or possibly inferred from the exercise's directory name. # Example: -# ./bin/test +# ./bin/verify-exercises.sh # Verify the Concept Exercises for concept_exercise_dir in ./exercises/concept/*/; do @@ -26,10 +26,47 @@ for concept_exercise_dir in ./exercises/concept/*/; do fi done + +SUCCESS=0 # Verify the Practice Exercises +for practice_exercise_dir in ./exercises/practice/*/; do + if [[ $SUCCESS -eq 0 && -d $practice_exercise_dir ]]; then + exercise=$(basename "${practice_exercise_dir}") + echo "Checking $exercise exercise..." + + practice_exercise_dir=${practice_exercise_dir%/} + if [ -f "$practice_exercise_dir/$exercise".arr ]; then + mv "$practice_exercise_dir/$exercise".arr "$practice_exercise_dir/$exercise".arr.bak + mv "$practice_exercise_dir/.meta/example.arr" "$practice_exercise_dir/$exercise".arr + + test_file="$practice_exercise_dir/$exercise"-test.arr + + redirect_file="$practice_exercise_dir/$exercise".out + npx pyret -q $test_file &> $redirect_file + test_output=$(cat $redirect_file) + rm $redirect_file + test_success=$(echo "${test_output}" | grep -c -E 'Looks shipshape') + + if [[ $test_success -le 0 ]]; then + echo "FAILURE: $exercise" + SUCCESS=1 + fi + fi + fi +done + for practice_exercise_dir in ./exercises/practice/*/; do if [ -d $practice_exercise_dir ]; then - echo "Checking $(basename "${practice_exercise_dir}") exercise..." - # TODO: run command to verify that the example solution passes the tests + exercise=$(basename "${practice_exercise_dir}") + if [ -f "$practice_exercise_dir/$exercise".arr ]; then + mv "$practice_exercise_dir/$exercise".arr "$practice_exercise_dir"/.meta/example.arr + if [ -f "$practice_exercise_dir/$exercise".arr.bak ]; then + mv "$practice_exercise_dir/$exercise".arr.bak "$practice_exercise_dir/$exercise".arr + fi + fi fi done + +if [ $SUCCESS -ne 0 ]; then + exit $SUCCESS +fi \ No newline at end of file diff --git a/concepts/booleans/.meta/config.json b/concepts/booleans/.meta/config.json new file mode 100644 index 00000000..8182c9bd --- /dev/null +++ b/concepts/booleans/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "Pyret provides a Boolean primitive with two distinct values: 'true' and 'false'.", + "authors": [ + "BNAndras" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/booleans/about.md b/concepts/booleans/about.md new file mode 100644 index 00000000..d3714387 --- /dev/null +++ b/concepts/booleans/about.md @@ -0,0 +1,36 @@ +# Introduction + +Pyret has two distinct Boolean primitives, `true` and `false`. +These two values represent whether a statement or condition is valid or not. + +```pyret +"a" == "a" # true +"a" == "b" # false +``` + +Pyret provides the operators `and` and `or` that can combine Boolean values. + +The and operator returns true if both sides are true and false for all other combinations. + +```pyret +true and true # true +true and false # true +false and true # false +false and false # false +``` + +The or operator returns true if at least one side is true and false if both sides are false. + +```pyret +true or true # true +true or false # true +false or true # true +false or false # false +``` + +`not()` is a built-in function that flips a true to false or a false to true. + +```pyret +not(true) # false +not(false) # true +``` diff --git a/concepts/booleans/introduction.md b/concepts/booleans/introduction.md new file mode 100644 index 00000000..d3714387 --- /dev/null +++ b/concepts/booleans/introduction.md @@ -0,0 +1,36 @@ +# Introduction + +Pyret has two distinct Boolean primitives, `true` and `false`. +These two values represent whether a statement or condition is valid or not. + +```pyret +"a" == "a" # true +"a" == "b" # false +``` + +Pyret provides the operators `and` and `or` that can combine Boolean values. + +The and operator returns true if both sides are true and false for all other combinations. + +```pyret +true and true # true +true and false # true +false and true # false +false and false # false +``` + +The or operator returns true if at least one side is true and false if both sides are false. + +```pyret +true or true # true +true or false # true +false or true # true +false or false # false +``` + +`not()` is a built-in function that flips a true to false or a false to true. + +```pyret +not(true) # false +not(false) # true +``` diff --git a/concepts/booleans/links.json b/concepts/booleans/links.json new file mode 100644 index 00000000..ce73316a --- /dev/null +++ b/concepts/booleans/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://pyret.org/docs/latest/A_Tour_of_Pyret.html#%28part._.Booleans%29", + "description": "A Tour of Pyret: Booleans" + } +] diff --git a/concepts/comparisons/.meta/config.json b/concepts/comparisons/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/comparisons/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/comparisons/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/comparisons/introduction.md b/concepts/comparisons/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/comparisons/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/comparisons/links.json b/concepts/comparisons/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/comparisons/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/conditionals/.meta/config.json b/concepts/conditionals/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/conditionals/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/conditionals/about.md b/concepts/conditionals/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/conditionals/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/conditionals/introduction.md b/concepts/conditionals/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/conditionals/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/conditionals/links.json b/concepts/conditionals/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/conditionals/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/data-definitions/.meta/config.json b/concepts/data-definitions/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/data-definitions/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/data-definitions/about.md b/concepts/data-definitions/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/data-definitions/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/data-definitions/introduction.md b/concepts/data-definitions/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/data-definitions/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/data-definitions/links.json b/concepts/data-definitions/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/data-definitions/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/function-arguments/.meta/config.json b/concepts/function-arguments/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/function-arguments/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/function-arguments/about.md b/concepts/function-arguments/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/function-arguments/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/function-arguments/introduction.md b/concepts/function-arguments/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/function-arguments/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/function-arguments/links.json b/concepts/function-arguments/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/function-arguments/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/lists/.meta/config.json b/concepts/lists/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/lists/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/lists/about.md b/concepts/lists/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/lists/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/lists/introduction.md b/concepts/lists/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/lists/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/lists/links.json b/concepts/lists/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/lists/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/loops/.meta/config.json b/concepts/loops/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/loops/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/loops/about.md b/concepts/loops/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/loops/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/loops/introduction.md b/concepts/loops/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/loops/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/loops/links.json b/concepts/loops/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/loops/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/numbers/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/numbers/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/numbers/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/numbers/links.json b/concepts/numbers/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/numbers/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/string-dicts/.meta/config.json b/concepts/string-dicts/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/string-dicts/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/string-dicts/about.md b/concepts/string-dicts/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/string-dicts/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/string-dicts/introduction.md b/concepts/string-dicts/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/string-dicts/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/string-dicts/links.json b/concepts/string-dicts/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/string-dicts/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/concepts/strings/.meta/config.json b/concepts/strings/.meta/config.json new file mode 100644 index 00000000..72aa251c --- /dev/null +++ b/concepts/strings/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "TODO: add blurb for this concept", + "authors": [ + "YourNameHere" + ], + "contributors": [] +} \ No newline at end of file diff --git a/concepts/strings/about.md b/concepts/strings/about.md new file mode 100644 index 00000000..e3aab23a --- /dev/null +++ b/concepts/strings/about.md @@ -0,0 +1 @@ +# TODO: Add about for this concept diff --git a/concepts/strings/introduction.md b/concepts/strings/introduction.md new file mode 100644 index 00000000..63af7123 --- /dev/null +++ b/concepts/strings/introduction.md @@ -0,0 +1 @@ +# TODO: Add introduction for this concept diff --git a/concepts/strings/links.json b/concepts/strings/links.json new file mode 100644 index 00000000..4840efbc --- /dev/null +++ b/concepts/strings/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "http://example.com/", + "description": "TODO: add new link (above) and write a short description here of the resource." + } +] diff --git a/config.json b/config.json index 16002c3a..7be0b1d7 100644 --- a/config.json +++ b/config.json @@ -1,41 +1,663 @@ { "language": "Pyret", "slug": "pyret", - "active": false, + "active": true, "status": { "concept_exercises": false, - "test_runner": false, + "test_runner": true, "representer": false, "analyzer": false }, - "blurb": "TODO: add blurb", + "blurb": "Designed in part to ease students into computer science fundamentals, Pyret plays a central role in 'A Data-Centric Introduction to Computing', an introductory college textbook, and is currently being taught in U.S. high schools.", "version": 3, "online_editor": { "indent_style": "space", - "indent_size": 4, - "highlightjs_language": "TODO: specify highlightjs language" + "indent_size": 2, + "highlightjs_language": "plaintext" + }, + "test_runner": { + "average_run_time": 3 }, "files": { - "solution": [], - "test": [], - "example": [], - "exemplar": [] + "solution": [ + "%{kebab_slug}.arr" + ], + "test": [ + "%{kebab_slug}-test.arr" + ], + "example": [ + ".meta/example.arr" + ], + "exemplar": [ + ".meta/exemplar.arr" + ] }, "exercises": { - "concept": [], "practice": [ + { + "slug": "acronym", + "name": "Acronym", + "uuid": "5ae47f42-45fc-4de2-b0df-3098ef622b0c", + "practices": [ + "loops", + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "allergies", + "name": "Allergies", + "uuid": "37555c56-5c77-4b6a-93eb-decde0ae8825", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "anagram", + "name": "Anagram", + "uuid": "52e3290f-b78f-423f-a1cf-d727edc9fa73", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "armstrong-numbers", + "name": "Armstrong Numbers", + "uuid": "26431238-01d5-4890-9bb1-f5b023e2c295", + "practices": [ + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "atbash-cipher", + "name": "Atbash Cipher", + "uuid": "7263c877-5ddd-4c48-886f-e3f57967f649", + "practices": [ + "strings", + "conditionals" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "bank-account", + "name": "Bank Account", + "uuid": "44b5048a-8e98-4300-8bd9-fce59dcc008b", + "practices": [ + "data-definitions" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "binary-search", + "name": "Binary Search", + "uuid": "dc4437e4-9ab3-4ab0-ae0d-ae3d2287e97c", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "status": "deprecated" + }, + { + "slug": "bob", + "name": "Bob", + "uuid": "28d1beed-ff83-4341-8a71-08fd287489fd", + "practices": [ + "conditionals" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "collatz-conjecture", + "name": "Collatz Conjecture", + "uuid": "00a0a185-0b05-4f83-a840-80070635255a", + "practices": [ + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "darts", "name": "Darts", "uuid": "d70a98f9-4743-40b7-ab18-f37d5508a7a5", + "practices": [ + "comparisons" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "difference-of-squares", + "name": "Difference of Squares", + "uuid": "6968b216-4478-4778-93d3-197ed849fd40", + "practices": [ + "numbers", + "loops" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "etl", + "name": "ETL", + "uuid": "a86c1654-da80-46c9-aba5-a2eec3f66262", + "practices": [ + "string-dicts" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "flatten-array", + "name": "Flatten Array", + "uuid": "23b5a864-186c-4a4d-ba32-83608b0d18f6", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "grains", + "name": "Grains", + "uuid": "04948c47-34d1-4200-a6f0-5131f63c5600", + "practices": [ + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "hamming", + "name": "Hamming", + "uuid": "731dcc09-a78c-4da4-a6c7-47bb0b6720f2", + "practices": [ + "comparisons", + "loops" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "hello-world", + "name": "Hello World", + "uuid": "21cb7132-eb7a-41cc-b100-4655288c3c98", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "high-scores", + "name": "High Scores", + "uuid": "8a71dfd1-01a6-485a-87b2-dcee0dd7b965", + "practices": [ + "data-definitions" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "isogram", + "name": "Isogram", + "uuid": "dff46c06-d8be-48e0-8158-4c6c484a3cbc", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "leap", + "name": "Leap", + "uuid": "c67175ca-9706-4aec-a45b-040a23ef25c7", + "practices": [ + "booleans", + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "list-ops", + "name": "List Ops", + "uuid": "0a28e195-6f3c-46e2-a62c-1a7097583032", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "luhn", + "name": "Luhn", + "uuid": "9476a1c3-1775-453d-bb08-be919fd03bad", + "practices": [ + "loops" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "matrix", + "name": "Matrix", + "uuid": "eef61f56-6dda-4389-b348-7c27835493b8", + "practices": [ + "loops" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "nucleotide-count", + "name": "Nucleotide Count", + "uuid": "437768f4-a4d4-4a87-bf8b-6fe403e34179", + "practices": [ + "loops", + "string-dicts" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "pangram", + "name": "Pangram", + "uuid": "ef2dc89f-a9d2-48f0-9bb5-4cafeb50c81c", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "uuid": "dbf1655b-c3d3-4443-8862-db4e6c3136e4", + "practices": [ + "comparisons" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "phone-number", + "name": "Phone Number", + "uuid": "dd3ab667-8743-4761-af2d-781fc80959f9", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "eliuds-eggs", + "name": "Eliud's Eggs", + "uuid": "ebfaddfb-d094-4471-8771-fc09cf14274f", + "practices": [ + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "line-up", + "name": "Line Up", + "uuid": "c3691abc-0732-4487-bf91-bcbfd55f22ca", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "protein-translation", + "name": "Protein Translation", + "uuid": "29563d5a-a703-43c8-953e-3fde7daae3b0", + "practices": [ + "loops", + "string-dicts" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "proverb", + "name": "Proverb", + "uuid": "18946346-e04c-4032-b3dc-4abdb6c8f5a1", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "queen-attack", + "name": "Queen Attack", + "uuid": "5a57c5fa-f4a2-460d-9dec-812834ae1cd4", + "practices": [ + "data-definitions" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "raindrops", + "name": "Raindrops", + "uuid": "8dcd513e-93f5-499b-a388-0a7f3c7898dd", + "practices": [ + "comparisons" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "resistor-color", + "name": "Resistor Color", + "uuid": "57bc4302-4c4b-43a7-9abb-2378dd6ef70e", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "resistor-color-duo", + "name": "Resistor Color Duo", + "uuid": "3f897d66-6f49-49ca-a6ab-a168d6ef5e68", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "resistor-color-trio", + "name": "Resistor Color Trio", + "uuid": "4e758c9f-3204-41cd-b8d1-8f978bd90809", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "reverse-string", + "name": "Reverse String", + "uuid": "69911771-a41a-4be9-b685-678def75e1b2", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "rna-transcription", + "name": "RNA Transcription", + "uuid": "00406c43-30c9-4756-a830-b110b6b53eb1", + "practices": [ + "comparisons", + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "roman-numerals", + "name": "Roman Numerals", + "uuid": "1382711a-e980-4ff1-825a-a6373ae3d88f", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "scrabble-score", + "name": "Scrabble Score", + "uuid": "8b1cfd2f-0d25-4eec-9b8e-75c7a339fc6c", + "practices": [ + "string-dicts" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "secret-handshake", + "name": "Secret Handshake", + "uuid": "3a830edd-253c-4889-ad6b-9402fea4af97", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "series", + "name": "Series", + "uuid": "f58b5305-3bf2-4c15-8aac-339a97204d06", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "simple-linked-list", + "name": "Simple Linked List", + "uuid": "c2529f83-00db-427c-a478-57a8c7512f12", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "space-age", + "name": "Space Age", + "uuid": "fcdf45a2-a72c-4983-8271-ca9276467f98", + "practices": [ + "string-dicts", + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "triangle", + "name": "Triangle", + "uuid": "aab55f24-e31a-4e99-8c6c-12e69de7ce7c", + "practices": [ + "booleans", + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "square-root", + "name": "Square Root", + "uuid": "351651f4-3e42-4392-8862-9a18f9acb70d", + "practices": [ + "numbers" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "strain", + "name": "Strain", + "uuid": "8a4f0fdd-831f-4e28-9cf9-391b725bcf69", + "practices": [ + "lists" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "word-count", + "name": "Word Count", + "uuid": "df93be4e-a862-4636-bb1c-e51e0e294797", + "practices": [ + "strings", + "string-dicts", + "loops" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "two-fer", + "name": "Two Fer", + "uuid": "198c1176-536a-49db-828b-0dc1655d7da9", + "practices": [ + "function-arguments" + ], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "circular-buffer", + "name": "Circular Buffer", + "uuid": "512d263f-b3ce-4f5a-ba43-6b05a2b5c0f5", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "clock", + "name": "Clock", + "uuid": "1adaf7d8-7154-4ab9-8db3-7c120f52338c", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "dnd-character", + "name": "D&D Character", + "uuid": "2401de1d-0a63-49b2-ad22-3807cd76778f", + "practices": [ + "data-definitions" + ], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "robot-simulator", + "name": "Robot Simulator", + "uuid": "e81a517a-16be-4760-980a-21ee549c24ea", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "sieve", + "name": "Sieve", + "uuid": "5f29c607-3d50-476b-967a-5c54b65402aa", "practices": [], "prerequisites": [], - "difficulty": 1, - "topics": null + "difficulty": 5 } + ], + "foregone": [ + "error-handling", + "gigasecond", + "lens-person", + "parallel-letter-frequency" ] }, - "concepts": [], - "key_features": [], - "tags": [] + "concepts": [ + { + "uuid": "f4293a2d-6b59-4372-8764-2344afe3195a", + "slug": "booleans", + "name": "Booleans" + }, + { + "uuid": "596b676c-2118-4fa4-a8be-f0ded17a6294", + "slug": "comparisons", + "name": "Comparisons" + }, + { + "uuid": "7ebdbbc7-68f8-40f1-bbe0-957d5cead31e", + "slug": "conditionals", + "name": "Conditionals" + }, + { + "uuid": "f03bf87f-e3ef-4771-b3f0-b360a864d9a3", + "slug": "data-definitions", + "name": "Data Definitions" + }, + { + "uuid": "1a5898a8-01f7-4b84-89ed-1cb2cb8dc8fe", + "slug": "function-arguments", + "name": "Function Arguments" + }, + { + "uuid": "33dd5c32-e13f-4d61-bdce-cc976e86351e", + "slug": "lists", + "name": "Lists" + }, + { + "uuid": "da0fd100-f1e1-4bc9-98fd-a2bb52962358", + "slug": "loops", + "name": "Loops" + }, + { + "uuid": "e2284f76-9c87-4b29-9c84-4fb628a92f2d", + "slug": "numbers", + "name": "Numbers" + }, + { + "uuid": "bec21c96-0c3d-48fb-ba7a-2b5f744a1d30", + "slug": "string-dicts", + "name": "StringDicts" + }, + { + "uuid": "38486099-8bf7-4721-9c94-fd3b30c522a9", + "slug": "strings", + "name": "Strings" + } + ], + "key_features": [ + { + "title": "Simplified syntax", + "content": "As a teaching language, Pyret prioritizes readability using clean and straight-forward syntax", + "icon": "easy" + }, + { + "title": "Runs almost everywhere", + "content": "Pyret is available wherever npm can be installed or online at https://code.pyret.org/editor", + "icon": "cross-platform" + }, + { + "title": "Pedagogical design", + "content": "Pyret was designed to have a gradual learning curve, focusing on general programming practices", + "icon": "easy" + }, + { + "title": "Lightweight", + "content": "Entire toolchain is about 100 MB download", + "icon": "small" + }, + { + "title": "Tooling", + "content": "Pyret provides a built-in test runner, making testing a natural part of the development process", + "icon": "tooling" + }, + { + "title": "Innovative", + "content": "Pyret is an actively-developed testbed for advancements in teaching computer science fundamentals", + "icon": "evolving" + } + ], + "tags": [ + "execution_mode/compiled", + "paradigm/functional", + "platform/linux", + "platform/mac", + "platform/web", + "runtime/language_specific", + "typing/dynamic", + "used_for/scientific_calculations", + "used_for/scripts" + ] } diff --git a/docs/ABOUT.md b/docs/ABOUT.md index 9864ed00..49c8ac70 100644 --- a/docs/ABOUT.md +++ b/docs/ABOUT.md @@ -1,14 +1,9 @@ # About - +[dcic]: https://dcic-world.org/ +[get-started]: https://pyret.org/docs/latest/Getting_Started_and_Running_Pyret.html diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index cd6ce852..e191f9aa 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -1,15 +1,12 @@ # Installation - +[pyret-npm]: https://www.npmjs.com/package/pyret-npm +[node-js]: https://nodejs.org diff --git a/docs/LEARNING.md b/docs/LEARNING.md index b14434dc..536ef745 100644 --- a/docs/LEARNING.md +++ b/docs/LEARNING.md @@ -1,13 +1,13 @@ # Learning - +[A Data-Centric Introduction to Computing]: https://dcic-world.org/ +[Programming and Programming Languages]: https://papl.cs.brown.edu/2020/ +[Grade 7 - 12 Lesson Plans for Bootstrap:Data Science]: https://www.bootstrapworld.org/materials/fall2023/en-us/courses/data-science/ +[Design Recipes for Pyret Functions]: https://www.cs.swarthmore.edu/~jpolitz/cs91/s15/n_design-recipe.html diff --git a/docs/RESOURCES.md b/docs/RESOURCES.md index 9811f046..eb027cb2 100644 --- a/docs/RESOURCES.md +++ b/docs/RESOURCES.md @@ -1,13 +1,15 @@ # Resources - +* [Official Documentation][official-docs] +* [code.pyret.org (CPO) Editor][online-editor] +* [CPO-Like Editor Extension for Visual Studio Code][pyret-vscode] +* [Pyret Discuss (Google Groups)][google-groups] +* [@PyretLang][pyret-twitter] +* [The Pyret Programming Discord][pyret-discord] + +[official-docs]: https://pyret.org/docs/latest/ +[online-editor]: https://code.pyret.org/editor +[pyret-vscode]: https://marketplace.visualstudio.com/items?itemName=PyretProgrammingLanguage.pyret-parley +[google-groups]: https://groups.google.com/g/pyret-discuss +[pyret-twitter]: https://twitter.com/pyretlang +[pyret-discord]: https://discord.com/invite/7aFMB3b6Mt diff --git a/docs/SNIPPET.txt b/docs/SNIPPET.txt index 4b4a4f0f..12a56370 100644 --- a/docs/SNIPPET.txt +++ b/docs/SNIPPET.txt @@ -1 +1,9 @@ -TODO: add snippet +fun sum(lst): + cases (List) lst: + | empty => 0 + | link(first, rest) => first + sum(rest) + end +where: + sum([list: ]) is 0 + sum([list: 1, 2, 3]) is 6 +end diff --git a/docs/TESTS.md b/docs/TESTS.md index bec9502b..c2b14cbe 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -1,15 +1,65 @@ -# Tests - +## provide + +Tests on this track will import your file, allowing access to anything explicitly exported from your code. + +To export variables, you need to add a [provide statement][provide-statement] at the beginning of your file. + +The following snippets are three valid ways to export `a`, `b`, and `c`. + +```pyret +# using a list of bindings +provide a, b, c end +``` + +```pyret +# using an object literal +provide { + a: a, + b: b, + c: c +} +end +``` + +`provide *` is a shorthand for exporting all the top-level bindings except for custom data types +However, it's generally not recommended because Pyret is strict about not allowing [shadowing][shadowing]. + +## provide-types + +Some exercises will require a [custom data type][data-definition] to be exported for testing purposes. +In those situations, you can use a [provide-types statement][provide-types-statement]. +Since a data type has additional functions that might not be exported, it's advised to use `provide-types *` despite the shadowing concern. + +```pyret +provide-types * + +data MyPoint: + | two-dim(x, y) + | three-dim(x, y, z) +end +``` + +All exercise stubs will have either `provide` or `provide-types` statements set up for your use. + +[provide-statement]: https://pyret.org/docs/latest/Provide_Statements.html +[shadowing]: https://pyret.org/docs/latest/Bindings.html#%28part._s~3ashadowing%29 +[data-definition]: https://pyret.org/docs/latest/s_declarations.html#%28elem._%28bnf-prod._%28.Pyret._data-decl%29%29%29 +[provide-types-statement]: https://pyret.org/docs/latest/Provide_Statements.html diff --git a/exercises/practice/acronym/.docs/instructions.md b/exercises/practice/acronym/.docs/instructions.md new file mode 100644 index 00000000..133bd2cb --- /dev/null +++ b/exercises/practice/acronym/.docs/instructions.md @@ -0,0 +1,17 @@ +# Instructions + +Convert a phrase to its acronym. + +Techies love their TLA (Three Letter Acronyms)! + +Help generate some jargon by writing a program that converts a long name like Portable Network Graphics to its acronym (PNG). + +Punctuation is handled as follows: hyphens are word separators (like whitespace); all other punctuation can be removed from the input. + +For example: + +| Input | Output | +| ------------------------- | ------ | +| As Soon As Possible | ASAP | +| Liquid-crystal display | LCD | +| Thank George It's Friday! | TGIF | diff --git a/exercises/practice/acronym/.meta/config.json b/exercises/practice/acronym/.meta/config.json new file mode 100644 index 00000000..3776bffa --- /dev/null +++ b/exercises/practice/acronym/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "acronym.arr" + ], + "test": [ + "acronym-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert a long phrase to its acronym.", + "source": "Julien Vanier", + "source_url": "https://github.com/monkbroc" +} diff --git a/exercises/practice/acronym/.meta/example.arr b/exercises/practice/acronym/.meta/example.arr new file mode 100644 index 00000000..3a802cd2 --- /dev/null +++ b/exercises/practice/acronym/.meta/example.arr @@ -0,0 +1,22 @@ +use context starter2024 + +provide: abbreviate end + + fun abbreviate(phrase): + string-split-all(phrase, " ").foldl( + lam(section, abbreviation): + cleaned = string-replace(section, "_", "") + fragments = string-split-all(cleaned, "-") + abbreviation + fragments.foldl( + lam(fragment, acc): + ask: + | fragment <> "" then: + acc + string-to-upper(string-char-at(fragment, 0)) + | otherwise: + acc + end + end, + "") + end, + "") +end diff --git a/exercises/practice/acronym/.meta/tests.toml b/exercises/practice/acronym/.meta/tests.toml new file mode 100644 index 00000000..6e3277c6 --- /dev/null +++ b/exercises/practice/acronym/.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. + +[1e22cceb-c5e4-4562-9afe-aef07ad1eaf4] +description = "basic" + +[79ae3889-a5c0-4b01-baf0-232d31180c08] +description = "lowercase words" + +[ec7000a7-3931-4a17-890e-33ca2073a548] +description = "punctuation" + +[32dd261c-0c92-469a-9c5c-b192e94a63b0] +description = "all caps word" + +[ae2ac9fa-a606-4d05-8244-3bcc4659c1d4] +description = "punctuation without whitespace" + +[0e4b1e7c-1a6d-48fb-81a7-bf65eb9e69f9] +description = "very long abbreviation" + +[6a078f49-c68d-4b7b-89af-33a1a98c28cc] +description = "consecutive delimiters" + +[5118b4b1-4572-434c-8d57-5b762e57973e] +description = "apostrophes" + +[adc12eab-ec2d-414f-b48c-66a4fc06cdef] +description = "underscore emphasis" diff --git a/exercises/practice/acronym/acronym-test.arr b/exercises/practice/acronym/acronym-test.arr new file mode 100644 index 00000000..0f02cf94 --- /dev/null +++ b/exercises/practice/acronym/acronym-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("acronym.arr") + +check "basic": + abbreviate("Portable Network Graphics") is "PNG" +end + +check "lowercase words": + abbreviate("Ruby on Rails") is "ROR" +end + +check "punctuation": + abbreviate("First In, First Out") is "FIFO" +end + +check "all caps word": + abbreviate("GNU Image Manipulation Program") is "GIMP" +end + +check "punctuation without whitespace": + abbreviate("Complementary metal-oxide semiconductor") is "CMOS" +end + +check "very long abbreviation": + abbreviate("Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me") is "ROTFLSHTMDCOALM" +end + +check "consecutive delimiters": + abbreviate("Something - I made up from thin air") is "SIMUFTA" +end + +check "apostrophes": + abbreviate("Halley's Comet") is "HC" +end + +check "underscore emphasis": + abbreviate("The Road _Not_ Taken") is "TRNT" +end + diff --git a/exercises/practice/acronym/acronym.arr b/exercises/practice/acronym/acronym.arr new file mode 100644 index 00000000..23f79d9c --- /dev/null +++ b/exercises/practice/acronym/acronym.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: abbreviate end + +fun abbreviate(phrase): + raise("please implement the abbreviate function") +end diff --git a/exercises/practice/allergies/.docs/instructions.md b/exercises/practice/allergies/.docs/instructions.md new file mode 100644 index 00000000..daf8cfde --- /dev/null +++ b/exercises/practice/allergies/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies. + +An allergy test produces a single numeric score which contains the information about all the allergies the person has (that they were tested for). + +The list of items (and their value) that were tested are: + +- eggs (1) +- peanuts (2) +- shellfish (4) +- strawberries (8) +- tomatoes (16) +- chocolate (32) +- pollen (64) +- cats (128) + +So if Tom is allergic to peanuts and chocolate, he gets a score of 34. + +Now, given just that score of 34, your program should be able to say: + +- Whether Tom is allergic to any one of those allergens listed above. +- All the allergens Tom is allergic to. + +Note: a given score may include allergens **not** listed above (i.e. allergens that score 256, 512, 1024, etc.). +Your program should ignore those components of the score. +For example, if the allergy score is 257, your program should only report the eggs (1) allergy. diff --git a/exercises/practice/allergies/.meta/config.json b/exercises/practice/allergies/.meta/config.json new file mode 100644 index 00000000..c405d871 --- /dev/null +++ b/exercises/practice/allergies/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "BNAndras" + ], + "contributors": [ + "IsaacG" + ], + "files": { + "solution": [ + "allergies.arr" + ], + "test": [ + "allergies-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://www.turing.edu/" +} diff --git a/exercises/practice/allergies/.meta/example.arr b/exercises/practice/allergies/.meta/example.arr new file mode 100644 index 00000000..0ac1f598 --- /dev/null +++ b/exercises/practice/allergies/.meta/example.arr @@ -0,0 +1,40 @@ +use context starter2024 + +provide-types * + +import lists as L + +ALLERGENS = [list: + "eggs", + "peanuts", + "shellfish", + "strawberries", + "tomatoes", + "chocolate", + "pollen", + "cats" +] + +data Allergies: + | allergies(score :: Number) with: + method allergicTo(self, allergen :: String) -> Boolean: + self.list().member(allergen) + end, + method list(self) -> List: + rec filtered = + lam(l :: List, num :: Number): + cases(List) l: + | empty => [list: ] + | link(f, r) => + new-num = num-truncate(num / 2) + if num-modulo(num, 2) == 1: + filtered(r, new-num).push(f) + else: + filtered(r, new-num) + end + end + end + + filtered(ALLERGENS, self.score) + end +end diff --git a/exercises/practice/allergies/.meta/tests.toml b/exercises/practice/allergies/.meta/tests.toml new file mode 100644 index 00000000..799ab856 --- /dev/null +++ b/exercises/practice/allergies/.meta/tests.toml @@ -0,0 +1,160 @@ +# 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. + +[17fc7296-2440-4ac4-ad7b-d07c321bc5a0] +description = "testing for eggs allergy -> not allergic to anything" + +[07ced27b-1da5-4c2e-8ae2-cb2791437546] +description = "testing for eggs allergy -> allergic only to eggs" + +[5035b954-b6fa-4b9b-a487-dae69d8c5f96] +description = "testing for eggs allergy -> allergic to eggs and something else" + +[64a6a83a-5723-4b5b-a896-663307403310] +description = "testing for eggs allergy -> allergic to something, but not eggs" + +[90c8f484-456b-41c4-82ba-2d08d93231c6] +description = "testing for eggs allergy -> allergic to everything" + +[d266a59a-fccc-413b-ac53-d57cb1f0db9d] +description = "testing for peanuts allergy -> not allergic to anything" + +[ea210a98-860d-46b2-a5bf-50d8995b3f2a] +description = "testing for peanuts allergy -> allergic only to peanuts" + +[eac69ae9-8d14-4291-ac4b-7fd2c73d3a5b] +description = "testing for peanuts allergy -> allergic to peanuts and something else" + +[9152058c-ce39-4b16-9b1d-283ec6d25085] +description = "testing for peanuts allergy -> allergic to something, but not peanuts" + +[d2d71fd8-63d5-40f9-a627-fbdaf88caeab] +description = "testing for peanuts allergy -> allergic to everything" + +[b948b0a1-cbf7-4b28-a244-73ff56687c80] +description = "testing for shellfish allergy -> not allergic to anything" + +[9ce9a6f3-53e9-4923-85e0-73019047c567] +description = "testing for shellfish allergy -> allergic only to shellfish" + +[b272fca5-57ba-4b00-bd0c-43a737ab2131] +description = "testing for shellfish allergy -> allergic to shellfish and something else" + +[21ef8e17-c227-494e-8e78-470a1c59c3d8] +description = "testing for shellfish allergy -> allergic to something, but not shellfish" + +[cc789c19-2b5e-4c67-b146-625dc8cfa34e] +description = "testing for shellfish allergy -> allergic to everything" + +[651bde0a-2a74-46c4-ab55-02a0906ca2f5] +description = "testing for strawberries allergy -> not allergic to anything" + +[b649a750-9703-4f5f-b7f7-91da2c160ece] +description = "testing for strawberries allergy -> allergic only to strawberries" + +[50f5f8f3-3bac-47e6-8dba-2d94470a4bc6] +description = "testing for strawberries allergy -> allergic to strawberries and something else" + +[23dd6952-88c9-48d7-a7d5-5d0343deb18d] +description = "testing for strawberries allergy -> allergic to something, but not strawberries" + +[74afaae2-13b6-43a2-837a-286cd42e7d7e] +description = "testing for strawberries allergy -> allergic to everything" + +[c49a91ef-6252-415e-907e-a9d26ef61723] +description = "testing for tomatoes allergy -> not allergic to anything" + +[b69c5131-b7d0-41ad-a32c-e1b2cc632df8] +description = "testing for tomatoes allergy -> allergic only to tomatoes" + +[1ca50eb1-f042-4ccf-9050-341521b929ec] +description = "testing for tomatoes allergy -> allergic to tomatoes and something else" + +[e9846baa-456b-4eff-8025-034b9f77bd8e] +description = "testing for tomatoes allergy -> allergic to something, but not tomatoes" + +[b2414f01-f3ad-4965-8391-e65f54dad35f] +description = "testing for tomatoes allergy -> allergic to everything" + +[978467ab-bda4-49f7-b004-1d011ead947c] +description = "testing for chocolate allergy -> not allergic to anything" + +[59cf4e49-06ea-4139-a2c1-d7aad28f8cbc] +description = "testing for chocolate allergy -> allergic only to chocolate" + +[b0a7c07b-2db7-4f73-a180-565e07040ef1] +description = "testing for chocolate allergy -> allergic to chocolate and something else" + +[f5506893-f1ae-482a-b516-7532ba5ca9d2] +description = "testing for chocolate allergy -> allergic to something, but not chocolate" + +[02debb3d-d7e2-4376-a26b-3c974b6595c6] +description = "testing for chocolate allergy -> allergic to everything" + +[17f4a42b-c91e-41b8-8a76-4797886c2d96] +description = "testing for pollen allergy -> not allergic to anything" + +[7696eba7-1837-4488-882a-14b7b4e3e399] +description = "testing for pollen allergy -> allergic only to pollen" + +[9a49aec5-fa1f-405d-889e-4dfc420db2b6] +description = "testing for pollen allergy -> allergic to pollen and something else" + +[3cb8e79f-d108-4712-b620-aa146b1954a9] +description = "testing for pollen allergy -> allergic to something, but not pollen" + +[1dc3fe57-7c68-4043-9d51-5457128744b2] +description = "testing for pollen allergy -> allergic to everything" + +[d3f523d6-3d50-419b-a222-d4dfd62ce314] +description = "testing for cats allergy -> not allergic to anything" + +[eba541c3-c886-42d3-baef-c048cb7fcd8f] +description = "testing for cats allergy -> allergic only to cats" + +[ba718376-26e0-40b7-bbbe-060287637ea5] +description = "testing for cats allergy -> allergic to cats and something else" + +[3c6dbf4a-5277-436f-8b88-15a206f2d6c4] +description = "testing for cats allergy -> allergic to something, but not cats" + +[1faabb05-2b98-4995-9046-d83e4a48a7c1] +description = "testing for cats allergy -> allergic to everything" + +[f9c1b8e7-7dc5-4887-aa93-cebdcc29dd8f] +description = "list when: -> no allergies" + +[9e1a4364-09a6-4d94-990f-541a94a4c1e8] +description = "list when: -> just eggs" + +[8851c973-805e-4283-9e01-d0c0da0e4695] +description = "list when: -> just peanuts" + +[2c8943cb-005e-435f-ae11-3e8fb558ea98] +description = "list when: -> just strawberries" + +[6fa95d26-044c-48a9-8a7b-9ee46ec32c5c] +description = "list when: -> eggs and peanuts" + +[19890e22-f63f-4c5c-a9fb-fb6eacddfe8e] +description = "list when: -> more than eggs but not peanuts" + +[4b68f470-067c-44e4-889f-c9fe28917d2f] +description = "list when: -> lots of stuff" + +[0881b7c5-9efa-4530-91bd-68370d054bc7] +description = "list when: -> everything" + +[12ce86de-b347-42a0-ab7c-2e0570f0c65b] +description = "list when: -> no allergen score parts" + +[93c2df3e-4f55-4fed-8116-7513092819cd] +description = "list when: -> no allergen score parts without highest valid score" diff --git a/exercises/practice/allergies/allergies-test.arr b/exercises/practice/allergies/allergies-test.arr new file mode 100644 index 00000000..f4af10bc --- /dev/null +++ b/exercises/practice/allergies/allergies-test.arr @@ -0,0 +1,292 @@ +use context starter2024 + +include file("allergies.arr") + +# Eggs + +check "Eggs allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("eggs") is false +end + +check "Eggs allergy - allergic only to eggs": + allergens = allergies(1) + allergens.allergicTo("eggs") is true +end + +check "Eggs allergy - allergic to eggs and something else": + allergens = allergies(3) + allergens.allergicTo("eggs") is true +end + +check "Eggs allergy - allergic to something, but not eggs": + allergens = allergies(2) + allergens.allergicTo("eggs") is false +end + +check "Eggs allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("eggs") is true +end + +# Peanuts + +check "peanuts allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("peanuts") is false +end + +check "peanuts allergy - allergic only to peanuts": + allergens = allergies(2) + allergens.allergicTo("peanuts") is true +end + +check "peanuts allergy - allergic to peanuts and something else": + allergens = allergies(7) + allergens.allergicTo("peanuts") is true +end + +check "peanuts allergy - allergic to something, but not peanuts": + allergens = allergies(5) + allergens.allergicTo("peanuts") is false +end + +check "peanuts allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("peanuts") is true +end + +# Shellfish + +check "shellfish allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("shellfish") is false +end + +check "shellfish allergy - allergic only to shellfish": + allergens = allergies(4) + allergens.allergicTo("shellfish") is true +end + +check "shellfish allergy - allergic to shellfish and something else": + allergens = allergies(14) + allergens.allergicTo("shellfish") is true +end + +check "shellfish allergy - allergic to something, but not shellfish": + allergens = allergies(10) + allergens.allergicTo("shellfish") is false +end + +check "shellfish allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("shellfish") is true +end + +# Strawberries + +check "strawberries allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("strawberries") is false +end + +check "strawberries allergy - allergic only to strawberries": + allergens = allergies(8) + allergens.allergicTo("strawberries") is true +end + +check "strawberries allergy - allergic to strawberries and something else": + allergens = allergies(28) + allergens.allergicTo("strawberries") is true +end + +check "strawberries allergy - allergic to something, but not strawberries": + allergens = allergies(20) + allergens.allergicTo("strawberries") is false +end + +check "strawberries allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("strawberries") is true +end + +# Tomatoes + +check "tomatoes allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("tomatoes") is false +end + +check "tomatoes allergy - allergic only to tomatoes": + allergens = allergies(16) + allergens.allergicTo("tomatoes") is true +end + +check "tomatoes allergy - allergic to tomatoes and something else": + allergens = allergies(56) + allergens.allergicTo("tomatoes") is true +end + +check "tomatoes allergy - allergic to something, but not tomatoes": + allergens = allergies(40) + allergens.allergicTo("tomatoes") is false +end + +check "tomatoes allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("tomatoes") is true +end + +# Chocolate + +check "chocolate allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("chocolate") is false +end + +check "chocolate allergy - allergic only to chocolate": + allergens = allergies(32) + allergens.allergicTo("chocolate") is true +end + +check "chocolate allergy - allergic to chocolate and something else": + allergens = allergies(112) + allergens.allergicTo("chocolate") is true +end + +check "chocolate allergy - allergic to something, but not chocolate": + allergens = allergies(80) + allergens.allergicTo("chocolate") is false +end + +check "chocolate allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("chocolate") is true +end + +# Pollen + +check "pollen allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("pollen") is false +end + +check "pollen allergy - allergic only to pollen": + allergens = allergies(64) + allergens.allergicTo("pollen") is true +end + +check "pollen allergy - allergic to pollen and something else": + allergens = allergies(224) + allergens.allergicTo("pollen") is true +end + +check "pollen allergy - allergic to something, but not pollen": + allergens = allergies(160) + allergens.allergicTo("pollen") is false +end + +check "pollen allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("pollen") is true +end + +# Cats + +check "cats allergy - not allergic to anything": + allergens = allergies(0) + allergens.allergicTo("cats") is false +end + +check "cats allergy - allergic only to cats": + allergens = allergies(128) + allergens.allergicTo("cats") is true +end + +check "cats allergy - allergic to cats and something else": + allergens = allergies(192) + allergens.allergicTo("cats") is true +end + +check "cats allergy - allergic to something, but not cats": + allergens = allergies(64) + allergens.allergicTo("cats") is false +end + +check "cats allergy - allergic to everything": + allergens = allergies(255) + allergens.allergicTo("cats") is true +end + +# List + +check "list when no allergies": + allergens = allergies(0) + allergens.list() is [list: ] +end + +check "list when just eggs": + allergens = allergies(1) + allergens.list() is [list: "eggs"] +end + +check "list when just peanuts": + allergens = allergies(2) + allergens.list() is [list: "peanuts"] +end + +check "list when just strawberries": + allergens = allergies(8) + allergens.list() is [list: "strawberries"] +end + +check "list when eggs and peanuts": + allergens = allergies(3) + allergens.list() is [list: "eggs", "peanuts"] +end + +check "list when more than eggs but not peanuts": + allergens = allergies(5) + allergens.list() is [list: "eggs", "shellfish"] +end + +check "list when lots of stuff": + allergens = allergies(248) + allergens.list() is [list: + "strawberries", + "tomatoes", + "chocolate", + "pollen", + "cats"] +end + +check "list when everything": + allergens = allergies(255) + allergens.list() is [list: + "eggs", + "peanuts", + "shellfish", + "strawberries", + "tomatoes", + "chocolate", + "pollen", + "cats"] +end + +check "list when no allergen score parts": + allergens = allergies(509) + allergens.list() is [list: + "eggs", + "shellfish", + "strawberries", + "tomatoes", + "chocolate", + "pollen", + "cats"] +end + +check "list when no allergen score parts without highest valid score": + allergens = allergies(257) + allergens.list() is [list: "eggs"] +end + diff --git a/exercises/practice/allergies/allergies.arr b/exercises/practice/allergies/allergies.arr new file mode 100644 index 00000000..c3ce59a1 --- /dev/null +++ b/exercises/practice/allergies/allergies.arr @@ -0,0 +1,13 @@ +use context starter2024 + +provide-types * + +data Allergies: + | allergies(score :: Number) with: + method allergicTo(self, allergen :: String) -> Boolean: + raise("please implement the allergicTo method") + end, + method list(self) -> List: + raise("please implement the list method") + end +end diff --git a/exercises/practice/anagram/.docs/instructions.append.md b/exercises/practice/anagram/.docs/instructions.append.md new file mode 100644 index 00000000..2b17bb7a --- /dev/null +++ b/exercises/practice/anagram/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions Append + +You must return the anagrams in the same order as they are listed in the candidate words. diff --git a/exercises/practice/anagram/.docs/instructions.md b/exercises/practice/anagram/.docs/instructions.md new file mode 100644 index 00000000..dca24f52 --- /dev/null +++ b/exercises/practice/anagram/.docs/instructions.md @@ -0,0 +1,12 @@ +# Instructions + +Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target. + +An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`. +A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`. + +The target word and candidate words are made up of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`). +Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `"StoP"` is not an anagram of `"sTOp"`. +The words you need to find should be taken from the candidate words, using the same letter case. + +Given the target `"stone"` and the candidate words `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, and `"Seton"`, the anagram words you need to find are `"tones"`, `"notes"`, and `"Seton"`. diff --git a/exercises/practice/anagram/.docs/introduction.md b/exercises/practice/anagram/.docs/introduction.md new file mode 100644 index 00000000..1acbdf00 --- /dev/null +++ b/exercises/practice/anagram/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +At a garage sale, you find a lovely vintage typewriter at a bargain price! +Excitedly, you rush home, insert a sheet of paper, and start typing away. +However, your excitement wanes when you examine the output: all words are garbled! +For example, it prints "stop" instead of "post" and "least" instead of "stale." +Carefully, you try again, but now it prints "spot" and "slate." +After some experimentation, you find there is a random delay before each letter is printed, which messes up the order. +You now understand why they sold it for so little money! + +You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word. +Pleased with your finding, you spend the rest of the day generating hundreds of anagrams. diff --git a/exercises/practice/anagram/.meta/config.json b/exercises/practice/anagram/.meta/config.json new file mode 100644 index 00000000..e4788baf --- /dev/null +++ b/exercises/practice/anagram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "anagram.arr" + ], + "test": [ + "anagram-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a word and a list of possible anagrams, select the correct sublist.", + "source": "Inspired by the Extreme Startup game", + "source_url": "https://github.com/rchatley/extreme_startup" +} diff --git a/exercises/practice/anagram/.meta/example.arr b/exercises/practice/anagram/.meta/example.arr new file mode 100644 index 00000000..0b55f80d --- /dev/null +++ b/exercises/practice/anagram/.meta/example.arr @@ -0,0 +1,21 @@ +use context starter2024 + +provide: find-anagrams end + +fun find-anagrams(phrase, candidates): + detect-anagram = lam(elt, acc): + phrase-lowered = string-explode(string-to-lower(phrase)) + elt-lowered = string-explode(string-to-lower(elt)) + + different-order = phrase-lowered <> elt-lowered + same-letters = phrase-lowered.sort() == elt-lowered.sort() + + ask: + | different-order and same-letters + then: acc.push(elt) + | otherwise: acc + end + end + + candidates.foldl(detect-anagram, [list: ]).reverse() +end diff --git a/exercises/practice/anagram/.meta/tests.toml b/exercises/practice/anagram/.meta/tests.toml new file mode 100644 index 00000000..8b680dac --- /dev/null +++ b/exercises/practice/anagram/.meta/tests.toml @@ -0,0 +1,88 @@ +# 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. + +[dd40c4d2-3c8b-44e5-992a-f42b393ec373] +description = "no matches" + +[b3cca662-f50a-489e-ae10-ab8290a09bdc] +description = "detects two anagrams" +include = false + +[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b] +description = "detects two anagrams" +reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc" + +[a27558ee-9ba0-4552-96b1-ecf665b06556] +description = "does not detect anagram subsets" + +[64cd4584-fc15-4781-b633-3d814c4941a4] +description = "detects anagram" + +[99c91beb-838f-4ccd-b123-935139917283] +description = "detects three anagrams" + +[78487770-e258-4e1f-a646-8ece10950d90] +description = "detects multiple anagrams with different case" + +[1d0ab8aa-362f-49b7-9902-3d0c668d557b] +description = "does not detect non-anagrams with identical checksum" + +[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8] +description = "detects anagrams case-insensitively" + +[b248e49f-0905-48d2-9c8d-bd02d8c3e392] +description = "detects anagrams using case-insensitive subject" + +[f367325c-78ec-411c-be76-e79047f4bd54] +description = "detects anagrams using case-insensitive possible matches" + +[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c] +description = "does not detect an anagram if the original word is repeated" +include = false + +[630abb71-a94e-4715-8395-179ec1df9f91] +description = "does not detect an anagram if the original word is repeated" +reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c" + +[9878a1c9-d6ea-4235-ae51-3ea2befd6842] +description = "anagrams must use all letters exactly once" + +[85757361-4535-45fd-ac0e-3810d40debc1] +description = "words are not anagrams of themselves (case-insensitive)" +include = false + +[68934ed0-010b-4ef9-857a-20c9012d1ebf] +description = "words are not anagrams of themselves" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e] +description = "words are not anagrams of themselves even if letter case is partially different" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[ba53e423-7e02-41ee-9ae2-71f91e6d18e6] +description = "words are not anagrams of themselves even if letter case is completely different" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[a0705568-628c-4b55-9798-82e4acde51ca] +description = "words other than themselves can be anagrams" +include = false + +[33d3f67e-fbb9-49d3-a90e-0beb00861da7] +description = "words other than themselves can be anagrams" +reimplements = "a0705568-628c-4b55-9798-82e4acde51ca" + +[a6854f66-eec1-4afd-a137-62ef2870c051] +description = "handles case of greek letters" +include = false + +[fd3509e5-e3ba-409d-ac3d-a9ac84d13296] +description = "different characters may have the same bytes" +include = false diff --git a/exercises/practice/anagram/anagram-test.arr b/exercises/practice/anagram/anagram-test.arr new file mode 100644 index 00000000..20b4af65 --- /dev/null +++ b/exercises/practice/anagram/anagram-test.arr @@ -0,0 +1,116 @@ +use context starter2024 + +include file("anagram.arr") + +check "no matches": + candidates = [list: "hello", "world", "zombies", "pants"] + expected = [list: ] + + find-anagrams("diaper", candidates) is expected +end + +check "detects two anagrams": + candidates = [list: "lemons", "cherry", "melons"] + expected = [list: "lemons", "melons"] + + find-anagrams("solemn", candidates) is expected +end + +check "does not detect anagram subsets": + candidates = [list: "dog", "goody"] + expected = [list: ] + + find-anagrams("good", candidates) is expected +end + +check "detects anagram": + candidates = [list: "enlists", "google", "inlets", "banana"] + expected = [list: "inlets"] + + find-anagrams("listen", candidates) is expected +end + +check "detects three anagrams": + candidates = [list: "gallery", "ballerina", "regally", "clergy", "largely", "leading"] + expected = [list: "gallery", "regally", "largely"] + + find-anagrams("allergy", candidates) is expected +end + +check "detects multiple anagrams with different case": + candidates = [list: "Eons", "ONES"] + expected = [list: "Eons", "ONES"] + + find-anagrams("nose", candidates) is expected +end + +check "does not detect non-anagrams with identical checksum": + candidates = [list: "last"] + expected = [list: ] + + find-anagrams("mass", candidates) is expected +end + +check "detects anagrams case-insensitively": + candidates = [list: "cashregister", "Carthorse", "radishes"] + expected = [list: "Carthorse"] + + find-anagrams("Orchestra", candidates) is expected +end + +check "detects anagrams using case-insensitive subject": + candidates = [list: "cashregister", "carthorse", "radishes"] + expected = [list: "carthorse"] + + find-anagrams("Orchestra", candidates) is expected +end + +check "detects anagrams using case-insensitive possible matches": + candidates = [list: "cashregister", "Carthorse", "radishes"] + expected = [list: "Carthorse"] + + find-anagrams("orchestra", candidates) is expected +end + +check "does not detect an anagram if the original word is repeated": + candidates = [list: "goGoGO"] + expected = [list: ] + + find-anagrams("go", candidates) is expected +end + +check "anagrams must use all letters exactly once": + candidates = [list: "patter"] + expected = [list: ] + + find-anagrams("tapper", candidates) is expected +end + +check "words are not anagrams of themselves": + candidates = [list: "BANANA"] + expected = [list: ] + + find-anagrams("BANANA", candidates) is expected +end + +check "words are not anagrams of themselves even if letter case is partially different": + candidates = [list: "Banana"] + expected = [list: ] + + find-anagrams("BANANA", candidates) is expected +end + +check "words are not anagrams of themselves even if letter case is completely different": + candidates = [list: "banana"] + expected = [list: ] + + find-anagrams("BANANA", candidates) is expected +end + +check "words other than themselves can be anagrams": + candidates = [list: "LISTEN", "Silent"] + expected = [list: "Silent"] + + find-anagrams("LISTEN", candidates) is expected +end + diff --git a/exercises/practice/anagram/anagram.arr b/exercises/practice/anagram/anagram.arr new file mode 100644 index 00000000..b5f4a804 --- /dev/null +++ b/exercises/practice/anagram/anagram.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: find-anagrams end + +fun find-anagrams(phrase, candidates): + raise("Please implement the find-anagrams function") +end diff --git a/exercises/practice/armstrong-numbers/.docs/instructions.md b/exercises/practice/armstrong-numbers/.docs/instructions.md new file mode 100644 index 00000000..5e56bbe4 --- /dev/null +++ b/exercises/practice/armstrong-numbers/.docs/instructions.md @@ -0,0 +1,14 @@ +# Instructions + +An [Armstrong number][armstrong-number] is a number that is the sum of its own digits each raised to the power of the number of digits. + +For example: + +- 9 is an Armstrong number, because `9 = 9^1 = 9` +- 10 is _not_ an Armstrong number, because `10 != 1^2 + 0^2 = 1` +- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` +- 154 is _not_ an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` + +Write some code to determine whether a number is an Armstrong number. + +[armstrong-number]: https://en.wikipedia.org/wiki/Narcissistic_number diff --git a/exercises/practice/armstrong-numbers/.meta/config.json b/exercises/practice/armstrong-numbers/.meta/config.json new file mode 100644 index 00000000..ea3d04e4 --- /dev/null +++ b/exercises/practice/armstrong-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "armstrong-numbers.arr" + ], + "test": [ + "armstrong-numbers-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine if a number is an Armstrong number.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Narcissistic_number" +} diff --git a/exercises/practice/armstrong-numbers/.meta/example.arr b/exercises/practice/armstrong-numbers/.meta/example.arr new file mode 100644 index 00000000..7b0935d0 --- /dev/null +++ b/exercises/practice/armstrong-numbers/.meta/example.arr @@ -0,0 +1,20 @@ +use context starter2024 + +provide: is-armstrong-number end + +import lists as L + +fun is-armstrong-number(number): + digits = string-explode(num-to-string(number)) + sum = L.foldl( + lam(acc, elt): + cases(Option) string-to-number(elt): + | some(digit) => acc + num-expt(digit, digits.length()) + | none => raise("unable to convert character to digit") + end + end, + 0, + digits) + + sum == number +end diff --git a/exercises/practice/armstrong-numbers/.meta/tests.toml b/exercises/practice/armstrong-numbers/.meta/tests.toml new file mode 100644 index 00000000..b3f09e4c --- /dev/null +++ b/exercises/practice/armstrong-numbers/.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. + +[c1ed103c-258d-45b2-be73-d8c6d9580c7b] +description = "Zero is an Armstrong number" + +[579e8f03-9659-4b85-a1a2-d64350f6b17a] +description = "Single-digit numbers are Armstrong numbers" + +[2d6db9dc-5bf8-4976-a90b-b2c2b9feba60] +description = "There are no two-digit Armstrong numbers" + +[509c087f-e327-4113-a7d2-26a4e9d18283] +description = "Three-digit number that is an Armstrong number" + +[7154547d-c2ce-468d-b214-4cb953b870cf] +description = "Three-digit number that is not an Armstrong number" + +[6bac5b7b-42e9-4ecb-a8b0-4832229aa103] +description = "Four-digit number that is an Armstrong number" + +[eed4b331-af80-45b5-a80b-19c9ea444b2e] +description = "Four-digit number that is not an Armstrong number" + +[f971ced7-8d68-4758-aea1-d4194900b864] +description = "Seven-digit number that is an Armstrong number" + +[7ee45d52-5d35-4fbd-b6f1-5c8cd8a67f18] +description = "Seven-digit number that is not an Armstrong number" + +[5ee2fdf8-334e-4a46-bb8d-e5c19c02c148] +description = "Armstrong number containing seven zeroes" + +[12ffbf10-307a-434e-b4ad-c925680e1dd4] +description = "The largest and last Armstrong number" diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers-test.arr b/exercises/practice/armstrong-numbers/armstrong-numbers-test.arr new file mode 100644 index 00000000..79051a15 --- /dev/null +++ b/exercises/practice/armstrong-numbers/armstrong-numbers-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("armstrong-numbers.arr") + +check "Zero is an Armstrong number": + is-armstrong-number(0) is true +end + +check "Single-digit numbers are Armstrong numbers": + is-armstrong-number(5) is true +end + +check "There are no two-digit Armstrong numbers": + is-armstrong-number(10) is false +end + +check "Three-digit number that is an Armstrong number": + is-armstrong-number(153) is true +end + +check "Three-digit number that is not an Armstrong number": + is-armstrong-number(100) is false +end + +check "Four-digit number that is an Armstrong number": + is-armstrong-number(9474) is true +end + +check "Four-digit number that is not an Armstrong number": + is-armstrong-number(9475) is false +end + +check "Seven-digit number that is an Armstrong number": + is-armstrong-number(9926315) is true +end + +check "Seven-digit number that is not an Armstrong number": + is-armstrong-number(9926314) is false +end + +check "Armstrong number containing seven zeroes": + is-armstrong-number(186709961001538790100634132976990) is true +end + +check "The largest and last Armstrong number": + is-armstrong-number(115132219018763992565095597973971522401) is true +end + diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers.arr b/exercises/practice/armstrong-numbers/armstrong-numbers.arr new file mode 100644 index 00000000..7d43711a --- /dev/null +++ b/exercises/practice/armstrong-numbers/armstrong-numbers.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: is-armstrong-number end + +fun is-armstrong-number(number): + raise("please implement the is-armstrong-number function") +end diff --git a/exercises/practice/atbash-cipher/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md new file mode 100644 index 00000000..1e7627b1 --- /dev/null +++ b/exercises/practice/atbash-cipher/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East. + +The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards. +The first letter is replaced with the last letter, the second with the second-last, and so on. + +An Atbash cipher for the Latin alphabet would be as follows: + +```text +Plain: abcdefghijklmnopqrstuvwxyz +Cipher: zyxwvutsrqponmlkjihgfedcba +``` + +It is a very weak cipher because it only has one possible key, and it is a simple mono-alphabetic substitution cipher. +However, this may not have been an issue in the cipher's time. + +Ciphertext is written out in groups of fixed length, the traditional group size being 5 letters, leaving numbers unchanged, and punctuation is excluded. +This is to make it harder to guess things based on word boundaries. +All text will be encoded as lowercase letters. + +## Examples + +- Encoding `test` gives `gvhg` +- Encoding `x123 yes` gives `c123b vh` +- Decoding `gvhg` gives `test` +- Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `thequickbrownfoxjumpsoverthelazydog` diff --git a/exercises/practice/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json new file mode 100644 index 00000000..4d8366a1 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "atbash-cipher.arr" + ], + "test": [ + "atbash-cipher-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Atbash" +} diff --git a/exercises/practice/atbash-cipher/.meta/example.arr b/exercises/practice/atbash-cipher/.meta/example.arr new file mode 100644 index 00000000..3dda3509 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/example.arr @@ -0,0 +1,59 @@ +use context starter2024 + +provide: encode, decode end + +include string-dict + +TRANSCRIBE-PAIRS = fold2( + lam(acc, elt1, elt2): + key = string-from-code-point(elt1) + value = string-from-code-point(elt2) + acc.set(key, value) + end, + [string-dict: ], + range(97, 123), + range(97, 123).reverse() +) + +fun chunk-a-list(lst, n): + ask: + | lst.length() <= n then: + [list: lst] + | otherwise: + [list: lst.take(n)] + chunk-a-list(lst.drop(n), n) + end +end + +fun transcribe(phrase): + string-explode( + string-to-lower(phrase)).filter( + lam(char): + code-point = string-to-code-point(char) + ask: + | (48 <= code-point) and (code-point <= 57) then: + true + | (97 <= code-point) and (code-point <= 122) then: + true + | otherwise: + false + end + end).map( + lam(char): + cases(Option) TRANSCRIBE-PAIRS.get(char): + | some(a) => a + | none => char + end + end).join-str("") +end + +fun encode(phrase): + encrypted = transcribe(phrase) + chunk-a-list( + string-explode(encrypted), 5).map( + lam(l): l.join-str("") end + ).join-str(" ") +end + +fun decode(phrase): + transcribe(phrase) +end diff --git a/exercises/practice/atbash-cipher/.meta/tests.toml b/exercises/practice/atbash-cipher/.meta/tests.toml new file mode 100644 index 00000000..c082d07c --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[2f47ebe1-eab9-4d6b-b3c6-627562a31c77] +description = "encode -> encode yes" + +[b4ffe781-ea81-4b74-b268-cc58ba21c739] +description = "encode -> encode no" + +[10e48927-24ab-4c4d-9d3f-3067724ace00] +description = "encode -> encode OMG" + +[d59b8bc3-509a-4a9a-834c-6f501b98750b] +description = "encode -> encode spaces" + +[31d44b11-81b7-4a94-8b43-4af6a2449429] +description = "encode -> encode mindblowingly" + +[d503361a-1433-48c0-aae0-d41b5baa33ff] +description = "encode -> encode numbers" + +[79c8a2d5-0772-42d4-b41b-531d0b5da926] +description = "encode -> encode deep thought" + +[9ca13d23-d32a-4967-a1fd-6100b8742bab] +description = "encode -> encode all the letters" + +[bb50e087-7fdf-48e7-9223-284fe7e69851] +description = "decode -> decode exercism" + +[ac021097-cd5d-4717-8907-b0814b9e292c] +description = "decode -> decode a sentence" + +[18729de3-de74-49b8-b68c-025eaf77f851] +description = "decode -> decode numbers" + +[0f30325f-f53b-415d-ad3e-a7a4f63de034] +description = "decode -> decode all the letters" + +[39640287-30c6-4c8c-9bac-9d613d1a5674] +description = "decode -> decode with too many spaces" + +[b34edf13-34c0-49b5-aa21-0768928000d5] +description = "decode -> decode with no spaces" diff --git a/exercises/practice/atbash-cipher/atbash-cipher-test.arr b/exercises/practice/atbash-cipher/atbash-cipher-test.arr new file mode 100644 index 00000000..c8dc6a40 --- /dev/null +++ b/exercises/practice/atbash-cipher/atbash-cipher-test.arr @@ -0,0 +1,102 @@ +use context starter2024 + +include file("atbash-cipher.arr") + +check "encode -> encode yes": + input = "yes" + expected = "bvh" + + encode(input) is expected +end + +check "encode -> encode no": + input = "no" + expected = "ml" + + encode(input) is expected +end + +check "encode -> encode OMG": + input = "OMG" + expected = "lnt" + + encode(input) is expected +end + +check "encode -> encode spaces": + input = "O M G" + expected = "lnt" + + encode(input) is expected +end + +check "encode -> encode mindblowingly": + input = "mindblowingly" + expected = "nrmwy oldrm tob" + + encode(input) is expected +end + +check "encode -> encode numbers": + input = "Testing,1 2 3, testing." + expected = "gvhgr mt123 gvhgr mt" + + encode(input) is expected +end + +check "encode -> encode deep thought": + input = "Truth is fiction." + expected = "gifgs rhurx grlm" + + encode(input) is expected +end + +check "encode -> encode all the letters": + input = "The quick brown fox jumps over the lazy dog." + expected = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt" + + encode(input) is expected +end + +check "decode -> decode exercism": + input = "vcvix rhn" + expected = "exercism" + + decode(input) is expected +end + +check "decode -> decode a sentence": + input = "zmlyh gzxov rhlug vmzhg vkkrm thglm v" + expected = "anobstacleisoftenasteppingstone" + + decode(input) is expected +end + +check "decode -> decode numbers": + input = "gvhgr mt123 gvhgr mt" + expected = "testing123testing" + + decode(input) is expected +end + +check "decode -> decode all the letters": + input = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt" + expected = "thequickbrownfoxjumpsoverthelazydog" + + decode(input) is expected +end + +check "decode -> decode with too many spaces": + input = "vc vix r hn" + expected = "exercism" + + decode(input) is expected +end + +check "decode -> decode with no spaces": + input = "zmlyhgzxovrhlugvmzhgvkkrmthglmv" + expected = "anobstacleisoftenasteppingstone" + + decode(input) is expected +end + diff --git a/exercises/practice/atbash-cipher/atbash-cipher.arr b/exercises/practice/atbash-cipher/atbash-cipher.arr new file mode 100644 index 00000000..9a8d2db6 --- /dev/null +++ b/exercises/practice/atbash-cipher/atbash-cipher.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: encode, decode end + +fun encode(phrase): + raise("please implement the encode function") +end + +fun decode(phrase): + raise("please implement the decode function") +end diff --git a/exercises/practice/bank-account/.docs/instructions.append.md b/exercises/practice/bank-account/.docs/instructions.append.md new file mode 100644 index 00000000..78702bb1 --- /dev/null +++ b/exercises/practice/bank-account/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions append + +Pyret doesn't support concurrency so that requirement can be set aside. Account operations are sequential on a single thread. diff --git a/exercises/practice/bank-account/.docs/instructions.md b/exercises/practice/bank-account/.docs/instructions.md new file mode 100644 index 00000000..7398fbea --- /dev/null +++ b/exercises/practice/bank-account/.docs/instructions.md @@ -0,0 +1,10 @@ +# Instructions + +Your task is to implement bank accounts supporting opening/closing, withdrawals, and deposits of money. + +As bank accounts can be accessed in many different ways (internet, mobile phones, automatic charges), your bank software must allow accounts to be safely accessed from multiple threads/processes (terminology depends on your programming language) in parallel. +For example, there may be many deposits and withdrawals occurring in parallel; you need to ensure there are no [race conditions][wikipedia] between when you read the account balance and set the new balance. + +It should be possible to close an account; operations against a closed account must fail. + +[wikipedia]: https://en.wikipedia.org/wiki/Race_condition#In_software diff --git a/exercises/practice/bank-account/.docs/introduction.md b/exercises/practice/bank-account/.docs/introduction.md new file mode 100644 index 00000000..650b5d9c --- /dev/null +++ b/exercises/practice/bank-account/.docs/introduction.md @@ -0,0 +1,20 @@ +# Introduction + +After years of filling out forms and waiting, you've finally acquired your banking license. +This means you are now officially eligible to open your own bank, hurray! + +Your first priority is to get the IT systems up and running. +After a day of hard work, you can already open and close accounts, as well as handle withdrawals and deposits. + +Since you couldn't be bothered writing tests, you invite some friends to help test the system. +However, after just five minutes, one of your friends claims they've lost money! +While you're confident your code is bug-free, you start looking through the logs to investigate. + +Ah yes, just as you suspected, your friend is at fault! +They shared their test credentials with another friend, and together they conspired to make deposits and withdrawals from the same account _in parallel_. +Who would do such a thing? + +While you argue that it's physically _impossible_ for someone to access their account in parallel, your friend smugly notifies you that the banking rules _require_ you to support this. +Thus, no parallel banking support, no go-live signal. +Sighing, you create a mental note to work on this tomorrow. +This will set your launch date back at _least_ one more day, but well... diff --git a/exercises/practice/bank-account/.meta/config.json b/exercises/practice/bank-account/.meta/config.json new file mode 100644 index 00000000..96ce42d6 --- /dev/null +++ b/exercises/practice/bank-account/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "bank-account.arr" + ], + "test": [ + "bank-account-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Simulate a bank account supporting opening/closing, withdraws, and deposits of money. Watch out for concurrent transactions!" +} diff --git a/exercises/practice/bank-account/.meta/example.arr b/exercises/practice/bank-account/.meta/example.arr new file mode 100644 index 00000000..83915a5a --- /dev/null +++ b/exercises/practice/bank-account/.meta/example.arr @@ -0,0 +1,48 @@ +use context starter2024 + +provide-types * + +data Account: + | account() with: + method open(self): + open-account(0) + end, + method get-balance(self): + raise("account not open") + end, + method deposit(self, amount): + raise("account not open") + end, + method withdraw(self, amount): + raise("account not open") + end, + method close(self): + raise("account not open") + end + | open-account(balance :: NumInteger) with: + method get-balance(self): + self.balance + end, + method deposit(self, amount :: NumInteger): + if amount <= 0: + raise("amount must be greater than 0") + else: + open-account(self.balance + amount) + end + end, + method withdraw(self, amount :: NumInteger): + if amount <= 0: + raise("amount must be greater than 0") + else if amount > self.balance: + raise("amount must be less than balance") + else: + open-account(self.balance - amount) + end + end, + method close(self): + account() + end, + method open(self): + raise("account already open") + end +end diff --git a/exercises/practice/bank-account/.meta/tests.toml b/exercises/practice/bank-account/.meta/tests.toml new file mode 100644 index 00000000..887c0646 --- /dev/null +++ b/exercises/practice/bank-account/.meta/tests.toml @@ -0,0 +1,63 @@ +# 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. + +[983a1528-4ceb-45e5-8257-8ce01aceb5ed] +description = "Newly opened account has zero balance" + +[e88d4ec3-c6bf-4752-8e59-5046c44e3ba7] +description = "Single deposit" + +[3d9147d4-63f4-4844-8d2b-1fee2e9a2a0d] +description = "Multiple deposits" + +[08f1af07-27ae-4b38-aa19-770bde558064] +description = "Withdraw once" + +[6f6d242f-8c31-4ac6-8995-a90d42cad59f] +description = "Withdraw twice" + +[45161c94-a094-4c77-9cec-998b70429bda] +description = "Can do multiple operations sequentially" + +[f9facfaa-d824-486e-8381-48832c4bbffd] +description = "Cannot check balance of closed account" + +[7a65ba52-e35c-4fd2-8159-bda2bde6e59c] +description = "Cannot deposit into closed account" + +[a0a1835d-faae-4ad4-a6f3-1fcc2121380b] +description = "Cannot deposit into unopened account" + +[570dfaa5-0532-4c1f-a7d3-0f65c3265608] +description = "Cannot withdraw from closed account" + +[c396d233-1c49-4272-98dc-7f502dbb9470] +description = "Cannot close an account that was not opened" + +[c06f534f-bdc2-4a02-a388-1063400684de] +description = "Cannot open an already opened account" + +[0722d404-6116-4f92-ba3b-da7f88f1669c] +description = "Reopened account does not retain balance" + +[ec42245f-9361-4341-8231-a22e8d19c52f] +description = "Cannot withdraw more than deposited" + +[4f381ef8-10ef-4507-8e1d-0631ecc8ee72] +description = "Cannot withdraw negative" + +[d45df9ea-1db0-47f3-b18c-d365db49d938] +description = "Cannot deposit negative" + +[ba0c1e0b-0f00-416f-8097-a7dfc97871ff] +description = "Can handle concurrent transactions" +include = false +comment = "no concurrency support" \ No newline at end of file diff --git a/exercises/practice/bank-account/bank-account-test.arr b/exercises/practice/bank-account/bank-account-test.arr new file mode 100644 index 00000000..77523925 --- /dev/null +++ b/exercises/practice/bank-account/bank-account-test.arr @@ -0,0 +1,74 @@ +use context starter2024 + +include file("bank-account.arr") + +check "Newly opened account has zero balance": + account().open().get-balance() is 0 +end + +check "Single deposit": + account().open().deposit(100).get-balance() is 100 +end + +check "Multiple deposits": + account().open().deposit(100).deposit(50).get-balance() is 150 +end + +check "Withdraw once": + account().open().deposit(100).withdraw(75).get-balance() is 25 +end + +check "Withdraw twice": + account().open().deposit(100).withdraw(80).withdraw(20).get-balance() is 0 +end + +check "Can do multiple operations sequentially": + account() + ^ _.open() + ^ _.deposit(100) + ^ _.deposit(110) + ^ _.withdraw(200) + ^ _.deposit(60) + ^ _.withdraw(50) + ^ _.get-balance() is 20 +end + +check "Cannot check balance of closed account": + account().open().close().get-balance() raises "account not open" +end + +check "Cannot deposit into closed account": + account().open().close().deposit(50) raises "account not open" +end + +check "Cannot deposit into unopened account": + account().deposit(50) raises "account not open" +end + +check "Cannot withdraw from closed account": + account().open().close().withdraw(50) raises "account not open" +end + +check "Cannot close an account that was not opened": + account().close() raises "account not open" +end + +check "Cannot open an already opened account": + account().open().open() raises "account already open" +end + +check "Reopened account does not retain balance": + account().open().deposit(50).close().open().get-balance() is 0 +end + +check "Cannot withdraw more than deposited": + account().open().deposit(25).withdraw(50) raises "amount must be less than balance" +end + +check "Cannot withdraw negative": + account().open().deposit(100).withdraw(-50) raises "amount must be greater than 0" +end + +check "Cannot deposit negative": + account().open().deposit(-50) raises "amount must be greater than 0" +end diff --git a/exercises/practice/bank-account/bank-account.arr b/exercises/practice/bank-account/bank-account.arr new file mode 100644 index 00000000..b29c09a6 --- /dev/null +++ b/exercises/practice/bank-account/bank-account.arr @@ -0,0 +1,22 @@ +use context starter2024 + +provide-types * + +data Account: + | account() with: + method open(self): + raise("please implement the open method") + end, + method close(self): + raise("please implement the close method") + end, + method deposit(self, amount): + raise("please implement the withdraw method") + end, + method withdraw(self, amount): + raise("please implement the withdraw method") + end, + method get-balance(self): + raise("please implement the get-balance method") + end +end diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md new file mode 100644 index 00000000..12f4358e --- /dev/null +++ b/exercises/practice/binary-search/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Your task is to implement a binary search algorithm. + +A binary search algorithm finds an item in a list by repeatedly splitting it in half, only keeping the half which contains the item we're looking for. +It allows us to quickly narrow down the possible locations of our item until we find it, or until we've eliminated all possible locations. + +~~~~exercism/caution +Binary search only works when a list has been sorted. +~~~~ + +The algorithm looks like this: + +- Find the middle element of a _sorted_ list and compare it with the item we're looking for. +- If the middle element is our item, then we're done! +- If the middle element is greater than our item, we can eliminate that element and all the elements **after** it. +- If the middle element is less than our item, we can eliminate that element and all the elements **before** it. +- If every element of the list has been eliminated then the item is not in the list. +- Otherwise, repeat the process on the part of the list that has not been eliminated. + +Here's an example: + +Let's say we're looking for the number 23 in the following sorted list: `[4, 8, 12, 16, 23, 28, 32]`. + +- We start by comparing 23 with the middle element, 16. +- Since 23 is greater than 16, we can eliminate the left half of the list, leaving us with `[23, 28, 32]`. +- We then compare 23 with the new middle element, 28. +- Since 23 is less than 28, we can eliminate the right half of the list: `[23]`. +- We've found our item. diff --git a/exercises/practice/binary-search/.docs/introduction.md b/exercises/practice/binary-search/.docs/introduction.md new file mode 100644 index 00000000..03496599 --- /dev/null +++ b/exercises/practice/binary-search/.docs/introduction.md @@ -0,0 +1,13 @@ +# Introduction + +You have stumbled upon a group of mathematicians who are also singer-songwriters. +They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers (like [0][zero] or [73][seventy-three] or [6174][kaprekars-constant]). + +You are curious to hear the song for your favorite number, but with so many songs to wade through, finding the right song could take a while. +Fortunately, they have organized their songs in a playlist sorted by the title — which is simply the number that the song is about. + +You realize that you can use a binary search algorithm to quickly find a song given the title. + +[zero]: https://en.wikipedia.org/wiki/0 +[seventy-three]: https://en.wikipedia.org/wiki/73_(number) +[kaprekars-constant]: https://en.wikipedia.org/wiki/6174_(number) diff --git a/exercises/practice/binary-search/.meta/config.json b/exercises/practice/binary-search/.meta/config.json new file mode 100644 index 00000000..e58a2dfa --- /dev/null +++ b/exercises/practice/binary-search/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "meatball133" + ], + "files": { + "solution": [ + "binary-search.arr" + ], + "test": [ + "binary-search-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Implement a binary search algorithm.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm" +} diff --git a/exercises/practice/binary-search/.meta/example.arr b/exercises/practice/binary-search/.meta/example.arr new file mode 100644 index 00000000..1d7f83a7 --- /dev/null +++ b/exercises/practice/binary-search/.meta/example.arr @@ -0,0 +1,24 @@ +use context starter2024 + +provide: binary-search end + +fun binary-search(numbers, x): + n = num-floor((numbers.length() - 1) / 2) + if numbers.length() == 0 block: + raise("value not in list") + else if numbers.get(n) == x: + n + else if numbers.get(n) < x: + var new_numbers = split-at(n + 1, numbers) + when new_numbers.suffix == numbers block: + raise("value not in list") + end + n + 1 + binary-search(new_numbers.suffix, x) + else: + var new_numbers = split-at(n, numbers) + when new_numbers.prefix == numbers block: + raise("value not in list") + end + binary-search(new_numbers.prefix, x) + end +end diff --git a/exercises/practice/binary-search/.meta/tests.toml b/exercises/practice/binary-search/.meta/tests.toml new file mode 100644 index 00000000..61e2b068 --- /dev/null +++ b/exercises/practice/binary-search/.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. + +[b55c24a9-a98d-4379-a08c-2adcf8ebeee8] +description = "finds a value in an array with one element" + +[73469346-b0a0-4011-89bf-989e443d503d] +description = "finds a value in the middle of an array" + +[327bc482-ab85-424e-a724-fb4658e66ddb] +description = "finds a value at the beginning of an array" + +[f9f94b16-fe5e-472c-85ea-c513804c7d59] +description = "finds a value at the end of an array" + +[f0068905-26e3-4342-856d-ad153cadb338] +description = "finds a value in an array of odd length" + +[fc316b12-c8b3-4f5e-9e89-532b3389de8c] +description = "finds a value in an array of even length" + +[da7db20a-354f-49f7-a6a1-650a54998aa6] +description = "identifies that a value is not included in the array" + +[95d869ff-3daf-4c79-b622-6e805c675f97] +description = "a value smaller than the array's smallest value is not found" + +[8b24ef45-6e51-4a94-9eac-c2bf38fdb0ba] +description = "a value larger than the array's largest value is not found" + +[f439a0fa-cf42-4262-8ad1-64bf41ce566a] +description = "nothing is found in an empty array" + +[2c353967-b56d-40b8-acff-ce43115eed64] +description = "nothing is found when the left and right bounds cross" diff --git a/exercises/practice/binary-search/binary-search-test.arr b/exercises/practice/binary-search/binary-search-test.arr new file mode 100644 index 00000000..e5309545 --- /dev/null +++ b/exercises/practice/binary-search/binary-search-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("binary-search.arr") + +check "finds a value in a list with one element": + binary-search([list: 6], 6) is 0 +end + +check "finds a value in the middle of a list": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 6) is 3 +end + +check "finds a value at the beginning of a list": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 1) is 0 +end + +check "finds a value at the end of a list": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 11) is 6 +end + +check "finds a value in a list of odd length": + binary-search([list: 1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144) is 9 +end + +check "finds a value in a list of even length": + binary-search([list: 1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377], 21) is 5 +end + +check "identifies that a value is not included in the list": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 7) raises "value not in list" +end + +check "a value smaller than the list's smallest value is not found": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 0) raises "value not in list" +end + +check "a value larger than the list's largest value is not found": + binary-search([list: 1, 3, 4, 6, 8, 9, 11], 13) raises "value not in list" +end + +check "nothing is found in an empty list": + binary-search([list: ], 1) raises "value not in list" +end + +check "nothing is found when the left and right bounds cross": + binary-search([list: 1, 2], 0) raises "value not in list" +end + diff --git a/exercises/practice/binary-search/binary-search.arr b/exercises/practice/binary-search/binary-search.arr new file mode 100644 index 00000000..9062bf7a --- /dev/null +++ b/exercises/practice/binary-search/binary-search.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: binary-search end + +fun binary-search(lst, elt): + raise("Please implement the binary-search function") +end diff --git a/exercises/practice/bob/.docs/instructions.md b/exercises/practice/bob/.docs/instructions.md new file mode 100644 index 00000000..bb702f7b --- /dev/null +++ b/exercises/practice/bob/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Your task is to determine what Bob will reply to someone when they say something to him or ask him a question. + +Bob only ever answers one of five things: + +- **"Sure."** + This is his response if you ask him a question, such as "How are you?" + The convention used for questions is that it ends with a question mark. +- **"Whoa, chill out!"** + This is his answer if you YELL AT HIM. + The convention used for yelling is ALL CAPITAL LETTERS. +- **"Calm down, I know what I'm doing!"** + This is what he says if you yell a question at him. +- **"Fine. Be that way!"** + This is how he responds to silence. + The convention used for silence is nothing, or various combinations of whitespace characters. +- **"Whatever."** + This is what he answers to anything else. diff --git a/exercises/practice/bob/.docs/introduction.md b/exercises/practice/bob/.docs/introduction.md new file mode 100644 index 00000000..ea4a8077 --- /dev/null +++ b/exercises/practice/bob/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +Bob is a [lackadaisical][] teenager. +He likes to think that he's very cool. +And he definitely doesn't get excited about things. +That wouldn't be cool. + +When people talk to him, his responses are pretty limited. + +[lackadaisical]: https://www.collinsdictionary.com/dictionary/english/lackadaisical diff --git a/exercises/practice/bob/.meta/config.json b/exercises/practice/bob/.meta/config.json new file mode 100644 index 00000000..fbbef195 --- /dev/null +++ b/exercises/practice/bob/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "bob.arr" + ], + "test": [ + "bob-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Bob is a lackadaisical teenager. In conversation, his responses are very limited.", + "source": "Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial.", + "source_url": "https://pine.fm/LearnToProgram/chap_06.html" +} diff --git a/exercises/practice/bob/.meta/example.arr b/exercises/practice/bob/.meta/example.arr new file mode 100644 index 00000000..be28fadd --- /dev/null +++ b/exercises/practice/bob/.meta/example.arr @@ -0,0 +1,61 @@ +use context starter2024 + +provide: response end + +import lists as L +import either as EI + +fun response(hey-bob): + trimmed = [list: "\n", "\t", " ", "\r"].foldl( + lam(elt, acc): + string-replace(acc, elt, "") + end, hey-bob) + + is-shouting = lam(): + uppercase = string-explode("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + lowercase = string-explode("abcdefghijklmnopqrstuvwxyz") + + index-of = lam(item, l): + find-item = lam(acc, elt): + ask: + | elt == item then: + EI.right(acc) + | otherwise: + EI.left(acc + 1) + end + end + L.fold-while(find-item, 0, l) + end + + only-the-letters = string-explode(trimmed).filter(lam(c): + in-case = lam(l): index-of(c, l) <> l.length() end + + in-case(uppercase) or in-case(lowercase) + end).join-str("") + + if only-the-letters == "": + false + else: + only-the-letters == string-to-upper(only-the-letters) + end + end + + is-silence = lam(): trimmed == "" end + + is-question = lam(): + last-index = ask: + | trimmed <> "" then: string-length(trimmed) - 1 + | otherwise: 0 + end + + string-char-at(trimmed, last-index) == "?" + end + + ask: + | is-silence() then: "Fine. Be that way!" + | is-shouting() and is-question() then: "Calm down, I know what I'm doing!" + | is-shouting() then: 'Whoa, chill out!' + | is-question() then: "Sure." + | otherwise: "Whatever." + end +end diff --git a/exercises/practice/bob/.meta/tests.toml b/exercises/practice/bob/.meta/tests.toml new file mode 100644 index 00000000..5299e289 --- /dev/null +++ b/exercises/practice/bob/.meta/tests.toml @@ -0,0 +1,90 @@ +# 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. + +[e162fead-606f-437a-a166-d051915cea8e] +description = "stating something" + +[73a966dc-8017-47d6-bb32-cf07d1a5fcd9] +description = "shouting" + +[d6c98afd-df35-4806-b55e-2c457c3ab748] +description = "shouting gibberish" + +[8a2e771d-d6f1-4e3f-b6c6-b41495556e37] +description = "asking a question" + +[81080c62-4e4d-4066-b30a-48d8d76920d9] +description = "asking a numeric question" + +[2a02716d-685b-4e2e-a804-2adaf281c01e] +description = "asking gibberish" + +[c02f9179-ab16-4aa7-a8dc-940145c385f7] +description = "talking forcefully" + +[153c0e25-9bb5-4ec5-966e-598463658bcd] +description = "using acronyms in regular speech" + +[a5193c61-4a92-4f68-93e2-f554eb385ec6] +description = "forceful question" + +[a20e0c54-2224-4dde-8b10-bd2cdd4f61bc] +description = "shouting numbers" + +[f7bc4b92-bdff-421e-a238-ae97f230ccac] +description = "no letters" + +[bb0011c5-cd52-4a5b-8bfb-a87b6283b0e2] +description = "question with no letters" + +[496143c8-1c31-4c01-8a08-88427af85c66] +description = "shouting with special characters" + +[e6793c1c-43bd-4b8d-bc11-499aea73925f] +description = "shouting with no exclamation mark" + +[aa8097cc-c548-4951-8856-14a404dd236a] +description = "statement containing question mark" + +[9bfc677d-ea3a-45f2-be44-35bc8fa3753e] +description = "non-letters with question" + +[8608c508-f7de-4b17-985b-811878b3cf45] +description = "prattling on" + +[bc39f7c6-f543-41be-9a43-fd1c2f753fc0] +description = "silence" + +[d6c47565-372b-4b09-b1dd-c40552b8378b] +description = "prolonged silence" + +[4428f28d-4100-4d85-a902-e5a78cb0ecd3] +description = "alternate silence" + +[66953780-165b-4e7e-8ce3-4bcb80b6385a] +description = "multiple line question" +include = false + +[5371ef75-d9ea-4103-bcfa-2da973ddec1b] +description = "starting with whitespace" + +[05b304d6-f83b-46e7-81e0-4cd3ca647900] +description = "ending with whitespace" + +[72bd5ad3-9b2f-4931-a988-dce1f5771de2] +description = "other whitespace" + +[12983553-8601-46a8-92fa-fcaa3bc4a2a0] +description = "non-question ending with whitespace" + +[2c7278ac-f955-4eb4-bf8f-e33eb4116a15] +description = "multiple line question" +reimplements = "66953780-165b-4e7e-8ce3-4bcb80b6385a" diff --git a/exercises/practice/bob/bob-test.arr b/exercises/practice/bob/bob-test.arr new file mode 100644 index 00000000..dc85ed3a --- /dev/null +++ b/exercises/practice/bob/bob-test.arr @@ -0,0 +1,179 @@ +use context starter2024 + +include file("bob.arr") + +check "stating something": + input = "Tom-ay-to, tom-aaaah-to." + expected = "Whatever." + + response(input) is expected +end + +check "shouting": + input = "WATCH OUT!" + expected = "Whoa, chill out!" + + response(input) is expected +end + +check "shouting gibberish": + input = "FCECDFCAAB" + expected = "Whoa, chill out!" + + response(input) is expected +end + +check "asking a question": + input = "Does this cryogenic chamber make me look fat?" + expected = "Sure." + + response(input) is expected +end + +check "asking a numeric question": + input = "You are, what, like 15?" + expected = "Sure." + + response(input) is expected +end + +check "asking gibberish": + input = "fffbbcbeab?" + expected = "Sure." + + response(input) is expected +end + +check "talking forcefully": + input = "Hi there!" + expected = "Whatever." + + response(input) is expected +end + +check "using acronyms in regular speech": + input = "It's OK if you don't want to go work for NASA." + expected = "Whatever." + + response(input) is expected +end + +check "forceful question": + input = "WHAT'S GOING ON?" + expected = "Calm down, I know what I'm doing!" + + response(input) is expected +end + +check "shouting numbers": + input = "1, 2, 3 GO!" + expected = "Whoa, chill out!" + + response(input) is expected +end + +check "no letters": + input = "1, 2, 3" + expected = "Whatever." + + response(input) is expected +end + +check "question with no letters": + input = "4?" + expected = "Sure." + + response(input) is expected +end + +check "shouting with special characters": + input = "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!" + expected = "Whoa, chill out!" + + response(input) is expected +end + +check "shouting with no exclamation mark": + input = "I HATE THE DENTIST" + expected = "Whoa, chill out!" + + response(input) is expected +end + +check "statement containing question mark": + input = "Ending with ? means a question." + expected = "Whatever." + + response(input) is expected +end + +check "non-letters with question": + input = ":) ?" + expected = "Sure." + + response(input) is expected +end + +check "prattling on": + input = "Wait! Hang on. Are you going to be OK?" + expected = "Sure." + + response(input) is expected +end + +check "silence": + input = "" + expected = "Fine. Be that way!" + + response(input) is expected +end + +check "prolonged silence": + input = " " + expected = "Fine. Be that way!" + + response(input) is expected +end + +check "alternate silence": + input = "\t\t\t\t\t\t\t\t\t\t" + expected = "Fine. Be that way!" + + response(input) is expected +end + +check "multiple line question": + input = "\nDoes this cryogenic chamber make\n me look fat?" + expected = "Sure." + + response(input) is expected +end + +check "starting with whitespace": + input = " hmmmmmmm..." + expected = "Whatever." + + response(input) is expected +end + +check "ending with whitespace": + input = "Okay if like my spacebar quite a bit? " + expected = "Sure." + + response(input) is expected +end + +check "other whitespace": + input = "\n\r \t" + expected = "Fine. Be that way!" + + response(input) is expected +end + +check "non-question ending with whitespace": + input = "This is a statement ending with whitespace " + expected = "Whatever." + + response(input) is expected +end + diff --git a/exercises/practice/bob/bob.arr b/exercises/practice/bob/bob.arr new file mode 100644 index 00000000..3178eef2 --- /dev/null +++ b/exercises/practice/bob/bob.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: response end + +fun response(hey-bob): + raise("Please implement the response function") +end diff --git a/exercises/practice/circular-buffer/.docs/instructions.md b/exercises/practice/circular-buffer/.docs/instructions.md new file mode 100644 index 00000000..2ba1fda2 --- /dev/null +++ b/exercises/practice/circular-buffer/.docs/instructions.md @@ -0,0 +1,58 @@ +# Instructions + +A circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. + +A circular buffer first starts empty and of some predefined length. +For example, this is a 7-element buffer: + +```text +[ ][ ][ ][ ][ ][ ][ ] +``` + +Assume that a 1 is written into the middle of the buffer (exact starting location does not matter in a circular buffer): + +```text +[ ][ ][ ][1][ ][ ][ ] +``` + +Then assume that two more elements are added — 2 & 3 — which get appended after the 1: + +```text +[ ][ ][ ][1][2][3][ ] +``` + +If two elements are then removed from the buffer, the oldest values inside the buffer are removed. +The two elements removed, in this case, are 1 & 2, leaving the buffer with just a 3: + +```text +[ ][ ][ ][ ][ ][3][ ] +``` + +If the buffer has 7 elements then it is completely full: + +```text +[5][6][7][8][9][3][4] +``` + +When the buffer is full an error will be raised, alerting the client that further writes are blocked until a slot becomes free. + +When the buffer is full, the client can opt to overwrite the oldest data with a forced write. +In this case, two more elements — A & B — are added and they overwrite the 3 & 4: + +```text +[5][6][7][8][9][A][B] +``` + +3 & 4 have been replaced by A & B making 5 now the oldest data in the buffer. +Finally, if two elements are removed then what would be returned is 5 & 6 yielding the buffer: + +```text +[ ][ ][7][8][9][A][B] +``` + +Because there is space available, if the client again uses overwrite to store C & D then the space where 5 & 6 were stored previously will be used not the location of 7 & 8. +7 is still the oldest element and the buffer is once again full. + +```text +[C][D][7][8][9][A][B] +``` diff --git a/exercises/practice/circular-buffer/.meta/config.json b/exercises/practice/circular-buffer/.meta/config.json new file mode 100644 index 00000000..2a64bfdd --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "circular-buffer.arr" + ], + "test": [ + "circular-buffer-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Circular_buffer" +} diff --git a/exercises/practice/circular-buffer/.meta/example.arr b/exercises/practice/circular-buffer/.meta/example.arr new file mode 100644 index 00000000..498fb1eb --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/example.arr @@ -0,0 +1,49 @@ +use context starter2024 + +provide-types * +provide: make-buffer end + +import arrays as arr + +data CircularBuffer: | buffer(capacity :: Number, size :: Number, elements :: arr.Array, read-index :: Number, write-index :: Number) + with: + method write(self, value :: T) block: + when self.size >= self.capacity: + raise("full buffer") + end + self.elements.set-now(self.write-index, value) + + buffer(self.capacity, + self.size + 1, + self.elements, + self.read-index, + num-modulo(self.write-index + 1, self.capacity)) + end, + method read(self) -> {T;CircularBuffer} block: + when self.size == 0: + raise("empty buffer") + end + val = self.elements.get-now(self.read-index) + updated = buffer(self.capacity, + self.size - 1, + self.elements, + num-modulo(self.read-index + 1, self.capacity), + self.write-index) + {val;updated} + end, + method overwrite(self, value :: T) block: + buff :: CircularBuffer = if self.size == self.capacity: + self.read().{1} + else: + self + end + buff.write(value) + end, + method clear(self): + make-buffer(self.capacity) + end +end + +fun make-buffer(capacity :: Number) -> CircularBuffer: + buffer(capacity, 0, array-of(-1, capacity), 0, 0) +end diff --git a/exercises/practice/circular-buffer/.meta/tests.toml b/exercises/practice/circular-buffer/.meta/tests.toml new file mode 100644 index 00000000..0fb3143d --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[28268ed4-4ff3-45f3-820e-895b44d53dfa] +description = "reading empty buffer should fail" + +[2e6db04a-58a1-425d-ade8-ac30b5f318f3] +description = "can read an item just written" + +[90741fe8-a448-45ce-be2b-de009a24c144] +description = "each item may only be read once" + +[be0e62d5-da9c-47a8-b037-5db21827baa7] +description = "items are read in the order they are written" + +[2af22046-3e44-4235-bfe6-05ba60439d38] +description = "full buffer can't be written to" + +[547d192c-bbf0-4369-b8fa-fc37e71f2393] +description = "a read frees up capacity for another write" + +[04a56659-3a81-4113-816b-6ecb659b4471] +description = "read position is maintained even across multiple writes" + +[60c3a19a-81a7-43d7-bb0a-f07242b1111f] +description = "items cleared out of buffer can't be read" + +[45f3ae89-3470-49f3-b50e-362e4b330a59] +description = "clear frees up capacity for another write" + +[e1ac5170-a026-4725-bfbe-0cf332eddecd] +description = "clear does nothing on empty buffer" + +[9c2d4f26-3ec7-453f-a895-7e7ff8ae7b5b] +description = "overwrite acts like write on non-full buffer" + +[880f916b-5039-475c-bd5c-83463c36a147] +description = "overwrite replaces the oldest item on full buffer" + +[bfecab5b-aca1-4fab-a2b0-cd4af2b053c3] +description = "overwrite replaces the oldest item remaining in buffer following a read" + +[9cebe63a-c405-437b-8b62-e3fdc1ecec5a] +description = "initial clear does not affect wrapping around" diff --git a/exercises/practice/circular-buffer/circular-buffer-test.arr b/exercises/practice/circular-buffer/circular-buffer-test.arr new file mode 100644 index 00000000..1ce5ad70 --- /dev/null +++ b/exercises/practice/circular-buffer/circular-buffer-test.arr @@ -0,0 +1,172 @@ +use context starter2024 + +include file("circular-buffer.arr") + +check "reading empty buffer should fail": + buff = make-buffer(1) + + buff.read() raises "empty buffer" +end + +check "can read an item just written": + var buff = make-buffer(1) + buff := buff.write(1) + + buff.read().{0} is 1 +end + +check "each item may only be read once": + var buff = make-buffer(1) + buff := buff.write(1) + + results = buff.read() + results.{0} is 1 + results.{1}.read() raises "empty buffer" +end + +check "items are read in the order they are written": + var buff = make-buffer(2) + buff := buff.write(1) + buff := buff.write(2) + + var results = buff.read() + results.{0} is 1 + results := results.{1}.read() + results.{0} is 2 +end + +check "full buffer can't be written to": + var buff = make-buffer(1) + buff := buff.write(1) + + buff.write(2) raises "full buffer" +end + +check "a read frees up capacity for another write": + var buff = make-buffer(1) + buff := buff.write(1) + + var results = buff.read() + results.{0} is 1 + + buff := results.{1}.write(2) + results := buff.read() + results.{0} is 2 +end + +check "read position is maintained even across multiple writes": + var buff = make-buffer(3) + buff := buff.write(1) + buff := buff.write(2) + + var results = buff.read() + results.{0} is 1 + + buff := results.{1} + buff := buff.write(3) + + results := buff.read() + results.{0} is 2 + + buff := results.{1} + results := buff.read() + results.{0} is 3 +end + +check "items cleared out of buffer can't be read": + var buff = make-buffer(1) + buff := buff.write(1) + buff := buff.clear() + + buff.read() raises "empty buffer" +end + +check "clear frees up capacity for another write": + var buff = make-buffer(1) + buff := buff.write(1) + buff := buff.clear() + buff := buff.write(2) + + buff.read().{0} is 2 +end + +check "clear does nothing on empty buffer": + var buff = make-buffer(1) + buff := buff.clear() + buff := buff.write(1) + + results = buff.read() + results.{0} is 1 +end + +check "overwrite acts like write on non-full buffer": + var buff = make-buffer(2) + buff := buff.write(1) + buff := buff.overwrite(2) + + var results = buff.read() + results.{0} is 1 + + buff := results.{1} + results := buff.read() + results.{0} is 2 +end + +check "overwrite replaces the oldest item on full buffer": + var buff = make-buffer(2) + buff := buff.write(1) + buff := buff.write(2) + buff := buff.overwrite(3) + + var results = buff.read() + results.{0} is 2 + + buff := results.{1} + results := buff.read() + results.{0} is 3 +end + +check "overwrite replaces the oldest item remaining in buffer following a read": + var buff = make-buffer(3) + buff := buff.write(1) + buff := buff.write(2) + buff := buff.write(3) + + var results = buff.read() + results.{0} is 1 + + buff := results.{1} + buff := buff.write(4) + buff := buff.overwrite(5) + + results := buff.read() + results.{0} is 3 + + buff := results.{1} + results := buff.read() + results.{0} is 4 + + buff := results.{1} + results := buff.read() + results.{0} is 5 +end + +check "initial clear does not affect wrapping around": + var buff = make-buffer(2) + buff := buff.clear() + buff := buff.write(1) + buff := buff.write(2) + buff := buff.overwrite(3) + buff := buff.overwrite(4) + + var results = buff.read() + results.{0} is 3 + + buff := results.{1} + results := buff.read() + results.{0} is 4 + + buff := results.{1} + buff.read() raises "empty buffer" +end + diff --git a/exercises/practice/circular-buffer/circular-buffer.arr b/exercises/practice/circular-buffer/circular-buffer.arr new file mode 100644 index 00000000..816d87a9 --- /dev/null +++ b/exercises/practice/circular-buffer/circular-buffer.arr @@ -0,0 +1,25 @@ +use context starter2024 + +provide-types * +provide: make-buffer end + +data CircularBuffer: + | buffer() + with: + method write(self, value): + ... + end, + method read(self): + ... + end, + method overwrite(self, value): + ... + end, + method clear(self): + ... + end +end + +fun make-buffer(capacity :: Number) -> CircularBuffer: + ... +end diff --git a/exercises/practice/clock/.docs/instructions.append.md b/exercises/practice/clock/.docs/instructions.append.md new file mode 100644 index 00000000..051babd0 --- /dev/null +++ b/exercises/practice/clock/.docs/instructions.append.md @@ -0,0 +1,13 @@ +# Instructions append + +The tests for this exercise expect that your clock will be implemented using a `data declaration` in Pyret. +If you are unfamiliar with data declarations, [the official documentation][data-declaration] is a good place to start. + +As part of the exercise, you'll be comparing values constructed by a `Clock` data variant. +Because a clock's hours and minutes can roll over in either direction, the following combinations are the same on a clock: 1 hour 1 minute, 0 hours 61 minutes, 25 hours 1 minutes, and 2 hours -59 minutes. +Pyret's `is` testing operator uses structural equality, which will result in none of these combinations being equal by default. +To that end, you'll need to define your own equality method for comparing time between values from a Clock data variant. +If you're unfamiliar with how Pyret handles equality, review the official docs on [equality]. + +[data-declaration]: https://pyret.org/docs/latest/s_declarations.html#%28part._s~3adata-decl%29 +[equality]: https://pyret.org/docs/latest/equality.html diff --git a/exercises/practice/clock/.docs/instructions.md b/exercises/practice/clock/.docs/instructions.md new file mode 100644 index 00000000..a1efc789 --- /dev/null +++ b/exercises/practice/clock/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Implement a clock that handles times without dates. + +You should be able to add and subtract minutes to it. + +Two clocks that represent the same time should be equal to each other. diff --git a/exercises/practice/clock/.meta/config.json b/exercises/practice/clock/.meta/config.json new file mode 100644 index 00000000..d1de9bfa --- /dev/null +++ b/exercises/practice/clock/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "clock.arr" + ], + "test": [ + "clock-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Implement a clock that handles times without dates.", + "source": "Pairing session with Erin Drummond" +} diff --git a/exercises/practice/clock/.meta/example.arr b/exercises/practice/clock/.meta/example.arr new file mode 100644 index 00000000..3ec1e713 --- /dev/null +++ b/exercises/practice/clock/.meta/example.arr @@ -0,0 +1,61 @@ +use context starter2024 + +provide-types * + +import equality as E + +data Clock: + | clock(hours :: Number, minutes :: Number) +sharing: + method add(self, minutes :: Number) -> Clock: + new-clock = clock(self.hours, self.minutes + minutes) + + new-clock.normalize() + end, + method subtract(self, minutes :: Number) -> Clock: + new-clock = clock(self.hours, self.minutes - minutes) + + new-clock.normalize() + end, + method normalize(self) -> Clock: + is-in-range = lam(): + ((self.minutes >= 0) and (self.minutes < 60)) + and ((self.hours >= 0) and (self.hours < 24)) + end + + ask: + | not(is-in-range()) then: + additional-hours = num-floor(self.minutes / 60) + hours = num-modulo((self.hours + additional-hours), 24) + minutes = num-modulo(self.minutes, 60) + + clock(hours, minutes) + | otherwise: + self + end + end, + method _equals(self, other :: Clock, _) -> E.EqualityResult: + left = self.normalize() + right = other.normalize() + + if (left.hours == right.hours) and (left.minutes == right.minutes): + E.Equal + else: + E.NotEqual("Clocks represent different periods in time", self, other) + end + end, + method to-string(self) -> String: + to-two-digits = lam(n): + stringified = num-to-string(n) + if string-length(stringified) == 1: + "0" + stringified + else: + stringified + end + end + + normalized = self.normalize() + + to-two-digits(normalized.hours) + ":" + to-two-digits(normalized.minutes) + end +end diff --git a/exercises/practice/clock/.meta/tests.toml b/exercises/practice/clock/.meta/tests.toml new file mode 100644 index 00000000..712c87bc --- /dev/null +++ b/exercises/practice/clock/.meta/tests.toml @@ -0,0 +1,166 @@ +# 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. + +[a577bacc-106b-496e-9792-b3083ea8705e] +description = "Create a new clock with an initial time -> on the hour" + +[b5d0c360-3b88-489b-8e84-68a1c7a4fa23] +description = "Create a new clock with an initial time -> past the hour" + +[473223f4-65f3-46ff-a9f7-7663c7e59440] +description = "Create a new clock with an initial time -> midnight is zero hours" + +[ca95d24a-5924-447d-9a96-b91c8334725c] +description = "Create a new clock with an initial time -> hour rolls over" + +[f3826de0-0925-4d69-8ac8-89aea7e52b78] +description = "Create a new clock with an initial time -> hour rolls over continuously" + +[a02f7edf-dfd4-4b11-b21a-86de3cc6a95c] +description = "Create a new clock with an initial time -> sixty minutes is next hour" + +[8f520df6-b816-444d-b90f-8a477789beb5] +description = "Create a new clock with an initial time -> minutes roll over" + +[c75c091b-47ac-4655-8d40-643767fc4eed] +description = "Create a new clock with an initial time -> minutes roll over continuously" + +[06343ecb-cf39-419d-a3f5-dcbae0cc4c57] +description = "Create a new clock with an initial time -> hour and minutes roll over" + +[be60810e-f5d9-4b58-9351-a9d1e90e660c] +description = "Create a new clock with an initial time -> hour and minutes roll over continuously" + +[1689107b-0b5c-4bea-aad3-65ec9859368a] +description = "Create a new clock with an initial time -> hour and minutes roll over to exactly midnight" + +[d3088ee8-91b7-4446-9e9d-5e2ad6219d91] +description = "Create a new clock with an initial time -> negative hour" + +[77ef6921-f120-4d29-bade-80d54aa43b54] +description = "Create a new clock with an initial time -> negative hour rolls over" + +[359294b5-972f-4546-bb9a-a85559065234] +description = "Create a new clock with an initial time -> negative hour rolls over continuously" + +[509db8b7-ac19-47cc-bd3a-a9d2f30b03c0] +description = "Create a new clock with an initial time -> negative minutes" + +[5d6bb225-130f-4084-84fd-9e0df8996f2a] +description = "Create a new clock with an initial time -> negative minutes roll over" + +[d483ceef-b520-4f0c-b94a-8d2d58cf0484] +description = "Create a new clock with an initial time -> negative minutes roll over continuously" + +[1cd19447-19c6-44bf-9d04-9f8305ccb9ea] +description = "Create a new clock with an initial time -> negative sixty minutes is previous hour" + +[9d3053aa-4f47-4afc-bd45-d67a72cef4dc] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over" + +[51d41fcf-491e-4ca0-9cae-2aa4f0163ad4] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over continuously" + +[d098e723-ad29-4ef9-997a-2693c4c9d89a] +description = "Add minutes -> add minutes" + +[b6ec8f38-e53e-4b22-92a7-60dab1f485f4] +description = "Add minutes -> add no minutes" + +[efd349dd-0785-453e-9ff8-d7452a8e7269] +description = "Add minutes -> add to next hour" + +[749890f7-aba9-4702-acce-87becf4ef9fe] +description = "Add minutes -> add more than one hour" + +[da63e4c1-1584-46e3-8d18-c9dc802c1713] +description = "Add minutes -> add more than two hours with carry" + +[be167a32-3d33-4cec-a8bc-accd47ddbb71] +description = "Add minutes -> add across midnight" + +[6672541e-cdae-46e4-8be7-a820cc3be2a8] +description = "Add minutes -> add more than one day (1500 min = 25 hrs)" + +[1918050d-c79b-4cb7-b707-b607e2745c7e] +description = "Add minutes -> add more than two days" + +[37336cac-5ede-43a5-9026-d426cbe40354] +description = "Subtract minutes -> subtract minutes" + +[0aafa4d0-3b5f-4b12-b3af-e3a9e09c047b] +description = "Subtract minutes -> subtract to previous hour" + +[9b4e809c-612f-4b15-aae0-1df0acb801b9] +description = "Subtract minutes -> subtract more than an hour" + +[8b04bb6a-3d33-4e6c-8de9-f5de6d2c70d6] +description = "Subtract minutes -> subtract across midnight" + +[07c3bbf7-ce4d-4658-86e8-4a77b7a5ccd9] +description = "Subtract minutes -> subtract more than two hours" + +[90ac8a1b-761c-4342-9c9c-cdc3ed5db097] +description = "Subtract minutes -> subtract more than two hours with borrow" + +[2149f985-7136-44ad-9b29-ec023a97a2b7] +description = "Subtract minutes -> subtract more than one day (1500 min = 25 hrs)" + +[ba11dbf0-ac27-4acb-ada9-3b853ec08c97] +description = "Subtract minutes -> subtract more than two days" + +[f2fdad51-499f-4c9b-a791-b28c9282e311] +description = "Compare two clocks for equality -> clocks with same time" + +[5d409d4b-f862-4960-901e-ec430160b768] +description = "Compare two clocks for equality -> clocks a minute apart" + +[a6045fcf-2b52-4a47-8bb2-ef10a064cba5] +description = "Compare two clocks for equality -> clocks an hour apart" + +[66b12758-0be5-448b-a13c-6a44bce83527] +description = "Compare two clocks for equality -> clocks with hour overflow" + +[2b19960c-212e-4a71-9aac-c581592f8111] +description = "Compare two clocks for equality -> clocks with hour overflow by several days" + +[6f8c6541-afac-4a92-b0c2-b10d4e50269f] +description = "Compare two clocks for equality -> clocks with negative hour" + +[bb9d5a68-e324-4bf5-a75e-0e9b1f97a90d] +description = "Compare two clocks for equality -> clocks with negative hour that wraps" + +[56c0326d-565b-4d19-a26f-63b3205778b7] +description = "Compare two clocks for equality -> clocks with negative hour that wraps multiple times" + +[c90b9de8-ddff-4ffe-9858-da44a40fdbc2] +description = "Compare two clocks for equality -> clocks with minute overflow" + +[533a3dc5-59a7-491b-b728-a7a34fe325de] +description = "Compare two clocks for equality -> clocks with minute overflow by several days" + +[fff49e15-f7b7-4692-a204-0f6052d62636] +description = "Compare two clocks for equality -> clocks with negative minute" + +[605c65bb-21bd-43eb-8f04-878edf508366] +description = "Compare two clocks for equality -> clocks with negative minute that wraps" + +[b87e64ed-212a-4335-91fd-56da8421d077] +description = "Compare two clocks for equality -> clocks with negative minute that wraps multiple times" + +[822fbf26-1f3b-4b13-b9bf-c914816b53dd] +description = "Compare two clocks for equality -> clocks with negative hours and minutes" + +[e787bccd-cf58-4a1d-841c-ff80eaaccfaa] +description = "Compare two clocks for equality -> clocks with negative hours and minutes that wrap" + +[96969ca8-875a-48a1-86ae-257a528c44f5] +description = "Compare two clocks for equality -> full clock and zeroed clock" diff --git a/exercises/practice/clock/clock-test.arr b/exercises/practice/clock/clock-test.arr new file mode 100644 index 00000000..fb1ca6fb --- /dev/null +++ b/exercises/practice/clock/clock-test.arr @@ -0,0 +1,212 @@ +use context starter2024 + +include file("clock.arr") + +check "Create a new clock with an initial time -> on the hour": + clock(8, 0).normalize().to-string() is "08:00" +end + +check "Create a new clock with an initial time -> past the hour": + clock(11, 09).normalize().to-string() is "11:09" +end + +check "Create a new clock with an initial time -> midnight is zero hours": + clock(24, 0).normalize().to-string() is "00:00" +end + +check "Create a new clock with an initial time -> hour rolls over": + clock(25, 0).normalize().to-string() is "01:00" +end + +check "Create a new clock with an initial time -> hour rolls over continuously": + clock(100, 0).normalize().to-string() is "04:00" +end + +check "Create a new clock with an initial time -> sixty minutes is next hour": + clock(1, 60).normalize().to-string() is "02:00" +end + +check "Create a new clock with an initial time -> minutes roll over": + clock(0, 160).normalize().to-string() is "02:40" +end + +check "Create a new clock with an initial time -> minutes roll over continuously": + clock(0, 1723).normalize().to-string() is "04:43" +end + +check "Create a new clock with an initial time -> hour and minutes roll over": + clock(25, 160).normalize().to-string() is "03:40" +end + +check "Create a new clock with an initial time -> hour and minutes roll over continuously": + clock(201, 3001).normalize().to-string() is "11:01" +end + +check "Create a new clock with an initial time -> hour and minutes roll over to exactly midnight": + clock(72, 8640).normalize().to-string() is "00:00" +end + +check "Create a new clock with an initial time -> negative hour": + clock(-1, 15).normalize().to-string() is "23:15" +end + +check "Create a new clock with an initial time -> negative hour rolls over": + clock(-25, 0).normalize().to-string() is "23:00" +end + +check "Create a new clock with an initial time -> negative hour rolls over continuously": + clock(-91, 0).normalize().to-string() is "05:00" +end + +check "Create a new clock with an initial time -> negative minutes": + clock(1, -40).normalize().to-string() is "00:20" +end + +check "Create a new clock with an initial time -> negative minutes roll over": + clock(1, -160).normalize().to-string() is "22:20" +end + +check "Create a new clock with an initial time -> negative minutes roll over continuously": + clock(1, -4820).normalize().to-string() is "16:40" +end + +check "Create a new clock with an initial time -> negative sixty minutes is previous hour": + clock(2, -60).normalize().to-string() is "01:00" +end + +check "Create a new clock with an initial time -> negative hour and minutes both roll over": + clock(-25, -160).normalize().to-string() is "20:20" +end + +check "Create a new clock with an initial time -> negative hour and minutes both roll over continuously": + clock(-121, -5810).normalize().to-string() is "22:10" +end + +check "Add minutes -> add minutes": + clock(10, 0).add(3).to-string() is "10:03" +end + +check "Add minutes -> add no minutes": + clock(6, 41).add(0).to-string() is "06:41" +end + +check "Add minutes -> add to next hour": + clock(0, 45).add(40).to-string() is "01:25" +end + +check "Add minutes -> add more than one hour": + clock(10, 0).add(61).to-string() is "11:01" +end + +check "Add minutes -> add more than two hours with carry": + clock(0, 45).add(160).to-string() is "03:25" +end + +check "Add minutes -> add across midnight": + clock(23, 59).add(2).to-string() is "00:01" +end + +check "Add minutes -> add more than one day (1500 min = 25 hrs)": + clock(5, 32).add(1500).to-string() is "06:32" +end + +check "Add minutes -> add more than two days": + clock(1, 1).add(3500).to-string() is "11:21" +end + +check "Subtract minutes -> subtract minutes": + clock(10, 3).subtract(3).to-string() is "10:00" +end + +check "Subtract minutes -> subtract to previous hour": + clock(10, 3).subtract(30).to-string() is "09:33" +end + +check "Subtract minutes -> subtract more than an hour": + clock(10, 3).subtract(70).to-string() is "08:53" +end + +check "Subtract minutes -> subtract across midnight": + clock(0, 3).subtract(4).to-string() is "23:59" +end + +check "Subtract minutes -> subtract more than two hours": + clock(0, 0).subtract(160).to-string() is "21:20" +end + +check "Subtract minutes -> subtract more than two hours with borrow": + clock(6, 15).subtract(160).to-string() is "03:35" +end + +check "Subtract minutes -> subtract more than one day (1500 min = 25 hrs)": + clock(5, 32).subtract(1500).to-string() is "04:32" +end + +check "Subtract minutes -> subtract more than two days": + clock(2, 20).subtract(3000).to-string() is "00:20" +end + +check "Compare two clocks for equality -> clocks with same time": + clock(15, 37) is clock(15, 37) +end + +check "Compare two clocks for equality -> clocks a minute apart": + clock(15, 36) is-not clock(15, 37) +end + +check "Compare two clocks for equality -> clocks an hour apart": + clock(14, 37) is-not clock(15, 37) +end + +check "Compare two clocks for equality -> clocks with hour overflow": + clock(10, 37) is clock(34, 37) +end + +check "Compare two clocks for equality -> clocks with hour overflow by several days": + clock(3, 11) is clock(99, 11) +end + +check "Compare two clocks for equality -> clocks with negative hour": + clock(22, 40) is clock(-2, 40) +end + +check "Compare two clocks for equality -> clocks with negative hour that wraps": + clock(17, 3) is clock(-31, 3) +end + +check "Compare two clocks for equality -> clocks with negative hour that wraps multiple times": + clock(13, 49) is clock(-83, 49) +end + +check "Compare two clocks for equality -> clocks with minute overflow": + clock(0, 1) is clock(0, 1441) +end + +check "Compare two clocks for equality -> clocks with minute overflow by several days": + clock(2, 2) is clock(2, 4322) +end + +check "Compare two clocks for equality -> clocks with negative minute": + clock(2, 40) is clock(3, -20) +end + +check "Compare two clocks for equality -> clocks with negative minute that wraps": + clock(4, 10) is clock(5, -1490) +end + +check "Compare two clocks for equality -> clocks with negative minute that wraps multiple times": + clock(6, 15) is clock(6, -4305) +end + +check "Compare two clocks for equality -> clocks with negative hours and minutes": + clock(7, 32) is clock(-12, -268) +end + +check "Compare two clocks for equality -> clocks with negative hours and minutes that wrap": + clock(18, 7) is clock(-54, -11513) +end + +check "Compare two clocks for equality -> full clock and zeroed clock": + clock(24, 0) is clock(0, 0) +end + diff --git a/exercises/practice/clock/clock.arr b/exercises/practice/clock/clock.arr new file mode 100644 index 00000000..4ebac4c2 --- /dev/null +++ b/exercises/practice/clock/clock.arr @@ -0,0 +1,28 @@ +use context starter2024 + +provide-types * + +import equality as E + +# Replace the ... with your code to pass the tests. Good luck! + +data Clock: + | clock(hours :: NumInteger, minutes :: NumInteger) +sharing: + method add(self, minutes :: NumInteger) -> Clock: + ... + end, + method subtract(self, minutes :: NumInteger) -> Clock: + ... + end, + method normalize(self) -> Clock: + ... + end, + method _equals(self, other :: Clock, _) -> E.EqualityResult: + # The callback argument after other is ignored for this exercise + ... + end, + method to-string(self) -> String: + ... + end +end diff --git a/exercises/practice/collatz-conjecture/.docs/instructions.md b/exercises/practice/collatz-conjecture/.docs/instructions.md new file mode 100644 index 00000000..af332a81 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.docs/instructions.md @@ -0,0 +1,3 @@ +# Instructions + +Given a positive integer, return the number of steps it takes to reach 1 according to the rules of the Collatz Conjecture. diff --git a/exercises/practice/collatz-conjecture/.docs/introduction.md b/exercises/practice/collatz-conjecture/.docs/introduction.md new file mode 100644 index 00000000..c35bdeb6 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.docs/introduction.md @@ -0,0 +1,28 @@ +# Introduction + +One evening, you stumbled upon an old notebook filled with cryptic scribbles, as though someone had been obsessively chasing an idea. +On one page, a single question stood out: **Can every number find its way to 1?** +It was tied to something called the **Collatz Conjecture**, a puzzle that has baffled thinkers for decades. + +The rules were deceptively simple. +Pick any positive integer. + +- If it's even, divide it by 2. +- If it's odd, multiply it by 3 and add 1. + +Then, repeat these steps with the result, continuing indefinitely. + +Curious, you picked number 12 to test and began the journey: + +12 ➜ 6 ➜ 3 ➜ 10 ➜ 5 ➜ 16 ➜ 8 ➜ 4 ➜ 2 ➜ 1 + +Counting from the second number (6), it took 9 steps to reach 1, and each time the rules repeated, the number kept changing. +At first, the sequence seemed unpredictable — jumping up, down, and all over. +Yet, the conjecture claims that no matter the starting number, we'll always end at 1. + +It was fascinating, but also puzzling. +Why does this always seem to work? +Could there be a number where the process breaks down, looping forever or escaping into infinity? +The notebook suggested solving this could reveal something profound — and with it, fame, [fortune][collatz-prize], and a place in history awaits whoever could unlock its secrets. + +[collatz-prize]: https://mathprize.net/posts/collatz-conjecture/ diff --git a/exercises/practice/collatz-conjecture/.meta/config.json b/exercises/practice/collatz-conjecture/.meta/config.json new file mode 100644 index 00000000..dedcf9ec --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "collatz-conjecture.arr" + ], + "test": [ + "collatz-conjecture-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Collatz_conjecture" +} diff --git a/exercises/practice/collatz-conjecture/.meta/example.arr b/exercises/practice/collatz-conjecture/.meta/example.arr new file mode 100644 index 00000000..e89525d6 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/example.arr @@ -0,0 +1,23 @@ +use context starter2024 + +provide: steps end + +fun steps(n): + if n > 0: + steps-recurse(n, 0) + else: + raise("Only positive numbers are allowed") + end +end + +fun is-even(num): + num-modulo(num, 2) == 0 +end + +fun steps-recurse(current, acc): + ask: + | current == 1 then: acc + | is-even(current) then: steps-recurse(current / 2, acc + 1) + | otherwise: steps-recurse((3 * current) + 1, acc + 1) + end +end diff --git a/exercises/practice/collatz-conjecture/.meta/tests.toml b/exercises/practice/collatz-conjecture/.meta/tests.toml new file mode 100644 index 00000000..cc34e168 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/tests.toml @@ -0,0 +1,38 @@ +# 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. + +[540a3d51-e7a6-47a5-92a3-4ad1838f0bfd] +description = "zero steps for one" + +[3d76a0a6-ea84-444a-821a-f7857c2c1859] +description = "divide if even" + +[754dea81-123c-429e-b8bc-db20b05a87b9] +description = "even and odd steps" + +[ecfd0210-6f85-44f6-8280-f65534892ff6] +description = "large number of even and odd steps" + +[7d4750e6-def9-4b86-aec7-9f7eb44f95a3] +description = "zero is an error" +include = false + +[2187673d-77d6-4543-975e-66df6c50e2da] +description = "zero is an error" +reimplements = "7d4750e6-def9-4b86-aec7-9f7eb44f95a3" + +[c6c795bf-a288-45e9-86a1-841359ad426d] +description = "negative value is an error" +include = false + +[ec11f479-56bc-47fd-a434-bcd7a31a7a2e] +description = "negative value is an error" +reimplements = "c6c795bf-a288-45e9-86a1-841359ad426d" diff --git a/exercises/practice/collatz-conjecture/collatz-conjecture-test.arr b/exercises/practice/collatz-conjecture/collatz-conjecture-test.arr new file mode 100644 index 00000000..c5fab346 --- /dev/null +++ b/exercises/practice/collatz-conjecture/collatz-conjecture-test.arr @@ -0,0 +1,28 @@ +use context starter2024 + +include file("collatz-conjecture.arr") + +check "zero steps for one": + steps(1) is 0 +end + +check "divide if even": + steps(16) is 4 +end + +check "even and odd steps": + steps(12) is 9 +end + +check "large number of even and odd steps": + steps(1000000) is 152 +end + +check "zero is an error": + steps(0) raises "Only positive numbers are allowed" +end + +check "negative value is an error": + steps(-15) raises "Only positive numbers are allowed" +end + diff --git a/exercises/practice/collatz-conjecture/collatz-conjecture.arr b/exercises/practice/collatz-conjecture/collatz-conjecture.arr new file mode 100644 index 00000000..bad67d99 --- /dev/null +++ b/exercises/practice/collatz-conjecture/collatz-conjecture.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: steps end + +fun steps(n): + raise("Please implement the steps function") +end diff --git a/exercises/practice/darts/.docs/instructions.md b/exercises/practice/darts/.docs/instructions.md index 70f0e53d..6518201c 100644 --- a/exercises/practice/darts/.docs/instructions.md +++ b/exercises/practice/darts/.docs/instructions.md @@ -1,11 +1,13 @@ # Instructions -Write a function that returns the earned points in a single toss of a Darts game. +Calculate the points scored in a single toss of a Darts game. [Darts][darts] is a game where players throw darts at a [target][darts-target]. In our particular instance of the game, the target rewards 4 different amounts of points, depending on where the dart lands: +![Our dart scoreboard with values from a complete miss to a bullseye](https://assets.exercism.org/images/exercises/darts/darts-scoreboard.svg) + - If the dart lands outside the target, player earns no points (0 points). - If the dart lands in the outer circle of the target, player earns 1 point. - If the dart lands in the middle circle of the target, player earns 5 points. @@ -14,10 +16,16 @@ In our particular instance of the game, the target rewards 4 different amounts o The outer circle has a radius of 10 units (this is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered at the same point — that is, the circles are [concentric][] defined by the coordinates (0, 0). -Write a function that given a point in the target (defined by its [Cartesian coordinates][cartesian-coordinates] `x` and `y`, where `x` and `y` are [real][real-numbers]), returns the correct amount earned by a dart landing at that point. +Given a point in the target (defined by its [Cartesian coordinates][cartesian-coordinates] `x` and `y`, where `x` and `y` are [real][real-numbers]), calculate the correct score earned by a dart landing at that point. + +## Credit + +The scoreboard image was created by [habere-et-dispertire][habere-et-dispertire] using [Inkscape][inkscape]. [darts]: https://en.wikipedia.org/wiki/Darts [darts-target]: https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg [concentric]: https://mathworld.wolfram.com/ConcentricCircles.html [cartesian-coordinates]: https://www.mathsisfun.com/data/cartesian-coordinates.html [real-numbers]: https://www.mathsisfun.com/numbers/real-numbers.html +[habere-et-dispertire]: https://exercism.org/profiles/habere-et-dispertire +[inkscape]: https://en.wikipedia.org/wiki/Inkscape diff --git a/exercises/practice/darts/.meta/config.json b/exercises/practice/darts/.meta/config.json index 8c88b075..cd1ea7ad 100644 --- a/exercises/practice/darts/.meta/config.json +++ b/exercises/practice/darts/.meta/config.json @@ -13,6 +13,6 @@ ".meta/example.arr" ] }, - "blurb": "Write a function that returns the earned points in a single toss of a Darts game.", + "blurb": "Calculate the points scored in a single toss of a Darts game.", "source": "Inspired by an exercise created by a professor Della Paolera in Argentina" } diff --git a/exercises/practice/darts/.meta/example.arr b/exercises/practice/darts/.meta/example.arr index db28d9ad..3da3abac 100644 --- a/exercises/practice/darts/.meta/example.arr +++ b/exercises/practice/darts/.meta/example.arr @@ -1,3 +1,5 @@ +use context starter2024 + provide: score end fun score(x, y): diff --git a/exercises/practice/darts/darts-test.arr b/exercises/practice/darts/darts-test.arr index 8a68cd2f..7e57d88e 100644 --- a/exercises/practice/darts/darts-test.arr +++ b/exercises/practice/darts/darts-test.arr @@ -1,3 +1,5 @@ +use context starter2024 + include file("darts.arr") check "Missed target": @@ -51,3 +53,4 @@ end check "Asymmetric position between the inner and middle circles": score(0.5, -4) is 5 end + diff --git a/exercises/practice/darts/darts.arr b/exercises/practice/darts/darts.arr index 4eca0f6a..924f0239 100644 --- a/exercises/practice/darts/darts.arr +++ b/exercises/practice/darts/darts.arr @@ -1,3 +1,5 @@ +use context starter2024 + provide: score end fun score(x, y): diff --git a/exercises/practice/difference-of-squares/.docs/instructions.md b/exercises/practice/difference-of-squares/.docs/instructions.md new file mode 100644 index 00000000..39c38b50 --- /dev/null +++ b/exercises/practice/difference-of-squares/.docs/instructions.md @@ -0,0 +1,14 @@ +# Instructions + +Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. + +The square of the sum of the first ten natural numbers is +(1 + 2 + ... + 10)² = 55² = 3025. + +The sum of the squares of the first ten natural numbers is +1² + 2² + ... + 10² = 385. + +Hence the difference between the square of the sum of the first ten natural numbers and the sum of the squares of the first ten natural numbers is 3025 - 385 = 2640. + +You are not expected to discover an efficient solution to this yourself from first principles; research is allowed, indeed, encouraged. +Finding the best algorithm for the problem is a key skill in software engineering. diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json new file mode 100644 index 00000000..b80430e1 --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "difference-of-squares.arr" + ], + "test": [ + "difference-of-squares-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.", + "source": "Problem 6 at Project Euler", + "source_url": "https://projecteuler.net/problem=6" +} diff --git a/exercises/practice/difference-of-squares/.meta/example.arr b/exercises/practice/difference-of-squares/.meta/example.arr new file mode 100644 index 00000000..e1bb6998 --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/example.arr @@ -0,0 +1,16 @@ +use context starter2024 + +provide: square-of-sum, sum-of-squares, difference-of-squares end + +fun square-of-sum(number): + sum = (number * (number + 1)) / 2 + sum * sum +end + +fun sum-of-squares(number): + (number * (number + 1) * ((2 * number) + 1)) / 6 +end + +fun difference-of-squares(number): + square-of-sum(number) - sum-of-squares(number) +end diff --git a/exercises/practice/difference-of-squares/.meta/tests.toml b/exercises/practice/difference-of-squares/.meta/tests.toml new file mode 100644 index 00000000..e54414c0 --- /dev/null +++ b/exercises/practice/difference-of-squares/.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. + +[e46c542b-31fc-4506-bcae-6b62b3268537] +description = "Square the sum of the numbers up to the given number -> square of sum 1" + +[9b3f96cb-638d-41ee-99b7-b4f9c0622948] +description = "Square the sum of the numbers up to the given number -> square of sum 5" + +[54ba043f-3c35-4d43-86ff-3a41625d5e86] +description = "Square the sum of the numbers up to the given number -> square of sum 100" + +[01d84507-b03e-4238-9395-dd61d03074b5] +description = "Sum the squares of the numbers up to the given number -> sum of squares 1" + +[c93900cd-8cc2-4ca4-917b-dd3027023499] +description = "Sum the squares of the numbers up to the given number -> sum of squares 5" + +[94807386-73e4-4d9e-8dec-69eb135b19e4] +description = "Sum the squares of the numbers up to the given number -> sum of squares 100" + +[44f72ae6-31a7-437f-858d-2c0837adabb6] +description = "Subtract sum of squares from square of sums -> difference of squares 1" + +[005cb2bf-a0c8-46f3-ae25-924029f8b00b] +description = "Subtract sum of squares from square of sums -> difference of squares 5" + +[b1bf19de-9a16-41c0-a62b-1f02ecc0b036] +description = "Subtract sum of squares from square of sums -> difference of squares 100" diff --git a/exercises/practice/difference-of-squares/difference-of-squares-test.arr b/exercises/practice/difference-of-squares/difference-of-squares-test.arr new file mode 100644 index 00000000..2cfeba2f --- /dev/null +++ b/exercises/practice/difference-of-squares/difference-of-squares-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("difference-of-squares.arr") + +check "Square the sum of the numbers up to the given number -> square of sum 1": + square-of-sum(1) is 1 +end + +check "Square the sum of the numbers up to the given number -> square of sum 5": + square-of-sum(5) is 225 +end + +check "Square the sum of the numbers up to the given number -> square of sum 100": + square-of-sum(100) is 25502500 +end + +check "Sum the squares of the numbers up to the given number -> sum of squares 1": + sum-of-squares(1) is 1 +end + +check "Sum the squares of the numbers up to the given number -> sum of squares 5": + sum-of-squares(5) is 55 +end + +check "Sum the squares of the numbers up to the given number -> sum of squares 100": + sum-of-squares(100) is 338350 +end + +check "Subtract sum of squares from square of sums -> difference of squares 1": + difference-of-squares(1) is 0 +end + +check "Subtract sum of squares from square of sums -> difference of squares 5": + difference-of-squares(5) is 170 +end + +check "Subtract sum of squares from square of sums -> difference of squares 100": + difference-of-squares(100) is 25164150 +end + diff --git a/exercises/practice/difference-of-squares/difference-of-squares.arr b/exercises/practice/difference-of-squares/difference-of-squares.arr new file mode 100644 index 00000000..bf36cc5f --- /dev/null +++ b/exercises/practice/difference-of-squares/difference-of-squares.arr @@ -0,0 +1,15 @@ +use context starter2024 + +provide: square-of-sum, sum-of-squares, difference-of-squares end + +fun square-of-sum(number): + raise("please implement the square-of-sum function") +end + +fun sum-of-squares(number): + raise("please implement the sum-of-squares function") +end + +fun difference-of-squares(number): + raise("please implement the difference-of-squares function") +end diff --git a/exercises/practice/dnd-character/.docs/instructions.md b/exercises/practice/dnd-character/.docs/instructions.md new file mode 100644 index 00000000..e14e7949 --- /dev/null +++ b/exercises/practice/dnd-character/.docs/instructions.md @@ -0,0 +1,32 @@ +# Instructions + +For a game of [Dungeons & Dragons][dnd], each player starts by generating a character they can play with. +This character has, among other things, six abilities; strength, dexterity, constitution, intelligence, wisdom and charisma. +These six abilities have scores that are determined randomly. +You do this by rolling four 6-sided dice and recording the sum of the largest three dice. +You do this six times, once for each ability. + +Your character's initial hitpoints are 10 + your character's constitution modifier. +You find your character's constitution modifier by subtracting 10 from your character's constitution, divide by 2 and round down. + +Write a random character generator that follows the above rules. + +For example, the six throws of four dice may look like: + +- 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength. +- 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity. +- 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution. +- 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence. +- 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom. +- 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma. + +Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6. + +~~~~exercism/note +Most programming languages feature (pseudo-)random generators, but few programming languages are designed to roll dice. +One such language is [Troll][troll]. + +[troll]: https://di.ku.dk/Ansatte/?pure=da%2Fpublications%2Ftroll-a-language-for-specifying-dicerolls(84a45ff0-068b-11df-825d-000ea68e967b)%2Fexport.html +~~~~ + +[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons diff --git a/exercises/practice/dnd-character/.docs/introduction.md b/exercises/practice/dnd-character/.docs/introduction.md new file mode 100644 index 00000000..5301f618 --- /dev/null +++ b/exercises/practice/dnd-character/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +After weeks of anticipation, you and your friends get together for your very first game of [Dungeons & Dragons][dnd] (D&D). +Since this is the first session of the game, each player has to generate a character to play with. +The character's abilities are determined by rolling 6-sided dice, but where _are_ the dice? +With a shock, you realize that your friends are waiting for _you_ to produce the dice; after all it was your idea to play D&D! +Panicking, you realize you forgot to bring the dice, which would mean no D&D game. +As you have some basic coding skills, you quickly come up with a solution: you'll write a program to simulate dice rolls. + +[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons diff --git a/exercises/practice/dnd-character/.meta/config.json b/exercises/practice/dnd-character/.meta/config.json new file mode 100644 index 00000000..1d332bf6 --- /dev/null +++ b/exercises/practice/dnd-character/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "dnd-character.arr" + ], + "test": [ + "dnd-character-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Randomly generate Dungeons & Dragons characters.", + "source": "Simon Shine, Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945" +} diff --git a/exercises/practice/dnd-character/.meta/example.arr b/exercises/practice/dnd-character/.meta/example.arr new file mode 100644 index 00000000..41f598f2 --- /dev/null +++ b/exercises/practice/dnd-character/.meta/example.arr @@ -0,0 +1,30 @@ +use context starter2024 + +provide-types * + +data Character: + | blank-character() with: + method randomize-stats(self) -> Character: + strength = self.ability() + dexterity = self.ability() + constitution = self.ability() + intelligence = self.ability() + wisdom = self.ability() + charisma = self.ability() + character(strength, dexterity, constitution, intelligence, wisdom, charisma) + end + | character(strength, dexterity, constitution, intelligence, wisdom, charisma) with: + method get-hitpoints(self) -> NumInteger: + 10 + self.modifier(self.constitution) + end +sharing: + method ability(self) -> NumInteger: + roll-dice = lam(_): num-random(6) + 1 end + rolls = map(roll-dice, repeat(4, 0)) + rolls.sort().drop(1).foldl(lam(elt, acc): elt + acc end, 0) + end, + method modifier(self, value :: NumInteger) -> NumInteger: + modified = (value - 10) / 2 + num-floor(modified) + end +end diff --git a/exercises/practice/dnd-character/.meta/tests.toml b/exercises/practice/dnd-character/.meta/tests.toml new file mode 100644 index 00000000..44b3c20e --- /dev/null +++ b/exercises/practice/dnd-character/.meta/tests.toml @@ -0,0 +1,73 @@ +# 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. + +[1e9ae1dc-35bd-43ba-aa08-e4b94c20fa37] +description = "ability modifier -> ability modifier for score 3 is -4" + +[cc9bb24e-56b8-4e9e-989d-a0d1a29ebb9c] +description = "ability modifier -> ability modifier for score 4 is -3" + +[5b519fcd-6946-41ee-91fe-34b4f9808326] +description = "ability modifier -> ability modifier for score 5 is -3" + +[dc2913bd-6d7a-402e-b1e2-6d568b1cbe21] +description = "ability modifier -> ability modifier for score 6 is -2" + +[099440f5-0d66-4b1a-8a10-8f3a03cc499f] +description = "ability modifier -> ability modifier for score 7 is -2" + +[cfda6e5c-3489-42f0-b22b-4acb47084df0] +description = "ability modifier -> ability modifier for score 8 is -1" + +[c70f0507-fa7e-4228-8463-858bfbba1754] +description = "ability modifier -> ability modifier for score 9 is -1" + +[6f4e6c88-1cd9-46a0-92b8-db4a99b372f7] +description = "ability modifier -> ability modifier for score 10 is 0" + +[e00d9e5c-63c8-413f-879d-cd9be9697097] +description = "ability modifier -> ability modifier for score 11 is 0" + +[eea06f3c-8de0-45e7-9d9d-b8cab4179715] +description = "ability modifier -> ability modifier for score 12 is +1" + +[9c51f6be-db72-4af7-92ac-b293a02c0dcd] +description = "ability modifier -> ability modifier for score 13 is +1" + +[94053a5d-53b6-4efc-b669-a8b5098f7762] +description = "ability modifier -> ability modifier for score 14 is +2" + +[8c33e7ca-3f9f-4820-8ab3-65f2c9e2f0e2] +description = "ability modifier -> ability modifier for score 15 is +2" + +[c3ec871e-1791-44d0-b3cc-77e5fb4cd33d] +description = "ability modifier -> ability modifier for score 16 is +3" + +[3d053cee-2888-4616-b9fd-602a3b1efff4] +description = "ability modifier -> ability modifier for score 17 is +3" + +[bafd997a-e852-4e56-9f65-14b60261faee] +description = "ability modifier -> ability modifier for score 18 is +4" + +[4f28f19c-2e47-4453-a46a-c0d365259c14] +description = "random ability is within range" + +[385d7e72-864f-4e88-8279-81a7d75b04ad] +description = "random character is valid" + +[2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe] +description = "each ability is only calculated once" +include = false + +[dca2b2ec-f729-4551-84b9-078876bb4808] +description = "each ability is only calculated once" +reimplements = "2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe" +include = false diff --git a/exercises/practice/dnd-character/dnd-character-test.arr b/exercises/practice/dnd-character/dnd-character-test.arr new file mode 100644 index 00000000..6bc86703 --- /dev/null +++ b/exercises/practice/dnd-character/dnd-character-test.arr @@ -0,0 +1,97 @@ +use context starter2024 + +include file("dnd-character.arr") + +# declaring some variables to be more concise inside the tests. +modifier = blank-character().modifier +ability = blank-character().ability +randomize-stats = blank-character().randomize-stats + +check "ability modifier -> ability modifier for score 3 is -4": + modifier(3) is -4 +end + +check "ability modifier -> ability modifier for score 4 is -3": + modifier(4) is -3 +end + +check "ability modifier -> ability modifier for score 5 is -3": + modifier(5) is -3 +end + +check "ability modifier -> ability modifier for score 6 is -2": + modifier(6) is -2 +end + +check "ability modifier -> ability modifier for score 7 is -2": + modifier(7) is -2 +end + +check "ability modifier -> ability modifier for score 8 is -1": + modifier(8) is -1 +end + +check "ability modifier -> ability modifier for score 9 is -1": + modifier(9) is -1 +end + +check "ability modifier -> ability modifier for score 10 is 0": + modifier(10) is 0 +end + +check "ability modifier -> ability modifier for score 11 is 0": + modifier(11) is 0 +end + +check "ability modifier -> ability modifier for score 12 is +1": + modifier(12) is 1 +end + +check "ability modifier -> ability modifier for score 13 is +1": + modifier(13) is 1 +end + +check "ability modifier -> ability modifier for score 14 is +2": + modifier(14) is 2 +end + +check "ability modifier -> ability modifier for score 15 is +2": + modifier(15) is 2 +end + +check "ability modifier -> ability modifier for score 16 is +3": + modifier(16) is 3 +end + +check "ability modifier -> ability modifier for score 17 is +3": + modifier(17) is 3 +end + +check "ability modifier -> ability modifier for score 18 is +4": + modifier(18) is 4 +end + +check "random ability is within range": + stat = ability() + + is-valid = lam(n): (n >= 3) and (n <= 18) end + + is-valid(stat) is true +end + +check "random character is valid": + new-character = randomize-stats() + + is-valid = lam(n): (n >= 3) and (n <= 18) end + + is-valid(new-character.strength) is true + is-valid(new-character.dexterity) is true + is-valid(new-character.constitution) is true + is-valid(new-character.intelligence) is true + is-valid(new-character.wisdom) is true + is-valid(new-character.charisma) is true + + expected = 10 + new-character.modifier(new-character.constitution) + new-character.get-hitpoints() is expected +end + diff --git a/exercises/practice/dnd-character/dnd-character.arr b/exercises/practice/dnd-character/dnd-character.arr new file mode 100644 index 00000000..7080ae96 --- /dev/null +++ b/exercises/practice/dnd-character/dnd-character.arr @@ -0,0 +1,27 @@ +use context starter2024 + +provide-types * + +#| + Replace the ... with your code to pass the tests. Good luck! +|# + +data Character: + | blank-character() + with: + method randomize-stats(self) -> Character: + ... + end + | character(strength, dexterity, constitution, intelligence, wisdom, charisma) + with: + method get-hitpoints(self) -> NumInteger: + ... + end +sharing: + method ability(self) -> NumInteger: + ... + end, + method modifier(self, value :: NumInteger) -> NumInteger: + ... + end +end diff --git a/exercises/practice/eliuds-eggs/.docs/instructions.md b/exercises/practice/eliuds-eggs/.docs/instructions.md new file mode 100644 index 00000000..b0c2df59 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.docs/instructions.md @@ -0,0 +1,8 @@ +# Instructions + +Your task is to count the number of 1 bits in the binary representation of a number. + +## Restrictions + +Keep your hands off that bit-count functionality provided by your standard library! +Solve this one yourself using other basic tools instead. diff --git a/exercises/practice/eliuds-eggs/.docs/introduction.md b/exercises/practice/eliuds-eggs/.docs/introduction.md new file mode 100644 index 00000000..2b2e5c43 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.docs/introduction.md @@ -0,0 +1,65 @@ +# Introduction + +Your friend Eliud inherited a farm from her grandma Tigist. +Her granny was an inventor and had a tendency to build things in an overly complicated manner. +The chicken coop has a digital display showing an encoded number representing the positions of all eggs that could be picked up. + +Eliud is asking you to write a program that shows the actual number of eggs in the coop. + +The position information encoding is calculated as follows: + +1. Scan the potential egg-laying spots and mark down a `1` for an existing egg or a `0` for an empty spot. +2. Convert the number from binary to decimal. +3. Show the result on the display. + +## Example 1 + +![Seven individual nest boxes arranged in a row whose first, third, fourth and seventh nests each have a single egg.](https://assets.exercism.org/images/exercises/eliuds-eggs/example-1-coop.svg) + +```text + _ _ _ _ _ _ _ +|E| |E|E| | |E| +``` + +### Resulting Binary + +![1011001](https://assets.exercism.org/images/exercises/eliuds-eggs/example-1-binary.svg) + +```text + _ _ _ _ _ _ _ +|1|0|1|1|0|0|1| +``` + +### Decimal number on the display + +89 + +### Actual eggs in the coop + +4 + +## Example 2 + +![Seven individual nest boxes arranged in a row where only the fourth nest has an egg.](https://assets.exercism.org/images/exercises/eliuds-eggs/example-2-coop.svg) + +```text + _ _ _ _ _ _ _ +| | | |E| | | | +``` + +### Resulting Binary + +![0001000](https://assets.exercism.org/images/exercises/eliuds-eggs/example-2-binary.svg) + +```text + _ _ _ _ _ _ _ +|0|0|0|1|0|0|0| +``` + +### Decimal number on the display + +8 + +### Actual eggs in the coop + +1 diff --git a/exercises/practice/eliuds-eggs/.meta/config.json b/exercises/practice/eliuds-eggs/.meta/config.json new file mode 100644 index 00000000..d82a0249 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "eliuds-eggs.arr" + ], + "test": [ + "eliuds-eggs-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Help Eliud count the number of eggs in her chicken coop by counting the number of 1 bits in a binary representation.", + "source": "Christian Willner, Eric Willigers", + "source_url": "https://forum.exercism.org/t/new-exercise-suggestion-pop-count/7632/5" +} diff --git a/exercises/practice/eliuds-eggs/.meta/example.arr b/exercises/practice/eliuds-eggs/.meta/example.arr new file mode 100644 index 00000000..df3fd98b --- /dev/null +++ b/exercises/practice/eliuds-eggs/.meta/example.arr @@ -0,0 +1,17 @@ +use context starter2024 + +provide: egg-count end + +fun egg-count(n): + fun recurse(val, eggs): + if val == 0: + eggs + else if num-is-positive(num-modulo(val, 2)): + recurse(num-truncate(val / 2), eggs + 1) + else: + recurse(num-truncate(val / 2), eggs) + end + end + + recurse(n, 0) +end diff --git a/exercises/practice/eliuds-eggs/.meta/tests.toml b/exercises/practice/eliuds-eggs/.meta/tests.toml new file mode 100644 index 00000000..e11683c2 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.meta/tests.toml @@ -0,0 +1,22 @@ +# 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. + +[559e789d-07d1-4422-9004-3b699f83bca3] +description = "0 eggs" + +[97223282-f71e-490c-92f0-b3ec9e275aba] +description = "1 egg" + +[1f8fd18f-26e9-4144-9a0e-57cdfc4f4ff5] +description = "4 eggs" + +[0c18be92-a498-4ef2-bcbb-28ac4b06cb81] +description = "13 eggs" diff --git a/exercises/practice/eliuds-eggs/eliuds-eggs-test.arr b/exercises/practice/eliuds-eggs/eliuds-eggs-test.arr new file mode 100644 index 00000000..5cd1a89c --- /dev/null +++ b/exercises/practice/eliuds-eggs/eliuds-eggs-test.arr @@ -0,0 +1,20 @@ +use context starter2024 + +include file("eliuds-eggs.arr") + +check "0 eggs": + egg-count(0) is 0 +end + +check "1 egg": + egg-count(16) is 1 +end + +check "4 eggs": + egg-count(89) is 4 +end + +check "13 eggs": + egg-count(2000000000) is 13 +end + diff --git a/exercises/practice/eliuds-eggs/eliuds-eggs.arr b/exercises/practice/eliuds-eggs/eliuds-eggs.arr new file mode 100644 index 00000000..056a5c0d --- /dev/null +++ b/exercises/practice/eliuds-eggs/eliuds-eggs.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: egg-count end + +fun egg-count(n): + raise("Please implement the egg-count function") +end diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md new file mode 100644 index 00000000..802863b5 --- /dev/null +++ b/exercises/practice/etl/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to change the data format of letters and their point values in the game. + +Currently, letters are stored in groups based on their score, in a one-to-many mapping. + +- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", +- 2 points: "D", "G", +- 3 points: "B", "C", "M", "P", +- 4 points: "F", "H", "V", "W", "Y", +- 5 points: "K", +- 8 points: "J", "X", +- 10 points: "Q", "Z", + +This needs to be changed to store each individual letter with its score in a one-to-one mapping. + +- "a" is worth 1 point. +- "b" is worth 3 points. +- "c" is worth 3 points. +- "d" is worth 2 points. +- etc. + +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. + +~~~~exercism/note +If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite. +~~~~ diff --git a/exercises/practice/etl/.docs/introduction.md b/exercises/practice/etl/.docs/introduction.md new file mode 100644 index 00000000..5be65147 --- /dev/null +++ b/exercises/practice/etl/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a company that makes an online multiplayer game called Lexiconia. + +To play the game, each player is given 13 letters, which they must rearrange to create words. +Different letters have different point values, since it's easier to create words with some letters than others. + +The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well. + +Different languages need to support different point values for letters. +The point values are determined by how often letters are used, compared to other letters in that language. + +For example, the letter 'C' is quite common in English, and is only worth 3 points. +But in Norwegian it's a very rare letter, and is worth 10 points. + +To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game. diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json new file mode 100644 index 00000000..e6ae073a --- /dev/null +++ b/exercises/practice/etl/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "meatball133" + ], + "files": { + "solution": [ + "etl.arr" + ], + "test": [ + "etl-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Change the data format for scoring a game to more easily add other languages.", + "source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://www.turing.edu/" +} diff --git a/exercises/practice/etl/.meta/example.arr b/exercises/practice/etl/.meta/example.arr new file mode 100644 index 00000000..b1055d35 --- /dev/null +++ b/exercises/practice/etl/.meta/example.arr @@ -0,0 +1,14 @@ +use context starter2024 + +provide: translate end + +include string-dict + +fun translate(legacy): + result = block: + var result = [mutable-string-dict:] + (legacy.keys().to-list()).each(lam(n): legacy.get-value(n).each(lam(letter): result.set-now(string-to-lower(letter), n) end) end) + result + end + result.freeze() +end diff --git a/exercises/practice/etl/.meta/tests.toml b/exercises/practice/etl/.meta/tests.toml new file mode 100644 index 00000000..e9371078 --- /dev/null +++ b/exercises/practice/etl/.meta/tests.toml @@ -0,0 +1,22 @@ +# 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. + +[78a7a9f9-4490-4a47-8ee9-5a38bb47d28f] +description = "single letter" + +[60dbd000-451d-44c7-bdbb-97c73ac1f497] +description = "single score with multiple letters" + +[f5c5de0c-301f-4fdd-a0e5-df97d4214f54] +description = "multiple scores with multiple letters" + +[5db8ea89-ecb4-4dcd-902f-2b418cc87b9d] +description = "multiple scores with differing numbers of letters" diff --git a/exercises/practice/etl/etl-test.arr b/exercises/practice/etl/etl-test.arr new file mode 100644 index 00000000..3971878a --- /dev/null +++ b/exercises/practice/etl/etl-test.arr @@ -0,0 +1,38 @@ +use context starter2024 + +include file("etl.arr") + +include string-dict + +check "single letter": + translate([string-dict: "1", [list: "A"]]) is [string-dict: "a", "1"] +end + +check "single score with multiple letters": + translate([string-dict: "1", [list: "A", "E", "I", "O", "U"]]) is [string-dict: "a", "1", "e", "1", "i", "1", "o", "1", "u", "1"] +end + +check "multiple scores with multiple letters": + input = [string-dict: "1", [list: "A", "E", "I", "O", "U"], "2", [list: "D", "G"]] + expected = [string-dict: "a", "1", "e", "1", "i", "1", "o", "1", "u", "1", "d", "2", "g", "2"] + translate(input) is expected +end + +check "multiple scores with differing numbers of letters": + input = [string-dict: "1", [list: "A", "E", "I", "O", "U"], + "2", [list: "D", "G"], + "3", [list: "B", "C", "M", "P"], + "4", [list: "F", "H", "V", "W", "Y"], + "5", [list: "K"], + "8", [list: "J", "X"], + "10", [list: "Q", "Z"]] + expected = [string-dict: "a", "1", "e", "1", "i", "1", "o", "1", "u", "1", + "d", "2", "g", "2", + "b", "3", "c", "3", "m", "3", "p", "3", + "f", "4", "h", "4", "v", "4", "w", "4", "y", "4", + "k", "5", + "j", "8", "x", "8", + "q", "10", "z", "10"] + translate(input) is expected +end + diff --git a/exercises/practice/etl/etl.arr b/exercises/practice/etl/etl.arr new file mode 100644 index 00000000..52921fbb --- /dev/null +++ b/exercises/practice/etl/etl.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: translate end + +fun translate(legacy): + raise("Please implement the translate function") +end diff --git a/exercises/practice/flatten-array/.docs/instructions.append.md b/exercises/practice/flatten-array/.docs/instructions.append.md new file mode 100644 index 00000000..01209f69 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/instructions.append.md @@ -0,0 +1,5 @@ +# Instructions append + +This exercise is implemented using the built-in [List] datatype, not RawArrays. + +[list]: https://pyret.org/docs/latest/lists.html diff --git a/exercises/practice/flatten-array/.docs/instructions.md b/exercises/practice/flatten-array/.docs/instructions.md new file mode 100644 index 00000000..b5b82713 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/instructions.md @@ -0,0 +1,16 @@ +# Instructions + +Take a nested array of any depth and return a fully flattened array. + +Note that some language tracks may include null-like values in the input array, and the way these values are represented varies by track. +Such values should be excluded from the flattened array. + +Additionally, the input may be of a different data type and contain different types, depending on the track. + +Check the test suite for details. + +## Example + +input: `[1, [2, 6, null], [[null, [4]], 5]]` + +output: `[1, 2, 6, 4, 5]` diff --git a/exercises/practice/flatten-array/.docs/introduction.md b/exercises/practice/flatten-array/.docs/introduction.md new file mode 100644 index 00000000..a3148574 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +A shipment of emergency supplies has arrived, but there's a problem. +To protect from damage, the items — flashlights, first-aid kits, blankets — are packed inside boxes, and some of those boxes are nested several layers deep inside other boxes! + +To be prepared for an emergency, everything must be easily accessible in one box. +Can you unpack all the supplies and place them into a single box, so they're ready when needed most? diff --git a/exercises/practice/flatten-array/.meta/config.json b/exercises/practice/flatten-array/.meta/config.json new file mode 100644 index 00000000..2da51560 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "flatten-array.arr" + ], + "test": [ + "flatten-array-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Take a nested list and return a single list with all values except nil/null.", + "source": "Interview Question", + "source_url": "https://reference.wolfram.com/language/ref/Flatten.html" +} diff --git a/exercises/practice/flatten-array/.meta/example.arr b/exercises/practice/flatten-array/.meta/example.arr new file mode 100644 index 00000000..0c36c8b4 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/example.arr @@ -0,0 +1,22 @@ +use context starter2024 + +provide: flatten end + +import lists as L + +fun flatten(lst): + lst.foldr(lam(elt, acc): + ask: + | elt == nothing then: + acc + | is-empty(elt) then: + acc + | is-link(elt) then: + result = flatten(elt) + result.append(acc) + | otherwise: + L.push(acc, elt) + end + end, + [list: ]) +end diff --git a/exercises/practice/flatten-array/.meta/tests.toml b/exercises/practice/flatten-array/.meta/tests.toml new file mode 100644 index 00000000..44acf175 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/tests.toml @@ -0,0 +1,63 @@ +# 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. + +[8c71dabd-da60-422d-a290-4a571471fb14] +description = "empty" + +[d268b919-963c-442d-9f07-82b93f1b518c] +description = "no nesting" + +[3f15bede-c856-479e-bb71-1684b20c6a30] +description = "flattens a nested array" + +[c84440cc-bb3a-48a6-862c-94cf23f2815d] +description = "flattens array with just integers present" + +[d3d99d39-6be5-44f5-a31d-6037d92ba34f] +description = "5 level nesting" + +[d572bdba-c127-43ed-bdcd-6222ac83d9f7] +description = "6 level nesting" + +[0705a8e5-dc86-4cec-8909-150c5e54fa9c] +description = "null values are omitted from the final result" + +[c6cf26de-8ccd-4410-84bd-b9efd88fd2bc] +description = "consecutive null values at the front of the list are omitted from the final result" +include = false + +[bc72da10-5f55-4ada-baf3-50e4da02ec8e] +description = "consecutive null values at the front of the array are omitted from the final result" +reimplements = "c6cf26de-8ccd-4410-84bd-b9efd88fd2bc" + +[382c5242-587e-4577-b8ce-a5fb51e385a1] +description = "consecutive null values in the middle of the list are omitted from the final result" +include = false + +[6991836d-0d9b-4703-80a0-3f1f23eb5981] +description = "consecutive null values in the middle of the array are omitted from the final result" +reimplements = "382c5242-587e-4577-b8ce-a5fb51e385a1" + +[ef1d4790-1b1e-4939-a179-51ace0829dbd] +description = "6 level nest list with null values" +include = false + +[dc90a09c-5376-449c-a7b3-c2d20d540069] +description = "6 level nested array with null values" +reimplements = "ef1d4790-1b1e-4939-a179-51ace0829dbd" + +[85721643-705a-4150-93ab-7ae398e2942d] +description = "all values in nested list are null" +include = false + +[51f5d9af-8f7f-4fb5-a156-69e8282cb275] +description = "all values in nested array are null" +reimplements = "85721643-705a-4150-93ab-7ae398e2942d" diff --git a/exercises/practice/flatten-array/flatten-array-test.arr b/exercises/practice/flatten-array/flatten-array-test.arr new file mode 100644 index 00000000..5089832a --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array-test.arr @@ -0,0 +1,158 @@ +use context starter2024 + +include file("flatten-array.arr") + +check "empty": + input = [list: ] + + expected = [list: ] + + flatten(input) is expected +end + +check "no nesting": + input = [list: 0, 1, 2] + + expected = [list: 0, 1, 2] + + flatten(input) is expected +end + +check "flattens a nested array": + input = [list: [list: ]] + + expected = [list: ] + + flatten(input) is expected +end + +check "flattens array with just integers present": + input = [list: 1, [list: 2, 3, 4, 5, 6, 7], 8] + + expected = [list: 1, 2, 3, 4, 5, 6, 7, 8] + + flatten(input) is expected +end + +check "5 level nesting": + input = [list: + 0, + 2, + [list: + [list: 2, 3], + 8, + 100, + 4, + [list: + [list: + [list: 50]]]], + -2] + + expected = [list: + 0, + 2, + 2, + 3, + 8, + 100, + 4, + 50, + -2] + + flatten(input) is expected +end + +check "6 level nesting": + input = [list: + 1, + [list: + 2, + [list: [list: 3]], + [list: + 4, + [list: [list: 5]]], + 6, + 7], + 8] + + expected = [list: + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8] + + flatten(input) is expected +end + +check "nothing values are omitted from the final result": + input = [list: 1, 2, nothing] + + expected = [list: 1, 2] + + flatten(input) is expected +end + +check "consecutive nothing values at the front of the array are omitted from the final result": + input = [list: nothing, nothing, 3] + + expected = [list: 3] + + flatten(input) is expected +end + +check "consecutive nothing values in the middle of the array are omitted from the final result": + input = [list: 1, nothing, nothing, 4] + + expected = [list: 1, 4] + + flatten(input) is expected +end + +check "6 level nest array with nothing values": + input = [list: + 0, + 2, + [list: + [list: 2, 3], + 8, + [list: [list: 100 ]], + nothing, + [list: [list: nothing]]], + -2] + + expected = [list: + 0, + 2, + 2, + 3, + 8, + 100, + -2] + + flatten(input) is expected +end + +check "all values in nested array are nothing": + input = [list: + nothing, + [list: + [list: + [list: nothing]]], + nothing, + nothing, + [list: + [list: + nothing, + nothing], + nothing], + nothing] + + expected = [list: ] + + flatten(input) is expected +end + diff --git a/exercises/practice/flatten-array/flatten-array.arr b/exercises/practice/flatten-array/flatten-array.arr new file mode 100644 index 00000000..28b41c11 --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: flatten end + +fun flatten(lst): + raise("please implement the flatten function") +end diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md new file mode 100644 index 00000000..f5b752a8 --- /dev/null +++ b/exercises/practice/grains/.docs/instructions.md @@ -0,0 +1,11 @@ +# Instructions + +Calculate the number of grains of wheat on a chessboard. + +A chessboard has 64 squares. +Square 1 has one grain, square 2 has two grains, square 3 has four grains, and so on, doubling each time. + +Write code that calculates: + +- the number of grains on a given square +- the total number of grains on the chessboard diff --git a/exercises/practice/grains/.docs/introduction.md b/exercises/practice/grains/.docs/introduction.md new file mode 100644 index 00000000..0df4f46f --- /dev/null +++ b/exercises/practice/grains/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +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 chessboard, with the number of grains doubling on each successive square. diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json new file mode 100644 index 00000000..9d35c412 --- /dev/null +++ b/exercises/practice/grains/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "grains.arr" + ], + "test": [ + "grains-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "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://web.archive.org/web/20240908084142/https://coderanch.com/wiki/718824/Grains" +} diff --git a/exercises/practice/grains/.meta/example.arr b/exercises/practice/grains/.meta/example.arr new file mode 100644 index 00000000..6bb79e14 --- /dev/null +++ b/exercises/practice/grains/.meta/example.arr @@ -0,0 +1,19 @@ +use context starter2024 + +provide: on-square, total end + + +fun on-square(n :: Number) -> Number: + ask: + | num-is-non-positive(n) then: + raise("square must be between 1 and 64") + | n > 64 then: + raise("square must be between 1 and 64") + | otherwise: + num-expt(2, (n - 1)) + end +end + +fun total() -> Number: + num-expt(2, 64) - 1 +end diff --git a/exercises/practice/grains/.meta/tests.toml b/exercises/practice/grains/.meta/tests.toml new file mode 100644 index 00000000..0d38bef7 --- /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 raises an exception" + +[61974483-eeb2-465e-be54-ca5dde366453] +description = "returns the number of grains on the square -> negative square raises an exception" + +[a95e4374-f32c-45a7-a10d-ffec475c012f] +description = "returns the number of grains on the square -> square greater than 64 raises an exception" + +[6eb07385-3659-4b45-a6be-9dc474222750] +description = "returns the total number of grains on the board" diff --git a/exercises/practice/grains/grains-test.arr b/exercises/practice/grains/grains-test.arr new file mode 100644 index 00000000..be70bb36 --- /dev/null +++ b/exercises/practice/grains/grains-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("grains.arr") + +check "returns the number of grains on the square -> grains on square 1": + on-square(1) is 1 +end + +check "returns the number of grains on the square -> grains on square 2": + on-square(2) is 2 +end + +check "returns the number of grains on the square -> grains on square 3": + on-square(3) is 4 +end + +check "returns the number of grains on the square -> grains on square 4": + on-square(4) is 8 +end + +check "returns the number of grains on the square -> grains on square 16": + on-square(16) is 32768 +end + +check "returns the number of grains on the square -> grains on square 32": + on-square(32) is 2147483648 +end + +check "returns the number of grains on the square -> grains on square 64": + on-square(64) is 9223372036854775808 +end + +check "returns the number of grains on the square -> square 0 raises an exception": + on-square(0) raises "square must be between 1 and 64" +end + +check "returns the number of grains on the square -> negative square raises an exception": + on-square(-1) raises "square must be between 1 and 64" +end + +check "returns the number of grains on the square -> square greater than 64 raises an exception": + on-square(65) raises "square must be between 1 and 64" +end + +check "returns the total number of grains on the board": + total() is 18446744073709551615 +end + diff --git a/exercises/practice/grains/grains.arr b/exercises/practice/grains/grains.arr new file mode 100644 index 00000000..017f6628 --- /dev/null +++ b/exercises/practice/grains/grains.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: on-square, total end + +fun on-square(n): + raise("Please implement the on-square function") +end + +fun total(): + raise("Please implement the total function") +end diff --git a/exercises/practice/hamming/.docs/instructions.md b/exercises/practice/hamming/.docs/instructions.md new file mode 100644 index 00000000..8f47a179 --- /dev/null +++ b/exercises/practice/hamming/.docs/instructions.md @@ -0,0 +1,16 @@ +# Instructions + +Calculate the Hamming distance between two DNA strands. + +We read DNA using the letters C, A, G and T. +Two strands might look like this: + + GAGCCTACTAACGGGAT + CATCGTAATGACGGCCT + ^ ^ ^ ^ ^ ^^ + +They have 7 differences, and therefore the Hamming distance is 7. + +## Implementation notes + +The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work. diff --git a/exercises/practice/hamming/.docs/introduction.md b/exercises/practice/hamming/.docs/introduction.md new file mode 100644 index 00000000..8419bf47 --- /dev/null +++ b/exercises/practice/hamming/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +Your body is made up of cells that contain DNA. +Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. +In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! + +When cells divide, their DNA replicates too. +Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. +If we compare two strands of DNA and count the differences between them, we can see how many mistakes occurred. +This is known as the "Hamming distance". + +The Hamming distance is useful in many areas of science, not just biology, so it's a nice phrase to be familiar with :) diff --git a/exercises/practice/hamming/.meta/config.json b/exercises/practice/hamming/.meta/config.json new file mode 100644 index 00000000..fc9cf0f5 --- /dev/null +++ b/exercises/practice/hamming/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "hamming.arr" + ], + "test": [ + "hamming-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Calculate the Hamming distance between two DNA strands.", + "source": "The Calculating Point Mutations problem at Rosalind", + "source_url": "https://rosalind.info/problems/hamm/" +} diff --git a/exercises/practice/hamming/.meta/example.arr b/exercises/practice/hamming/.meta/example.arr new file mode 100644 index 00000000..47656a3b --- /dev/null +++ b/exercises/practice/hamming/.meta/example.arr @@ -0,0 +1,29 @@ +use context starter2024 + +provide: distance end + +import lists as L + +fun distance(first-strand, second-strand): + ask: + | string-length(first-strand) <> string-length(second-strand) then: + raise("Strands must be of equal length.") + | (first-strand == "") and (second-strand <> "") then: + raise("Strands must be of equal length.") + | (first-strand <> "") and (second-strand == "") then: + raise("Strands must be of equal length.") + | otherwise: + L.fold2( + lam(acc, elt1, elt2): + ask: + | elt1 <> elt2 then: + acc + 1 + | otherwise: + acc + end + end, + 0, + string-explode(first-strand), + string-explode(second-strand)) + end +end diff --git a/exercises/practice/hamming/.meta/tests.toml b/exercises/practice/hamming/.meta/tests.toml new file mode 100644 index 00000000..5dc17ed4 --- /dev/null +++ b/exercises/practice/hamming/.meta/tests.toml @@ -0,0 +1,67 @@ +# 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. + +[f6dcb64f-03b0-4b60-81b1-3c9dbf47e887] +description = "empty strands" + +[54681314-eee2-439a-9db0-b0636c656156] +description = "single letter identical strands" + +[294479a3-a4c8-478f-8d63-6209815a827b] +description = "single letter different strands" + +[9aed5f34-5693-4344-9b31-40c692fb5592] +description = "long identical strands" + +[cd2273a5-c576-46c8-a52b-dee251c3e6e5] +description = "long different strands" + +[919f8ef0-b767-4d1b-8516-6379d07fcb28] +description = "disallow first strand longer" +include = false + +[b9228bb1-465f-4141-b40f-1f99812de5a8] +description = "disallow first strand longer" +reimplements = "919f8ef0-b767-4d1b-8516-6379d07fcb28" + +[8a2d4ed0-ead5-4fdd-924d-27c4cf56e60e] +description = "disallow second strand longer" +include = false + +[dab38838-26bb-4fff-acbe-3b0a9bfeba2d] +description = "disallow second strand longer" +reimplements = "8a2d4ed0-ead5-4fdd-924d-27c4cf56e60e" + +[5dce058b-28d4-4ca7-aa64-adfe4e17784c] +description = "disallow left empty strand" +include = false + +[db92e77e-7c72-499d-8fe6-9354d2bfd504] +description = "disallow left empty strand" +include = false +reimplements = "5dce058b-28d4-4ca7-aa64-adfe4e17784c" + +[b764d47c-83ff-4de2-ab10-6cfe4b15c0f3] +description = "disallow empty first strand" +reimplements = "db92e77e-7c72-499d-8fe6-9354d2bfd504" + +[38826d4b-16fb-4639-ac3e-ba027dec8b5f] +description = "disallow right empty strand" +include = false + +[920cd6e3-18f4-4143-b6b8-74270bb8f8a3] +description = "disallow right empty strand" +include = false +reimplements = "38826d4b-16fb-4639-ac3e-ba027dec8b5f" + +[9ab9262f-3521-4191-81f5-0ed184a5aa89] +description = "disallow empty second strand" +reimplements = "920cd6e3-18f4-4143-b6b8-74270bb8f8a3" diff --git a/exercises/practice/hamming/hamming-test.arr b/exercises/practice/hamming/hamming-test.arr new file mode 100644 index 00000000..72b730bf --- /dev/null +++ b/exercises/practice/hamming/hamming-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("hamming.arr") + +check "empty strands": + distance("", "") is 0 +end + +check "single letter identical strands": + distance("A", "A") is 0 +end + +check "single letter different strands": + distance("G", "T") is 1 +end + +check "long identical strands": + distance("GGACTGAAATCTG", "GGACTGAAATCTG") is 0 +end + +check "long different strands": + distance("GGACGGATTCTG", "AGGACGGATTCT") is 9 +end + +check "disallow first strand longer": + distance("AATG", "AAA") raises "Strands must be of equal length." +end + +check "disallow second strand longer": + distance("ATA", "AGTG") raises "Strands must be of equal length." +end + +check "disallow empty first strand": + distance("", "G") raises "Strands must be of equal length." +end + +check "disallow empty second strand": + distance("G", "") raises "Strands must be of equal length." +end + diff --git a/exercises/practice/hamming/hamming.arr b/exercises/practice/hamming/hamming.arr new file mode 100644 index 00000000..79c30a90 --- /dev/null +++ b/exercises/practice/hamming/hamming.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: distance end + +fun distance(first-strand, second-strand): + raise("Please implement the distance function") +end diff --git a/exercises/practice/hello-world/.docs/instructions.md b/exercises/practice/hello-world/.docs/instructions.md new file mode 100644 index 00000000..c9570e48 --- /dev/null +++ b/exercises/practice/hello-world/.docs/instructions.md @@ -0,0 +1,16 @@ +# Instructions + +The classical introductory exercise. +Just say "Hello, World!". + +["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment. + +The objectives are simple: + +- Modify the provided code so that it produces the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json new file mode 100644 index 00000000..d271f93c --- /dev/null +++ b/exercises/practice/hello-world/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "hello-world.arr" + ], + "test": [ + "hello-world-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Exercism's classic introductory exercise. Just say \"Hello, World!\".", + "source": "This is an exercise to introduce users to using Exercism", + "source_url": "https://en.wikipedia.org/wiki/%22Hello,_world!%22_program" +} diff --git a/exercises/practice/hello-world/.meta/example.arr b/exercises/practice/hello-world/.meta/example.arr new file mode 100644 index 00000000..49089e90 --- /dev/null +++ b/exercises/practice/hello-world/.meta/example.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: hello end + +fun hello(): + "Hello, World!" +end diff --git a/exercises/practice/hello-world/.meta/tests.toml b/exercises/practice/hello-world/.meta/tests.toml new file mode 100644 index 00000000..73466d67 --- /dev/null +++ b/exercises/practice/hello-world/.meta/tests.toml @@ -0,0 +1,13 @@ +# 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. + +[af9ffe10-dc13-42d8-a742-e7bdafac449d] +description = "Say Hi!" diff --git a/exercises/practice/hello-world/hello-world-test.arr b/exercises/practice/hello-world/hello-world-test.arr new file mode 100644 index 00000000..6dbc23ef --- /dev/null +++ b/exercises/practice/hello-world/hello-world-test.arr @@ -0,0 +1,8 @@ +use context starter2024 + +include file("hello-world.arr") + +check "Say Hi!": + hello() is "Hello, World!" +end + diff --git a/exercises/practice/hello-world/hello-world.arr b/exercises/practice/hello-world/hello-world.arr new file mode 100644 index 00000000..80fdebe0 --- /dev/null +++ b/exercises/practice/hello-world/hello-world.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: hello end + +fun hello(): + "Goodbye, Mars!" +end diff --git a/exercises/practice/high-scores/.docs/instructions.append.md b/exercises/practice/high-scores/.docs/instructions.append.md new file mode 100644 index 00000000..3dece387 --- /dev/null +++ b/exercises/practice/high-scores/.docs/instructions.append.md @@ -0,0 +1,8 @@ +# Instructions append + +Many of our lower-level Pyret exercises are solveable with only [function application expressions] in the form of `foo(1)` . This exercise however expects that [dot expressions] can be used to access data and method fields on what your function returns. This can be done with [object expressions], allowing you to access the fields of an object literal. [Data declarations] are a similar but more advanced approach that utilizes data and method fields as well. + +[function application expressions]: https://pyret.org/docs/latest/Expressions.html#%28part._s~3aapp-expr%29 +[dot expressions]: https://pyret.org/docs/latest/Expressions.html#%28part._s~3adot-expr%29 +[object expressions]: https://pyret.org/docs/latest/Expressions.html#%28elem._%28bnf-prod._%28.Pyret._obj-expr%29%29%29 +[data declarations]: https://pyret.org/docs/latest/s_declarations.html#%28part._s~3adata-decl%29 diff --git a/exercises/practice/high-scores/.docs/instructions.md b/exercises/practice/high-scores/.docs/instructions.md new file mode 100644 index 00000000..55802488 --- /dev/null +++ b/exercises/practice/high-scores/.docs/instructions.md @@ -0,0 +1,6 @@ +# Instructions + +Manage a game player's High Score list. + +Your task is to build a high-score component of the classic Frogger game, one of the highest selling and most addictive games of all time, and a classic of the arcade era. +Your task is to write methods that return the highest score from the list, the last added score and the three highest scores. diff --git a/exercises/practice/high-scores/.meta/config.json b/exercises/practice/high-scores/.meta/config.json new file mode 100644 index 00000000..006a2a92 --- /dev/null +++ b/exercises/practice/high-scores/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "high-scores.arr" + ], + "test": [ + "high-scores-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Manage a player's High Score list.", + "source": "Tribute to the eighties' arcade game Frogger" +} diff --git a/exercises/practice/high-scores/.meta/example.arr b/exercises/practice/high-scores/.meta/example.arr new file mode 100644 index 00000000..f8aac00a --- /dev/null +++ b/exercises/practice/high-scores/.meta/example.arr @@ -0,0 +1,38 @@ +provide: high-scores end + +import lists as L + +fun high-scores(scores :: List) -> Object: + { + method latest(self) -> NumInteger: + self.scores.last() + end, + method personal-best(self) -> NumInteger: + cases(List) self.scores: + | empty() => 0 + | link(f, _) => + self.scores.drop(1).foldl( + lam(elt, acc): + if elt > acc: + elt + else: + acc + end + end, + f) + end + end, + method personal-top-three(self) -> List: + len = self.scores.length() + n = + if len < 3: + len + else: + 3 + end + + L.sort(self.scores).reverse().take(n) + end, + scores: scores + } +end \ No newline at end of file diff --git a/exercises/practice/high-scores/.meta/tests.toml b/exercises/practice/high-scores/.meta/tests.toml new file mode 100644 index 00000000..55fc332e --- /dev/null +++ b/exercises/practice/high-scores/.meta/tests.toml @@ -0,0 +1,54 @@ +# 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. + +[1035eb93-2208-4c22-bab8-fef06769a73c] +description = "List of scores" + +[6aa5dbf5-78fa-4375-b22c-ffaa989732d2] +description = "Latest score" + +[b661a2e1-aebf-4f50-9139-0fb817dd12c6] +description = "Personal best" + +[3d996a97-c81c-4642-9afc-80b80dc14015] +description = "Top 3 scores -> Personal top three from a list of scores" + +[1084ecb5-3eb4-46fe-a816-e40331a4e83a] +description = "Top 3 scores -> Personal top highest to lowest" + +[e6465b6b-5a11-4936-bfe3-35241c4f4f16] +description = "Top 3 scores -> Personal top when there is a tie" + +[f73b02af-c8fd-41c9-91b9-c86eaa86bce2] +description = "Top 3 scores -> Personal top when there are less than 3" + +[16608eae-f60f-4a88-800e-aabce5df2865] +description = "Top 3 scores -> Personal top when there is only one" + +[2df075f9-fec9-4756-8f40-98c52a11504f] +description = "Top 3 scores -> Latest score after personal top scores" +include = false +comment = "Skip immutability test case" + +[809c4058-7eb1-4206-b01e-79238b9b71bc] +description = "Top 3 scores -> Scores after personal top scores" +include = false +comment = "Skip immutability test case" + +[ddb0efc0-9a86-4f82-bc30-21ae0bdc6418] +description = "Top 3 scores -> Latest score after personal best" +include = false +comment = "Skip immutability test case" + +[6a0fd2d1-4cc4-46b9-a5bb-2fb667ca2364] +description = "Top 3 scores -> Scores after personal best" +include = false +comment = "Skip immutability test case" \ No newline at end of file diff --git a/exercises/practice/high-scores/high-scores-test.arr b/exercises/practice/high-scores/high-scores-test.arr new file mode 100644 index 00000000..4138c2c8 --- /dev/null +++ b/exercises/practice/high-scores/high-scores-test.arr @@ -0,0 +1,60 @@ +use context starter2024 + +include file("high-scores.arr") + +check "List of scores": + scores = [list: 30, 50, 20, 70] + expected = [list: 30, 50, 20, 70] + + high-scores(scores).scores is expected +end + +check "Latest score": + scores = [list: 100, 0, 90, 30] + expected = 30 + + high-scores(scores).latest() is expected +end + +check "Personal best": + scores = [list: 40, 100, 70] + expected = 100 + + high-scores(scores).personal-best() is expected +end + +check "Top 3 scores -> Personal top three from a list of scores": + scores = [list: 10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70] + expected = [list: 100, 90, 70] + + high-scores(scores).personal-top-three() is expected +end + +check "Top 3 scores -> Personal top highest to lowest": + scores = [list: 20, 10, 30] + expected = [list: 30, 20, 10] + + high-scores(scores).personal-top-three() is expected +end + +check "Top 3 scores -> Personal top when there is a tie": + scores = [list: 40, 20, 40, 30] + expected = [list: 40, 40, 30] + + high-scores(scores).personal-top-three() is expected +end + +check "Top 3 scores -> Personal top when there are less than 3": + scores = [list: 30, 70] + expected = [list: 70, 30] + + high-scores(scores).personal-top-three() is expected +end + +check "Top 3 scores -> Personal top when there is only one": + scores = [list: 40] + expected = [list: 40] + + high-scores(scores).personal-top-three() is expected +end + diff --git a/exercises/practice/high-scores/high-scores.arr b/exercises/practice/high-scores/high-scores.arr new file mode 100644 index 00000000..a4031e86 --- /dev/null +++ b/exercises/practice/high-scores/high-scores.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: high-scores end + +fun high-scores(scores): + raise("Please implement the high-scores function") +end diff --git a/exercises/practice/isogram/.docs/instructions.md b/exercises/practice/isogram/.docs/instructions.md new file mode 100644 index 00000000..2e8df851 --- /dev/null +++ b/exercises/practice/isogram/.docs/instructions.md @@ -0,0 +1,14 @@ +# Instructions + +Determine if a word or phrase is an isogram. + +An isogram (also known as a "non-pattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times. + +Examples of isograms: + +- lumberjacks +- background +- downstream +- six-year-old + +The word _isograms_, however, is not an isogram, because the s repeats. diff --git a/exercises/practice/isogram/.meta/config.json b/exercises/practice/isogram/.meta/config.json new file mode 100644 index 00000000..a3f477eb --- /dev/null +++ b/exercises/practice/isogram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "isogram.arr" + ], + "test": [ + "isogram-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine if a word or phrase is an isogram.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Isogram" +} diff --git a/exercises/practice/isogram/.meta/example.arr b/exercises/practice/isogram/.meta/example.arr new file mode 100644 index 00000000..bbcd05ca --- /dev/null +++ b/exercises/practice/isogram/.meta/example.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: is-isogram end + +import sets as S + +fun is-isogram(phrase): + lowered = string-to-lower(phrase) + chars = string-to-code-points(lowered).filter(lam(c): (c >= 61) and (c <= 122) end) + chars.length() == S.list-to-list-set(chars).size() +end diff --git a/exercises/practice/isogram/.meta/tests.toml b/exercises/practice/isogram/.meta/tests.toml new file mode 100644 index 00000000..ba04c664 --- /dev/null +++ b/exercises/practice/isogram/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[a0e97d2d-669e-47c7-8134-518a1e2c4555] +description = "empty string" + +[9a001b50-f194-4143-bc29-2af5ec1ef652] +description = "isogram with only lower case characters" + +[8ddb0ca3-276e-4f8b-89da-d95d5bae78a4] +description = "word with one duplicated character" + +[6450b333-cbc2-4b24-a723-0b459b34fe18] +description = "word with one duplicated character from the end of the alphabet" + +[a15ff557-dd04-4764-99e7-02cc1a385863] +description = "longest reported english isogram" + +[f1a7f6c7-a42f-4915-91d7-35b2ea11c92e] +description = "word with duplicated character in mixed case" + +[14a4f3c1-3b47-4695-b645-53d328298942] +description = "word with duplicated character in mixed case, lowercase first" + +[423b850c-7090-4a8a-b057-97f1cadd7c42] +description = "hypothetical isogrammic word with hyphen" + +[93dbeaa0-3c5a-45c2-8b25-428b8eacd4f2] +description = "hypothetical word with duplicated character following hyphen" + +[36b30e5c-173f-49c6-a515-93a3e825553f] +description = "isogram with duplicated hyphen" + +[cdabafa0-c9f4-4c1f-b142-689c6ee17d93] +description = "made-up name that is an isogram" + +[5fc61048-d74e-48fd-bc34-abfc21552d4d] +description = "duplicated character in the middle" + +[310ac53d-8932-47bc-bbb4-b2b94f25a83e] +description = "same first and last characters" + +[0d0b8644-0a1e-4a31-a432-2b3ee270d847] +description = "word with duplicated character and with two hyphens" diff --git a/exercises/practice/isogram/isogram-test.arr b/exercises/practice/isogram/isogram-test.arr new file mode 100644 index 00000000..58ef64bb --- /dev/null +++ b/exercises/practice/isogram/isogram-test.arr @@ -0,0 +1,60 @@ +use context starter2024 + +include file("isogram.arr") + +check "empty string": + is-isogram("") is true +end + +check "isogram with only lower case characters": + is-isogram("isogram") is true +end + +check "word with one duplicated character": + is-isogram("eleven") is false +end + +check "word with one duplicated character from the end of the alphabet": + is-isogram("zzyzx") is false +end + +check "longest reported english isogram": + is-isogram("subdermatoglyphic") is true +end + +check "word with duplicated character in mixed case": + is-isogram("Alphabet") is false +end + +check "word with duplicated character in mixed case, lowercase first": + is-isogram("alphAbet") is false +end + +check "hypothetical isogrammic word with hyphen": + is-isogram("thumbscrew-japingly") is true +end + +check "hypothetical word with duplicated character following hyphen": + is-isogram("thumbscrew-jappingly") is false +end + +check "isogram with duplicated hyphen": + is-isogram("six-year-old") is true +end + +check "made-up name that is an isogram": + is-isogram("Emily Jung Schwartzkopf") is true +end + +check "duplicated character in the middle": + is-isogram("accentor") is false +end + +check "same first and last characters": + is-isogram("angola") is false +end + +check "word with duplicated character and with two hyphens": + is-isogram("up-to-date") is false +end + diff --git a/exercises/practice/isogram/isogram.arr b/exercises/practice/isogram/isogram.arr new file mode 100644 index 00000000..4819ded1 --- /dev/null +++ b/exercises/practice/isogram/isogram.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: is-isogram end + +fun is-isogram(phrase): + raise("Please implement the is-isogram function") +end diff --git a/exercises/practice/leap/.approaches/boolean-chain/content.md b/exercises/practice/leap/.approaches/boolean-chain/content.md new file mode 100644 index 00000000..648129a8 --- /dev/null +++ b/exercises/practice/leap/.approaches/boolean-chain/content.md @@ -0,0 +1,36 @@ +# Boolean chaining + +```pyret +fun leap(year): + fun year-is-divisible-by(divisor): + num-equal(num-modulo(year, divisor), 0) + end + + year-is-divisible-by(4) and (not(year-is-divisible-by(100)) or year-is-divisible-by(400)) +end +``` + +This approach uses a helper function `year-is-divisible-by` and the Boolean operators `and` and `or` to run each check (divisible by 4, not divisible by 100, and divisible by 400) in order, producing a single Boolean value at the end. +The `and` operator returns `true` when both sides are true but `false` otherwise. +The `or` operator returns `false` when both sides are false but `true` otherwise. + +| n1 | n2 | n1 OR n2 | n1 AND n2 | +| ----- | ----- | ------- | --------- | +| false | false | false | false | +| false | true | true | false | +| true | false | true | false | +| true | true | true | true | + +Both operators can short-circuit which means in some scenarios, the operators don't evaluate the expression to the right. +For `and`, if the left side produces `false`, that value is returned immediately. +For `or`, if the left side produces `true`, that value is returned immediately. + +We can now test if a year is evenly divisible by 4, 100, and 400. +All leap years are divisible by 4 but not by 100 unless they're also divisible by 100. + +| year | year % 4 == 0 | year % 100 != 0 | year % 400 == 0 | is leap year | +| ---- | ------------- | --------------- | --------------- | ------------ | +| 2020 | true | true | (not evaluated) | true | +| 2019 | false | (not evaluated) | (not evaluated) | false | +| 2000 | true | false | true | true | +| 1900 | true | false | false | false | diff --git a/exercises/practice/leap/.approaches/boolean-chain/snippet.txt b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt new file mode 100644 index 00000000..d2c45c82 --- /dev/null +++ b/exercises/practice/leap/.approaches/boolean-chain/snippet.txt @@ -0,0 +1,7 @@ +fun leap(year): + fun year-is-divisible-by(divisor): + num-equal(num-modulo(year, divisor), 0) + end + + year-is-divisible-by(4) and (not(year-is-divisible-by(100)) or year-is-divisible-by(400)) +end \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/config.json b/exercises/practice/leap/.approaches/config.json new file mode 100644 index 00000000..d1e7b01c --- /dev/null +++ b/exercises/practice/leap/.approaches/config.json @@ -0,0 +1,27 @@ +{ + "introduction": { + "authors": [ + "BNAndras" + ] + }, + "approaches": [ + { + "uuid": "2f53896f-b3e7-4c7d-be3a-a3342087826e", + "slug": "boolean-chain", + "title": "Boolean Chaining", + "blurb": "Use operators to check Boolean values in a chain", + "authors": [ + "BNAndras" + ] + }, + { + "uuid": "19b192ea-7dbe-4c22-b29c-93df1127f7e0", + "slug": "if-expressions", + "title": "If Expressions", + "blurb": "Use if expressions to conditionally check a leap year", + "authors": [ + "BNAndras" + ] + } + ] +} diff --git a/exercises/practice/leap/.approaches/if-expressions/content.md b/exercises/practice/leap/.approaches/if-expressions/content.md new file mode 100644 index 00000000..590f0d63 --- /dev/null +++ b/exercises/practice/leap/.approaches/if-expressions/content.md @@ -0,0 +1,32 @@ +# If expressions + +```pyret +fun leap(year): + if num-equal(num-modulo(year, 400), 0): + true + else if num-equal(num-modulo(year, 100), 0): + false + else if num-equal(num-modulo(year, 4), 0): + true + else: + false +end +``` + +An [if expression][if-expression] contains one or more branches with conditions that are checked sequentially and associated blocks of code. +Once a condition produces `true`, the associated block is evaluated and its contents returned. +No subsequent conditions will be tested. +One of the conditions in the if expression must produce `true` or an error is thrown. +Therefore, `else` is used to provide a branch that is evaluated when the other branches' conditions aren't satisified. + +If a year is evenly divisible by 400, the first condition `num-equal(num-modulo(year, 400), 0)` produces `true`, and the associated block is evaluated, returning `true`. + +If a year is divisible by 100, the second condition `num-equal(num-modulo(year, 100), 0)` produces `true`, and the associated block is evaluated, returning `false`. If this year had also been divisible by 400, the first condition would have been satisfied and the code would not have reached this point. + +If a year is divisible by 4, the third condition `num-equal(num-modulo(year, 4), 0)` produces `true`, and the associated block is evaluated, returning `true`. +Because of the previous conditions, this year isn't also divisible by 400 or 100. + +Finally, if no other branches' conditions were true, the block associated with `else` is evaluated, representing when a year isn't divisible by 4, 100, or 400. +Here, `false` is returned. + +[if-expression]: https://pyret.org/docs/latest/Expressions.html#%28part._s~3aif-expr%29 diff --git a/exercises/practice/leap/.approaches/if-expressions/snippet.txt b/exercises/practice/leap/.approaches/if-expressions/snippet.txt new file mode 100644 index 00000000..70612c5e --- /dev/null +++ b/exercises/practice/leap/.approaches/if-expressions/snippet.txt @@ -0,0 +1,8 @@ +fun leap(year): + if num-equal(num-modulo(year, 400), 0): + true + else if num-equal(num-modulo(year, 100), 0): + false + else if num-equal(num-modulo(year, 4), 0): + true +# clipped for brevity \ No newline at end of file diff --git a/exercises/practice/leap/.approaches/introduction.md b/exercises/practice/leap/.approaches/introduction.md new file mode 100644 index 00000000..94e978a5 --- /dev/null +++ b/exercises/practice/leap/.approaches/introduction.md @@ -0,0 +1,43 @@ +# Introduction + +There are two approaches highlighted here for his exercise. +Both involve checking if a year is evenly divisible by 4, 100, and 400 using [num-modulo][num-modulo]. + +## General guidance + +Regardless of the approach chosen, this exercise requires students to use Boolean logic to decide if a given year is a leap year. + +## Approach: Boolean chaining + +```pyret +fun leap(year): + fun year-is-divisible-by(divisor): + num-equal(num-modulo(year, divisor), 0) + end + + year-is-divisible-by(4) and (not(year-is-divisible-by(100)) or year-is-divisible-by(400)) +end +``` + +For more information, check the [Boolean chain approach][approach-boolean-chain]. + +## Approach: If expressions + +```pyret +fun leap(year): + if num-equal(num-modulo(year, 400), 0): + true + else if num-equal(num-modulo(year, 100), 0): + false + else if num-equal(num-modulo(year, 4), 0): + true + else: + false +end +``` + +For more information, check the [if expressions approach][approach-if-expressions]. + +[num-modulo]: https://pyret.org/docs/latest/numbers.html#%28part._numbers_num-modulo%29 +[approach-boolean-chain]: https://exercism.org/tracks/pyret/exercises/leap/approaches/boolean-chain +[approach-if-expressions]: https://exercism.org/tracks/pyret/exercises/leap/approaches/if-expressions \ No newline at end of file diff --git a/exercises/practice/leap/.docs/instructions.md b/exercises/practice/leap/.docs/instructions.md new file mode 100644 index 00000000..b14f8565 --- /dev/null +++ b/exercises/practice/leap/.docs/instructions.md @@ -0,0 +1,3 @@ +# Instructions + +Your task is to determine whether a given year is a leap year. diff --git a/exercises/practice/leap/.docs/introduction.md b/exercises/practice/leap/.docs/introduction.md new file mode 100644 index 00000000..4ffd2da5 --- /dev/null +++ b/exercises/practice/leap/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +A leap year (in the Gregorian calendar) occurs: + +- In every year that is evenly divisible by 4. +- Unless the year is evenly divisible by 100, in which case it's only a leap year if the year is also evenly divisible by 400. + +Some examples: + +- 1997 was not a leap year as it's not divisible by 4. +- 1900 was not a leap year as it's not divisible by 400. +- 2000 was a leap year! + +~~~~exercism/note +For a delightful, four-minute explanation of the whole phenomenon of leap years, check out [this YouTube video](https://www.youtube.com/watch?v=xX96xng7sAE). +~~~~ diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json new file mode 100644 index 00000000..828a2406 --- /dev/null +++ b/exercises/practice/leap/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "leap.arr" + ], + "test": [ + "leap-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine whether a given year is a leap year.", + "source": "CodeRanch Cattle Drive, Assignment 3", + "source_url": "https://web.archive.org/web/20240907033714/https://coderanch.com/t/718816/Leap" +} diff --git a/exercises/practice/leap/.meta/example.arr b/exercises/practice/leap/.meta/example.arr new file mode 100644 index 00000000..23965dfa --- /dev/null +++ b/exercises/practice/leap/.meta/example.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: leap end + +fun leap(year): + fun year-is-divisible-by(divisor): + num-equal(num-modulo(year, divisor), 0) + end + + year-is-divisible-by(4) and (not(year-is-divisible-by(100)) or year-is-divisible-by(400)) +end diff --git a/exercises/practice/leap/.meta/tests.toml b/exercises/practice/leap/.meta/tests.toml new file mode 100644 index 00000000..ce6ba325 --- /dev/null +++ b/exercises/practice/leap/.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. + +[6466b30d-519c-438e-935d-388224ab5223] +description = "year not divisible by 4 in common year" + +[ac227e82-ee82-4a09-9eb6-4f84331ffdb0] +description = "year divisible by 2, not divisible by 4 in common year" + +[4fe9b84c-8e65-489e-970b-856d60b8b78e] +description = "year divisible by 4, not divisible by 100 in leap year" + +[7fc6aed7-e63c-48f5-ae05-5fe182f60a5d] +description = "year divisible by 4 and 5 is still a leap year" + +[78a7848f-9667-4192-ae53-87b30c9a02dd] +description = "year divisible by 100, not divisible by 400 in common year" + +[9d70f938-537c-40a6-ba19-f50739ce8bac] +description = "year divisible by 100 but not by 3 is still not a leap year" + +[42ee56ad-d3e6-48f1-8e3f-c84078d916fc] +description = "year divisible by 400 is leap year" + +[57902c77-6fe9-40de-8302-587b5c27121e] +description = "year divisible by 400 but not by 125 is still a leap year" + +[c30331f6-f9f6-4881-ad38-8ca8c12520c1] +description = "year divisible by 200, not divisible by 400 in common year" diff --git a/exercises/practice/leap/leap-test.arr b/exercises/practice/leap/leap-test.arr new file mode 100644 index 00000000..789b3f38 --- /dev/null +++ b/exercises/practice/leap/leap-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("leap.arr") + +check "year not divisible by 4 in common year": + leap(2015) is false +end + +check "year divisible by 2, not divisible by 4 in common year": + leap(1970) is false +end + +check "year divisible by 4, not divisible by 100 in leap year": + leap(1996) is true +end + +check "year divisible by 4 and 5 is still a leap year": + leap(1960) is true +end + +check "year divisible by 100, not divisible by 400 in common year": + leap(2100) is false +end + +check "year divisible by 100 but not by 3 is still not a leap year": + leap(1900) is false +end + +check "year divisible by 400 is leap year": + leap(2000) is true +end + +check "year divisible by 400 but not by 125 is still a leap year": + leap(2400) is true +end + +check "year divisible by 200, not divisible by 400 in common year": + leap(1800) is false +end + diff --git a/exercises/practice/leap/leap.arr b/exercises/practice/leap/leap.arr new file mode 100644 index 00000000..8c87f51b --- /dev/null +++ b/exercises/practice/leap/leap.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: leap end + +fun leap(year): + raise("Please implement this function") +end diff --git a/exercises/practice/line-up/.docs/instructions.md b/exercises/practice/line-up/.docs/instructions.md new file mode 100644 index 00000000..9e686ecb --- /dev/null +++ b/exercises/practice/line-up/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Given a name and a number, your task is to produce a sentence using that name and that number as an [ordinal numeral][ordinal-numeral]. +Yaʻqūb expects to use numbers from 1 up to 999. + +Rules: + +- Numbers ending in 1 (unless ending in 11) → `"st"` +- Numbers ending in 2 (unless ending in 12) → `"nd"` +- Numbers ending in 3 (unless ending in 13) → `"rd"` +- All other numbers → `"th"` + +Examples: + +- `"Mary", 1` → `"Mary, you are the 1st customer we serve today. Thank you!"` +- `"John", 12` → `"John, you are the 12th customer we serve today. Thank you!"` +- `"Dahir", 162` → `"Dahir, you are the 162nd customer we serve today. Thank you!"` + +[ordinal-numeral]: https://en.wikipedia.org/wiki/Ordinal_numeral diff --git a/exercises/practice/line-up/.docs/introduction.md b/exercises/practice/line-up/.docs/introduction.md new file mode 100644 index 00000000..ea07268a --- /dev/null +++ b/exercises/practice/line-up/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +Your friend Yaʻqūb works the counter at a deli in town, slicing, weighing, and wrapping orders for a line of hungry customers that gets longer every day. +Waiting customers are starting to lose track of who is next, so he wants numbered tickets they can use to track the order in which they arrive. + +To make the customers feel special, he does not want the ticket to have only a number on it. +They shall get a proper English sentence with their name and number on it. diff --git a/exercises/practice/line-up/.meta/config.json b/exercises/practice/line-up/.meta/config.json new file mode 100644 index 00000000..c87d1e38 --- /dev/null +++ b/exercises/practice/line-up/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "line-up.arr" + ], + "test": [ + "line-up-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Help lining up customers at Yaʻqūb's Deli.", + "source": "mk-mxp, based on previous work from Exercism contributors codedge and neenjaw", + "source_url": "https://forum.exercism.org/t/new-exercise-ordinal-numbers/19147" +} diff --git a/exercises/practice/line-up/.meta/example.arr b/exercises/practice/line-up/.meta/example.arr new file mode 100644 index 00000000..cd9eecc4 --- /dev/null +++ b/exercises/practice/line-up/.meta/example.arr @@ -0,0 +1,29 @@ +use context starter2024 + +provide: + format-message +end + +fun format-message(name, position): + name + + ", you are the " + + num-to-string(position) + + ordinal-suffix-for(position) + + " customer we serve today. Thank you!" +end + +fun ordinal-suffix-for(n): + mod10 = num-modulo(n, 10) + mod100 = num-modulo(n, 100) + if (mod100 == 11) or (mod100 == 12) or (mod100 == 13): + "th" + else if mod10 == 1: + "st" + else if mod10 == 2: + "nd" + else if mod10 == 3: + "rd" + else: + "th" + end +end diff --git a/exercises/practice/line-up/.meta/tests.toml b/exercises/practice/line-up/.meta/tests.toml new file mode 100644 index 00000000..36fdf1d0 --- /dev/null +++ b/exercises/practice/line-up/.meta/tests.toml @@ -0,0 +1,67 @@ +# 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. + +[7760d1b8-4864-4db4-953b-0fa7c047dbc0] +description = "format smallest non-exceptional ordinal numeral 4" + +[e8b7c715-6baa-4f7b-8fb3-2fa48044ab7a] +description = "format greatest single digit non-exceptional ordinal numeral 9" + +[f370aae9-7ae7-4247-90ce-e8ff8c6934df] +description = "format non-exceptional ordinal numeral 5" + +[37f10dea-42a2-49de-bb92-0b690b677908] +description = "format non-exceptional ordinal numeral 6" + +[d8dfb9a2-3a1f-4fee-9dae-01af3600054e] +description = "format non-exceptional ordinal numeral 7" + +[505ec372-1803-42b1-9377-6934890fd055] +description = "format non-exceptional ordinal numeral 8" + +[8267072d-be1f-4f70-b34a-76b7557a47b9] +description = "format exceptional ordinal numeral 1" + +[4d8753cb-0364-4b29-84b8-4374a4fa2e3f] +description = "format exceptional ordinal numeral 2" + +[8d44c223-3a7e-4f48-a0ca-78e67bf98aa7] +description = "format exceptional ordinal numeral 3" + +[6c4f6c88-b306-4f40-bc78-97cdd583c21a] +description = "format smallest two digit non-exceptional ordinal numeral 10" + +[e257a43f-d2b1-457a-97df-25f0923fc62a] +description = "format non-exceptional ordinal numeral 11" + +[bb1db695-4d64-457f-81b8-4f5a2107e3f4] +description = "format non-exceptional ordinal numeral 12" + +[60a3187c-9403-4835-97de-4f10ebfd63e2] +description = "format non-exceptional ordinal numeral 13" + +[2bdcebc5-c029-4874-b6cc-e9bec80d603a] +description = "format exceptional ordinal numeral 21" + +[74ee2317-0295-49d2-baf0-d56bcefa14e3] +description = "format exceptional ordinal numeral 62" + +[b37c332d-7f68-40e3-8503-e43cbd67a0c4] +description = "format exceptional ordinal numeral 100" + +[0375f250-ce92-4195-9555-00e28ccc4d99] +description = "format exceptional ordinal numeral 101" + +[0d8a4974-9a8a-45a4-aca7-a9fb473c9836] +description = "format non-exceptional ordinal numeral 112" + +[06b62efe-199e-4ce7-970d-4bf73945713f] +description = "format exceptional ordinal numeral 123" diff --git a/exercises/practice/line-up/line-up-test.arr b/exercises/practice/line-up/line-up-test.arr new file mode 100644 index 00000000..1b7de933 --- /dev/null +++ b/exercises/practice/line-up/line-up-test.arr @@ -0,0 +1,80 @@ +use context starter2024 + +include file("line-up.arr") + +check "format smallest non-exceptional ordinal numeral 4": + format-message("Gianna", 4) is "Gianna, you are the 4th customer we serve today. Thank you!" +end + +check "format greatest single digit non-exceptional ordinal numeral 9": + format-message("Maarten", 9) is "Maarten, you are the 9th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 5": + format-message("Petronila", 5) is "Petronila, you are the 5th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 6": + format-message("Attakullakulla", 6) is "Attakullakulla, you are the 6th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 7": + format-message("Kate", 7) is "Kate, you are the 7th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 8": + format-message("Maximiliano", 8) is "Maximiliano, you are the 8th customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 1": + format-message("Mary", 1) is "Mary, you are the 1st customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 2": + format-message("Haruto", 2) is "Haruto, you are the 2nd customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 3": + format-message("Henriette", 3) is "Henriette, you are the 3rd customer we serve today. Thank you!" +end + +check "format smallest two digit non-exceptional ordinal numeral 10": + format-message("Alvarez", 10) is "Alvarez, you are the 10th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 11": + format-message("Jacqueline", 11) is "Jacqueline, you are the 11th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 12": + format-message("Juan", 12) is "Juan, you are the 12th customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 13": + format-message("Patricia", 13) is "Patricia, you are the 13th customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 21": + format-message("Washi", 21) is "Washi, you are the 21st customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 62": + format-message("Nayra", 62) is "Nayra, you are the 62nd customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 100": + format-message("John", 100) is "John, you are the 100th customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 101": + format-message("Zeinab", 101) is "Zeinab, you are the 101st customer we serve today. Thank you!" +end + +check "format non-exceptional ordinal numeral 112": + format-message("Knud", 112) is "Knud, you are the 112th customer we serve today. Thank you!" +end + +check "format exceptional ordinal numeral 123": + format-message("Yma", 123) is "Yma, you are the 123rd customer we serve today. Thank you!" +end + diff --git a/exercises/practice/line-up/line-up.arr b/exercises/practice/line-up/line-up.arr new file mode 100644 index 00000000..80c0dc91 --- /dev/null +++ b/exercises/practice/line-up/line-up.arr @@ -0,0 +1,9 @@ +use context starter2024 + +provide: + format-message +end + +fun format-message(person, position): + raise("please implement the format-message function") +end diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md new file mode 100644 index 00000000..ebc5dffe --- /dev/null +++ b/exercises/practice/list-ops/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Implement basic list operations. + +In functional languages list operations like `length`, `map`, and `reduce` are very common. +Implement a series of basic list operations, without using existing functions. + +The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include: + +- `append` (_given two lists, add all items in the second list to the end of the first list_); +- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_); +- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_); +- `length` (_given a list, return the total number of items within it_); +- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_); +- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_); +- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_); +- `reverse` (_given a list, return a list with all the original items, but in reversed order_). + +Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant. diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json new file mode 100644 index 00000000..2dea27b3 --- /dev/null +++ b/exercises/practice/list-ops/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "list-ops.arr" + ], + "test": [ + "list-ops-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Implement basic list operations." +} diff --git a/exercises/practice/list-ops/.meta/example.arr b/exercises/practice/list-ops/.meta/example.arr new file mode 100644 index 00000000..e2633ca3 --- /dev/null +++ b/exercises/practice/list-ops/.meta/example.arr @@ -0,0 +1,92 @@ +use context starter2024 + +provide: + my-append, + my-concatenate, + my-filter, + my-length, + my-map, + my-foldl, + my-foldr, + my-reverse +end + +fun my-append(lst1, lst2): + cases(List) lst1: + | empty => lst2 + | link(first, rest) => + [list: first] + my-append(rest, lst2) + end +end + +fun my-concatenate(lst): + my-foldl(lst, my-append, [list: ]) +end + +fun my-filter(lst, f): + cases(List) lst: + | empty => lst + | link(first, rest) => + if f(first): + my-append( + [list: first], + my-filter(rest, f)) + else: + my-filter(rest, f) + end + end +end + +fun my-length(lst): + cases(List) lst: + | empty => 0 + | link(_, rest) => 1 + my-length(rest) + end +end + +fun my-map(lst, f): + cases(List) lst: + | empty => lst + | link(first, rest) => + first + ^ f(_) + ^ link(_, empty) + ^ my-append(_, my-map(rest, f)) + end +end + + +fun my-foldl(lst, f, acc): + cases(List) lst: + | empty => acc + | link(first, rest) => + first + ^ f(acc, _) + ^ my-foldl(rest, f, _) + end +end + +fun my-foldr(lst, f, acc): + rec do-foldr = lam(items, initial): + cases(List) items: + | empty => initial + | link(first, rest) => + first + ^ f(_, initial) + ^ do-foldr(rest, _) + end + end + + reversed = my-reverse(lst) + do-foldr(reversed, acc) +end + +fun my-reverse(lst): + cases(List) lst: + | empty => lst + | link(first, rest) => + rest + ^ my-reverse(_) + ^ my-append(_, [list: first]) + end +end diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml new file mode 100644 index 00000000..08b1edc0 --- /dev/null +++ b/exercises/practice/list-ops/.meta/tests.toml @@ -0,0 +1,106 @@ +# 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. + +[485b9452-bf94-40f7-a3db-c3cf4850066a] +description = "append entries to a list and return the new list -> empty lists" + +[2c894696-b609-4569-b149-8672134d340a] +description = "append entries to a list and return the new list -> list to empty list" + +[e842efed-3bf6-4295-b371-4d67a4fdf19c] +description = "append entries to a list and return the new list -> empty list to list" + +[71dcf5eb-73ae-4a0e-b744-a52ee387922f] +description = "append entries to a list and return the new list -> non-empty lists" + +[28444355-201b-4af2-a2f6-5550227bde21] +description = "concatenate a list of lists -> empty list" + +[331451c1-9573-42a1-9869-2d06e3b389a9] +description = "concatenate a list of lists -> list of lists" + +[d6ecd72c-197f-40c3-89a4-aa1f45827e09] +description = "concatenate a list of lists -> list of nested lists" + +[0524fba8-3e0f-4531-ad2b-f7a43da86a16] +description = "filter list returning only values that satisfy the filter function -> empty list" + +[88494bd5-f520-4edb-8631-88e415b62d24] +description = "filter list returning only values that satisfy the filter function -> non-empty list" + +[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad] +description = "returns the length of a list -> empty list" + +[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e] +description = "returns the length of a list -> non-empty list" + +[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list" + +[11e71a95-e78b-4909-b8e4-60cdcaec0e91] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list" + +[613b20b7-1873-4070-a3a6-70ae5f50d7cc] +description = "folds (reduces) the given list from the left with a function -> empty list" +include = false + +[e56df3eb-9405-416a-b13a-aabb4c3b5194] +description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +include = false + +[d2cf5644-aee1-4dfc-9b88-06896676fe27] +description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +include = false + +[36549237-f765-4a4c-bfd9-5d3a8f7b07d2] +description = "folds (reduces) the given list from the left with a function -> empty list" +reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc" + +[7a626a3c-03ec-42bc-9840-53f280e13067] +description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194" + +[d7fcad99-e88e-40e1-a539-4c519681f390] +description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27" + +[aeb576b9-118e-4a57-a451-db49fac20fdc] +description = "folds (reduces) the given list from the right with a function -> empty list" +include = false + +[c4b64e58-313e-4c47-9c68-7764964efb8e] +description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +include = false + +[be396a53-c074-4db3-8dd6-f7ed003cce7c] +description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +include = false + +[17214edb-20ba-42fc-bda8-000a5ab525b0] +description = "folds (reduces) the given list from the right with a function -> empty list" +reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc" + +[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd] +description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e" + +[8066003b-f2ff-437e-9103-66e6df474844] +description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c" + +[94231515-050e-4841-943d-d4488ab4ee30] +description = "reverse the elements of the list -> empty list" + +[fcc03d1e-42e0-4712-b689-d54ad761f360] +description = "reverse the elements of the list -> non-empty list" + +[40872990-b5b8-4cb8-9085-d91fc0d05d26] +description = "reverse the elements of the list -> list of lists is not flattened" diff --git a/exercises/practice/list-ops/list-ops-test.arr b/exercises/practice/list-ops/list-ops-test.arr new file mode 100644 index 00000000..65d293d1 --- /dev/null +++ b/exercises/practice/list-ops/list-ops-test.arr @@ -0,0 +1,194 @@ +use context starter2024 + +include file("list-ops.arr") + +check "append entries to a list and return the new list -> empty lists": + list1 = [list: ] + list2 = [list: ] + expected = [list: ] + + my-append(list1, list2) is expected +end + +check "append entries to a list and return the new list -> list to empty list": + list1 = [list: ] + list2 = [list: 1, 2, 3, 4] + expected = [list: 1, 2, 3, 4] + + my-append(list1, list2) is expected +end + +check "append entries to a list and return the new list -> empty list to list": + list1 = [list: 1, 2, 3, 4] + list2 = [list: ] + expected = [list: 1, 2, 3, 4] + + my-append(list1, list2) is expected +end + +check "append entries to a list and return the new list -> non-empty lists": + list1 = [list: 1, 2] + list2 = [list: 3, 4, 5] + expected = [list: 1, 2, 3, 4, 5] + + my-append(list1, list2) is expected +end + +check "concatenate a list of lists -> empty list": + input = [list: ] + expected = [list: ] + + my-concatenate(input) is expected +end + +check "concatenate a list of lists -> list of lists": + input = [list: [list: 1, 2], [list: 3], [list: ], [list: 4, 5, 6]] + expected = [list: 1, 2, 3, 4, 5, 6] + + my-concatenate(input) is expected +end + +check "concatenate a list of lists -> list of nested lists": + input = [list: + [list: [list: 1], [list: 2]], + [list: [list: 3]], + [list: [list: ]], + [list: [list: 4, 5, 6]]] + expected = [list: + [list: 1], + [list: 2], + [list: 3], + [list:], + [list: 4, 5, 6]] + + my-concatenate(input) is expected +end + +check "filter list returning only values that satisfy the filter function -> empty list": + input = [list: ] + f = lam(x): num-modulo(x, 2) == 1 end + expected = [list: ] + + my-filter(input, f) is expected +end + +check "filter list returning only values that satisfy the filter function -> non-empty list": + input = [list: 1, 2, 3, 5] + f = lam(x): num-modulo(x, 2) == 1 end + expected = [list: 1, 3, 5] + + my-filter(input, f) is expected +end + +check "returns the length of a list -> empty list": + input = [list: ] + expected = 0 + + my-length(input) is expected +end + +check "returns the length of a list -> non-empty list": + input = [list: 1, 2, 3, 4] + expected = 4 + my-length(input) is expected +end + +check "return a list of elements whose values equal the list value transformed by the mapping function -> empty list": + input = [list: ] + f = lam(x): x + 1 end + expected = [list: ] + + my-map(input, f) is expected +end + +check "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list": + input = [list: 1, 3, 5, 7] + f = lam(x): x + 1 end + expected = [list: 2, 4, 6, 8] + + my-map(input, f) is expected +end + +check "folds (reduces) the given list from the left with a function -> empty list": + input = [list: ] + f = lam(elt, acc): elt * acc end + initial = 2 + expected = 2 + + my-foldl(input, f, initial) is expected +end + +check "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list": + input = [list: 1, 2, 3, 4] + f = lam(elt, acc): elt + acc end + initial = 5 + expected = 15 + + my-foldl(input, f, initial) is expected +end + +check "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list": + input = [list: 1, 2, 3, 4] + f = lam(elt, acc): acc / elt end + initial = 24 + expected = 64 + + my-foldl(input, f, initial) is expected +end + +check "folds (reduces) the given list from the right with a function -> empty list": + input = [list: ] + f = lam(elt, acc): elt * acc end + initial = 2 + expected = 2 + + my-foldr(input, f, initial) is expected +end + +check "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list": + input = [list: 1, 2, 3, 4] + f = lam(elt, acc): elt + acc end + initial = 5 + expected = 15 + + my-foldr(input, f, initial) is expected +end + +check "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list": + input = [list: 1, 2, 3, 4] + f = lam(elt, acc): elt / acc end + initial = 24 + expected = 9 + + my-foldr(input, f, initial) is expected +end + +check "reverse the elements of the list -> empty list": + input = [list: ] + expected = [list: ] + + my-reverse(input) is expected +end + +check "reverse the elements of the list -> non-empty list": + input = [list: 1, 3, 5, 7] + expected = [list: 7, 5, 3, 1] + + my-reverse(input) is expected +end + +check "reverse the elements of the list -> list of lists is not flattened": + input = [list: + [list: 1, 2], + [list: 3], + [list: ], + [list: 4, 5, 6]] + expected = [list: + [list: 4, 5, 6], + [list: ], + [list: 3], + [list: 1, 2]] + + my-reverse(input) is expected +end + diff --git a/exercises/practice/list-ops/list-ops.arr b/exercises/practice/list-ops/list-ops.arr new file mode 100644 index 00000000..b330748a --- /dev/null +++ b/exercises/practice/list-ops/list-ops.arr @@ -0,0 +1,44 @@ +use context starter2024 + +provide: + my-append, + my-concatenate, + my-filter, + my-length, + my-map, + my-foldl, + my-foldr, + my-reverse +end + +fun my-append(lst1, lst2): + raise("please implement the my-append function") +end + +fun my-concatenate(lst): + raise("please implement the my-concatenate function") +end + +fun my-filter(lst, f): + raise("please implement the my-filter function") +end + +fun my-length(lst): + raise("please implement the my-length function") +end + +fun my-map(lst, f): + raise("please implement the my-map function") +end + +fun my-foldl(lst, f, initial): + raise("please implement the my-foldl function") +end + +fun my-foldr(lst, f, initial): + raise("please implement the my-foldr function") +end + +fun my-reverse(lst): + raise("please implement the my-reverse function") +end diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md new file mode 100644 index 00000000..7702c6bb --- /dev/null +++ b/exercises/practice/luhn/.docs/instructions.md @@ -0,0 +1,68 @@ +# Instructions + +Determine whether a number is valid according to the [Luhn formula][luhn]. + +The number will be provided as a string. + +## Validating a number + +Strings of length 1 or less are not valid. +Spaces are allowed in the input, but they should be stripped before checking. +All other non-digit characters are disallowed. + +## Examples + +### Valid credit card number + +The number to be checked is `4539 3195 0343 6467`. + +The first step of the Luhn algorithm is to start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. + +```text +4539 3195 0343 6467 +↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these) +``` + +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: + +```text +8569 6195 0383 3437 +``` + +Finally, we sum all digits. +If the sum is evenly divisible by 10, the original number is valid. + +```text +8 + 5 + 6 + 9 + 6 + 1 + 9 + 5 + 0 + 3 + 8 + 3 + 3 + 4 + 3 + 7 = 80 +``` + +80 is evenly divisible by 10, so number `4539 3195 0343 6467` is valid! + +### Invalid Canadian SIN + +The number to be checked is `066 123 478`. + +We start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. + +```text +066 123 478 + ↑ ↑ ↑ ↑ (double these) +``` + +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: + +```text +036 226 458 +``` + +We sum the digits: + +```text +0 + 3 + 6 + 2 + 2 + 6 + 4 + 5 + 8 = 36 +``` + +36 is not evenly divisible by 10, so number `066 123 478` is not valid! + +[luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm diff --git a/exercises/practice/luhn/.docs/introduction.md b/exercises/practice/luhn/.docs/introduction.md new file mode 100644 index 00000000..dee48006 --- /dev/null +++ b/exercises/practice/luhn/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +At the Global Verification Authority, you've just been entrusted with a critical assignment. +Across the city, from online purchases to secure logins, countless operations rely on the accuracy of numerical identifiers like credit card numbers, bank account numbers, transaction codes, and tracking IDs. +The Luhn algorithm is a simple checksum formula used to help identify mistyped numbers. + +A batch of identifiers has just arrived on your desk. +All of them must pass the Luhn test to ensure they're legitimate. +If any fail, they'll be flagged as invalid, preventing mistakes such as incorrect transactions or failed account verifications. + +Can you ensure this is done right? The integrity of many services depends on you. diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json new file mode 100644 index 00000000..db2c7bd0 --- /dev/null +++ b/exercises/practice/luhn/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "luhn.arr" + ], + "test": [ + "luhn-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a number determine whether or not it is valid per the Luhn formula.", + "source": "The Luhn Algorithm on Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm" +} diff --git a/exercises/practice/luhn/.meta/example.arr b/exercises/practice/luhn/.meta/example.arr new file mode 100644 index 00000000..a5552d84 --- /dev/null +++ b/exercises/practice/luhn/.meta/example.arr @@ -0,0 +1,39 @@ +use context starter2024 + +provide: is-valid end + +import lists as L + +fun is-valid(card-number): + to-lower-bound = lam(n, b): if n > b: n - b else: n end end + + card-digits = string-replace(card-number, " ", "") + len = string-length(card-digits) + ask: + | len > 1 then: + cases (Option) string-to-number(card-digits): + | none => false + | some(digits) => + step = num-modulo(len, 2) + checksum = L.fold_n( + lam(index, acc, elt): + cases (Option) string-to-number(elt): + | none => acc + | some(digit) => + ask: + | num-modulo(index, 2) == step then: + acc + (to-lower-bound(digit * 2, 9)) + | otherwise: + acc + digit + end + end + end, + 0, + 0, + string-explode(card-digits)) + num-modulo(checksum, 10) == 0 + end + | otherwise: + false + end +end diff --git a/exercises/practice/luhn/.meta/tests.toml b/exercises/practice/luhn/.meta/tests.toml new file mode 100644 index 00000000..c0be0c4d --- /dev/null +++ b/exercises/practice/luhn/.meta/tests.toml @@ -0,0 +1,76 @@ +# 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. + +[792a7082-feb7-48c7-b88b-bbfec160865e] +description = "single digit strings can not be valid" + +[698a7924-64d4-4d89-8daa-32e1aadc271e] +description = "a single zero is invalid" + +[73c2f62b-9b10-4c9f-9a04-83cee7367965] +description = "a simple valid SIN that remains valid if reversed" + +[9369092e-b095-439f-948d-498bd076be11] +description = "a simple valid SIN that becomes invalid if reversed" + +[8f9f2350-1faf-4008-ba84-85cbb93ffeca] +description = "a valid Canadian SIN" + +[1cdcf269-6560-44fc-91f6-5819a7548737] +description = "invalid Canadian SIN" + +[656c48c1-34e8-4e60-9a5a-aad8a367810a] +description = "invalid credit card" + +[20e67fad-2121-43ed-99a8-14b5b856adb9] +description = "invalid long number with an even remainder" + +[7e7c9fc1-d994-457c-811e-d390d52fba5e] +description = "invalid long number with a remainder divisible by 5" + +[ad2a0c5f-84ed-4e5b-95da-6011d6f4f0aa] +description = "valid number with an even number of digits" + +[ef081c06-a41f-4761-8492-385e13c8202d] +description = "valid number with an odd number of spaces" + +[bef66f64-6100-4cbb-8f94-4c9713c5e5b2] +description = "valid strings with a non-digit added at the end become invalid" + +[2177e225-9ce7-40f6-b55d-fa420e62938e] +description = "valid strings with punctuation included become invalid" + +[ebf04f27-9698-45e1-9afe-7e0851d0fe8d] +description = "valid strings with symbols included become invalid" + +[08195c5e-ce7f-422c-a5eb-3e45fece68ba] +description = "single zero with space is invalid" + +[12e63a3c-f866-4a79-8c14-b359fc386091] +description = "more than a single zero is valid" + +[ab56fa80-5de8-4735-8a4a-14dae588663e] +description = "input digit 9 is correctly converted to output digit 9" + +[b9887ee8-8337-46c5-bc45-3bcab51bc36f] +description = "very long input is valid" + +[8a7c0e24-85ea-4154-9cf1-c2db90eabc08] +description = "valid luhn with an odd number of digits and non zero first digit" + +[39a06a5a-5bad-4e0f-b215-b042d46209b1] +description = "using ascii value for non-doubled non-digit isn't allowed" + +[f94cf191-a62f-4868-bc72-7253114aa157] +description = "using ascii value for doubled non-digit isn't allowed" + +[8b72ad26-c8be-49a2-b99c-bcc3bf631b33] +description = "non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed" diff --git a/exercises/practice/luhn/luhn-test.arr b/exercises/practice/luhn/luhn-test.arr new file mode 100644 index 00000000..24b273a4 --- /dev/null +++ b/exercises/practice/luhn/luhn-test.arr @@ -0,0 +1,92 @@ +use context starter2024 + +include file("luhn.arr") + +check "single digit strings can not be valid": + is-valid("1") is false +end + +check "a single zero is invalid": + is-valid("0") is false +end + +check "a simple valid SIN that remains valid if reversed": + is-valid("059") is true +end + +check "a simple valid SIN that becomes invalid if reversed": + is-valid("59") is true +end + +check "a valid Canadian SIN": + is-valid("055 444 285") is true +end + +check "invalid Canadian SIN": + is-valid("055 444 286") is false +end + +check "invalid credit card": + is-valid("8273 1232 7352 0569") is false +end + +check "invalid long number with an even remainder": + is-valid("1 2345 6789 1234 5678 9012") is false +end + +check "invalid long number with a remainder divisible by 5": + is-valid("1 2345 6789 1234 5678 9013") is false +end + +check "valid number with an even number of digits": + is-valid("095 245 88") is true +end + +check "valid number with an odd number of spaces": + is-valid("234 567 891 234") is true +end + +check "valid strings with a non-digit added at the end become invalid": + is-valid("059a") is false +end + +check "valid strings with punctuation included become invalid": + is-valid("055-444-285") is false +end + +check "valid strings with symbols included become invalid": + is-valid("055# 444$ 285") is false +end + +check "single zero with space is invalid": + is-valid(" 0") is false +end + +check "more than a single zero is valid": + is-valid("0000 0") is true +end + +check "input digit 9 is correctly converted to output digit 9": + is-valid("091") is true +end + +check "very long input is valid": + is-valid("9999999999 9999999999 9999999999 9999999999") is true +end + +check "valid luhn with an odd number of digits and non zero first digit": + is-valid("109") is true +end + +check "using ascii value for non-doubled non-digit isn't allowed": + is-valid("055b 444 285") is false +end + +check "using ascii value for doubled non-digit isn't allowed": + is-valid(":9") is false +end + +check "non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed": + is-valid("59%59") is false +end + diff --git a/exercises/practice/luhn/luhn.arr b/exercises/practice/luhn/luhn.arr new file mode 100644 index 00000000..8ac974d0 --- /dev/null +++ b/exercises/practice/luhn/luhn.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: is-valid end + +fun is-valid(card-number): + raise("Please implement the is-valid function") +end diff --git a/exercises/practice/matrix/.docs/instructions.md b/exercises/practice/matrix/.docs/instructions.md new file mode 100644 index 00000000..dadea8ac --- /dev/null +++ b/exercises/practice/matrix/.docs/instructions.md @@ -0,0 +1,38 @@ +# Instructions + +Given a string representing a matrix of numbers, return the rows and columns of that matrix. + +So given a string with embedded newlines like: + +```text +9 8 7 +5 3 2 +6 6 7 +``` + +representing this matrix: + +```text + 1 2 3 + |--------- +1 | 9 8 7 +2 | 5 3 2 +3 | 6 6 7 +``` + +your code should be able to spit out: + +- A list of the rows, reading each row left-to-right while moving top-to-bottom across the rows, +- A list of the columns, reading each column top-to-bottom while moving from left-to-right. + +The rows for our example matrix: + +- 9, 8, 7 +- 5, 3, 2 +- 6, 6, 7 + +And its columns: + +- 9, 5, 6 +- 8, 3, 6 +- 7, 2, 7 diff --git a/exercises/practice/matrix/.meta/config.json b/exercises/practice/matrix/.meta/config.json new file mode 100644 index 00000000..26ba30da --- /dev/null +++ b/exercises/practice/matrix/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "matrix.arr" + ], + "test": [ + "matrix-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a string representing a matrix of numbers, return the rows and columns of that matrix.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://www.turing.edu/" +} diff --git a/exercises/practice/matrix/.meta/example.arr b/exercises/practice/matrix/.meta/example.arr new file mode 100644 index 00000000..d17e58ba --- /dev/null +++ b/exercises/practice/matrix/.meta/example.arr @@ -0,0 +1,34 @@ +use context starter2024 + +provide: matrix end + +fun matrix(input :: String): + process-input = lam(): + to-number = lam(s :: String): + cases(Option) string-to-number(s): + | some(a) => a + | none => raise("invalid number") + end + end + + rows = string-split-all(input, "\n") + rows.map(lam(r): string-split-all(r, " ").map(to-number) end) + end + + { + method row(self, nth :: Number): self.values.get(nth - 1) end, + method column(self, nth :: Number): + n = nth - 1 + self.values.foldl(lam(elt, acc): + ask: + | elt.length() > n then: + val = elt.get(n) + acc.push(val) + | otherwise: + acc + end + end, [list: ]).reverse() + end, + values: process-input() + } +end diff --git a/exercises/practice/matrix/.meta/tests.toml b/exercises/practice/matrix/.meta/tests.toml new file mode 100644 index 00000000..90b509c4 --- /dev/null +++ b/exercises/practice/matrix/.meta/tests.toml @@ -0,0 +1,34 @@ +# 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. + +[ca733dab-9d85-4065-9ef6-a880a951dafd] +description = "extract row from one number matrix" + +[5c93ec93-80e1-4268-9fc2-63bc7d23385c] +description = "can extract row" + +[2f1aad89-ad0f-4bd2-9919-99a8bff0305a] +description = "extract row where numbers have different widths" + +[68f7f6ba-57e2-4e87-82d0-ad09889b5204] +description = "can extract row from non-square matrix with no corresponding column" + +[e8c74391-c93b-4aed-8bfe-f3c9beb89ebb] +description = "extract column from one number matrix" + +[7136bdbd-b3dc-48c4-a10c-8230976d3727] +description = "can extract column" + +[ad64f8d7-bba6-4182-8adf-0c14de3d0eca] +description = "can extract column from non-square matrix with no corresponding row" + +[9eddfa5c-8474-440e-ae0a-f018c2a0dd89] +description = "extract column where numbers have different widths" diff --git a/exercises/practice/matrix/matrix-test.arr b/exercises/practice/matrix/matrix-test.arr new file mode 100644 index 00000000..1dc342cc --- /dev/null +++ b/exercises/practice/matrix/matrix-test.arr @@ -0,0 +1,44 @@ +use context starter2024 + +include file("matrix.arr") + +check "extract row from one number matrix": + m = matrix("1") + m.row(1) is [list: 1] +end + +check "can extract row": + m = matrix("1 2\n3 4") + m.row(2) is [list: 3, 4] +end + +check "extract row where numbers have different widths": + m = matrix("1 2\n10 20") + m.row(2) is [list: 10, 20] +end + +check "can extract row from non-square matrix with no corresponding column": + m = matrix("1 2 3\n4 5 6\n7 8 9\n8 7 6") + m.row(4) is [list: 8, 7, 6] +end + +check "extract column from one number matrix": + m = matrix("1") + m.column(1) is [list: 1] +end + +check "can extract column": + m = matrix("1 2 3\n4 5 6\n7 8 9") + m.column(3) is [list: 3, 6, 9] +end + +check "can extract column from non-square matrix with no corresponding row": + m = matrix("1 2 3 4\n5 6 7 8\n9 8 7 6") + m.column(4) is [list: 4, 8, 6] +end + +check "extract column where numbers have different widths": + m = matrix("89 1903 3\n18 3 1\n9 4 800") + m.column(2) is [list: 1903, 3, 4] +end + diff --git a/exercises/practice/matrix/matrix.arr b/exercises/practice/matrix/matrix.arr new file mode 100644 index 00000000..c5e4395c --- /dev/null +++ b/exercises/practice/matrix/matrix.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: matrix end + +fun matrix(input): + raise("Please implement the matrix function") +end diff --git a/exercises/practice/nucleotide-count/.docs/instructions.md b/exercises/practice/nucleotide-count/.docs/instructions.md new file mode 100644 index 00000000..548d9ba5 --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. +All known life depends on DNA! + +> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise. + +DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. +A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! +We call the order of these nucleotides in a bit of DNA a "DNA sequence". + +We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides. +'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine. + +Given a string representing a DNA sequence, count how many of each nucleotide is present. +If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error. + +For example: + +```text +"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2 +"INVALID" -> error +``` diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json new file mode 100644 index 00000000..8e699cef --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "nucleotide-count.arr" + ], + "test": [ + "nucleotide-count-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a DNA string, compute how many times each nucleotide occurs in the string.", + "source": "The Calculating DNA Nucleotides_problem at Rosalind", + "source_url": "https://rosalind.info/problems/dna/" +} diff --git a/exercises/practice/nucleotide-count/.meta/example.arr b/exercises/practice/nucleotide-count/.meta/example.arr new file mode 100644 index 00000000..f9896421 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/example.arr @@ -0,0 +1,17 @@ +use context starter2024 + +provide: nucleotide-counts end + +include string-dict + +fun nucleotide-counts(strand): + string-explode(strand).foldl(lam(elt, acc): + ask: + | not(acc.has-key(elt)) then: + raise("Invalid nucleotide in strand") + | otherwise: + count = acc.get-value(elt) + acc.set(elt, count + 1) + end + end, [string-dict: "A", 0, "C", 0, "G", 0, "T", 0]) +end diff --git a/exercises/practice/nucleotide-count/.meta/tests.toml b/exercises/practice/nucleotide-count/.meta/tests.toml new file mode 100644 index 00000000..7c55e53f --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/tests.toml @@ -0,0 +1,25 @@ +# 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. + +[3e5c30a8-87e2-4845-a815-a49671ade970] +description = "empty strand" + +[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec] +description = "can count one nucleotide in single-character input" + +[eca0d565-ed8c-43e7-9033-6cefbf5115b5] +description = "strand with repeated nucleotide" + +[40a45eac-c83f-4740-901a-20b22d15a39f] +description = "strand with multiple nucleotides" + +[b4c47851-ee9e-4b0a-be70-a86e343bd851] +description = "strand with invalid nucleotides" diff --git a/exercises/practice/nucleotide-count/nucleotide-count-test.arr b/exercises/practice/nucleotide-count/nucleotide-count-test.arr new file mode 100644 index 00000000..d1472f2b --- /dev/null +++ b/exercises/practice/nucleotide-count/nucleotide-count-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("nucleotide-count.arr") + +include string-dict + +check "empty strand": + input = "" + expected = [string-dict: "A", 0, "C", 0, "G", 0, "T", 0] + + nucleotide-counts(input) is expected +end + +check "can count one nucleotide in single-character input": + input = "G" + expected = [string-dict: "A", 0, "C", 0, "G", 1, "T", 0] + + nucleotide-counts(input) is expected +end + +check "strand with repeated nucleotide": + input = "GGGGGGG" + expected = [string-dict: "A", 0, "C", 0, "G", 7, "T", 0] + + nucleotide-counts(input) is expected +end + +check "strand with multiple nucleotides": + input = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC" + expected = [string-dict: "A", 20, "C", 12, "G", 17, "T", 21] + + nucleotide-counts(input) is expected +end + +check "strand with invalid nucleotides": + input = "AGXXACT" + + nucleotide-counts(input) raises "Invalid nucleotide in strand" +end + diff --git a/exercises/practice/nucleotide-count/nucleotide-count.arr b/exercises/practice/nucleotide-count/nucleotide-count.arr new file mode 100644 index 00000000..d8ff371f --- /dev/null +++ b/exercises/practice/nucleotide-count/nucleotide-count.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: nucleotide-counts end + +fun nucleotide-counts(strand): + raise("Please implement the nucleotide-counts function") +end diff --git a/exercises/practice/pangram/.docs/instructions.md b/exercises/practice/pangram/.docs/instructions.md new file mode 100644 index 00000000..817c872d --- /dev/null +++ b/exercises/practice/pangram/.docs/instructions.md @@ -0,0 +1,8 @@ +# Instructions + +Your task is to figure out if a sentence is a pangram. + +A pangram is a sentence using every letter of the alphabet at least once. +It is case insensitive, so it doesn't matter if a letter is lower-case (e.g. `k`) or upper-case (e.g. `K`). + +For this exercise, a sentence is a pangram if it contains each of the 26 letters in the English alphabet. diff --git a/exercises/practice/pangram/.docs/introduction.md b/exercises/practice/pangram/.docs/introduction.md new file mode 100644 index 00000000..32b6f1fc --- /dev/null +++ b/exercises/practice/pangram/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a company that sells fonts through their website. +They'd like to show a different sentence each time someone views a font on their website. +To give a comprehensive sense of the font, the random sentences should use **all** the letters in the English alphabet. + +They're running a competition to get suggestions for sentences that they can use. +You're in charge of checking the submissions to see if they are valid. + +~~~~exercism/note +Pangram comes from Greek, παν γράμμα, pan gramma, which means "every letter". + +The best known English pangram is: + +> The quick brown fox jumps over the lazy dog. +~~~~ diff --git a/exercises/practice/pangram/.meta/config.json b/exercises/practice/pangram/.meta/config.json new file mode 100644 index 00000000..105f53c7 --- /dev/null +++ b/exercises/practice/pangram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "pangram.arr" + ], + "test": [ + "pangram-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine if a sentence is a pangram.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Pangram" +} diff --git a/exercises/practice/pangram/.meta/example.arr b/exercises/practice/pangram/.meta/example.arr new file mode 100644 index 00000000..0c19263c --- /dev/null +++ b/exercises/practice/pangram/.meta/example.arr @@ -0,0 +1,15 @@ +use context starter2024 + +provide: is-pangram end + +import lists as L +import sets as S + +LOWERCASE = string-explode("abcdefghijklmnopqrstuvwxyz") + +fun is-pangram(phrase): + letters = S.list-to-set(string-explode(string-tolower(phrase))) + L.all( + lam(char): letters.member(char) end, + LOWERCASE) +end diff --git a/exercises/practice/pangram/.meta/tests.toml b/exercises/practice/pangram/.meta/tests.toml new file mode 100644 index 00000000..10b5a335 --- /dev/null +++ b/exercises/practice/pangram/.meta/tests.toml @@ -0,0 +1,45 @@ +# 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. + +[64f61791-508e-4f5c-83ab-05de042b0149] +description = "empty sentence" + +[74858f80-4a4d-478b-8a5e-c6477e4e4e84] +description = "perfect lower case" + +[61288860-35ca-4abe-ba08-f5df76ecbdcd] +description = "only lower case" + +[6564267d-8ac5-4d29-baf2-e7d2e304a743] +description = "missing the letter 'x'" + +[c79af1be-d715-4cdb-a5f2-b2fa3e7e0de0] +description = "missing the letter 'h'" + +[d835ec38-bc8f-48e4-9e36-eb232427b1df] +description = "with underscores" + +[8cc1e080-a178-4494-b4b3-06982c9be2a8] +description = "with numbers" + +[bed96b1c-ff95-45b8-9731-fdbdcb6ede9a] +description = "missing letters replaced by numbers" + +[938bd5d8-ade5-40e2-a2d9-55a338a01030] +description = "mixed case and punctuation" + +[2577bf54-83c8-402d-a64b-a2c0f7bb213a] +description = "case insensitive" +include = false + +[7138e389-83e4-4c6e-8413-1e40a0076951] +description = "a-m and A-M are 26 different characters but not a pangram" +reimplements = "2577bf54-83c8-402d-a64b-a2c0f7bb213a" diff --git a/exercises/practice/pangram/pangram-test.arr b/exercises/practice/pangram/pangram-test.arr new file mode 100644 index 00000000..83ee0ce0 --- /dev/null +++ b/exercises/practice/pangram/pangram-test.arr @@ -0,0 +1,54 @@ +use context starter2024 + +include file("pangram.arr") + +check "empty sentence": + input = "" + is-pangram(input) is false +end + +check "perfect lower case": + input = "abcdefghijklmnopqrstuvwxyz" + is-pangram(input) is true +end + +check "only lower case": + input = "the quick brown fox jumps over the lazy dog" + is-pangram(input) is true +end + +check "missing the letter 'x'": + input = "a quick movement of the enemy will jeopardize five gunboats" + is-pangram(input) is false +end + +check "missing the letter 'h'": + input = "five boxing wizards jump quickly at it" + is-pangram(input) is false +end + +check "with underscores": + input = "the_quick_brown_fox_jumps_over_the_lazy_dog" + is-pangram(input) is true +end + +check "with numbers": + input = "the 1 quick brown fox jumps over the 2 lazy dogs" + is-pangram(input) is true +end + +check "missing letters replaced by numbers": + input = "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog" + is-pangram(input) is false +end + +check "mixed case and punctuation": + input = "\"Five quacking Zephyrs jolt my wax bed.\"" + is-pangram(input) is true +end + +check "a-m and A-M are 26 different characters but not a pangram": + input = "abcdefghijklm ABCDEFGHIJKLM" + is-pangram(input) is false +end + diff --git a/exercises/practice/pangram/pangram.arr b/exercises/practice/pangram/pangram.arr new file mode 100644 index 00000000..9c59fcbf --- /dev/null +++ b/exercises/practice/pangram/pangram.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: is-pangram end + +fun is-pangram(phrase): + raise("Please implement the is-pangram function") +end diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md new file mode 100644 index 00000000..b2bc82ca --- /dev/null +++ b/exercises/practice/perfect-numbers/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers. + +The Greek mathematician [Nicomachus][nicomachus] devised a classification scheme for positive integers, identifying each as belonging uniquely to the categories of [perfect](#perfect), [abundant](#abundant), or [deficient](#deficient) based on their [aliquot sum][aliquot-sum]. +The _aliquot sum_ is defined as the sum of the factors of a number not including the number itself. +For example, the aliquot sum of `15` is `1 + 3 + 5 = 9`. + +## Perfect + +A number is perfect when it equals its aliquot sum. +For example: + +- `6` is a perfect number because `1 + 2 + 3 = 6` +- `28` is a perfect number because `1 + 2 + 4 + 7 + 14 = 28` + +## Abundant + +A number is abundant when it is less than its aliquot sum. +For example: + +- `12` is an abundant number because `1 + 2 + 3 + 4 + 6 = 16` +- `24` is an abundant number because `1 + 2 + 3 + 4 + 6 + 8 + 12 = 36` + +## Deficient + +A number is deficient when it is greater than its aliquot sum. +For example: + +- `8` is a deficient number because `1 + 2 + 4 = 7` +- Prime numbers are deficient + +## Task + +Implement a way to determine whether a given number is [perfect](#perfect). +Depending on your language track, you may also need to implement a way to determine whether a given number is [abundant](#abundant) or [deficient](#deficient). + +[nicomachus]: https://en.wikipedia.org/wiki/Nicomachus +[aliquot-sum]: https://en.wikipedia.org/wiki/Aliquot_sum diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json new file mode 100644 index 00000000..d80b2f36 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "perfect-numbers.arr" + ], + "test": [ + "perfect-numbers-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.", + "source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.", + "source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/" +} diff --git a/exercises/practice/perfect-numbers/.meta/example.arr b/exercises/practice/perfect-numbers/.meta/example.arr new file mode 100644 index 00000000..c1ad2972 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/example.arr @@ -0,0 +1,40 @@ +use context starter2024 + +provide: classify end + +import lists as L + +fun classify(number :: NumInteger) -> String: + if number <= 0: + raise("Classification is only possible for positive integers.") + else if number == 1: + "deficient" # Can't range from 2 to 1 + else: + stop = num-truncate(num-to-rational(num-sqrt(number))) + 1 + start = if stop < 2: stop else: 2 end + + aliquot-sum = L.range(start, stop).foldl( + lam(elt, acc): + if num-modulo(number, elt) == 0: + increment = + if (elt * elt) <> number: + elt + num-truncate(number / elt) + else: + elt + end + acc + increment + else: + acc + end + end, + 1) + + if aliquot-sum < number: + "deficient" + else if aliquot-sum == number: + "perfect" + else: + "abundant" + end + end +end diff --git a/exercises/practice/perfect-numbers/.meta/tests.toml b/exercises/practice/perfect-numbers/.meta/tests.toml new file mode 100644 index 00000000..81d48408 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[163e8e86-7bfd-4ee2-bd68-d083dc3381a3] +description = "Perfect numbers -> Smallest perfect number is classified correctly" + +[169a7854-0431-4ae0-9815-c3b6d967436d] +description = "Perfect numbers -> Medium perfect number is classified correctly" + +[ee3627c4-7b36-4245-ba7c-8727d585f402] +description = "Perfect numbers -> Large perfect number is classified correctly" + +[80ef7cf8-9ea8-49b9-8b2d-d9cb3db3ed7e] +description = "Abundant numbers -> Smallest abundant number is classified correctly" + +[3e300e0d-1a12-4f11-8c48-d1027165ab60] +description = "Abundant numbers -> Medium abundant number is classified correctly" + +[ec7792e6-8786-449c-b005-ce6dd89a772b] +description = "Abundant numbers -> Large abundant number is classified correctly" + +[05f15b93-849c-45e9-9c7d-1ea131ef7d10] +description = "Abundant numbers -> Perfect square abundant number is classified correctly" + +[e610fdc7-2b6e-43c3-a51c-b70fb37413ba] +description = "Deficient numbers -> Smallest prime deficient number is classified correctly" + +[0beb7f66-753a-443f-8075-ad7fbd9018f3] +description = "Deficient numbers -> Smallest non-prime deficient number is classified correctly" + +[1c802e45-b4c6-4962-93d7-1cad245821ef] +description = "Deficient numbers -> Medium deficient number is classified correctly" + +[47dd569f-9e5a-4a11-9a47-a4e91c8c28aa] +description = "Deficient numbers -> Large deficient number is classified correctly" + +[a696dec8-6147-4d68-afad-d38de5476a56] +description = "Deficient numbers -> Edge case (no factors other than itself) is classified correctly" + +[72445cee-660c-4d75-8506-6c40089dc302] +description = "Invalid inputs -> Zero is rejected (as it is not a positive integer)" + +[2d72ce2c-6802-49ac-8ece-c790ba3dae13] +description = "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)" diff --git a/exercises/practice/perfect-numbers/perfect-numbers-test.arr b/exercises/practice/perfect-numbers/perfect-numbers-test.arr new file mode 100644 index 00000000..b969c954 --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers-test.arr @@ -0,0 +1,60 @@ +use context starter2024 + +include file("perfect-numbers.arr") + +check "Perfect numbers -> Smallest perfect number is classified correctly": + classify(6) is "perfect" +end + +check "Perfect numbers -> Medium perfect number is classified correctly": + classify(28) is "perfect" +end + +check "Perfect numbers -> Large perfect number is classified correctly": + classify(33550336) is "perfect" +end + +check "Abundant numbers -> Smallest abundant number is classified correctly": + classify(12) is "abundant" +end + +check "Abundant numbers -> Medium abundant number is classified correctly": + classify(30) is "abundant" +end + +check "Abundant numbers -> Large abundant number is classified correctly": + classify(33550335) is "abundant" +end + +check "Abundant numbers -> Perfect square abundant number is classified correctly": + classify(196) is "abundant" +end + +check "Deficient numbers -> Smallest prime deficient number is classified correctly": + classify(2) is "deficient" +end + +check "Deficient numbers -> Smallest non-prime deficient number is classified correctly": + classify(4) is "deficient" +end + +check "Deficient numbers -> Medium deficient number is classified correctly": + classify(32) is "deficient" +end + +check "Deficient numbers -> Large deficient number is classified correctly": + classify(33550337) is "deficient" +end + +check "Deficient numbers -> Edge case (no factors other than itself) is classified correctly": + classify(1) is "deficient" +end + +check "Invalid inputs -> Zero is rejected (as it is not a positive integer)": + classify(0) raises "Classification is only possible for positive integers." +end + +check "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)": + classify(-1) raises "Classification is only possible for positive integers." +end + diff --git a/exercises/practice/perfect-numbers/perfect-numbers.arr b/exercises/practice/perfect-numbers/perfect-numbers.arr new file mode 100644 index 00000000..fa7ff3ed --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: classify end + +fun classify(number): + raise("Please implement the classify function") +end diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md new file mode 100644 index 00000000..5d4d3739 --- /dev/null +++ b/exercises/practice/phone-number/.docs/instructions.md @@ -0,0 +1,34 @@ +# Instructions + +Clean up phone numbers so that they can be sent SMS messages. + +The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. +All NANP-countries share the same international country code: `1`. + +NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as _area code_, followed by a seven-digit local number. +The first three digits of the local number represent the _exchange code_, followed by the unique four-digit number which is the _subscriber number_. + +The format is usually represented as + +```text +NXX NXX-XXXX +``` + +where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9. + +Sometimes they also have the country code (represented as `1` or `+1`) prefixed. + +Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code if present. + +For example, the inputs + +- `+1 (613)-995-0253` +- `613-995-0253` +- `1 613 995 0253` +- `613.995.0253` + +should all produce the output + +`6139950253` + +**Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code. diff --git a/exercises/practice/phone-number/.docs/introduction.md b/exercises/practice/phone-number/.docs/introduction.md new file mode 100644 index 00000000..c4142c5a --- /dev/null +++ b/exercises/practice/phone-number/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +You've joined LinkLine, a leading communications company working to ensure reliable connections for everyone. +The team faces a big challenge: users submit phone numbers in all sorts of formats — dashes, spaces, dots, parentheses, and even prefixes. +Some numbers are valid, while others are impossible to use. + +Your mission is to turn this chaos into order. +You'll clean up valid numbers, formatting them appropriately for use in the system. +At the same time, you'll identify and filter out any invalid entries. + +The success of LinkLine's operations depends on your ability to separate the useful from the unusable. +Are you ready to take on the challenge and keep the connections running smoothly? diff --git a/exercises/practice/phone-number/.meta/config.json b/exercises/practice/phone-number/.meta/config.json new file mode 100644 index 00000000..501649d4 --- /dev/null +++ b/exercises/practice/phone-number/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "phone-number.arr" + ], + "test": [ + "phone-number-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://www.turing.edu/" +} diff --git a/exercises/practice/phone-number/.meta/example.arr b/exercises/practice/phone-number/.meta/example.arr new file mode 100644 index 00000000..c79cd471 --- /dev/null +++ b/exercises/practice/phone-number/.meta/example.arr @@ -0,0 +1,64 @@ +use context starter2024 + +provide: clean end + +is-alpha = lam(char :: String) -> Boolean: + cp = string-to-code-point(string-to-lower(char)) + (cp >= 97) and (cp <= 122) +end + +is-numeric = lam(char :: String) -> Boolean: + cp = string-to-code-point(string-to-lower(char)) + (cp >= 48) and (cp <= 57) +end + +WHITELIST = [list: " ", "+", "(", ")", ".", "-"] + +fun clean(phone-number): + digits = string-explode(phone-number).foldl( + lam(elt, acc): + ask: + | WHITELIST.member(elt) then: + acc + | is-alpha(elt) then: + raise("letters not permitted") + | not(is-numeric(elt)) then: + raise("punctuations not permitted") + | otherwise: + acc.push(elt) + end + end, + [list: ] + ).reverse() + + ask: + | digits.length() < 10 then: + raise("must not be fewer than 10 digits") + | digits.length() > 11 then: + raise("must not be greater than 11 digits") + | (digits.length() == 11) and (digits.get(0) <> "1") then: + raise("11 digits must start with 1") + | otherwise: + truncated = + if digits.length() == 10: + digits + else: + digits.drop(1) + end + + area-code-start = truncated.get(0) + exchange-code-start = truncated.get(3) + ask: + | area-code-start == "0" then: + raise("area code cannot start with zero") + | area-code-start == "1" then: + raise("area code cannot start with one") + | exchange-code-start == "0" then: + raise("exchange code cannot start with zero") + | exchange-code-start == "1" then: + raise("exchange code cannot start with one") + | otherwise: + truncated.join-str("") + end + end +end diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml new file mode 100644 index 00000000..24dbf07a --- /dev/null +++ b/exercises/practice/phone-number/.meta/tests.toml @@ -0,0 +1,84 @@ +# 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. + +[79666dce-e0f1-46de-95a1-563802913c35] +description = "cleans the number" + +[c360451f-549f-43e4-8aba-fdf6cb0bf83f] +description = "cleans numbers with dots" + +[08f94c34-9a37-46a2-a123-2a8e9727395d] +description = "cleans numbers with multiple spaces" + +[598d8432-0659-4019-a78b-1c6a73691d21] +description = "invalid when 9 digits" +include = false + +[2de74156-f646-42b5-8638-0ef1d8b58bc2] +description = "invalid when 9 digits" +reimplements = "598d8432-0659-4019-a78b-1c6a73691d21" + +[57061c72-07b5-431f-9766-d97da7c4399d] +description = "invalid when 11 digits does not start with a 1" + +[9962cbf3-97bb-4118-ba9b-38ff49c64430] +description = "valid when 11 digits and starting with 1" + +[fa724fbf-054c-4d91-95da-f65ab5b6dbca] +description = "valid when 11 digits and starting with 1 even with punctuation" + +[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad] +description = "invalid when more than 11 digits" +include = false + +[4a1509b7-8953-4eec-981b-c483358ff531] +description = "invalid when more than 11 digits" +reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad" + +[63f38f37-53f6-4a5f-bd86-e9b404f10a60] +description = "invalid with letters" +include = false + +[eb8a1fc0-64e5-46d3-b0c6-33184208e28a] +description = "invalid with letters" +reimplements = "63f38f37-53f6-4a5f-bd86-e9b404f10a60" + +[4bd97d90-52fd-45d3-b0db-06ab95b1244e] +description = "invalid with punctuations" +include = false + +[065f6363-8394-4759-b080-e6c8c351dd1f] +description = "invalid with punctuations" +reimplements = "4bd97d90-52fd-45d3-b0db-06ab95b1244e" + +[d77d07f8-873c-4b17-8978-5f66139bf7d7] +description = "invalid if area code starts with 0" + +[c7485cfb-1e7b-4081-8e96-8cdb3b77f15e] +description = "invalid if area code starts with 1" + +[4d622293-6976-413d-b8bf-dd8a94d4e2ac] +description = "invalid if exchange code starts with 0" + +[4cef57b4-7d8e-43aa-8328-1e1b89001262] +description = "invalid if exchange code starts with 1" + +[9925b09c-1a0d-4960-a197-5d163cbe308c] +description = "invalid if area code starts with 0 on valid 11-digit number" + +[3f809d37-40f3-44b5-ad90-535838b1a816] +description = "invalid if area code starts with 1 on valid 11-digit number" + +[e08e5532-d621-40d4-b0cc-96c159276b65] +description = "invalid if exchange code starts with 0 on valid 11-digit number" + +[57b32f3d-696a-455c-8bf1-137b6d171cdf] +description = "invalid if exchange code starts with 1 on valid 11-digit number" diff --git a/exercises/practice/phone-number/phone-number-test.arr b/exercises/practice/phone-number/phone-number-test.arr new file mode 100644 index 00000000..b058322e --- /dev/null +++ b/exercises/practice/phone-number/phone-number-test.arr @@ -0,0 +1,76 @@ +use context starter2024 + +include file("phone-number.arr") + +check "cleans the number": + clean("(223) 456-7890") is "2234567890" +end + +check "cleans numbers with dots": + clean("223.456.7890") is "2234567890" +end + +check "cleans numbers with multiple spaces": + clean("223 456 7890 ") is "2234567890" +end + +check "invalid when 9 digits": + clean("123456789") raises "must not be fewer than 10 digits" +end + +check "invalid when 11 digits does not start with a 1": + clean("22234567890") raises "11 digits must start with 1" +end + +check "valid when 11 digits and starting with 1": + clean("12234567890") is "2234567890" +end + +check "valid when 11 digits and starting with 1 even with punctuation": + clean("+1 (223) 456-7890") is "2234567890" +end + +check "invalid when more than 11 digits": + clean("321234567890") raises "must not be greater than 11 digits" +end + +check "invalid with letters": + clean("523-abc-7890") raises "letters not permitted" +end + +check "invalid with punctuations": + clean("523-@:!-7890") raises "punctuations not permitted" +end + +check "invalid if area code starts with 0": + clean("(023) 456-7890") raises "area code cannot start with zero" +end + +check "invalid if area code starts with 1": + clean("(123) 456-7890") raises "area code cannot start with one" +end + +check "invalid if exchange code starts with 0": + clean("(223) 056-7890") raises "exchange code cannot start with zero" +end + +check "invalid if exchange code starts with 1": + clean("(223) 156-7890") raises "exchange code cannot start with one" +end + +check "invalid if area code starts with 0 on valid 11-digit number": + clean("1 (023) 456-7890") raises "area code cannot start with zero" +end + +check "invalid if area code starts with 1 on valid 11-digit number": + clean("1 (123) 456-7890") raises "area code cannot start with one" +end + +check "invalid if exchange code starts with 0 on valid 11-digit number": + clean("1 (223) 056-7890") raises "exchange code cannot start with zero" +end + +check "invalid if exchange code starts with 1 on valid 11-digit number": + clean("1 (223) 156-7890") raises "exchange code cannot start with one" +end + diff --git a/exercises/practice/phone-number/phone-number.arr b/exercises/practice/phone-number/phone-number.arr new file mode 100644 index 00000000..bd9d9dd4 --- /dev/null +++ b/exercises/practice/phone-number/phone-number.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: clean end + +fun clean(phone-number): + raise("please implement the clean function") +end diff --git a/exercises/practice/protein-translation/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md new file mode 100644 index 00000000..35c953b1 --- /dev/null +++ b/exercises/practice/protein-translation/.docs/instructions.md @@ -0,0 +1,38 @@ +# Instructions + +Your job is to translate RNA sequences into proteins. + +RNA strands are made up of three-nucleotide sequences called **codons**. +Each codon translates to an **amino acid**. +When joined together, those amino acids make a protein. + +In the real world, there are 64 codons, which in turn correspond to 20 amino acids. +However, for this exercise, you’ll only use a few of the possible 64. +They are listed below: + +| Codon | Amino Acid | +| ------------------ | ------------- | +| AUG | Methionine | +| UUU, UUC | Phenylalanine | +| UUA, UUG | Leucine | +| UCU, UCC, UCA, UCG | Serine | +| UAU, UAC | Tyrosine | +| UGU, UGC | Cysteine | +| UGG | Tryptophan | +| UAA, UAG, UGA | STOP | + +For example, the RNA string “AUGUUUUCU” has three codons: “AUG”, “UUU” and “UCU”. +These map to Methionine, Phenylalanine, and Serine. + +## “STOP” Codons + +You’ll note from the table above that there are three **“STOP” codons**. +If you encounter any of these codons, ignore the rest of the sequence — the protein is complete. + +For example, “AUGUUUUCUUAAAUG” contains a STOP codon (“UAA”). +Once we reach that point, we stop processing. +We therefore only consider the part before it (i.e. “AUGUUUUCU”), not any further codons after it (i.e. “AUG”). + +Learn more about [protein translation on Wikipedia][protein-translation]. + +[protein-translation]: https://en.wikipedia.org/wiki/Translation_(biology) diff --git a/exercises/practice/protein-translation/.meta/config.json b/exercises/practice/protein-translation/.meta/config.json new file mode 100644 index 00000000..34bb7fd2 --- /dev/null +++ b/exercises/practice/protein-translation/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "protein-translation.arr" + ], + "test": [ + "protein-translation-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Translate RNA sequences into proteins.", + "source": "Tyler Long" +} diff --git a/exercises/practice/protein-translation/.meta/example.arr b/exercises/practice/protein-translation/.meta/example.arr new file mode 100644 index 00000000..443820a4 --- /dev/null +++ b/exercises/practice/protein-translation/.meta/example.arr @@ -0,0 +1,55 @@ +use context starter2024 + +provide: proteins end + +include string-dict +import lists as L + +codon_mappings = [mutable-string-dict: + "AUG", "Methionine", + "UUU", "Phenylalanine", + "UUC", "Phenylalanine", + "UUA", "Leucine", + "UUG", "Leucine", + "UCU", "Serine", + "UCC", "Serine", + "UCA", "Serine", + "UCG", "Serine", + "UAU", "Tyrosine", + "UAC", "Tyrosine", + "UGU", "Cysteine", + "UGC", "Cysteine", + "UGG", "Tryptophan", + "UAA", "STOP", + "UAG", "STOP", + "UGA", "STOP"] + +fun proteins(strand): + proteins-recursive(string-explode(strand), [list: ]) +end + +fun proteins-recursive(current, acc): + ask: + | current.length() == 0 then: acc + | current.length() < 3 then: raise("Invalid codon") + | otherwise: + block: + codon = current.take(3).join-str("") + protein = translate-codon(codon) + ask: + | protein == "STOP" then: acc + | otherwise: + rest = split-at(3, current).suffix + L.append([list: protein], proteins-recursive(rest, acc)) + end + end + end +end + + +fun translate-codon(codon): + cases(Option) codon_mappings.get-now(codon): + | some(a) => a + | none => raise("Invalid codon") + end +end diff --git a/exercises/practice/protein-translation/.meta/tests.toml b/exercises/practice/protein-translation/.meta/tests.toml new file mode 100644 index 00000000..b465aed2 --- /dev/null +++ b/exercises/practice/protein-translation/.meta/tests.toml @@ -0,0 +1,104 @@ +# 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. + +[2c44f7bf-ba20-43f7-a3bf-f2219c0c3f98] +description = "Empty RNA sequence results in no proteins" + +[96d3d44f-34a2-4db4-84cd-fff523e069be] +description = "Methionine RNA sequence" + +[1b4c56d8-d69f-44eb-be0e-7b17546143d9] +description = "Phenylalanine RNA sequence 1" + +[81b53646-bd57-4732-b2cb-6b1880e36d11] +description = "Phenylalanine RNA sequence 2" + +[42f69d4f-19d2-4d2c-a8b0-f0ae9ee1b6b4] +description = "Leucine RNA sequence 1" + +[ac5edadd-08ed-40a3-b2b9-d82bb50424c4] +description = "Leucine RNA sequence 2" + +[8bc36e22-f984-44c3-9f6b-ee5d4e73f120] +description = "Serine RNA sequence 1" + +[5c3fa5da-4268-44e5-9f4b-f016ccf90131] +description = "Serine RNA sequence 2" + +[00579891-b594-42b4-96dc-7ff8bf519606] +description = "Serine RNA sequence 3" + +[08c61c3b-fa34-4950-8c4a-133945570ef6] +description = "Serine RNA sequence 4" + +[54e1e7d8-63c0-456d-91d2-062c72f8eef5] +description = "Tyrosine RNA sequence 1" + +[47bcfba2-9d72-46ad-bbce-22f7666b7eb1] +description = "Tyrosine RNA sequence 2" + +[3a691829-fe72-43a7-8c8e-1bd083163f72] +description = "Cysteine RNA sequence 1" + +[1b6f8a26-ca2f-43b8-8262-3ee446021767] +description = "Cysteine RNA sequence 2" + +[1e91c1eb-02c0-48a0-9e35-168ad0cb5f39] +description = "Tryptophan RNA sequence" + +[e547af0b-aeab-49c7-9f13-801773a73557] +description = "STOP codon RNA sequence 1" + +[67640947-ff02-4f23-a2ef-816f8a2ba72e] +description = "STOP codon RNA sequence 2" + +[9c2ad527-ebc9-4ace-808b-2b6447cb54cb] +description = "STOP codon RNA sequence 3" + +[f4d9d8ee-00a8-47bf-a1e3-1641d4428e54] +description = "Sequence of two protein codons translates into proteins" + +[dd22eef3-b4f1-4ad6-bb0b-27093c090a9d] +description = "Sequence of two different protein codons translates into proteins" + +[d0f295df-fb70-425c-946c-ec2ec185388e] +description = "Translate RNA strand into correct protein list" + +[e30e8505-97ec-4e5f-a73e-5726a1faa1f4] +description = "Translation stops if STOP codon at beginning of sequence" + +[5358a20b-6f4c-4893-bce4-f929001710f3] +description = "Translation stops if STOP codon at end of two-codon sequence" + +[ba16703a-1a55-482f-bb07-b21eef5093a3] +description = "Translation stops if STOP codon at end of three-codon sequence" + +[4089bb5a-d5b4-4e71-b79e-b8d1f14a2911] +description = "Translation stops if STOP codon in middle of three-codon sequence" + +[2c2a2a60-401f-4a80-b977-e0715b23b93d] +description = "Translation stops if STOP codon in middle of six-codon sequence" + +[f6f92714-769f-4187-9524-e353e8a41a80] +description = "Sequence of two non-STOP codons does not translate to a STOP codon" + +[1e75ea2a-f907-4994-ae5c-118632a1cb0f] +description = "Non-existing codon can't translate" + +[9eac93f3-627a-4c90-8653-6d0a0595bc6f] +description = "Unknown amino acids, not part of a codon, can't translate" +reimplements = "1e75ea2a-f907-4994-ae5c-118632a1cb0f" + +[9d73899f-e68e-4291-b1e2-7bf87c00f024] +description = "Incomplete RNA sequence can't translate" + +[43945cf7-9968-402d-ab9f-b8a28750b050] +description = "Incomplete RNA sequence can translate if valid until a STOP codon" diff --git a/exercises/practice/protein-translation/protein-translation-test.arr b/exercises/practice/protein-translation/protein-translation-test.arr new file mode 100644 index 00000000..7a11dd07 --- /dev/null +++ b/exercises/practice/protein-translation/protein-translation-test.arr @@ -0,0 +1,124 @@ +use context starter2024 + +include file("protein-translation.arr") + +check "Empty RNA sequence results in no proteins": + proteins("") is [list: ] +end + +check "Methionine RNA sequence": + proteins("AUG") is [list: "Methionine"] +end + +check "Phenylalanine RNA sequence 1": + proteins("UUU") is [list: "Phenylalanine"] +end + +check "Phenylalanine RNA sequence 2": + proteins("UUC") is [list: "Phenylalanine"] +end + +check "Leucine RNA sequence 1": + proteins("UUA") is [list: "Leucine"] +end + +check "Leucine RNA sequence 2": + proteins("UUG") is [list: "Leucine"] +end + +check "Serine RNA sequence 1": + proteins("UCU") is [list: "Serine"] +end + +check "Serine RNA sequence 2": + proteins("UCC") is [list: "Serine"] +end + +check "Serine RNA sequence 3": + proteins("UCA") is [list: "Serine"] +end + +check "Serine RNA sequence 4": + proteins("UCG") is [list: "Serine"] +end + +check "Tyrosine RNA sequence 1": + proteins("UAU") is [list: "Tyrosine"] +end + +check "Tyrosine RNA sequence 2": + proteins("UAC") is [list: "Tyrosine"] +end + +check "Cysteine RNA sequence 1": + proteins("UGU") is [list: "Cysteine"] +end + +check "Cysteine RNA sequence 2": + proteins("UGC") is [list: "Cysteine"] +end + +check "Tryptophan RNA sequence": + proteins("UGG") is [list: "Tryptophan"] +end + +check "STOP codon RNA sequence 1": + proteins("UAA") is [list: ] +end + +check "STOP codon RNA sequence 2": + proteins("UAG") is [list: ] +end + +check "STOP codon RNA sequence 3": + proteins("UGA") is [list: ] +end + +check "Sequence of two protein codons translates into proteins": + proteins("UUUUUU") is [list: "Phenylalanine", "Phenylalanine"] +end + +check "Sequence of two different protein codons translates into proteins": + proteins("UUAUUG") is [list: "Leucine", "Leucine"] +end + +check "Translate RNA strand into correct protein list": + proteins("AUGUUUUGG") is [list: "Methionine", "Phenylalanine", "Tryptophan"] +end + +check "Translation stops if STOP codon at beginning of sequence": + proteins("UAGUGG") is [list: ] +end + +check "Translation stops if STOP codon at end of two-codon sequence": + proteins("UGGUAG") is [list: "Tryptophan"] +end + +check "Translation stops if STOP codon at end of three-codon sequence": + proteins("AUGUUUUAA") is [list: "Methionine", "Phenylalanine"] +end + +check "Translation stops if STOP codon in middle of three-codon sequence": + proteins("UGGUAGUGG") is [list: "Tryptophan"] +end + +check "Translation stops if STOP codon in middle of six-codon sequence": + proteins("UGGUGUUAUUAAUGGUUU") is [list: "Tryptophan", "Cysteine", "Tyrosine"] +end + +check "Sequence of two non-STOP codons does not translate to a STOP codon": + proteins("AUGAUG") is [list: "Methionine", "Methionine"] +end + +check "Unknown amino acids, not part of a codon, can't translate": + proteins("XYZ") raises "Invalid codon" +end + +check "Incomplete RNA sequence can't translate": + proteins("AUGU") raises "Invalid codon" +end + +check "Incomplete RNA sequence can translate if valid until a STOP codon": + proteins("UUCUUCUAAUGGU") is [list: "Phenylalanine", "Phenylalanine"] +end + diff --git a/exercises/practice/protein-translation/protein-translation.arr b/exercises/practice/protein-translation/protein-translation.arr new file mode 100644 index 00000000..443820a4 --- /dev/null +++ b/exercises/practice/protein-translation/protein-translation.arr @@ -0,0 +1,55 @@ +use context starter2024 + +provide: proteins end + +include string-dict +import lists as L + +codon_mappings = [mutable-string-dict: + "AUG", "Methionine", + "UUU", "Phenylalanine", + "UUC", "Phenylalanine", + "UUA", "Leucine", + "UUG", "Leucine", + "UCU", "Serine", + "UCC", "Serine", + "UCA", "Serine", + "UCG", "Serine", + "UAU", "Tyrosine", + "UAC", "Tyrosine", + "UGU", "Cysteine", + "UGC", "Cysteine", + "UGG", "Tryptophan", + "UAA", "STOP", + "UAG", "STOP", + "UGA", "STOP"] + +fun proteins(strand): + proteins-recursive(string-explode(strand), [list: ]) +end + +fun proteins-recursive(current, acc): + ask: + | current.length() == 0 then: acc + | current.length() < 3 then: raise("Invalid codon") + | otherwise: + block: + codon = current.take(3).join-str("") + protein = translate-codon(codon) + ask: + | protein == "STOP" then: acc + | otherwise: + rest = split-at(3, current).suffix + L.append([list: protein], proteins-recursive(rest, acc)) + end + end + end +end + + +fun translate-codon(codon): + cases(Option) codon_mappings.get-now(codon): + | some(a) => a + | none => raise("Invalid codon") + end +end diff --git a/exercises/practice/proverb/.docs/instructions.md b/exercises/practice/proverb/.docs/instructions.md new file mode 100644 index 00000000..f6fb8593 --- /dev/null +++ b/exercises/practice/proverb/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +For want of a horseshoe nail, a kingdom was lost, or so the saying goes. + +Given a list of inputs, generate the relevant proverb. +For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme: + +```text +For want of a nail the shoe was lost. +For want of a shoe the horse was lost. +For want of a horse the rider was lost. +For want of a rider the message was lost. +For want of a message the battle was lost. +For want of a battle the kingdom was lost. +And all for the want of a nail. +``` + +Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. +No line of the output text should be a static, unchanging string; all should vary according to the input given. diff --git a/exercises/practice/proverb/.meta/config.json b/exercises/practice/proverb/.meta/config.json new file mode 100644 index 00000000..2251c47e --- /dev/null +++ b/exercises/practice/proverb/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "proverb.arr" + ], + "test": [ + "proverb-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "For want of a horseshoe nail, a kingdom was lost, or so the saying goes. Output the full text of this proverbial rhyme.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/For_Want_of_a_Nail" +} diff --git a/exercises/practice/proverb/.meta/example.arr b/exercises/practice/proverb/.meta/example.arr new file mode 100644 index 00000000..e5b6b528 --- /dev/null +++ b/exercises/practice/proverb/.meta/example.arr @@ -0,0 +1,18 @@ +provide: recite end + +import lists as L + +fun recite(items): + cases (List) items: + | empty => items + | link(f, r) => + L.range(0, L.length(items) - 1) + ^ map( + lam(n): + 'For want of a ' + items.get(n) + ' the ' + items.get(n + 1) + ' was lost.' + end, _) + ^ _.reverse() + ^ _.push('And all for the want of a ' + f + '.') + ^ _.reverse() + end +end diff --git a/exercises/practice/proverb/.meta/tests.toml b/exercises/practice/proverb/.meta/tests.toml new file mode 100644 index 00000000..dc92a0c9 --- /dev/null +++ b/exercises/practice/proverb/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[e974b73e-7851-484f-8d6d-92e07fe742fc] +description = "zero pieces" + +[2fcd5f5e-8b82-4e74-b51d-df28a5e0faa4] +description = "one piece" + +[d9d0a8a1-d933-46e2-aa94-eecf679f4b0e] +description = "two pieces" + +[c95ef757-5e94-4f0d-a6cb-d2083f5e5a83] +description = "three pieces" + +[433fb91c-35a2-4d41-aeab-4de1e82b2126] +description = "full proverb" + +[c1eefa5a-e8d9-41c7-91d4-99fab6d6b9f7] +description = "four pieces modernized" diff --git a/exercises/practice/proverb/proverb-test.arr b/exercises/practice/proverb/proverb-test.arr new file mode 100644 index 00000000..eb9a3839 --- /dev/null +++ b/exercises/practice/proverb/proverb-test.arr @@ -0,0 +1,55 @@ +use context starter2024 + +include file("proverb.arr") + +check "zero pieces": + recite([list: ]) is [list: ] +end + +check "one piece": + input = [list: "nail"] + expected = [list: "And all for the want of a nail."] + + recite(input) is expected +end + +check "two pieces": + input = [list: "nail", "shoe"] + expected = [list: "For want of a nail the shoe was lost.", + "And all for the want of a nail."] + + recite(input) is expected +end + +check "three pieces": + input = [list: "nail", "shoe", "horse"] + expected = [list: "For want of a nail the shoe was lost.", + "For want of a shoe the horse was lost.", + "And all for the want of a nail."] + + recite(input) is expected +end + +check "full proverb": + input = [list: "nail", "shoe", "horse", "rider", "message", "battle", "kingdom"] + expected = [list: "For want of a nail the shoe was lost.", + "For want of a shoe the horse was lost.", + "For want of a horse the rider was lost.", + "For want of a rider the message was lost.", + "For want of a message the battle was lost.", + "For want of a battle the kingdom was lost.", + "And all for the want of a nail."] + + recite(input) is expected +end + +check "four pieces modernized": + input = [list: "pin", "gun", "soldier", "battle"] + expected = [list: "For want of a pin the gun was lost.", + "For want of a gun the soldier was lost.", + "For want of a soldier the battle was lost.", + "And all for the want of a pin."] + + recite(input) is expected +end + diff --git a/exercises/practice/proverb/proverb.arr b/exercises/practice/proverb/proverb.arr new file mode 100644 index 00000000..54913a75 --- /dev/null +++ b/exercises/practice/proverb/proverb.arr @@ -0,0 +1,5 @@ +provide: recite end + +fun recite(items): + raise("please implement the recite function.") +end \ No newline at end of file diff --git a/exercises/practice/queen-attack/.docs/instructions.md b/exercises/practice/queen-attack/.docs/instructions.md new file mode 100644 index 00000000..97f22a0a --- /dev/null +++ b/exercises/practice/queen-attack/.docs/instructions.md @@ -0,0 +1,21 @@ +# Instructions + +Given the position of two queens on a chess board, indicate whether or not they are positioned so that they can attack each other. + +In the game of chess, a queen can attack pieces which are on the same row, column, or diagonal. + +A chessboard can be represented by an 8 by 8 array. + +So if you are told the white queen is at `c5` (zero-indexed at column 2, row 3) and the black queen at `f2` (zero-indexed at column 5, row 6), then you know that the set-up is like so: + +![A chess board with two queens. Arrows emanating from the queen at c5 indicate possible directions of capture along file, rank and diagonal.](https://assets.exercism.org/images/exercises/queen-attack/queen-capture.svg) + +You are also able to answer whether the queens can attack each other. +In this case, that answer would be yes, they can, because both pieces share a diagonal. + +## Credit + +The chessboard image was made by [habere-et-dispertire][habere-et-dispertire] using LaTeX and the [chessboard package][chessboard-package] by Ulrike Fischer. + +[habere-et-dispertire]: https://exercism.org/profiles/habere-et-dispertire +[chessboard-package]: https://github.com/u-fischer/chessboard diff --git a/exercises/practice/queen-attack/.meta/config.json b/exercises/practice/queen-attack/.meta/config.json new file mode 100644 index 00000000..2f41783a --- /dev/null +++ b/exercises/practice/queen-attack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "queen-attack.arr" + ], + "test": [ + "queen-attack-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given the position of two queens on a chess board, indicate whether or not they are positioned so that they can attack each other.", + "source": "J Dalbey's Programming Practice problems", + "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html" +} diff --git a/exercises/practice/queen-attack/.meta/example.arr b/exercises/practice/queen-attack/.meta/example.arr new file mode 100644 index 00000000..090e0255 --- /dev/null +++ b/exercises/practice/queen-attack/.meta/example.arr @@ -0,0 +1,20 @@ +use context starter2024 + +provide-types * + +fun is-on-board(n :: NumInteger) -> Boolean: + (n >= 0) and (n <= 7) +end + +data Queen: + | queen(row :: NumInteger%(is-on-board), column :: NumInteger%(is-on-board)) + with: + method can-attack(self, other :: Queen) -> Boolean: + (self.row == other.row) or + (self.column == other.column) or + ( + num-abs(other.column - self.column) + == num-abs(other.row - self.row) + ) + end +end diff --git a/exercises/practice/queen-attack/.meta/tests.toml b/exercises/practice/queen-attack/.meta/tests.toml new file mode 100644 index 00000000..e0624123 --- /dev/null +++ b/exercises/practice/queen-attack/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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. + +[3ac4f735-d36c-44c4-a3e2-316f79704203] +description = "Test creation of Queens with valid and invalid positions -> queen with a valid position" + +[4e812d5d-b974-4e38-9a6b-8e0492bfa7be] +description = "Test creation of Queens with valid and invalid positions -> queen must have positive row" + +[f07b7536-b66b-4f08-beb9-4d70d891d5c8] +description = "Test creation of Queens with valid and invalid positions -> queen must have row on board" + +[15a10794-36d9-4907-ae6b-e5a0d4c54ebe] +description = "Test creation of Queens with valid and invalid positions -> queen must have positive column" + +[6907762d-0e8a-4c38-87fb-12f2f65f0ce4] +description = "Test creation of Queens with valid and invalid positions -> queen must have column on board" + +[33ae4113-d237-42ee-bac1-e1e699c0c007] +description = "Test the ability of one queen to attack another -> cannot attack" + +[eaa65540-ea7c-4152-8c21-003c7a68c914] +description = "Test the ability of one queen to attack another -> can attack on same row" + +[bae6f609-2c0e-4154-af71-af82b7c31cea] +description = "Test the ability of one queen to attack another -> can attack on same column" + +[0e1b4139-b90d-4562-bd58-dfa04f1746c7] +description = "Test the ability of one queen to attack another -> can attack on first diagonal" + +[ff9b7ed4-e4b6-401b-8d16-bc894d6d3dcd] +description = "Test the ability of one queen to attack another -> can attack on second diagonal" + +[0a71e605-6e28-4cc2-aa47-d20a2e71037a] +description = "Test the ability of one queen to attack another -> can attack on third diagonal" + +[0790b588-ae73-4f1f-a968-dd0b34f45f86] +description = "Test the ability of one queen to attack another -> can attack on fourth diagonal" + +[543f8fd4-2597-4aad-8d77-cbdab63619f8] +description = "Test the ability of one queen to attack another -> cannot attack if falling diagonals are only the same when reflected across the longest falling diagonal" diff --git a/exercises/practice/queen-attack/queen-attack-test.arr b/exercises/practice/queen-attack/queen-attack-test.arr new file mode 100644 index 00000000..4d77d960 --- /dev/null +++ b/exercises/practice/queen-attack/queen-attack-test.arr @@ -0,0 +1,56 @@ +use context starter2024 + +include file("queen-attack.arr") + +check "Test creation of Queens with valid and invalid positions -> queen with a valid position": + queen(2, 2) does-not-raise +end + +check "Test creation of Queens with valid and invalid positions -> queen must have positive row": + queen(-2, 2) raises "" # matches any exception message +end + +check "Test creation of Queens with valid and invalid positions -> queen must have row on board": + queen(8, 4) raises "" # matches any exception message +end + +check "Test creation of Queens with valid and invalid positions -> queen must have positive column": + queen(2, -2) raises "" # matches any exception message +end + +check "Test creation of Queens with valid and invalid positions -> queen must have column on board": + queen(4, 8) raises "" # matches any exception message +end + +check "Test the ability of one queen to attack another -> cannot attack": + queen(2, 4).can-attack(queen(6, 6)) is false +end + +check "Test the ability of one queen to attack another -> can attack on same row": + queen(2, 4).can-attack(queen(2, 6)) is true +end + +check "Test the ability of one queen to attack another -> can attack on same column": + queen(4, 5).can-attack(queen(2, 5)) is true +end + +check "Test the ability of one queen to attack another -> can attack on first diagonal": + queen(2, 2).can-attack(queen(0, 4)) is true +end + +check "Test the ability of one queen to attack another -> can attack on second diagonal": + queen(2, 2).can-attack(queen(3, 1)) is true +end + +check "Test the ability of one queen to attack another -> can attack on third diagonal": + queen(2, 2).can-attack(queen(1, 1)) is true +end + +check "Test the ability of one queen to attack another -> can attack on fourth diagonal": + queen(1, 7).can-attack(queen(0, 6)) is true +end + +check "Test the ability of one queen to attack another -> cannot attack if falling diagonals are only the same when reflected across the longest falling diagonal": + queen(4, 1).can-attack(queen(2, 5)) is false +end + diff --git a/exercises/practice/queen-attack/queen-attack.arr b/exercises/practice/queen-attack/queen-attack.arr new file mode 100644 index 00000000..71833e51 --- /dev/null +++ b/exercises/practice/queen-attack/queen-attack.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide-types * + +data Queen: + ... # replace the dots with your implementation +end diff --git a/exercises/practice/raindrops/.docs/instructions.md b/exercises/practice/raindrops/.docs/instructions.md new file mode 100644 index 00000000..df644107 --- /dev/null +++ b/exercises/practice/raindrops/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +Your task is to convert a number into its corresponding raindrop sounds. + +If a given number: + +- is divisible by 3, add "Pling" to the result. +- is divisible by 5, add "Plang" to the result. +- is divisible by 7, add "Plong" to the result. +- **is not** divisible by 3, 5, or 7, the result should be the number as a string. + +## Examples + +- 28 is divisible by 7, but not 3 or 5, so the result would be `"Plong"`. +- 30 is divisible by 3 and 5, but not 7, so the result would be `"PlingPlang"`. +- 34 is not divisible by 3, 5, or 7, so the result would be `"34"`. + +~~~~exercism/note +A common way to test if one number is evenly divisible by another is to compare the [remainder][remainder] or [modulus][modulo] to zero. +Most languages provide operators or functions for one (or both) of these. + +[remainder]: https://exercism.org/docs/programming/operators/remainder +[modulo]: https://en.wikipedia.org/wiki/Modulo_operation +~~~~ diff --git a/exercises/practice/raindrops/.docs/introduction.md b/exercises/practice/raindrops/.docs/introduction.md new file mode 100644 index 00000000..ba12100f --- /dev/null +++ b/exercises/practice/raindrops/.docs/introduction.md @@ -0,0 +1,3 @@ +# Introduction + +Raindrops is a slightly more complex version of the FizzBuzz challenge, a classic interview question. diff --git a/exercises/practice/raindrops/.meta/config.json b/exercises/practice/raindrops/.meta/config.json new file mode 100644 index 00000000..33a4c97b --- /dev/null +++ b/exercises/practice/raindrops/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "raindrops.arr" + ], + "test": [ + "raindrops-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert a number into its corresponding raindrop sounds - Pling, Plang and Plong.", + "source": "A variation on FizzBuzz, a famous technical interview question that is intended to weed out potential candidates. That question is itself derived from Fizz Buzz, a popular children's game for teaching division.", + "source_url": "https://en.wikipedia.org/wiki/Fizz_buzz" +} diff --git a/exercises/practice/raindrops/.meta/example.arr b/exercises/practice/raindrops/.meta/example.arr new file mode 100644 index 00000000..104672bc --- /dev/null +++ b/exercises/practice/raindrops/.meta/example.arr @@ -0,0 +1,25 @@ +use context starter2024 + +provide: convert end + +RULES = [list: [list: 3, "Pling"], [list: 5, "Plang"], [list: 7, "Plong"]] + +fun convert(n): + + is-divisible-by = lam(f): num-modulo(n, f) == 0 end + + fun check-rules(l, acc): + cases (List) l: + | empty => acc + | link(f, r) => if (is-divisible-by(f.get(0))): f.get(1) + check-rules(r, acc) else: check-rules(r, acc) end + end + end + + sound = check-rules(RULES, "") + + if string-length(sound) == 0: + num-to-string(n) + else: + sound + end +end diff --git a/exercises/practice/raindrops/.meta/tests.toml b/exercises/practice/raindrops/.meta/tests.toml new file mode 100644 index 00000000..756d16ca --- /dev/null +++ b/exercises/practice/raindrops/.meta/tests.toml @@ -0,0 +1,64 @@ +# 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. + +[1575d549-e502-46d4-a8e1-6b7bec6123d8] +description = "the sound for 1 is 1" + +[1f51a9f9-4895-4539-b182-d7b0a5ab2913] +description = "the sound for 3 is Pling" + +[2d9bfae5-2b21-4bcd-9629-c8c0e388f3e0] +description = "the sound for 5 is Plang" + +[d7e60daa-32ef-4c23-b688-2abff46c4806] +description = "the sound for 7 is Plong" + +[6bb4947b-a724-430c-923f-f0dc3d62e56a] +description = "the sound for 6 is Pling as it has a factor 3" + +[ce51e0e8-d9d4-446d-9949-96eac4458c2d] +description = "2 to the power 3 does not make a raindrop sound as 3 is the exponent not the base" + +[0dd66175-e3e2-47fc-8750-d01739856671] +description = "the sound for 9 is Pling as it has a factor 3" + +[022c44d3-2182-4471-95d7-c575af225c96] +description = "the sound for 10 is Plang as it has a factor 5" + +[37ab74db-fed3-40ff-b7b9-04acdfea8edf] +description = "the sound for 14 is Plong as it has a factor of 7" + +[31f92999-6afb-40ee-9aa4-6d15e3334d0f] +description = "the sound for 15 is PlingPlang as it has factors 3 and 5" + +[ff9bb95d-6361-4602-be2c-653fe5239b54] +description = "the sound for 21 is PlingPlong as it has factors 3 and 7" + +[d2e75317-b72e-40ab-8a64-6734a21dece1] +description = "the sound for 25 is Plang as it has a factor 5" + +[a09c4c58-c662-4e32-97fe-f1501ef7125c] +description = "the sound for 27 is Pling as it has a factor 3" + +[bdf061de-8564-4899-a843-14b48b722789] +description = "the sound for 35 is PlangPlong as it has factors 5 and 7" + +[c4680bee-69ba-439d-99b5-70c5fd1a7a83] +description = "the sound for 49 is Plong as it has a factor 7" + +[17f2bc9a-b65a-4d23-8ccd-266e8c271444] +description = "the sound for 52 is 52" + +[e46677ed-ff1a-419f-a740-5c713d2830e4] +description = "the sound for 105 is PlingPlangPlong as it has factors 3, 5 and 7" + +[13c6837a-0fcd-4b86-a0eb-20572f7deb0b] +description = "the sound for 3125 is Plang as it has a factor 5" diff --git a/exercises/practice/raindrops/raindrops-test.arr b/exercises/practice/raindrops/raindrops-test.arr new file mode 100644 index 00000000..e1ac23e6 --- /dev/null +++ b/exercises/practice/raindrops/raindrops-test.arr @@ -0,0 +1,76 @@ +use context starter2024 + +include file("raindrops.arr") + +check "the sound for 1 is 1": + convert(1) is "1" +end + +check "the sound for 3 is Pling": + convert(3) is "Pling" +end + +check "the sound for 5 is Plang": + convert(5) is "Plang" +end + +check "the sound for 7 is Plong": + convert(7) is "Plong" +end + +check "the sound for 6 is Pling as it has a factor 3": + convert(6) is "Pling" +end + +check "2 to the power 3 does not make a raindrop sound as 3 is the exponent not the base": + convert(8) is "8" +end + +check "the sound for 9 is Pling as it has a factor 3": + convert(9) is "Pling" +end + +check "the sound for 10 is Plang as it has a factor 5": + convert(10) is "Plang" +end + +check "the sound for 14 is Plong as it has a factor of 7": + convert(14) is "Plong" +end + +check "the sound for 15 is PlingPlang as it has factors 3 and 5": + convert(15) is "PlingPlang" +end + +check "the sound for 21 is PlingPlong as it has factors 3 and 7": + convert(21) is "PlingPlong" +end + +check "the sound for 25 is Plang as it has a factor 5": + convert(25) is "Plang" +end + +check "the sound for 27 is Pling as it has a factor 3": + convert(27) is "Pling" +end + +check "the sound for 35 is PlangPlong as it has factors 5 and 7": + convert(35) is "PlangPlong" +end + +check "the sound for 49 is Plong as it has a factor 7": + convert(49) is "Plong" +end + +check "the sound for 52 is 52": + convert(52) is "52" +end + +check "the sound for 105 is PlingPlangPlong as it has factors 3, 5 and 7": + convert(105) is "PlingPlangPlong" +end + +check "the sound for 3125 is Plang as it has a factor 5": + convert(3125) is "Plang" +end + diff --git a/exercises/practice/raindrops/raindrops.arr b/exercises/practice/raindrops/raindrops.arr new file mode 100644 index 00000000..b5778b5d --- /dev/null +++ b/exercises/practice/raindrops/raindrops.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: convert end + +fun convert(n): + raise("Please implement the convert function") +end diff --git a/exercises/practice/resistor-color-duo/.docs/instructions.md b/exercises/practice/resistor-color-duo/.docs/instructions.md new file mode 100644 index 00000000..4ae694da --- /dev/null +++ b/exercises/practice/resistor-color-duo/.docs/instructions.md @@ -0,0 +1,33 @@ +# Instructions + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. +For this exercise, you need to know two things about them: + +- Each resistor has a resistance value. +- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + +To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +Each band has a position and a numeric value. + +The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. +For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + +In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. +The program will take color names as input and output a two digit number, even if the input is more than two colors! + +The band colors are encoded as follows: + +- black: 0 +- brown: 1 +- red: 2 +- orange: 3 +- yellow: 4 +- green: 5 +- blue: 6 +- violet: 7 +- grey: 8 +- white: 9 + +From the example above: +brown-green should return 15, and +brown-green-violet should return 15 too, ignoring the third color. diff --git a/exercises/practice/resistor-color-duo/.meta/config.json b/exercises/practice/resistor-color-duo/.meta/config.json new file mode 100644 index 00000000..9d691b2f --- /dev/null +++ b/exercises/practice/resistor-color-duo/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "resistor-color-duo.arr" + ], + "test": [ + "resistor-color-duo-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert color codes, as used on resistors, to a numeric value.", + "source": "Maud de Vries, Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/issues/1464" +} diff --git a/exercises/practice/resistor-color-duo/.meta/example.arr b/exercises/practice/resistor-color-duo/.meta/example.arr new file mode 100644 index 00000000..a305c963 --- /dev/null +++ b/exercises/practice/resistor-color-duo/.meta/example.arr @@ -0,0 +1,31 @@ +use context starter2024 + +provide: color-code end + +RESISTOR-COLORS = [list: + "black", + "brown", + "red", + "orange", + "yellow", + "green", + "blue", + "violet", + "grey", + "white", + ] + +fun color-code(colors): + fun index-of(l, elem): + if not(l.member(elem)): + -1 + else: + cases (List) l: + | empty => 0 + | link(f, r) => if f == elem: 0 else: 1 + index-of(r, elem) end + end + end + end + + (index-of(RESISTOR-COLORS, colors.get(0)) * 10) + index-of(RESISTOR-COLORS, colors.get(1)) +end diff --git a/exercises/practice/resistor-color-duo/.meta/tests.toml b/exercises/practice/resistor-color-duo/.meta/tests.toml new file mode 100644 index 00000000..9036fc78 --- /dev/null +++ b/exercises/practice/resistor-color-duo/.meta/tests.toml @@ -0,0 +1,31 @@ +# 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. + +[ce11995a-5b93-4950-a5e9-93423693b2fc] +description = "Brown and black" + +[7bf82f7a-af23-48ba-a97d-38d59406a920] +description = "Blue and grey" + +[f1886361-fdfd-4693-acf8-46726fe24e0c] +description = "Yellow and violet" + +[b7a6cbd2-ae3c-470a-93eb-56670b305640] +description = "White and red" + +[77a8293d-2a83-4016-b1af-991acc12b9fe] +description = "Orange and orange" + +[0c4fb44f-db7c-4d03-afa8-054350f156a8] +description = "Ignore additional colors" + +[4a8ceec5-0ab4-4904-88a4-daf953a5e818] +description = "Black and brown, one-digit" diff --git a/exercises/practice/resistor-color-duo/resistor-color-duo-test.arr b/exercises/practice/resistor-color-duo/resistor-color-duo-test.arr new file mode 100644 index 00000000..39a4fbaf --- /dev/null +++ b/exercises/practice/resistor-color-duo/resistor-color-duo-test.arr @@ -0,0 +1,32 @@ +use context starter2024 + +include file("resistor-color-duo.arr") + +check "Brown and black": + color-code([list: "brown", "black"]) is 10 +end + +check "Blue and grey": + color-code([list: "blue", "grey"]) is 68 +end + +check "Yellow and violet": + color-code([list: "yellow", "violet"]) is 47 +end + +check "White and red": + color-code([list: "white", "red"]) is 92 +end + +check "Orange and orange": + color-code([list: "orange", "orange"]) is 33 +end + +check "Ignore additional colors": + color-code([list: "green", "brown", "orange"]) is 51 +end + +check "Black and brown, one-digit": + color-code([list: "black", "brown"]) is 1 +end + diff --git a/exercises/practice/resistor-color-duo/resistor-color-duo.arr b/exercises/practice/resistor-color-duo/resistor-color-duo.arr new file mode 100644 index 00000000..b5b17e60 --- /dev/null +++ b/exercises/practice/resistor-color-duo/resistor-color-duo.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: color-code end + +fun color-code(colors): + raise("Please implement the color-code function") +end diff --git a/exercises/practice/resistor-color-trio/.docs/instructions.md b/exercises/practice/resistor-color-trio/.docs/instructions.md new file mode 100644 index 00000000..1ac5cf5e --- /dev/null +++ b/exercises/practice/resistor-color-trio/.docs/instructions.md @@ -0,0 +1,56 @@ +# Instructions + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. +For this exercise, you need to know only three things about them: + +- Each resistor has a resistance value. +- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +- Each band acts as a digit of a number. + For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands. + The program will take 3 colors as input, and outputs the correct value, in ohms. + The color bands are encoded as follows: + +- black: 0 +- brown: 1 +- red: 2 +- orange: 3 +- yellow: 4 +- green: 5 +- blue: 6 +- violet: 7 +- grey: 8 +- white: 9 + +In Resistor Color Duo you decoded the first two colors. +For instance: orange-orange got the main value `33`. +The third color stands for how many zeros need to be added to the main value. +The main value plus the zeros gives us a value in ohms. +For the exercise it doesn't matter what ohms really are. +For example: + +- orange-orange-black would be 33 and no zeros, which becomes 33 ohms. +- orange-orange-red would be 33 and 2 zeros, which becomes 3300 ohms. +- orange-orange-orange would be 33 and 3 zeros, which becomes 33000 ohms. + +(If Math is your thing, you may want to think of the zeros as exponents of 10. +If Math is not your thing, go with the zeros. +It really is the same thing, just in plain English instead of Math lingo.) + +This exercise is about translating the colors into a label: + +> "... ohms" + +So an input of `"orange", "orange", "black"` should return: + +> "33 ohms" + +When we get to larger resistors, a [metric prefix][metric-prefix] is used to indicate a larger magnitude of ohms, such as "kiloohms". +That is similar to saying "2 kilometers" instead of "2000 meters", or "2 kilograms" for "2000 grams". + +For example, an input of `"orange", "orange", "orange"` should return: + +> "33 kiloohms" + +[metric-prefix]: https://en.wikipedia.org/wiki/Metric_prefix diff --git a/exercises/practice/resistor-color-trio/.meta/config.json b/exercises/practice/resistor-color-trio/.meta/config.json new file mode 100644 index 00000000..a1bd58df --- /dev/null +++ b/exercises/practice/resistor-color-trio/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "resistor-color-trio.arr" + ], + "test": [ + "resistor-color-trio-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert color codes, as used on resistors, to a human-readable label.", + "source": "Maud de Vries, Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/issues/1549" +} diff --git a/exercises/practice/resistor-color-trio/.meta/example.arr b/exercises/practice/resistor-color-trio/.meta/example.arr new file mode 100644 index 00000000..6ca03bc7 --- /dev/null +++ b/exercises/practice/resistor-color-trio/.meta/example.arr @@ -0,0 +1,65 @@ +use context starter2024 + +provide: label end + +RESISTOR-COLORS = [list: + "black", + "brown", + "red", + "orange", + "yellow", + "green", + "blue", + "violet", + "grey", + "white", + ] + +fun label(colors): + fun index-of(l, elem): + if not(l.member(elem)): + -1 + else: + cases (List) l: + | empty => 0 + | link(f, r) => + if f == elem: + 0 + else: + 1 + index-of(r, elem) + end + end + end + end + + tens = index-of(RESISTOR-COLORS, colors.get(0)) + ones = index-of(RESISTOR-COLORS, colors.get(1)) + pow = index-of(RESISTOR-COLORS, colors.get(2)) + raw-ohms = ((tens * 10) + ones) * num-expt(10, pow) + + digits = num-to-string(raw-ohms) + + unit = ask: + | string-length(digits) < 4 then: + 'ohms' + | string-length(digits) < 7 then: + 'kiloohms' + | string-length(digits) < 10 then: + 'megaohms' + | string-length(digits) < 13 then: + 'gigaohms' + end + + value = ask: + | string-length(digits) < 4 then: + raw-ohms + | string-length(digits) < 7 then: + raw-ohms / 1e3 + | string-length(digits) < 10 then: + raw-ohms / 1e6 + | string-length(digits) < 13 then: + raw-ohms / 1e9 + end + + num-to-string(num-truncate(value)) + " " + unit +end diff --git a/exercises/practice/resistor-color-trio/.meta/tests.toml b/exercises/practice/resistor-color-trio/.meta/tests.toml new file mode 100644 index 00000000..b7d45fa5 --- /dev/null +++ b/exercises/practice/resistor-color-trio/.meta/tests.toml @@ -0,0 +1,40 @@ +# 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. + +[d6863355-15b7-40bb-abe0-bfb1a25512ed] +description = "Orange and orange and black" + +[1224a3a9-8c8e-4032-843a-5224e04647d6] +description = "Blue and grey and brown" + +[b8bda7dc-6b95-4539-abb2-2ad51d66a207] +description = "Red and black and red" + +[5b1e74bc-d838-4eda-bbb3-eaba988e733b] +description = "Green and brown and orange" + +[f5d37ef9-1919-4719-a90d-a33c5a6934c9] +description = "Yellow and violet and yellow" + +[5f6404a7-5bb3-4283-877d-3d39bcc33854] +description = "Blue and violet and blue" + +[7d3a6ab8-e40e-46c3-98b1-91639fff2344] +description = "Minimum possible value" + +[ca0aa0ac-3825-42de-9f07-dac68cc580fd] +description = "Maximum possible value" + +[0061a76c-903a-4714-8ce2-f26ce23b0e09] +description = "First two colors make an invalid octal number" + +[30872c92-f567-4b69-a105-8455611c10c4] +description = "Ignore extra colors" diff --git a/exercises/practice/resistor-color-trio/resistor-color-trio-test.arr b/exercises/practice/resistor-color-trio/resistor-color-trio-test.arr new file mode 100644 index 00000000..a13b4954 --- /dev/null +++ b/exercises/practice/resistor-color-trio/resistor-color-trio-test.arr @@ -0,0 +1,74 @@ +use context starter2024 + +include file("resistor-color-trio.arr") + +check "Orange and orange and black": + colors = [list: "orange", "orange", "black"] + expected = "33 ohms" + + label(colors) is expected +end + +check "Blue and grey and brown": + colors = [list: "blue", "grey", "brown"] + expected = "680 ohms" + + label(colors) is expected +end + +check "Red and black and red": + colors = [list: "red", "black", "red"] + expected = "2 kiloohms" + + label(colors) is expected +end + +check "Green and brown and orange": + colors = [list: "green", "brown", "orange"] + expected = "51 kiloohms" + + label(colors) is expected +end + +check "Yellow and violet and yellow": + colors = [list: "yellow", "violet", "yellow"] + expected = "470 kiloohms" + + label(colors) is expected +end + +check "Blue and violet and blue": + colors = [list: "blue", "violet", "blue"] + expected = "67 megaohms" + + label(colors) is expected +end + +check "Minimum possible value": + colors = [list: "black", "black", "black"] + expected = "0 ohms" + + label(colors) is expected +end + +check "Maximum possible value": + colors = [list: "white", "white", "white"] + expected = "99 gigaohms" + + label(colors) is expected +end + +check "First two colors make an invalid octal number": + colors = [list: "black", "grey", "black"] + expected = "8 ohms" + + label(colors) is expected +end + +check "Ignore extra colors": + colors = [list: "blue", "green", "yellow", "orange"] + expected = "650 kiloohms" + + label(colors) is expected +end + diff --git a/exercises/practice/resistor-color-trio/resistor-color-trio.arr b/exercises/practice/resistor-color-trio/resistor-color-trio.arr new file mode 100644 index 00000000..fcf4df93 --- /dev/null +++ b/exercises/practice/resistor-color-trio/resistor-color-trio.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: label end + +fun label(colors): + raise("Please implement the label function") +end diff --git a/exercises/practice/resistor-color/.docs/instructions.md b/exercises/practice/resistor-color/.docs/instructions.md new file mode 100644 index 00000000..0125e718 --- /dev/null +++ b/exercises/practice/resistor-color/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. +For this exercise, you need to know two things about them: + +- Each resistor has a resistance value. +- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + +To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +Each band has a position and a numeric value. + +The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. + +In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. + +These colors are encoded as follows: + +- black: 0 +- brown: 1 +- red: 2 +- orange: 3 +- yellow: 4 +- green: 5 +- blue: 6 +- violet: 7 +- grey: 8 +- white: 9 + +The goal of this exercise is to create a way: + +- to look up the numerical value associated with a particular color band +- to list the different band colors + +Mnemonics map the colors to the numbers, that, when stored as an array, happen to map to their index in the array: +Better Be Right Or Your Great Big Values Go Wrong. + +More information on the color encoding of resistors can be found in the [Electronic color code Wikipedia article][e-color-code]. + +[e-color-code]: https://en.wikipedia.org/wiki/Electronic_color_code diff --git a/exercises/practice/resistor-color/.meta/config.json b/exercises/practice/resistor-color/.meta/config.json new file mode 100644 index 00000000..3796bb0b --- /dev/null +++ b/exercises/practice/resistor-color/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "resistor-color.arr" + ], + "test": [ + "resistor-color-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert a resistor band's color to its numeric representation.", + "source": "Maud de Vries, Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/issues/1458" +} diff --git a/exercises/practice/resistor-color/.meta/example.arr b/exercises/practice/resistor-color/.meta/example.arr new file mode 100644 index 00000000..2f6d898e --- /dev/null +++ b/exercises/practice/resistor-color/.meta/example.arr @@ -0,0 +1,36 @@ +use context starter2024 + +provide: color-code, colors end + +RESISTOR-COLORS = [list: + "black", + "brown", + "red", + "orange", + "yellow", + "green", + "blue", + "violet", + "grey", + "white", + ] + +fun color-code(color): + fun index-of(l, elem): + if not(l.member(elem)): + -1 + else: + cases (List) l: + | empty => 0 + | link(f, r) => if f == elem: 0 else: 1 + index-of(r, elem) end + end + end + end + + index-of(RESISTOR-COLORS, color) +end + + +fun colors(): + RESISTOR-COLORS +end diff --git a/exercises/practice/resistor-color/.meta/tests.toml b/exercises/practice/resistor-color/.meta/tests.toml new file mode 100644 index 00000000..9d4ee973 --- /dev/null +++ b/exercises/practice/resistor-color/.meta/tests.toml @@ -0,0 +1,22 @@ +# 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. + +[49eb31c5-10a8-4180-9f7f-fea632ab87ef] +description = "Color codes -> Black" + +[0a4df94b-92da-4579-a907-65040ce0b3fc] +description = "Color codes -> White" + +[5f81608d-f36f-4190-8084-f45116b6f380] +description = "Color codes -> Orange" + +[581d68fa-f968-4be2-9f9d-880f2fb73cf7] +description = "Colors" diff --git a/exercises/practice/resistor-color/resistor-color-test.arr b/exercises/practice/resistor-color/resistor-color-test.arr new file mode 100644 index 00000000..fa36f9c2 --- /dev/null +++ b/exercises/practice/resistor-color/resistor-color-test.arr @@ -0,0 +1,31 @@ +use context starter2024 + +include file("resistor-color.arr") + +check "Color codes -> Black": + color-code("black") is 0 +end + +check "Color codes -> White": + color-code("white") is 9 +end + +check "Color codes -> Orange": + color-code("orange") is 3 +end + +check "Colors": + colors() is [list: + "black", + "brown", + "red", + "orange", + "yellow", + "green", + "blue", + "violet", + "grey", + "white", + ] +end + diff --git a/exercises/practice/resistor-color/resistor-color.arr b/exercises/practice/resistor-color/resistor-color.arr new file mode 100644 index 00000000..6c8d4d55 --- /dev/null +++ b/exercises/practice/resistor-color/resistor-color.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: color-code, colors end + +fun color-code(color): + raise("Please implement the color-code function") +end + +fun colors(): + raise("Please implement the colors function") +end diff --git a/exercises/practice/reverse-string/.docs/instructions.md b/exercises/practice/reverse-string/.docs/instructions.md new file mode 100644 index 00000000..0ff4198e --- /dev/null +++ b/exercises/practice/reverse-string/.docs/instructions.md @@ -0,0 +1,9 @@ +# Instructions + +Your task is to reverse a given string. + +Some examples: + +- Turn `"stressed"` into `"desserts"`. +- Turn `"strops"` into `"sports"`. +- Turn `"racecar"` into `"racecar"`. diff --git a/exercises/practice/reverse-string/.docs/introduction.md b/exercises/practice/reverse-string/.docs/introduction.md new file mode 100644 index 00000000..02233e07 --- /dev/null +++ b/exercises/practice/reverse-string/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +Reversing strings (reading them from right to left, rather than from left to right) is a surprisingly common task in programming. + +For example, in bioinformatics, reversing the sequence of DNA or RNA strings is often important for various analyses, such as finding complementary strands or identifying palindromic sequences that have biological significance. diff --git a/exercises/practice/reverse-string/.meta/config.json b/exercises/practice/reverse-string/.meta/config.json new file mode 100644 index 00000000..3a460f0e --- /dev/null +++ b/exercises/practice/reverse-string/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "reverse-string.arr" + ], + "test": [ + "reverse-string-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Reverse a given string.", + "source": "Introductory challenge to reverse an input string", + "source_url": "https://www.freecodecamp.org/news/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb" +} diff --git a/exercises/practice/reverse-string/.meta/example.arr b/exercises/practice/reverse-string/.meta/example.arr new file mode 100644 index 00000000..26150b15 --- /dev/null +++ b/exercises/practice/reverse-string/.meta/example.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: reversed end + +fun reversed(input): + string-explode(input).reverse().join-str("") +end diff --git a/exercises/practice/reverse-string/.meta/tests.toml b/exercises/practice/reverse-string/.meta/tests.toml new file mode 100644 index 00000000..52fda995 --- /dev/null +++ b/exercises/practice/reverse-string/.meta/tests.toml @@ -0,0 +1,40 @@ +# 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. + +[c3b7d806-dced-49ee-8543-933fd1719b1c] +description = "an empty string" + +[01ebf55b-bebb-414e-9dec-06f7bb0bee3c] +description = "a word" + +[0f7c07e4-efd1-4aaa-a07a-90b49ce0b746] +description = "a capitalized word" + +[71854b9c-f200-4469-9f5c-1e8e5eff5614] +description = "a sentence with punctuation" + +[1f8ed2f3-56f3-459b-8f3e-6d8d654a1f6c] +description = "a palindrome" + +[b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c] +description = "an even-sized word" + +[1bed0f8a-13b0-4bd3-9d59-3d0593326fa2] +description = "wide characters" +include = false + +[93d7e1b8-f60f-4f3c-9559-4056e10d2ead] +description = "grapheme cluster with pre-combined form" +include = false + +[1028b2c1-6763-4459-8540-2da47ca512d9] +description = "grapheme clusters" +include = false diff --git a/exercises/practice/reverse-string/reverse-string-test.arr b/exercises/practice/reverse-string/reverse-string-test.arr new file mode 100644 index 00000000..e46e9e34 --- /dev/null +++ b/exercises/practice/reverse-string/reverse-string-test.arr @@ -0,0 +1,28 @@ +use context starter2024 + +include file("reverse-string.arr") + +check "an empty string": + reversed("") is "" +end + +check "a word": + reversed("robot") is "tobor" +end + +check "a capitalized word": + reversed("Ramen") is "nemaR" +end + +check "a sentence with punctuation": + reversed("I'm hungry!") is "!yrgnuh m'I" +end + +check "a palindrome": + reversed("racecar") is "racecar" +end + +check "an even-sized word": + reversed("drawer") is "reward" +end + diff --git a/exercises/practice/reverse-string/reverse-string.arr b/exercises/practice/reverse-string/reverse-string.arr new file mode 100644 index 00000000..6ce49b17 --- /dev/null +++ b/exercises/practice/reverse-string/reverse-string.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: reversed end + +fun reversed(text): + raise("please implement the reversed function") +end diff --git a/exercises/practice/rna-transcription/.docs/instructions.md b/exercises/practice/rna-transcription/.docs/instructions.md new file mode 100644 index 00000000..4dbfd3a2 --- /dev/null +++ b/exercises/practice/rna-transcription/.docs/instructions.md @@ -0,0 +1,20 @@ +# Instructions + +Your task is to determine the RNA complement of a given DNA sequence. + +Both DNA and RNA strands are a sequence of nucleotides. + +The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), guanine (**G**), and thymine (**T**). + +The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), guanine (**G**), and uracil (**U**). + +Given a DNA strand, its transcribed RNA strand is formed by replacing each nucleotide with its complement: + +- `G` -> `C` +- `C` -> `G` +- `T` -> `A` +- `A` -> `U` + +~~~~exercism/note +If you want to look at how the inputs and outputs are structured, take a look at the examples in the test suite. +~~~~ diff --git a/exercises/practice/rna-transcription/.docs/introduction.md b/exercises/practice/rna-transcription/.docs/introduction.md new file mode 100644 index 00000000..6b3f44b5 --- /dev/null +++ b/exercises/practice/rna-transcription/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a bioengineering company that specializes in developing therapeutic solutions. + +Your team has just been given a new project to develop a targeted therapy for a rare type of cancer. + +~~~~exercism/note +It's all very complicated, but the basic idea is that sometimes people's bodies produce too much of a given protein. +That can cause all sorts of havoc. + +But if you can create a very specific molecule (called a micro-RNA), it can prevent the protein from being produced. + +This technique is called [RNA Interference][rnai]. + +[rnai]: https://admin.acceleratingscience.com/ask-a-scientist/what-is-rnai/ +~~~~ diff --git a/exercises/practice/rna-transcription/.meta/config.json b/exercises/practice/rna-transcription/.meta/config.json new file mode 100644 index 00000000..44970347 --- /dev/null +++ b/exercises/practice/rna-transcription/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "rna-transcription.arr" + ], + "test": [ + "rna-transcription-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a DNA strand, return its RNA complement.", + "source": "Hyperphysics", + "source_url": "https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html" +} diff --git a/exercises/practice/rna-transcription/.meta/example.arr b/exercises/practice/rna-transcription/.meta/example.arr new file mode 100644 index 00000000..33a1fa25 --- /dev/null +++ b/exercises/practice/rna-transcription/.meta/example.arr @@ -0,0 +1,21 @@ +use context starter2024 + +provide: to-rna end + +include string-dict + +COMPLEMENTS = [string-dict: +"C", "G", +"G", "C", +"T", "A", +"A", "U" +] + +fun to-rna(dna): + string-explode(dna).foldl( + lam(elt, acc): + acc.push(COMPLEMENTS.get-value(elt)) + end, + [list: ] + ).reverse().join-str("") +end diff --git a/exercises/practice/rna-transcription/.meta/tests.toml b/exercises/practice/rna-transcription/.meta/tests.toml new file mode 100644 index 00000000..68005140 --- /dev/null +++ b/exercises/practice/rna-transcription/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[b4631f82-c98c-4a2f-90b3-c5c2b6c6f661] +description = "Empty RNA sequence" + +[a9558a3c-318c-4240-9256-5d5ed47005a6] +description = "RNA complement of cytosine is guanine" + +[6eedbb5c-12cb-4c8b-9f51-f8320b4dc2e7] +description = "RNA complement of guanine is cytosine" + +[870bd3ec-8487-471d-8d9a-a25046488d3e] +description = "RNA complement of thymine is adenine" + +[aade8964-02e1-4073-872f-42d3ffd74c5f] +description = "RNA complement of adenine is uracil" + +[79ed2757-f018-4f47-a1d7-34a559392dbf] +description = "RNA complement" diff --git a/exercises/practice/rna-transcription/rna-transcription-test.arr b/exercises/practice/rna-transcription/rna-transcription-test.arr new file mode 100644 index 00000000..5dd6b738 --- /dev/null +++ b/exercises/practice/rna-transcription/rna-transcription-test.arr @@ -0,0 +1,28 @@ +use context starter2024 + +include file("rna-transcription.arr") + +check "Empty RNA sequence": + to-rna("") is "" +end + +check "RNA complement of cytosine is guanine": + to-rna("C") is "G" +end + +check "RNA complement of guanine is cytosine": + to-rna("G") is "C" +end + +check "RNA complement of thymine is adenine": + to-rna("T") is "A" +end + +check "RNA complement of adenine is uracil": + to-rna("A") is "U" +end + +check "RNA complement": + to-rna("ACGTGGTCTTAA") is "UGCACCAGAAUU" +end + diff --git a/exercises/practice/rna-transcription/rna-transcription.arr b/exercises/practice/rna-transcription/rna-transcription.arr new file mode 100644 index 00000000..277d49c7 --- /dev/null +++ b/exercises/practice/rna-transcription/rna-transcription.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: to-rna end + +fun to-rna(dna): + raise("Please implement the to-rna function") +end diff --git a/exercises/practice/robot-simulator/.docs/instructions.md b/exercises/practice/robot-simulator/.docs/instructions.md new file mode 100644 index 00000000..0ac96ce0 --- /dev/null +++ b/exercises/practice/robot-simulator/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Write a robot simulator. + +A robot factory's test facility needs a program to verify robot movements. + +The robots have three possible movements: + +- turn right +- turn left +- advance + +Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, +e.g., {3,8}, with coordinates increasing to the north and east. + +The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing. + +- The letter-string "RAALAL" means: + - Turn right + - Advance twice + - Turn left + - Advance once + - Turn left yet again +- Say a robot starts at {7, 3} facing north. + Then running this stream of instructions should leave it at {9, 4} facing west. diff --git a/exercises/practice/robot-simulator/.meta/config.json b/exercises/practice/robot-simulator/.meta/config.json new file mode 100644 index 00000000..bb6454a6 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "robot-simulator.arr" + ], + "test": [ + "robot-simulator-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Write a robot simulator.", + "source": "Inspired by an interview question at a famous company." +} diff --git a/exercises/practice/robot-simulator/.meta/example.arr b/exercises/practice/robot-simulator/.meta/example.arr new file mode 100644 index 00000000..a74c730e --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/example.arr @@ -0,0 +1,51 @@ +use context starter2024 + +provide-types * + +include string-dict +import lists as L + +data Axis: axis(label, left, right) end + +cardinal_axes = [string-dict: + 'north', axis('north', 'west', 'east'), + 'south', axis('south', 'east', 'west'), + 'east', axis('east', 'north','south'), + 'west', axis('west','south', 'north'), +] + +data Robot: + | robot(x :: NumInteger, y :: NumInteger, direction :: String) with: + method move(self, directions :: String) -> Robot: + L.foldl( + lam(acc, elt): + direction = + ask: + | elt == 'L' then: + cardinal_axes.get-value(acc.direction).left + | elt == 'R' then: + cardinal_axes.get-value(acc.direction).right + | otherwise: + acc.direction + end + {x; y} = + if elt == 'A': + ask: + | direction == 'north' then: + {acc.x; acc.y + 1} + | direction == 'south' then: + {acc.x; acc.y - 1} + | direction == 'east' then: + {acc.x + 1; acc.y} + | direction == 'west' then: + {acc.x - 1; acc.y} + end + else: + {acc.x; acc.y} + end + robot(x, y, direction) + end, + self, + string-explode(directions)) + end +end diff --git a/exercises/practice/robot-simulator/.meta/tests.toml b/exercises/practice/robot-simulator/.meta/tests.toml new file mode 100644 index 00000000..16da03d4 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/tests.toml @@ -0,0 +1,64 @@ +# 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. + +[c557c16d-26c1-4e06-827c-f6602cd0785c] +description = "Create robot -> at origin facing north" + +[bf0dffce-f11c-4cdb-8a5e-2c89d8a5a67d] +description = "Create robot -> at negative position facing south" + +[8cbd0086-6392-4680-b9b9-73cf491e67e5] +description = "Rotating clockwise -> changes north to east" + +[8abc87fc-eab2-4276-93b7-9c009e866ba1] +description = "Rotating clockwise -> changes east to south" + +[3cfe1b85-bbf2-4bae-b54d-d73e7e93617a] +description = "Rotating clockwise -> changes south to west" + +[5ea9fb99-3f2c-47bd-86f7-46b7d8c3c716] +description = "Rotating clockwise -> changes west to north" + +[fa0c40f5-6ba3-443d-a4b3-58cbd6cb8d63] +description = "Rotating counter-clockwise -> changes north to west" + +[da33d734-831f-445c-9907-d66d7d2a92e2] +description = "Rotating counter-clockwise -> changes west to south" + +[bd1ca4b9-4548-45f4-b32e-900fc7c19389] +description = "Rotating counter-clockwise -> changes south to east" + +[2de27b67-a25c-4b59-9883-bc03b1b55bba] +description = "Rotating counter-clockwise -> changes east to north" + +[f0dc2388-cddc-4f83-9bed-bcf46b8fc7b8] +description = "Moving forward one -> facing north increments Y" + +[2786cf80-5bbf-44b0-9503-a89a9c5789da] +description = "Moving forward one -> facing south decrements Y" + +[84bf3c8c-241f-434d-883d-69817dbd6a48] +description = "Moving forward one -> facing east increments X" + +[bb69c4a7-3bbf-4f64-b415-666fa72d7b04] +description = "Moving forward one -> facing west decrements X" + +[e34ac672-4ed4-4be3-a0b8-d9af259cbaa1] +description = "Follow series of instructions -> moving east and north from README" + +[f30e4955-4b47-4aa3-8b39-ae98cfbd515b] +description = "Follow series of instructions -> moving west and north" + +[3e466bf6-20ab-4d79-8b51-264165182fca] +description = "Follow series of instructions -> moving west and south" + +[41f0bb96-c617-4e6b-acff-a4b279d44514] +description = "Follow series of instructions -> moving east and north" diff --git a/exercises/practice/robot-simulator/robot-simulator-test.arr b/exercises/practice/robot-simulator/robot-simulator-test.arr new file mode 100644 index 00000000..dd65575c --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator-test.arr @@ -0,0 +1,112 @@ +use context starter2024 + +include file("robot-simulator.arr") + +check "Create robot at origin facing north": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'north') + r is expected +end + +check "Create robot at negative position facing south": + r = robot(-1, -1, 'south') + expected = robot(-1, -1, 'south') + r is expected +end + +check "Rotating clockwise changes north to east": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'east') + r.move('R') is expected +end + +check "Rotating clockwise changes east to south": + r = robot(0, 0, 'east') + expected = robot(0, 0, 'south') + r.move('R') is expected +end + +check "Rotating clockwise changes south to west": + r = robot(0, 0, 'south') + expected = robot(0, 0, 'west') + r.move('R') is expected +end + +check "Rotating clockwise changes west to north": + r = robot(0, 0, 'west') + expected = robot(0, 0, 'north') + r.move('R') is expected +end + +check "Rotating counter-clockwise changes north to west": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'west') + r.move('L') is expected +end + +check "Rotating counter-clockwise changes west to south": + r = robot(0, 0, 'west') + expected = robot(0, 0, 'south') + r.move('L') is expected +end + +check "Rotating counter-clockwise changes south to east": + r = robot(0, 0, 'south') + expected = robot(0, 0, 'east') + r.move('L') is expected +end + +check "Rotating counter-clockwise changes east to north": + r = robot(0, 0, 'east') + expected = robot(0, 0, 'north') + r.move('L') is expected +end + +check "Moving forward one facing north increments Y": + r = robot(0, 0, 'north') + expected = robot(0, 1, 'north') + r.move('A') is expected +end + +check "Moving forward one facing south decrements Y": + r = robot(0, 0, 'south') + expected = robot(0, -1, 'south') + r.move('A') is expected +end + +check "Moving forward one facing east increments X": + r = robot(0, 0, 'east') + expected = robot(1, 0, 'east') + r.move('A') is expected +end + +check "Moving forward one facing west decrements X": + r = robot(0, 0, 'west') + expected = robot(-1, 0, 'west') + r.move('A') is expected +end + +check "Follow series of directions moving east and north from README": + r = robot(7, 3, 'north') + expected = robot(9, 4, 'west') + r.move('RAALAL') is expected +end + +check "Follow series of directions moving west and north": + r = robot(0, 0, 'north') + expected = robot(-4, 1, 'west') + r.move('LAAARALA') is expected +end + +check "Follow series of directions moving west and south": + r = robot(2, -7, 'east') + expected = robot(-3, -8, 'south') + r.move('RRAAAAALA') is expected +end + +check "Follow series of directions moving east and north": + r = robot(8, 4, 'south') + expected = robot(11, 5, 'north') + r.move('LAAARRRALLLL') is expected +end + diff --git a/exercises/practice/robot-simulator/robot-simulator.arr b/exercises/practice/robot-simulator/robot-simulator.arr new file mode 100644 index 00000000..3b095e87 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator.arr @@ -0,0 +1,11 @@ +use context starter2024 # Don't delete this line when using Pyret on Exercism + +provide-types * + +data Robot: + | robot() + with: + method move(self, directions :: String) -> Robot: + ... + end +end diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md new file mode 100644 index 00000000..50e2f5bf --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/instructions.md @@ -0,0 +1,12 @@ +# Introduction + +Your task is to convert a number from Arabic numerals to Roman numerals. + +For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999). + +~~~~exercism/note +There are lots of different ways to convert between Arabic and Roman numerals. +We recommend taking a naive approach first to familiarise yourself with the concept of Roman numerals and then search for more efficient methods. + +Make sure to check out our Deep Dive video at the end to explore the different approaches you can take! +~~~~ diff --git a/exercises/practice/roman-numerals/.docs/introduction.md b/exercises/practice/roman-numerals/.docs/introduction.md new file mode 100644 index 00000000..6fd942fe --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/introduction.md @@ -0,0 +1,59 @@ +# Description + +Today, most people in the world use Arabic numerals (0–9). +But if you travelled back two thousand years, you'd find that most Europeans were using Roman numerals instead. + +To write a Roman numeral we use the following Latin letters, each of which has a value: + +| M | D | C | L | X | V | I | +| ---- | --- | --- | --- | --- | --- | --- | +| 1000 | 500 | 100 | 50 | 10 | 5 | 1 | + +A Roman numeral is a sequence of these letters, and its value is the sum of the letters' values. +For example, `XVIII` has the value 18 (`10 + 5 + 1 + 1 + 1 = 18`). + +There's one rule that makes things trickier though, and that's that **the same letter cannot be used more than three times in succession**. +That means that we can't express numbers such as 4 with the seemingly natural `IIII`. +Instead, for those numbers, we use a subtraction method between two letters. +So we think of `4` not as `1 + 1 + 1 + 1` but instead as `5 - 1`. +And slightly confusingly to our modern thinking, we write the smaller number first. +This applies only in the following cases: 4 (`IV`), 9 (`IX`), 40 (`XL`), 90 (`XC`), 400 (`CD`) and 900 (`CM`). + +Order matters in Roman numerals! +Letters (and the special compounds above) must be ordered by decreasing value from left to right. + +Here are some examples: + +```text + 105 => CV +---- => -- + 100 => C ++ 5 => V +``` + +```text + 106 => CVI +---- => -- + 100 => C ++ 5 => V ++ 1 => I +``` + +```text + 104 => CIV +---- => --- + 100 => C ++ 4 => IV +``` + +And a final more complex example: + +```text + 1996 => MCMXCVI +----- => ------- + 1000 => M ++ 900 => CM ++ 90 => XC ++ 5 => V ++ 1 => I +``` diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json new file mode 100644 index 00000000..8e8fbf77 --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "roman-numerals.arr" + ], + "test": [ + "roman-numerals-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Convert modern Arabic numbers into Roman numerals.", + "source": "The Roman Numeral Kata", + "source_url": "https://codingdojo.org/kata/RomanNumerals/" +} diff --git a/exercises/practice/roman-numerals/.meta/example.arr b/exercises/practice/roman-numerals/.meta/example.arr new file mode 100644 index 00000000..488383cf --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/example.arr @@ -0,0 +1,41 @@ +use context starter2024 + +provide: to-roman end + +NUMERALS = [list: + {"M";1000}, + {"CM";900}, + {"D";500}, + {"CD";400}, + {"C";100}, + {"XC";90}, + {"L";50}, + {"XL";40}, + {"X";10}, + {"IX";9}, + {"V";5}, + {"IV";4}, + {"I";1}] + + +fun to-roman(number): + rec to-roman-recursive = + lam(current, acc, numerals): + if numerals.length() == 0: + acc + else: + numeral-rule = numerals.get(0) + numeral = numeral-rule.{0} + value = numeral-rule.{1} + if value <= current: + updated-value = current - value + + to-roman-recursive(updated-value, acc + numeral, numerals) + else: + to-roman-recursive(current, acc, numerals.drop(1)) + end + end + end + + to-roman-recursive(number, "", NUMERALS) +end diff --git a/exercises/practice/roman-numerals/.meta/tests.toml b/exercises/practice/roman-numerals/.meta/tests.toml new file mode 100644 index 00000000..709011b5 --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/tests.toml @@ -0,0 +1,91 @@ +# 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. + +[19828a3a-fbf7-4661-8ddd-cbaeee0e2178] +description = "1 is I" + +[f088f064-2d35-4476-9a41-f576da3f7b03] +description = "2 is II" + +[b374a79c-3bea-43e6-8db8-1286f79c7106] +description = "3 is III" + +[05a0a1d4-a140-4db1-82e8-fcc21fdb49bb] +description = "4 is IV" + +[57c0f9ad-5024-46ab-975d-de18c430b290] +description = "5 is V" + +[20a2b47f-e57f-4797-a541-0b3825d7f249] +description = "6 is VI" + +[ff3fb08c-4917-4aab-9f4e-d663491d083d] +description = "9 is IX" + +[6d1d82d5-bf3e-48af-9139-87d7165ed509] +description = "16 is XVI" + +[2bda64ca-7d28-4c56-b08d-16ce65716cf6] +description = "27 is XXVII" + +[a1f812ef-84da-4e02-b4f0-89c907d0962c] +description = "48 is XLVIII" + +[607ead62-23d6-4c11-a396-ef821e2e5f75] +description = "49 is XLIX" + +[d5b283d4-455d-4e68-aacf-add6c4b51915] +description = "59 is LIX" + +[4465ffd5-34dc-44f3-ada5-56f5007b6dad] +description = "66 is LXVI" + +[46b46e5b-24da-4180-bfe2-2ef30b39d0d0] +description = "93 is XCIII" + +[30494be1-9afb-4f84-9d71-db9df18b55e3] +description = "141 is CXLI" + +[267f0207-3c55-459a-b81d-67cec7a46ed9] +description = "163 is CLXIII" + +[902ad132-0b4d-40e3-8597-ba5ed611dd8d] +description = "166 is CLXVI" + +[cdb06885-4485-4d71-8bfb-c9d0f496b404] +description = "402 is CDII" + +[6b71841d-13b2-46b4-ba97-dec28133ea80] +description = "575 is DLXXV" + +[dacb84b9-ea1c-4a61-acbb-ce6b36674906] +description = "666 is DCLXVI" + +[432de891-7fd6-4748-a7f6-156082eeca2f] +description = "911 is CMXI" + +[e6de6d24-f668-41c0-88d7-889c0254d173] +description = "1024 is MXXIV" + +[efbe1d6a-9f98-4eb5-82bc-72753e3ac328] +description = "1666 is MDCLXVI" + +[bb550038-d4eb-4be2-a9ce-f21961ac3bc6] +description = "3000 is MMM" + +[3bc4b41c-c2e6-49d9-9142-420691504336] +description = "3001 is MMMI" + +[2f89cad7-73f6-4d1b-857b-0ef531f68b7e] +description = "3888 is MMMDCCCLXXXVIII" + +[4e18e96b-5fbb-43df-a91b-9cb511fe0856] +description = "3999 is MMMCMXCIX" diff --git a/exercises/practice/roman-numerals/roman-numerals-test.arr b/exercises/practice/roman-numerals/roman-numerals-test.arr new file mode 100644 index 00000000..ae276dda --- /dev/null +++ b/exercises/practice/roman-numerals/roman-numerals-test.arr @@ -0,0 +1,166 @@ +use context starter2024 + +include file("roman-numerals.arr") + +fun I(): + check "1 is I": + to-roman(1) is "I" + end +end + +fun II(): + check "2 is II": + to-roman(2) is "II" + end +end + +fun III(): + check "3 is III": + to-roman(3) is "III" + end +end + +fun IV(): + check "4 is IV": + to-roman(4) is "IV" + end +end + +fun V(): + check "5 is V": + to-roman(5) is "V" + end +end + +fun VI(): + check "6 is VI": + to-roman(6) is "VI" + end +end + +fun IX(): + check "9 is IX": + to-roman(9) is "IX" + end +end + +fun XVI(): + check "16 is XVI": + to-roman(16) is "XVI" + end +end + +fun XXVII(): + check "27 is XXVII": + to-roman(27) is "XXVII" + end +end + +fun XLVIII(): + check "48 is XLVIII": + to-roman(48) is "XLVIII" + end +end + +fun XLIX(): + check "49 is XLIX": + to-roman(49) is "XLIX" + end +end + +fun LIX(): + check "59 is LIX": + to-roman(59) is "LIX" + end +end + +fun LXVI(): + check "66 is LXVI": + to-roman(66) is "LXVI" + end +end + +fun XCIII(): + check "93 is XCIII": + to-roman(93) is "XCIII" + end +end + +fun CXLI(): + check "141 is CXLI": + to-roman(141) is "CXLI" + end +end + +fun CLXIII(): + check "163 is CLXIII": + to-roman(163) is "CLXIII" + end +end + +fun CLXVI(): + check "166 is CLXVI": + to-roman(166) is "CLXVI" + end +end + +fun CDII(): + check "402 is CDII": + to-roman(402) is "CDII" + end +end + +fun DLXXV(): + check "575 is DLXXV": + to-roman(575) is "DLXXV" + end +end + +fun DCLXVI(): + check "666 is DCLXVI": + to-roman(666) is "DCLXVI" + end +end + +fun CMXI(): + check "911 is CMXI": + to-roman(911) is "CMXI" + end +end + +fun MXXIV(): + check "1024 is MXXIV": + to-roman(1024) is "MXXIV" + end +end + +fun MDCLXVI(): + check "1666 is MDCLXVI": + to-roman(1666) is "MDCLXVI" + end +end + +fun MMM(): + check "3000 is MMM": + to-roman(3000) is "MMM" + end +end + +fun MMMI(): + check "3001 is MMMI": + to-roman(3001) is "MMMI" + end +end + +fun MMMDCCCLXXXVIII(): + check "3888 is MMMDCCCLXXXVIII": + to-roman(3888) is "MMMDCCCLXXXVIII" + end +end + +fun MMMCMXCIX(): + check "3999 is MMMCMXCIX": + to-roman(3999) is "MMMCMXCIX" + end +end + diff --git a/exercises/practice/roman-numerals/roman-numerals.arr b/exercises/practice/roman-numerals/roman-numerals.arr new file mode 100644 index 00000000..1670ad11 --- /dev/null +++ b/exercises/practice/roman-numerals/roman-numerals.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: to-roman end + +fun to-roman(number): + raise("Please implement the to-roman function") +end diff --git a/exercises/practice/scrabble-score/.docs/instructions.md b/exercises/practice/scrabble-score/.docs/instructions.md new file mode 100644 index 00000000..738f928c --- /dev/null +++ b/exercises/practice/scrabble-score/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to compute a word's Scrabble score by summing the values of its letters. + +The letters are valued as follows: + +| Letter | Value | +| ---------------------------- | ----- | +| A, E, I, O, U, L, N, R, S, T | 1 | +| D, G | 2 | +| B, C, M, P | 3 | +| F, H, V, W, Y | 4 | +| K | 5 | +| J, X | 8 | +| Q, Z | 10 | + +For example, the word "cabbage" is worth 14 points: + +- 3 points for C +- 1 point for A +- 3 points for B +- 3 points for B +- 1 point for A +- 2 points for G +- 1 point for E diff --git a/exercises/practice/scrabble-score/.docs/introduction.md b/exercises/practice/scrabble-score/.docs/introduction.md new file mode 100644 index 00000000..8821f240 --- /dev/null +++ b/exercises/practice/scrabble-score/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +[Scrabble][wikipedia] is a word game where players place letter tiles on a board to form words. +Each letter has a value. +A word's score is the sum of its letters' values. + +[wikipedia]: https://en.wikipedia.org/wiki/Scrabble diff --git a/exercises/practice/scrabble-score/.meta/config.json b/exercises/practice/scrabble-score/.meta/config.json new file mode 100644 index 00000000..2720b2e8 --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "scrabble-score.arr" + ], + "test": [ + "scrabble-score-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a word, compute the Scrabble score for that word.", + "source": "Inspired by the Extreme Startup game", + "source_url": "https://github.com/rchatley/extreme_startup" +} diff --git a/exercises/practice/scrabble-score/.meta/example.arr b/exercises/practice/scrabble-score/.meta/example.arr new file mode 100644 index 00000000..92a8786b --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/example.arr @@ -0,0 +1,57 @@ +use context starter2024 + +provide: score end + +include string-dict + +POINTS = [string-dict: + "A", 1, + "E", 1, + "I", 1, + "O", 1, + "U", 1, + "L", 1, + "N", 1, + "R", 1, + "S", 1, + "T", 1, + "D", 2, + "G", 2, + "B", 3, + "C", 3, + "M", 3, + "P", 3, + "F", 4, + "H", 4, + "V", 4, + "W", 4, + "Y", 4, + "K", 5, + "J", 8, + "X", 8, + "Q", 10, + "Z", 10, +] + +fun score(word): + uppercased = string-toupper(word) + counts = string-explode(uppercased).foldl( + lam(char, acc): + new-value = acc.get(char).or-else(0) + 1 + acc.set(char, new-value) + end, + [string-dict: ] + ) + + counts.keys().to-list().foldl( + lam(char, acc): + ask: + | POINTS.has-key(char) then: + points = counts.get-value(char) * POINTS.get-value(char) + acc + points + | otherwise: + acc + end + end, + 0) +end diff --git a/exercises/practice/scrabble-score/.meta/tests.toml b/exercises/practice/scrabble-score/.meta/tests.toml new file mode 100644 index 00000000..33a873c0 --- /dev/null +++ b/exercises/practice/scrabble-score/.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. + +[f46cda29-1ca5-4ef2-bd45-388a767e3db2] +description = "lowercase letter" + +[f7794b49-f13e-45d1-a933-4e48459b2201] +description = "uppercase letter" + +[eaba9c76-f9fa-49c9-a1b0-d1ba3a5b31fa] +description = "valuable letter" + +[f3c8c94e-bb48-4da2-b09f-e832e103151e] +description = "short word" + +[71e3d8fa-900d-4548-930e-68e7067c4615] +description = "short, valuable word" + +[d3088ad9-570c-4b51-8764-c75d5a430e99] +description = "medium word" + +[fa20c572-ad86-400a-8511-64512daac352] +description = "medium, valuable word" + +[9336f0ba-9c2b-4fa0-bd1c-2e2d328cf967] +description = "long, mixed-case word" + +[1e34e2c3-e444-4ea7-b598-3c2b46fd2c10] +description = "english-like word" + +[4efe3169-b3b6-4334-8bae-ff4ef24a7e4f] +description = "empty input" + +[3b305c1c-f260-4e15-a5b5-cb7d3ea7c3d7] +description = "entire alphabet available" diff --git a/exercises/practice/scrabble-score/scrabble-score-test.arr b/exercises/practice/scrabble-score/scrabble-score-test.arr new file mode 100644 index 00000000..88a16843 --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("scrabble-score.arr") + +check "lowercase letter": + score("a") is 1 +end + +check "uppercase letter": + score("A") is 1 +end + +check "valuable letter": + score("f") is 4 +end + +check "short word": + score("at") is 2 +end + +check "short, valuable word": + score("zoo") is 12 +end + +check "medium word": + score("street") is 6 +end + +check "medium, valuable word": + score("quirky") is 22 +end + +check "long, mixed-case word": + score("OxyphenButazone") is 41 +end + +check "english-like word": + score("pinata") is 8 +end + +check "empty input": + score("") is 0 +end + +check "entire alphabet available": + score("abcdefghijklmnopqrstuvwxyz") is 87 +end + diff --git a/exercises/practice/scrabble-score/scrabble-score.arr b/exercises/practice/scrabble-score/scrabble-score.arr new file mode 100644 index 00000000..a76a5c75 --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: score end + +fun score(word): + raise("Please implement the score function") +end diff --git a/exercises/practice/secret-handshake/.docs/instructions.md b/exercises/practice/secret-handshake/.docs/instructions.md new file mode 100644 index 00000000..d2120b9b --- /dev/null +++ b/exercises/practice/secret-handshake/.docs/instructions.md @@ -0,0 +1,48 @@ +# Instructions + +Your task is to convert a number between 1 and 31 to a sequence of actions in the secret handshake. + +The sequence of actions is chosen by looking at the rightmost five digits of the number once it's been converted to binary. +Start at the right-most digit and move left. + +The actions for each number place are: + +```plaintext +00001 = wink +00010 = double blink +00100 = close your eyes +01000 = jump +10000 = Reverse the order of the operations in the secret handshake. +``` + +Let's use the number `9` as an example: + +- 9 in binary is `1001`. +- The digit that is farthest to the right is 1, so the first action is `wink`. +- Going left, the next digit is 0, so there is no double-blink. +- Going left again, the next digit is 0, so you leave your eyes open. +- Going left again, the next digit is 1, so you jump. + +That was the last digit, so the final code is: + +```plaintext +wink, jump +``` + +Given the number 26, which is `11010` in binary, we get the following actions: + +- double blink +- jump +- reverse actions + +The secret handshake for 26 is therefore: + +```plaintext +jump, double blink +``` + +~~~~exercism/note +If you aren't sure what binary is or how it works, check out [this binary tutorial][intro-to-binary]. + +[intro-to-binary]: https://medium.com/basecs/bits-bytes-building-with-binary-13cb4289aafa +~~~~ diff --git a/exercises/practice/secret-handshake/.docs/introduction.md b/exercises/practice/secret-handshake/.docs/introduction.md new file mode 100644 index 00000000..176b92e8 --- /dev/null +++ b/exercises/practice/secret-handshake/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +You are starting a secret coding club with some friends and friends-of-friends. +Not everyone knows each other, so you and your friends have decided to create a secret handshake that you can use to recognize that someone is a member. +You don't want anyone who isn't in the know to be able to crack the code. + +You've designed the code so that one person says a number between 1 and 31, and the other person turns it into a series of actions. diff --git a/exercises/practice/secret-handshake/.meta/config.json b/exercises/practice/secret-handshake/.meta/config.json new file mode 100644 index 00000000..347afb6b --- /dev/null +++ b/exercises/practice/secret-handshake/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "secret-handshake.arr" + ], + "test": [ + "secret-handshake-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a decimal number, convert it to the appropriate sequence of events for a secret handshake.", + "source": "Bert, in Mary Poppins", + "source_url": "https://www.imdb.com/title/tt0058331/quotes/?item=qt0437047" +} diff --git a/exercises/practice/secret-handshake/.meta/example.arr b/exercises/practice/secret-handshake/.meta/example.arr new file mode 100644 index 00000000..b7b54d4a --- /dev/null +++ b/exercises/practice/secret-handshake/.meta/example.arr @@ -0,0 +1,31 @@ +use context starter2024 + +provide: commands end + +import lists as L + +fun commands(binary): + to-commands = lam(index, acc, elt): + ask: + | elt == "1" then: + ask: + | index == 0 then: + acc.push("wink") + | index == 1 then: + acc.push("double blink") + | index == 2 then: + acc.push("close your eyes") + | index == 3 then: + acc.push("jump") + | index == 4 then: + L.reverse(acc) + end + | otherwise: + acc + end + end + + digits = L.reverse(string-explode(binary)) + value = L.fold_n(to-commands, 0, [list: ], digits) + L.reverse(value) +end diff --git a/exercises/practice/secret-handshake/.meta/tests.toml b/exercises/practice/secret-handshake/.meta/tests.toml new file mode 100644 index 00000000..f318e528 --- /dev/null +++ b/exercises/practice/secret-handshake/.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. + +[b8496fbd-6778-468c-8054-648d03c4bb23] +description = "wink for 1" + +[83ec6c58-81a9-4fd1-bfaf-0160514fc0e3] +description = "double blink for 10" + +[0e20e466-3519-4134-8082-5639d85fef71] +description = "close your eyes for 100" + +[b339ddbb-88b7-4b7d-9b19-4134030d9ac0] +description = "jump for 1000" + +[40499fb4-e60c-43d7-8b98-0de3ca44e0eb] +description = "combine two actions" + +[9730cdd5-ef27-494b-afd3-5c91ad6c3d9d] +description = "reverse two actions" + +[0b828205-51ca-45cd-90d5-f2506013f25f] +description = "reversing one action gives the same action" + +[9949e2ac-6c9c-4330-b685-2089ab28b05f] +description = "reversing no actions still gives no actions" + +[23fdca98-676b-4848-970d-cfed7be39f81] +description = "all possible actions" + +[ae8fe006-d910-4d6f-be00-54b7c3799e79] +description = "reverse all possible actions" + +[3d36da37-b31f-4cdb-a396-d93a2ee1c4a5] +description = "do nothing for zero" diff --git a/exercises/practice/secret-handshake/secret-handshake-test.arr b/exercises/practice/secret-handshake/secret-handshake-test.arr new file mode 100644 index 00000000..c8c2289b --- /dev/null +++ b/exercises/practice/secret-handshake/secret-handshake-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("secret-handshake.arr") + +check "wink for 1": + commands("00001") is [list: "wink"] +end + +check "double blink for 10": + commands("00010") is [list: "double blink"] +end + +check "close your eyes for 100": + commands("00100") is [list: "close your eyes"] +end + +check "jump for 1000": + commands("01000") is [list: "jump"] +end + +check "combine two actions": + commands("00011") is [list: "wink", "double blink"] +end + +check "reverse two actions": + commands("10011") is [list: "double blink", "wink"] +end + +check "reversing one action gives the same action": + commands("11000") is [list: "jump"] +end + +check "reversing no actions still gives no actions": + commands("10000") is [list: ] +end + +check "all possible actions": + commands("01111") is [list: "wink", "double blink", "close your eyes", "jump"] +end + +check "reverse all possible actions": + commands("11111") is [list: "jump", "close your eyes", "double blink", "wink"] +end + +check "do nothing for zero": + commands("00000") is [list: ] +end + diff --git a/exercises/practice/secret-handshake/secret-handshake.arr b/exercises/practice/secret-handshake/secret-handshake.arr new file mode 100644 index 00000000..c6134562 --- /dev/null +++ b/exercises/practice/secret-handshake/secret-handshake.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: commands end + +fun commands(binary): + raise("Please implement the commands function") +end diff --git a/exercises/practice/series/.docs/instructions.md b/exercises/practice/series/.docs/instructions.md new file mode 100644 index 00000000..fd97a670 --- /dev/null +++ b/exercises/practice/series/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Given a string of digits, output all the contiguous substrings of length `n` in that string in the order that they appear. + +For example, the string "49142" has the following 3-digit series: + +- "491" +- "914" +- "142" + +And the following 4-digit series: + +- "4914" +- "9142" + +And if you ask for a 6-digit series from a 5-digit string, you deserve whatever you get. + +Note that these series are only required to occupy _adjacent positions_ in the input; +the digits need not be _numerically consecutive_. diff --git a/exercises/practice/series/.meta/config.json b/exercises/practice/series/.meta/config.json new file mode 100644 index 00000000..41f9a355 --- /dev/null +++ b/exercises/practice/series/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "series.arr" + ], + "test": [ + "series-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a string of digits, output all the contiguous substrings of length `n` in that string.", + "source": "A subset of the Problem 8 at Project Euler", + "source_url": "https://projecteuler.net/problem=8" +} diff --git a/exercises/practice/series/.meta/example.arr b/exercises/practice/series/.meta/example.arr new file mode 100644 index 00000000..e262c8a8 --- /dev/null +++ b/exercises/practice/series/.meta/example.arr @@ -0,0 +1,19 @@ +use context starter2024 + +provide: slices end + +fun slices(series, len): + ask: + | series == "" then: + raise("series cannot be empty") + | len == 0 then: + raise("slice length cannot be zero") + | len < 0 then: + raise("slice length cannot be negative") + | string-length(series) < len then: + raise("slice length cannot be greater than series length") + | otherwise: + range(0, (string-length(series) - len) + 1).map( + lam(idx): string-substring(series, idx, idx + len) end) + end +end diff --git a/exercises/practice/series/.meta/tests.toml b/exercises/practice/series/.meta/tests.toml new file mode 100644 index 00000000..9696f51f --- /dev/null +++ b/exercises/practice/series/.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. + +[7ae7a46a-d992-4c2a-9c15-a112d125ebad] +description = "slices of one from one" + +[3143b71d-f6a5-4221-aeae-619f906244d2] +description = "slices of one from two" + +[dbb68ff5-76c5-4ccd-895a-93dbec6d5805] +description = "slices of two" + +[19bbea47-c987-4e11-a7d1-e103442adf86] +description = "slices of two overlap" + +[8e17148d-ba0a-4007-a07f-d7f87015d84c] +description = "slices can include duplicates" + +[bd5b085e-f612-4f81-97a8-6314258278b0] +description = "slices of a long series" + +[6d235d85-46cf-4fae-9955-14b6efef27cd] +description = "slice length is too large" + +[d7957455-346d-4e47-8e4b-87ed1564c6d7] +description = "slice length is way too large" + +[d34004ad-8765-4c09-8ba1-ada8ce776806] +description = "slice length cannot be zero" + +[10ab822d-8410-470a-a85d-23fbeb549e54] +description = "slice length cannot be negative" + +[c7ed0812-0e4b-4bf3-99c4-28cbbfc246a2] +description = "empty series is invalid" diff --git a/exercises/practice/series/series-test.arr b/exercises/practice/series/series-test.arr new file mode 100644 index 00000000..cf0b132f --- /dev/null +++ b/exercises/practice/series/series-test.arr @@ -0,0 +1,48 @@ +use context starter2024 + +include file("series.arr") + +check "slices of one from one": + slices("1", 1) is [list: "1"] +end + +check "slices of one from two": + slices("12", 1) is [list: "1", "2"] +end + +check "slices of two": + slices("35", 2) is [list: "35"] +end + +check "slices of two overlap": + slices("9142", 2) is [list: "91", "14", "42"] +end + +check "slices can include duplicates": + slices("777777", 3) is [list: "777", "777", "777", "777"] +end + +check "slices of a long series": + slices("918493904243", 5) is [list: "91849", "18493", "84939", "49390", "93904", "39042", "90424", "04243"] +end + +check "slice length is too large": + slices("12345", 6) raises "slice length cannot be greater than series length" +end + +check "slice length is way too large": + slices("12345", 42) raises "slice length cannot be greater than series length" +end + +check "slice length cannot be zero": + slices("12345", 0) raises "slice length cannot be zero" +end + +check "slice length cannot be negative": + slices("12345", -1) raises "slice length cannot be negative" +end + +check "empty series is invalid": + slices("", 1) raises "series cannot be empty" +end + diff --git a/exercises/practice/series/series.arr b/exercises/practice/series/series.arr new file mode 100644 index 00000000..471ff278 --- /dev/null +++ b/exercises/practice/series/series.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: slices end + +fun slices(series, len): + raise("Implement the slices function") +end diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md new file mode 100644 index 00000000..71292e17 --- /dev/null +++ b/exercises/practice/sieve/.docs/instructions.md @@ -0,0 +1,101 @@ +# Instructions + +Your task is to create a program that implements the Sieve of Eratosthenes algorithm to find all prime numbers less than or equal to a given number. + +A prime number is a number larger than 1 that is only divisible by 1 and itself. +For example, 2, 3, 5, 7, 11, and 13 are prime numbers. +By contrast, 6 is _not_ a prime number as it not only divisible by 1 and itself, but also by 2 and 3. + +To use the Sieve of Eratosthenes, first, write out all the numbers from 2 up to and including your given number. +Then, follow these steps: + +1. Find the next unmarked number (skipping over marked numbers). + This is a prime number. +2. Mark all the multiples of that prime number as **not** prime. + +Repeat the steps until you've gone through every number. +At the end, all the unmarked numbers are prime. + +~~~~exercism/note +The Sieve of Eratosthenes marks off multiples of each prime using addition (repeatedly adding the prime) or multiplication (directly computing its multiples), rather than checking each number for divisibility. + +The tests don't check that you've implemented the algorithm, only that you've come up with the correct primes. +~~~~ + +## Example + +Let's say you're finding the primes less than or equal to 10. + +- Write out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked. + + ```text + 2 3 4 5 6 7 8 9 10 + ``` + +- 2 is unmarked and is therefore a prime. + Mark 4, 6, 8 and 10 as "not prime". + + ```text + 2 3 [4] 5 [6] 7 [8] 9 [10] + ↑ + ``` + +- 3 is unmarked and is therefore a prime. + Mark 6 and 9 as not prime _(marking 6 is optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 4 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 5 is unmarked and is therefore a prime. + Mark 10 as not prime _(optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 6 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 7 is unmarked and is therefore a prime. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 8 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 9 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 10 is marked as "not prime", so we stop as there are no more numbers to check. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +You've examined all the numbers and found that 2, 3, 5, and 7 are still unmarked, meaning they're the primes less than or equal to 10. diff --git a/exercises/practice/sieve/.docs/introduction.md b/exercises/practice/sieve/.docs/introduction.md new file mode 100644 index 00000000..f6c1cf79 --- /dev/null +++ b/exercises/practice/sieve/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +You bought a big box of random computer parts at a garage sale. +You've started putting the parts together to build custom computers. + +You want to test the performance of different combinations of parts, and decide to create your own benchmarking program to see how your computers compare. +You choose the famous "Sieve of Eratosthenes" algorithm, an ancient algorithm, but one that should push your computers to the limits. diff --git a/exercises/practice/sieve/.meta/config.json b/exercises/practice/sieve/.meta/config.json new file mode 100644 index 00000000..b0791e56 --- /dev/null +++ b/exercises/practice/sieve/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "sieve.arr" + ], + "test": [ + "sieve-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.", + "source": "Sieve of Eratosthenes at Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes" +} diff --git a/exercises/practice/sieve/.meta/example.arr b/exercises/practice/sieve/.meta/example.arr new file mode 100644 index 00000000..404ed381 --- /dev/null +++ b/exercises/practice/sieve/.meta/example.arr @@ -0,0 +1,28 @@ +use context starter2024 + +provide: primes end + +fun primes(limit) block: + marks = array-of(true, limit + 1) + + var ps = [list:] + for each(i from range(2, limit + 1)): + when marks.get-now(i) block: + ps := ps.push(i) + mark-multiples(marks, i, limit) + end + end + + ps.reverse() +end + +fun mark-multiples(a :: Array, prime :: NumInteger, limit :: NumPositive): + rec do-mark = lam(i): + when i <= limit block: + a.set-now(i, false) + do-mark(i + prime) + end + end + + do-mark(prime * prime) +end diff --git a/exercises/practice/sieve/.meta/tests.toml b/exercises/practice/sieve/.meta/tests.toml new file mode 100644 index 00000000..fec5e1a1 --- /dev/null +++ b/exercises/practice/sieve/.meta/tests.toml @@ -0,0 +1,25 @@ +# 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. + +[88529125-c4ce-43cc-bb36-1eb4ddd7b44f] +description = "no primes under two" + +[4afe9474-c705-4477-9923-840e1024cc2b] +description = "find first prime" + +[974945d8-8cd9-4f00-9463-7d813c7f17b7] +description = "find primes up to 10" + +[2e2417b7-3f3a-452a-8594-b9af08af6d82] +description = "limit is prime" + +[92102a05-4c7c-47de-9ed0-b7d5fcd00f21] +description = "find primes up to 1000" diff --git a/exercises/practice/sieve/sieve-test.arr b/exercises/practice/sieve/sieve-test.arr new file mode 100644 index 00000000..34d45508 --- /dev/null +++ b/exercises/practice/sieve/sieve-test.arr @@ -0,0 +1,40 @@ +use context starter2024 + +include file("sieve.arr") + +check "no primes under two": + primes(1) is [list:] +end + + + fun find-first-prime(): + check "find first prime": + primes(2) is [list:2] + end +end + +check "find primes up to 10": + primes(10) is [list:2, 3, 5, 7] +end + +check "limit is prime": + primes(13) is [list:2, 3, 5, 7, 11, 13] +end + +check "find primes up to 1000": + primes(1000) is [list: + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, + 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, + 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, + 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, + 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, + 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, + 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, + 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 + ] +end + diff --git a/exercises/practice/sieve/sieve.arr b/exercises/practice/sieve/sieve.arr new file mode 100644 index 00000000..2f09e834 --- /dev/null +++ b/exercises/practice/sieve/sieve.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: primes end + +fun primes(limit): + raise("Please implement the primes function") +end diff --git a/exercises/practice/simple-linked-list/.docs/instructions.md b/exercises/practice/simple-linked-list/.docs/instructions.md new file mode 100644 index 00000000..04640b1f --- /dev/null +++ b/exercises/practice/simple-linked-list/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Write a prototype of the music player application. + +For the prototype, each song will simply be represented by a number. +Given a range of numbers (the song IDs), create a singly linked list. + +Given a singly linked list, you should be able to reverse the list to play the songs in the opposite order. + +~~~~exercism/note +The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures. + +The simplest kind of linked list is a **singly** linked list. +That means that each element (or "node") contains data, along with something that points to the next node in the list. + +If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings. + +[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d +~~~~ diff --git a/exercises/practice/simple-linked-list/.docs/introduction.md b/exercises/practice/simple-linked-list/.docs/introduction.md new file mode 100644 index 00000000..0e1df72f --- /dev/null +++ b/exercises/practice/simple-linked-list/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +You work for a music streaming company. + +You've been tasked with creating a playlist feature for your music player application. diff --git a/exercises/practice/simple-linked-list/.meta/config.json b/exercises/practice/simple-linked-list/.meta/config.json new file mode 100644 index 00000000..177392b6 --- /dev/null +++ b/exercises/practice/simple-linked-list/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "simple-linked-list.arr" + ], + "test": [ + "simple-linked-list-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Write a simple linked list implementation that uses Elements and a List.", + "source": "Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists.", + "source_url": "https://web.archive.org/web/20160731005714/http://brpreiss.com/books/opus8/html/page96.html" +} diff --git a/exercises/practice/simple-linked-list/.meta/example.arr b/exercises/practice/simple-linked-list/.meta/example.arr new file mode 100644 index 00000000..c6e127ac --- /dev/null +++ b/exercises/practice/simple-linked-list/.meta/example.arr @@ -0,0 +1,37 @@ +use context starter2024 + +provide-types * + +data LinkedList: + | empty-list with: + method foldl(_, _, acc): acc end, + | linked-list(head, tail) with: + method foldl(self, f, acc): + self.tail.foldl(f, f(self.head, acc)) + end, + method get-head(self): + self.head + end, + method get-tail(self): + self.tail + end +sharing: + method length(self): + cases(LinkedList) self: + | empty-list => 0 + | linked-list(_, r) => + r.foldl(lam(elt, acc): acc + 1 end, 1) + end + end, + method push(self, val): linked-list(val, self) end, + method reversed(self): + self.foldl( + lam(elt, acc): linked-list(elt, acc) end, + empty-list) + end, + method to-list(self): + self.foldl( + lam(elt, acc): acc.push(elt) end, + [list: ]) + end +end diff --git a/exercises/practice/simple-linked-list/simple-linked-list-test.arr b/exercises/practice/simple-linked-list/simple-linked-list-test.arr new file mode 100644 index 00000000..53b7c957 --- /dev/null +++ b/exercises/practice/simple-linked-list/simple-linked-list-test.arr @@ -0,0 +1,115 @@ +use context starter2024 + +include file("simple-linked-list.arr") + +# No canonical data available for this exercise so these have been ported from Python and F# + +check "empty list has length zero": + empty-list.length() is 0 +end + +check "singleton list has length one": + linked-list(1, empty-list).length() is 1 +end + +check "non-empty list has correct length": + result = empty-list + ^ linked-list(3, _) + ^ linked-list(2, _) + ^ linked-list(1, _) + + result.length() is 3 +end + +check "singleton list has head": + linked-list(1, empty-list).get-head() is 1 +end + +check "non-empty list has correct head": + result = empty-list + ^ linked-list(1, _) + ^ linked-list(2, _) + + result.get-head() is 2 +end + +check "can push to non-empty list": + result = empty-list + ^ linked-list(3, _) + ^ linked-list(2, _) + ^ linked-list(1, _) + + result.push(4).length() is 4 +end + +check "pushing to empty list changes head": + result = empty-list.push(5) + + result.length() is 1 + result.get-head() is 5 +end + +check "can access second element in list": + result = empty-list + ^ linked-list(3, _) + ^ linked-list(4, _) + ^ linked-list(5, _) + + result.get-tail().get-head() is 4 +end + +check "test singleton list head has no tail": + linked-list(1, empty-list).get-tail() is empty-list +end + +check "non-empty list traverse": + my-list = range(0, 11).foldl( + lam(elt, acc): + acc.push(elt) + end, + empty-list) + + traversed = range(-10, 0).foldl( + lam(n, acc) block: + acc.get-head() is num-abs(n) + acc.get-tail() + end, + my-list) + + traversed.get-tail() is empty-list +end + +check "empty linked list to list is empty": + empty-list.to-list() is [list: ] +end + +check "singleton linked list to list with single element": + linked-list(1, empty-list).to-list() is [list: 1] +end + +check "non-empty linked list to list is list with all elements": + result = empty-list + ^ linked-list(3, _) + ^ linked-list(2, _) + ^ linked-list(1, _) + + result.to-list() is [list: 3, 2, 1] +end + +check "reversed empty list is empty list": + empty-list.reversed().to-list() is [list: ] +end + +check "reversed singleton list is same list": + linked-list(1, empty-list).reversed() is linked-list(1, empty-list) +end + +check "reverse non-empty list": + result = empty-list + ^ linked-list(3, _) + ^ linked-list(2, _) + ^ linked-list(1, _) + + result.reversed().to-list() is [list: 1, 2, 3] +end + diff --git a/exercises/practice/simple-linked-list/simple-linked-list.arr b/exercises/practice/simple-linked-list/simple-linked-list.arr new file mode 100644 index 00000000..65d69db2 --- /dev/null +++ b/exercises/practice/simple-linked-list/simple-linked-list.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide-types * + +data LinkedList: + ... # Replace the dots with your implementation +end diff --git a/exercises/practice/space-age/.docs/instructions.md b/exercises/practice/space-age/.docs/instructions.md new file mode 100644 index 00000000..f23b5e2c --- /dev/null +++ b/exercises/practice/space-age/.docs/instructions.md @@ -0,0 +1,28 @@ +# Instructions + +Given an age in seconds, calculate how old someone would be on a planet in our Solar System. + +One Earth year equals 365.25 Earth days, or 31,557,600 seconds. +If you were told someone was 1,000,000,000 seconds old, their age would be 31.69 Earth-years. + +For the other planets, you have to account for their orbital period in Earth Years: + +| Planet | Orbital period in Earth Years | +| ------- | ----------------------------- | +| Mercury | 0.2408467 | +| Venus | 0.61519726 | +| Earth | 1.0 | +| Mars | 1.8808158 | +| Jupiter | 11.862615 | +| Saturn | 29.447498 | +| Uranus | 84.016846 | +| Neptune | 164.79132 | + +~~~~exercism/note +The actual length of one complete orbit of the Earth around the sun is closer to 365.256 days (1 sidereal year). +The Gregorian calendar has, on average, 365.2425 days. +While not entirely accurate, 365.25 is the value used in this exercise. +See [Year on Wikipedia][year] for more ways to measure a year. + +[year]: https://en.wikipedia.org/wiki/Year#Summary +~~~~ diff --git a/exercises/practice/space-age/.docs/introduction.md b/exercises/practice/space-age/.docs/introduction.md new file mode 100644 index 00000000..014d7885 --- /dev/null +++ b/exercises/practice/space-age/.docs/introduction.md @@ -0,0 +1,20 @@ +# Introduction + +The year is 2525 and you've just embarked on a journey to visit all planets in the Solar System (Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune). +The first stop is Mercury, where customs require you to fill out a form (bureaucracy is apparently _not_ Earth-specific). +As you hand over the form to the customs officer, they scrutinize it and frown. +"Do you _really_ expect me to believe you're just 50 years old? +You must be closer to 200 years old!" + +Amused, you wait for the customs officer to start laughing, but they appear to be dead serious. +You realize that you've entered your age in _Earth years_, but the officer expected it in _Mercury years_! +As Mercury's orbital period around the sun is significantly shorter than Earth, you're actually a lot older in Mercury years. +After some quick calculations, you're able to provide your age in Mercury Years. +The customs officer smiles, satisfied, and waves you through. +You make a mental note to pre-calculate your planet-specific age _before_ future customs checks, to avoid such mix-ups. + +~~~~exercism/note +If you're wondering why Pluto didn't make the cut, go watch [this YouTube video][pluto-video]. + +[pluto-video]: https://www.youtube.com/watch?v=Z_2gbGXzFbs +~~~~ diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json new file mode 100644 index 00000000..dda9c671 --- /dev/null +++ b/exercises/practice/space-age/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "space-age.arr" + ], + "test": [ + "space-age-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.", + "source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.", + "source_url": "https://pine.fm/LearnToProgram/chap_01.html" +} diff --git a/exercises/practice/space-age/.meta/example.arr b/exercises/practice/space-age/.meta/example.arr new file mode 100644 index 00000000..4ac5d49c --- /dev/null +++ b/exercises/practice/space-age/.meta/example.arr @@ -0,0 +1,26 @@ +use context starter2024 + +provide: on-planet end + +include string-dict + +EARTH-SECONDS = 31557600 + +ORBITAL-RATIOS = [string-dict: + "Mercury", 0.2408467, + "Venus", 0.61519726, + "Earth", 1.00, + "Mars", 1.8808158, + "Jupiter", 11.862615, + "Saturn", 29.447498, + "Uranus", 84.016846, + "Neptune", 164.79132 +] + +fun on-planet(planet, seconds): + cases(Option) ORBITAL-RATIOS.get(planet): + | none => raise("not a planet") + | some(ratio) => + (seconds / EARTH-SECONDS) / ratio + end +end diff --git a/exercises/practice/space-age/.meta/tests.toml b/exercises/practice/space-age/.meta/tests.toml new file mode 100644 index 00000000..7957bb77 --- /dev/null +++ b/exercises/practice/space-age/.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. + +[84f609af-5a91-4d68-90a3-9e32d8a5cd34] +description = "age on Earth" + +[ca20c4e9-6054-458c-9312-79679ffab40b] +description = "age on Mercury" + +[502c6529-fd1b-41d3-8fab-65e03082b024] +description = "age on Venus" + +[9ceadf5e-a0d5-4388-9d40-2c459227ceb8] +description = "age on Mars" + +[42927dc3-fe5e-4f76-a5b5-f737fc19bcde] +description = "age on Jupiter" + +[8469b332-7837-4ada-b27c-00ee043ebcad] +description = "age on Saturn" + +[999354c1-76f8-4bb5-a672-f317b6436743] +description = "age on Uranus" + +[80096d30-a0d4-4449-903e-a381178355d8] +description = "age on Neptune" + +[57b96e2a-1178-40b7-b34d-f3c9c34e4bf4] +description = "invalid planet causes error" diff --git a/exercises/practice/space-age/space-age-test.arr b/exercises/practice/space-age/space-age-test.arr new file mode 100644 index 00000000..0ec735ca --- /dev/null +++ b/exercises/practice/space-age/space-age-test.arr @@ -0,0 +1,45 @@ +use context starter2024 + +include file("space-age.arr") + +doc: "provides a predicate that returns true if the absolute values of two numbers are less than or equal to the specified delta" +lam(actual, target): + num-abs(target - actual) <= delta +end + +check "age on Earth": + on-planet("Earth", 1000000000) is%(around(0.01)) 31.69 +end + +check "age on Mercury": + on-planet("Mercury", 2134835688) is%(around(0.01)) 280.88 +end + +check "age on Venus": + on-planet("Venus", 189839836) is%(around(0.01)) 9.78 +end + +check "age on Mars": + on-planet("Mars", 2129871239) is%(around(0.01)) 35.88 +end + +check "age on Jupiter": + on-planet("Jupiter", 901876382) is%(around(0.01)) 2.41 +end + +check "age on Saturn": + on-planet("Saturn", 2000000000) is%(around(0.01)) 2.15 +end + +check "age on Uranus": + on-planet("Uranus", 1210123456) is%(around(0.01)) 0.46 +end + +check "age on Neptune": + on-planet("Neptune", 1821023456) is%(around(0.01)) 0.35 +end + +check "invalid planet causes error": + on-planet("Sun", 680804807) raises "not a planet" +end + diff --git a/exercises/practice/space-age/space-age.arr b/exercises/practice/space-age/space-age.arr new file mode 100644 index 00000000..09ee2148 --- /dev/null +++ b/exercises/practice/space-age/space-age.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: on-planet end + +fun on-planet(planet, seconds): + raise("Implement the on-planet function") +end diff --git a/exercises/practice/square-root/.docs/instructions.md b/exercises/practice/square-root/.docs/instructions.md new file mode 100644 index 00000000..d258b868 --- /dev/null +++ b/exercises/practice/square-root/.docs/instructions.md @@ -0,0 +1,18 @@ +# Instructions + +Your task is to calculate the square root of a given number. + +- Try to avoid using the pre-existing math libraries of your language. +- As input you'll be given a positive whole number, i.e. 1, 2, 3, 4… +- You are only required to handle cases where the result is a positive whole number. + +Some potential approaches: + +- Linear or binary search for a number that gives the input number when squared. +- Successive approximation using Newton's or Heron's method. +- Calculating one digit at a time or one bit at a time. + +You can check out the Wikipedia pages on [integer square root][integer-square-root] and [methods of computing square roots][computing-square-roots] to help with choosing a method of calculation. + +[integer-square-root]: https://en.wikipedia.org/wiki/Integer_square_root +[computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots diff --git a/exercises/practice/square-root/.docs/introduction.md b/exercises/practice/square-root/.docs/introduction.md new file mode 100644 index 00000000..1d692934 --- /dev/null +++ b/exercises/practice/square-root/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +We are launching a deep space exploration rocket and we need a way to make sure the navigation system stays on target. + +As the first step in our calculation, we take a target number and find its square root (that is, the number that when multiplied by itself equals the target number). + +The journey will be very long. +To make the batteries last as long as possible, we had to make our rocket's onboard computer very power efficient. +Unfortunately that means that we can't rely on fancy math libraries and functions, as they use more power. +Instead we want to implement our own square root calculation. diff --git a/exercises/practice/square-root/.meta/config.json b/exercises/practice/square-root/.meta/config.json new file mode 100644 index 00000000..21fc47ec --- /dev/null +++ b/exercises/practice/square-root/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "square-root.arr" + ], + "test": [ + "square-root-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a natural radicand, return its square root.", + "source": "wolf99", + "source_url": "https://github.com/exercism/problem-specifications/pull/1582" +} diff --git a/exercises/practice/square-root/.meta/example.arr b/exercises/practice/square-root/.meta/example.arr new file mode 100644 index 00000000..f197a65e --- /dev/null +++ b/exercises/practice/square-root/.meta/example.arr @@ -0,0 +1,16 @@ +use context starter2024 + +provide: square-root end + +fun square-root(number): + rec do-square-root = lam(n): + ask: + | num-expt(n, 2) <> number then: + do-square-root(n + 1) + | otherwise: + n + end + end + + do-square-root(0) +end diff --git a/exercises/practice/square-root/.meta/tests.toml b/exercises/practice/square-root/.meta/tests.toml new file mode 100644 index 00000000..ead7882f --- /dev/null +++ b/exercises/practice/square-root/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[9b748478-7b0a-490c-b87a-609dacf631fd] +description = "root of 1" + +[7d3aa9ba-9ac6-4e93-a18b-2e8b477139bb] +description = "root of 4" + +[6624aabf-3659-4ae0-a1c8-25ae7f33c6ef] +description = "root of 25" + +[93beac69-265e-4429-abb1-94506b431f81] +description = "root of 81" + +[fbddfeda-8c4f-4bc4-87ca-6991af35360e] +description = "root of 196" + +[c03d0532-8368-4734-a8e0-f96a9eb7fc1d] +description = "root of 65025" diff --git a/exercises/practice/square-root/square-root-test.arr b/exercises/practice/square-root/square-root-test.arr new file mode 100644 index 00000000..ed4c5477 --- /dev/null +++ b/exercises/practice/square-root/square-root-test.arr @@ -0,0 +1,28 @@ +use context starter2024 + +include file("square-root.arr") + +check "root of 1": + square-root(1) is 1 +end + +check "root of 4": + square-root(4) is 2 +end + +check "root of 25": + square-root(25) is 5 +end + +check "root of 81": + square-root(81) is 9 +end + +check "root of 196": + square-root(196) is 14 +end + +check "root of 65025": + square-root(65025) is 255 +end + diff --git a/exercises/practice/square-root/square-root.arr b/exercises/practice/square-root/square-root.arr new file mode 100644 index 00000000..ef522828 --- /dev/null +++ b/exercises/practice/square-root/square-root.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: square-root end + +fun square-root(number): + raise("Please implement the square-root function") +end diff --git a/exercises/practice/strain/.docs/instructions.md b/exercises/practice/strain/.docs/instructions.md new file mode 100644 index 00000000..3469ae65 --- /dev/null +++ b/exercises/practice/strain/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Implement the `keep` and `discard` operation on collections. +Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false. + +For example, given the collection of numbers: + +- 1, 2, 3, 4, 5 + +And the predicate: + +- is the number even? + +Then your keep operation should produce: + +- 2, 4 + +While your discard operation should produce: + +- 1, 3, 5 + +Note that the union of keep and discard is all the elements. + +The functions may be called `keep` and `discard`, or they may need different names in order to not clash with existing functions or concepts in your language. + +## Restrictions + +Keep your hands off that filter/reject/whatchamacallit functionality provided by your standard library! +Solve this one yourself using other basic tools instead. diff --git a/exercises/practice/strain/.meta/config.json b/exercises/practice/strain/.meta/config.json new file mode 100644 index 00000000..ac01e903 --- /dev/null +++ b/exercises/practice/strain/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "strain.arr" + ], + "test": [ + "strain-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Implement the `keep` and `discard` operation on collections.", + "source": "Conversation with James Edward Gray II", + "source_url": "http://graysoftinc.com/" +} diff --git a/exercises/practice/strain/.meta/example.arr b/exercises/practice/strain/.meta/example.arr new file mode 100644 index 00000000..88c14c7e --- /dev/null +++ b/exercises/practice/strain/.meta/example.arr @@ -0,0 +1,19 @@ +use context starter2024 + +provide: keep, discard end + +fun keep(sequence, predicate): + sequence.foldl( + lam(elt, acc): + if predicate(elt): + acc.push(elt) + else: + acc + end + end, + [list: ]).reverse() +end + +fun discard(sequence, predicate): + keep(sequence, lam(elt): not(predicate(elt)) end) +end diff --git a/exercises/practice/strain/.meta/tests.toml b/exercises/practice/strain/.meta/tests.toml new file mode 100644 index 00000000..3a617b4a --- /dev/null +++ b/exercises/practice/strain/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[26af8c32-ba6a-4eb3-aa0a-ebd8f136e003] +description = "keep on empty list returns empty list" + +[f535cb4d-e99b-472a-bd52-9fa0ffccf454] +description = "keeps everything" + +[950b8e8e-f628-42a8-85e2-9b30f09cde38] +description = "keeps nothing" + +[92694259-6e76-470c-af87-156bdf75018a] +description = "keeps first and last" + +[938f7867-bfc7-449e-a21b-7b00cbb56994] +description = "keeps neither first nor last" + +[8908e351-4437-4d2b-a0f7-770811e48816] +description = "keeps strings" + +[2728036b-102a-4f1e-a3ef-eac6160d876a] +description = "keeps lists" + +[ef16beb9-8d84-451a-996a-14e80607fce6] +description = "discard on empty list returns empty list" + +[2f42f9bc-8e06-4afe-a222-051b5d8cd12a] +description = "discards everything" + +[ca990fdd-08c2-4f95-aa50-e0f5e1d6802b] +description = "discards nothing" + +[71595dae-d283-48ca-a52b-45fa96819d2f] +description = "discards first and last" + +[ae141f79-f86d-4567-b407-919eaca0f3dd] +description = "discards neither first nor last" + +[daf25b36-a59f-4f29-bcfe-302eb4e43609] +description = "discards strings" + +[a38d03f9-95ad-4459-80d1-48e937e4acaf] +description = "discards lists" diff --git a/exercises/practice/strain/strain-test.arr b/exercises/practice/strain/strain-test.arr new file mode 100644 index 00000000..24fe6f8d --- /dev/null +++ b/exercises/practice/strain/strain-test.arr @@ -0,0 +1,141 @@ +use context starter2024 + +include file("strain.arr") + +check "keep on empty list returns empty list": + input = [list: ] + predicate = lam(elt): true end + expected = [list: ] + + keep(input, predicate) is expected +end + +check "keeps everything": + input = [list: 1, 3, 5] + predicate = lam(elt): true end + expected = [list: 1, 3, 5] + + keep(input, predicate) is expected +end + +check "keeps nothing": + input = [list: 1, 3, 5] + predicate = lam(elt): false end + expected = [list: ] + + keep(input, predicate) is expected +end + +check "keeps first and last": + input = [list: 1, 2, 3] + predicate = lam(elt): num-modulo(elt, 2) == 1 end + expected = [list: 1, 3] + + keep(input, predicate) is expected +end + +check "keeps neither first nor last": + input = [list: 1, 2, 3] + predicate = lam(elt): num-modulo(elt, 2) == 0 end + expected = [list: 2] + + keep(input, predicate) is expected +end + +check "keeps strings": + input = [list: "apple", "zebra", "banana", "zombies", "cherimoya", "zealot"] + predicate = lam(elt): string-char-at(elt, 0) ^ string-equal(_, "z") end + expected = [list: "zebra", "zombies", "zealot"] + + keep(input, predicate) is expected +end + +check "keeps lists": + input = [list: + [list: 1, 2, 3], + [list: 5, 5, 5], + [list: 5, 1, 2], + [list: 2, 1, 2], + [list: 1, 5, 2], + [list: 2, 2, 1], + [list: 1, 2, 5]] + + predicate = lam(elt): elt.member(5) end + + expected = [list: + [list: 5, 5, 5], + [list: 5, 1, 2], + [list: 1, 5, 2], + [list: 1, 2, 5]] + + keep(input, predicate) is expected +end + +check "discard on empty list returns empty list": + input = [list: ] + predicate = lam(elt): true end + expected = [list: ] + + discard(input, predicate) is expected +end + +check "discards everything": + input = [list: 1, 3, 5] + predicate = lam(elt): true end + expected = [list: ] + + discard(input, predicate) is expected +end + +check "discards nothing": + input = [list: 1, 3, 5] + predicate = lam(elt): false end + expected = [list: 1, 3, 5] + + discard(input, predicate) is expected +end + +check "discards first and last": + input = [list: 1, 2, 3] + predicate = lam(elt): num-modulo(elt, 2) == 1 end + expected = [list: 2] + + discard(input, predicate) is expected +end + +check "discards neither first nor last": + input = [list: 1, 2, 3] + predicate = lam(elt): num-modulo(elt, 2) == 0 end + expected = [list: 1, 3] + + discard(input, predicate) is expected +end + +check "discards strings": + input = [list: "apple", "zebra", "banana", "zombies", "cherimoya", "zealot"] + predicate = lam(elt): string-char-at(elt, 0) ^ string-equal(_, "z") end + expected = [list: "apple", "banana", "cherimoya"] + + discard(input, predicate) is expected +end + +check "discards lists": + input = [list: + [list: 1, 2, 3], + [list: 5, 5, 5], + [list: 5, 1, 2], + [list: 2, 1, 2], + [list: 1, 5, 2], + [list: 2, 2, 1], + [list: 1, 2, 5]] + + predicate = lam(elt): elt.member(5) end + + expected = [list: + [list: 1, 2, 3], + [list: 2, 1, 2], + [list: 2, 2, 1]] + + discard(input, predicate) is expected +end + diff --git a/exercises/practice/strain/strain.arr b/exercises/practice/strain/strain.arr new file mode 100644 index 00000000..547f2d62 --- /dev/null +++ b/exercises/practice/strain/strain.arr @@ -0,0 +1,11 @@ +use context starter2024 + +provide: keep, discard end + +fun keep(sequence, predicate): + raise("please implement the keep function") +end + +fun discard(sequence, predicate): + raise("please implement the discard function") +end diff --git a/exercises/practice/triangle/.docs/instructions.md b/exercises/practice/triangle/.docs/instructions.md new file mode 100644 index 00000000..e9b053dc --- /dev/null +++ b/exercises/practice/triangle/.docs/instructions.md @@ -0,0 +1,35 @@ +# Instructions + +Determine if a triangle is equilateral, isosceles, or scalene. + +An _equilateral_ triangle has all three sides the same length. + +An _isosceles_ triangle has at least two sides the same length. +(It is sometimes specified as having exactly two sides the same length, but for the purposes of this exercise we'll say at least two.) + +A _scalene_ triangle has all sides of different lengths. + +## Note + +For a shape to be a triangle at all, all sides have to be of length > 0, and the sum of the lengths of any two sides must be greater than or equal to the length of the third side. + +~~~~exercism/note +_Degenerate triangles_ are triangles where the sum of the length of two sides is **equal** to the length of the third side, e.g. `1, 1, 2`. +We opted to not include tests for degenerate triangles in this exercise. +You may handle those situations if you wish to do so, or safely ignore them. +~~~~ + +In equations: + +Let `a`, `b`, and `c` be sides of the triangle. +Then all three of the following expressions must be true: + +```text +a + b ≥ c +b + c ≥ a +a + c ≥ b +``` + +See [Triangle Inequality][triangle-inequality] + +[triangle-inequality]: https://en.wikipedia.org/wiki/Triangle_inequality diff --git a/exercises/practice/triangle/.meta/config.json b/exercises/practice/triangle/.meta/config.json new file mode 100644 index 00000000..28686ac0 --- /dev/null +++ b/exercises/practice/triangle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "triangle.arr" + ], + "test": [ + "triangle-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Determine if a triangle is equilateral, isosceles, or scalene.", + "source": "The Ruby Koans triangle project, parts 1 & 2", + "source_url": "https://www.rubykoans.com/" +} diff --git a/exercises/practice/triangle/.meta/example.arr b/exercises/practice/triangle/.meta/example.arr new file mode 100644 index 00000000..32c7820d --- /dev/null +++ b/exercises/practice/triangle/.meta/example.arr @@ -0,0 +1,38 @@ +use context starter2024 + +provide: equilateral, isosceles, scalene end + +import lists as L +import sets as S + +fun equilateral(sides): + valid = is_triangle(sides) + ask: + | valid == true then: S.list-to-list-set(sides).size() == 1 + | otherwise: false + end +end + +fun isosceles(sides): + valid = is_triangle(sides) + ask: + | valid == true then: S.list-to-list-set(sides).size() <= 2 + | otherwise: false + end +end + +fun scalene(sides): + valid = is_triangle(sides) + ask: + | valid == true then: S.list-to-list-set(sides).size() == 3 + | otherwise: false + end +end + +fun is_triangle(sides): + sorted = sides.sort() + a = sorted.get(0) + b = sorted.get(1) + c = sorted.get(2) + ((a + b) > c) and L.all(lam(side): not(num-equal(side, 0)) end, sides) +end diff --git a/exercises/practice/triangle/.meta/tests.toml b/exercises/practice/triangle/.meta/tests.toml new file mode 100644 index 00000000..7db09164 --- /dev/null +++ b/exercises/practice/triangle/.meta/tests.toml @@ -0,0 +1,73 @@ +# 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. + +[8b2c43ac-7257-43f9-b552-7631a91988af] +description = "equilateral triangle -> all sides are equal" + +[33eb6f87-0498-4ccf-9573-7f8c3ce92b7b] +description = "equilateral triangle -> any side is unequal" + +[c6585b7d-a8c0-4ad8-8a34-e21d36f7ad87] +description = "equilateral triangle -> no sides are equal" + +[16e8ceb0-eadb-46d1-b892-c50327479251] +description = "equilateral triangle -> all zero sides is not a triangle" + +[3022f537-b8e5-4cc1-8f12-fd775827a00c] +description = "equilateral triangle -> sides may be floats" + +[cbc612dc-d75a-4c1c-87fc-e2d5edd70b71] +description = "isosceles triangle -> last two sides are equal" + +[e388ce93-f25e-4daf-b977-4b7ede992217] +description = "isosceles triangle -> first two sides are equal" + +[d2080b79-4523-4c3f-9d42-2da6e81ab30f] +description = "isosceles triangle -> first and last sides are equal" + +[8d71e185-2bd7-4841-b7e1-71689a5491d8] +description = "isosceles triangle -> equilateral triangles are also isosceles" + +[840ed5f8-366f-43c5-ac69-8f05e6f10bbb] +description = "isosceles triangle -> no sides are equal" + +[2eba0cfb-6c65-4c40-8146-30b608905eae] +description = "isosceles triangle -> first triangle inequality violation" + +[278469cb-ac6b-41f0-81d4-66d9b828f8ac] +description = "isosceles triangle -> second triangle inequality violation" + +[90efb0c7-72bb-4514-b320-3a3892e278ff] +description = "isosceles triangle -> third triangle inequality violation" + +[adb4ee20-532f-43dc-8d31-e9271b7ef2bc] +description = "isosceles triangle -> sides may be floats" + +[e8b5f09c-ec2e-47c1-abec-f35095733afb] +description = "scalene triangle -> no sides are equal" + +[2510001f-b44d-4d18-9872-2303e7977dc1] +description = "scalene triangle -> all sides are equal" + +[c6e15a92-90d9-4fb3-90a2-eef64f8d3e1e] +description = "scalene triangle -> first and second sides are equal" + +[3da23a91-a166-419a-9abf-baf4868fd985] +description = "scalene triangle -> first and third sides are equal" + +[b6a75d98-1fef-4c42-8e9a-9db854ba0a4d] +description = "scalene triangle -> second and third sides are equal" + +[70ad5154-0033-48b7-af2c-b8d739cd9fdc] +description = "scalene triangle -> may not violate triangle inequality" + +[26d9d59d-f8f1-40d3-ad58-ae4d54123d7d] +description = "scalene triangle -> sides may be floats" diff --git a/exercises/practice/triangle/triangle-test.arr b/exercises/practice/triangle/triangle-test.arr new file mode 100644 index 00000000..26d3db13 --- /dev/null +++ b/exercises/practice/triangle/triangle-test.arr @@ -0,0 +1,88 @@ +use context starter2024 + +include file("triangle.arr") + +check "equilateral triangle -> all sides are equal": + equilateral([list: 2, 2, 2]) is true +end + +check "equilateral triangle -> any side is unequal": + equilateral([list: 2, 3, 2]) is false +end + +check "equilateral triangle -> no sides are equal": + equilateral([list: 5, 4, 6]) is false +end + +check "equilateral triangle -> all zero sides is not a triangle": + equilateral([list: 0, 0, 0]) is false +end + +check "equilateral triangle -> sides may be decimals": + equilateral([list: 0.5, 0.5, 0.5]) is true +end + +check "isosceles triangle -> last two sides are equal": + isosceles([list: 3, 4, 4]) is true +end + +check "isosceles triangle -> first two sides are equal": + isosceles([list: 4, 4, 3]) is true +end + +check "isosceles triangle -> first and last sides are equal": + isosceles([list: 4, 3, 4]) is true +end + +check "isosceles triangle -> equilateral triangles are also isosceles": + isosceles([list: 4, 4, 4]) is true +end + +check "isosceles triangle -> no sides are equal": + isosceles([list: 2, 3, 4]) is false +end + +check "isosceles triangle -> first triangle inequality violation": + isosceles([list: 1, 1, 3]) is false +end + +check "isosceles triangle -> second triangle inequality violation": + isosceles([list: 1, 3, 1]) is false +end + +check "isosceles triangle -> third triangle inequality violation": + isosceles([list: 3, 1, 1]) is false +end + +check "isosceles triangle -> sides may be decimasl": + isosceles([list: 0.5, 0.4, 0.5]) is true +end + +check "scalene triangle -> no sides are equal": + scalene([list: 5, 4, 6]) is true +end + +check "scalene triangle -> all sides are equal": + scalene([list: 4, 4, 4]) is false +end + +check "scalene triangle -> first and second sides are equal": + scalene([list: 4, 4, 3]) is false +end + +check "scalene triangle -> first and third sides are equal": + scalene([list: 3, 4, 3]) is false +end + +check "scalene triangle -> second and third sides are equal": + scalene([list: 4, 3, 3]) is false +end + +check "scalene triangle -> may not violate triangle inequality": + scalene([list: 7, 3, 2]) is false +end + +check "scalene triangle -> sides may be decimals": + scalene([list: 0.5, 0.4, 0.6]) is true +end + diff --git a/exercises/practice/triangle/triangle.arr b/exercises/practice/triangle/triangle.arr new file mode 100644 index 00000000..802a4c3c --- /dev/null +++ b/exercises/practice/triangle/triangle.arr @@ -0,0 +1,15 @@ +use context starter2024 + +provide: equilateral, isosceles, scalene end + +fun equilateral(sides): + raise("Please implement the equilateral function") +end + +fun isosceles(sides): + raise("Please implement the isosceles function") +end + +fun scalene(sides): + raise("Please implement the scalene function") +end diff --git a/exercises/practice/two-fer/.approaches/config.json b/exercises/practice/two-fer/.approaches/config.json new file mode 100644 index 00000000..8fdeedd6 --- /dev/null +++ b/exercises/practice/two-fer/.approaches/config.json @@ -0,0 +1,7 @@ +{ + "introduction": { + "authors": [ + "BNAndras" + ] + } +} diff --git a/exercises/practice/two-fer/.approaches/introduction.md b/exercises/practice/two-fer/.approaches/introduction.md new file mode 100644 index 00000000..5fce2036 --- /dev/null +++ b/exercises/practice/two-fer/.approaches/introduction.md @@ -0,0 +1,79 @@ +# Introduction + +There are generally two approaches for solving Two Fer. +One approach would be using an [`if expression`][if-expression] to test a passed-in value. +A variant of this approach would be an [`ask expression`][ask-expression] to concisely write the `if expression`. + +## General guidance + +Regardless of the approach used, Pyret doesn't allow one to [re-assign a variable binding][shadowing] that is already in scope +Therefore, you can't rebind `name`. + + +## Approach: `if` expressions + +```pyret +fun two-fer(name): + fun get-name(): + if name == "": + "you" + else: + name + end + end + + "One for " + get-name() + ", one for me." +end + + +``` + +## Approach: `ask` expressions + +```pyret +fun two-fer(name): + fun get-name(): + ask: + | name == "" then: "you" + | otherwise: name + end + end + + "One for " + get-name() + ", one for me." +end + +``` + +## Other approaches + +Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows: + +### Other approach: Option datatypes + +Pyret provides a type of data called an [`Option`][option-datatype] that can represent a piece of data that is or is not available. The value `none` represents missing or invalid data and `some` wraps around data that is present. An empty incoming string `""` is still considered valid data so we'd need to replace it with `none`. Once we've done that, we can use [`or-else`][or-else] to either return the wrapped value or provide a default value when the Option is `none`. + +```pyret +fun two-fer(name): + fun get-option(): + if name == "": + none + else: + some(name) + end + end + + option = get-option() + "One for " + option.or-else("you") + ", one for me." +end + +``` + +## Which approach to use? + +Either the `ask` or `if` expressions work well here and work similarly so the decision is largely stylistic. + +[if-expression]: https://pyret.org/docs/latest/Expressions.html#%28elem._%28bnf-prod._%28.Pyret._if-expr%29%29%29 +[ask-expression]: https://pyret.org/docs/latest/Expressions.html#%28part._s~3aask-expr%29 +[shadowing]: https://pyret.org/docs/latest/Bindings.html#%28part._s~3ashadowing%29 +[option-datatype]: https://pyret.org/docs/latest/option.html +[or-else]: https://pyret.org/docs/latest/option.html#%28idx._%28gentag._342%29%29 diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md new file mode 100644 index 00000000..adc53487 --- /dev/null +++ b/exercises/practice/two-fer/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +Your task is to determine what you will say as you give away the extra cookie. + +If you know the person's name (e.g. if they're named Do-yun), then you will say: + +```text +One for Do-yun, one for me. +``` + +If you don't know the person's name, you will say _you_ instead. + +```text +One for you, one for me. +``` + +Here are some examples: + +| Name | Dialogue | +| :----- | :-------------------------- | +| Alice | One for Alice, one for me. | +| Bohdan | One for Bohdan, one for me. | +| | One for you, one for me. | +| Zaphod | One for Zaphod, one for me. | diff --git a/exercises/practice/two-fer/.docs/introduction.md b/exercises/practice/two-fer/.docs/introduction.md new file mode 100644 index 00000000..5947a223 --- /dev/null +++ b/exercises/practice/two-fer/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +In some English accents, when you say "two for" quickly, it sounds like "two fer". +Two-for-one is a way of saying that if you buy one, you also get one for free. +So the phrase "two-fer" often implies a two-for-one offer. + +Imagine a bakery that has a holiday offer where you can buy two cookies for the price of one ("two-fer one!"). +You take the offer and (very generously) decide to give the extra cookie to someone else in the queue. diff --git a/exercises/practice/two-fer/.meta/config.json b/exercises/practice/two-fer/.meta/config.json new file mode 100644 index 00000000..e4adcc54 --- /dev/null +++ b/exercises/practice/two-fer/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "two-fer.arr" + ], + "test": [ + "two-fer-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Create a sentence of the form \"One for X, one for me.\".", + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} diff --git a/exercises/practice/two-fer/.meta/example.arr b/exercises/practice/two-fer/.meta/example.arr new file mode 100644 index 00000000..2823a86a --- /dev/null +++ b/exercises/practice/two-fer/.meta/example.arr @@ -0,0 +1,10 @@ +use context starter2024 + +provide: two-fer end + +fun two-fer(name): + ask: + | name == "" then: "One for you, one for me." + | otherwise: "One for " + name + ", one for me." + end +end diff --git a/exercises/practice/two-fer/.meta/tests.toml b/exercises/practice/two-fer/.meta/tests.toml new file mode 100644 index 00000000..d0e38573 --- /dev/null +++ b/exercises/practice/two-fer/.meta/tests.toml @@ -0,0 +1,19 @@ +# 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. + +[1cf3e15a-a3d7-4a87-aeb3-ba1b43bc8dce] +description = "no name given" + +[b4c6dbb8-b4fb-42c2-bafd-10785abe7709] +description = "a name given" + +[3549048d-1a6e-4653-9a79-b0bda163e8d5] +description = "another name given" diff --git a/exercises/practice/two-fer/two-fer-test.arr b/exercises/practice/two-fer/two-fer-test.arr new file mode 100644 index 00000000..7305ad6d --- /dev/null +++ b/exercises/practice/two-fer/two-fer-test.arr @@ -0,0 +1,16 @@ +use context starter2024 + +include file("two-fer.arr") + +check "no name given": + two-fer("") is "One for you, one for me." +end + +check "a name given": + two-fer("Alice") is "One for Alice, one for me." +end + +check "another name given": + two-fer("Bob") is "One for Bob, one for me." +end + diff --git a/exercises/practice/two-fer/two-fer.arr b/exercises/practice/two-fer/two-fer.arr new file mode 100644 index 00000000..77b627e2 --- /dev/null +++ b/exercises/practice/two-fer/two-fer.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: two-fer end + +fun two-fer(name): + raise("please implement the two-fer function") +end diff --git a/exercises/practice/word-count/.docs/instructions.md b/exercises/practice/word-count/.docs/instructions.md new file mode 100644 index 00000000..064393c8 --- /dev/null +++ b/exercises/practice/word-count/.docs/instructions.md @@ -0,0 +1,47 @@ +# Instructions + +Your task is to count how many times each word occurs in a subtitle of a drama. + +The subtitles from these dramas use only ASCII characters. + +The characters often speak in casual English, using contractions like _they're_ or _it's_. +Though these contractions come from two words (e.g. _we are_), the contraction (_we're_) is considered a single word. + +Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " "). +The only punctuation that does not separate words is the apostrophe in contractions. + +Numbers are considered words. +If the subtitles say _It costs 100 dollars._ then _100_ will be its own word. + +Words are case insensitive. +For example, the word _you_ occurs three times in the following sentence: + +> You come back, you hear me? DO YOU HEAR ME? + +The ordering of the word counts in the results doesn't matter. + +Here's an example that incorporates several of the elements discussed above: + +- simple words +- contractions +- numbers +- case insensitive words +- punctuation (including apostrophes) to separate words +- different forms of whitespace to separate words + +`"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` + +The mapping for this subtitle would be: + +```text +123: 1 +agent: 1 +cried: 1 +fled: 1 +i: 1 +password: 2 +so: 1 +special: 1 +that's: 1 +the: 2 +``` diff --git a/exercises/practice/word-count/.docs/introduction.md b/exercises/practice/word-count/.docs/introduction.md new file mode 100644 index 00000000..1654508e --- /dev/null +++ b/exercises/practice/word-count/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You teach English as a foreign language to high school students. + +You've decided to base your entire curriculum on TV shows. +You need to analyze which words are used, and how often they're repeated. + +This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes. diff --git a/exercises/practice/word-count/.meta/config.json b/exercises/practice/word-count/.meta/config.json new file mode 100644 index 00000000..302e0e10 --- /dev/null +++ b/exercises/practice/word-count/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "word-count.arr" + ], + "test": [ + "word-count-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Given a phrase, count the occurrences of each word in that phrase.", + "source": "This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour." +} diff --git a/exercises/practice/word-count/.meta/example.arr b/exercises/practice/word-count/.meta/example.arr new file mode 100644 index 00000000..df854ccc --- /dev/null +++ b/exercises/practice/word-count/.meta/example.arr @@ -0,0 +1,60 @@ +use context starter2024 + +provide: word-count end + +include string-dict + +fun word-count(phrase): + remove-trailing-quote = lam(fragment): + last-index = string-length(fragment) - 1 + if string-char-at(fragment, last-index) == "'": + string-substring(fragment, 0, last-index) + else: + fragment + end + end + + remove-leading-quote = lam(fragment): + last-index = string-length(fragment) + if string-char-at(fragment, 0) == "'": + string-substring(fragment, 1, last-index) + else: + fragment + end + end + + remove-surrounding-quotes = lam(fragment): + fragment + ^ remove-trailing-quote + ^ remove-leading-quote + end + + phrase + ^ string-replace(_, ",", " ") + ^ string-replace(_, "\n", " ") + ^ string-replace(_, ": ", " ") + ^ string-replace(_, " '", " ") + ^ string-replace(_, "' ", " ") + ^ string-to-lower + ^ string-split-all(_, " ") + ^ _.foldl( + lam(elt, acc): + if elt == "": + acc + else: + cleaned = elt + ^ string-to-code-points + ^ _.filter( + lam(cp): + # a-z or 0-9 or ' + ((cp >= 97) and (cp <= 122)) or ((cp >= 48) and (cp <= 57)) or (cp == 39) + end) + ^ string-from-code-points + ^ remove-surrounding-quotes + + value = acc.get(cleaned).or-else(0) + 1 + acc.set(cleaned, value) + end + end, + [string-dict: ]) +end diff --git a/exercises/practice/word-count/.meta/tests.toml b/exercises/practice/word-count/.meta/tests.toml new file mode 100644 index 00000000..1be425b3 --- /dev/null +++ b/exercises/practice/word-count/.meta/tests.toml @@ -0,0 +1,57 @@ +# 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. + +[61559d5f-2cad-48fb-af53-d3973a9ee9ef] +description = "count one word" + +[5abd53a3-1aed-43a4-a15a-29f88c09cbbd] +description = "count one of each word" + +[2a3091e5-952e-4099-9fac-8f85d9655c0e] +description = "multiple occurrences of a word" + +[e81877ae-d4da-4af4-931c-d923cd621ca6] +description = "handles cramped lists" + +[7349f682-9707-47c0-a9af-be56e1e7ff30] +description = "handles expanded lists" + +[a514a0f2-8589-4279-8892-887f76a14c82] +description = "ignore punctuation" + +[d2e5cee6-d2ec-497b-bdc9-3ebe092ce55e] +description = "include numbers" + +[dac6bc6a-21ae-4954-945d-d7f716392dbf] +description = "normalize case" + +[4185a902-bdb0-4074-864c-f416e42a0f19] +description = "with apostrophes" +include = false + +[4ff6c7d7-fcfc-43ef-b8e7-34ff1837a2d3] +description = "with apostrophes" +reimplements = "4185a902-bdb0-4074-864c-f416e42a0f19" + +[be72af2b-8afe-4337-b151-b297202e4a7b] +description = "with quotations" + +[8d6815fe-8a51-4a65-96f9-2fb3f6dc6ed6] +description = "substrings from the beginning" + +[c5f4ef26-f3f7-4725-b314-855c04fb4c13] +description = "multiple spaces not detected as a word" + +[50176e8a-fe8e-4f4c-b6b6-aa9cf8f20360] +description = "alternating word separators not detected as a word" + +[6d00f1db-901c-4bec-9829-d20eb3044557] +description = "quotation for word with apostrophe" diff --git a/exercises/practice/word-count/word-count-test.arr b/exercises/practice/word-count/word-count-test.arr new file mode 100644 index 00000000..68637e91 --- /dev/null +++ b/exercises/practice/word-count/word-count-test.arr @@ -0,0 +1,131 @@ +use context starter2024 + +include file("word-count.arr") + +include string-dict + +check "count one word": + input = "word" + expected = [string-dict: "word", 1] + + word-count(input) is expected +end + +check "count one of each word": + input = "one of each" + expected = [string-dict: "one", 1, "of", 1, "each", 1] + + word-count(input) is expected +end + +check "multiple occurrences of a word": + input = "one fish two fish red fish blue fish" + expected = [string-dict: "one", 1, "fish", 4, "two", 1, "red", 1, "blue", 1] + + word-count(input) is expected +end + +check "handles cramped lists": + input = "one,two,three" + expected = [string-dict: "one", 1, "two", 1, "three", 1] + + word-count(input) is expected +end + +check "handles expanded lists": + input = "one,\ntwo,\nthree" + expected = [string-dict: "one", 1, "two", 1, "three", 1] + + word-count(input) is expected +end + +check "ignore punctuation": + input = "car: carpet as java: javascript!!&@$%^&" + expected = [string-dict: + "car", 1, + "carpet", 1, + "as", 1, + "java", 1, + "javascript", 1] + + word-count(input) is expected +end + +check "include numbers": + input = "testing, 1, 2 testing" + expected = [string-dict: "testing", 2, "1", 1, "2", 1] + + word-count(input) is expected +end + +check "normalize case": + input = "go Go GO Stop stop" + expected = [string-dict: "go", 3, "stop", 2] + + word-count(input) is expected +end + +check "with apostrophes": + input = "'First: don't laugh. Then: don't cry. You're getting it.'" + expected = [string-dict: + "first", 1, + "don't", 2, + "laugh", 1, + "then", 1, + "cry", 1, + "you're", 1, + "getting", 1, + "it", 1] + + word-count(input) is expected +end + +check "with quotations": + input = "Joe can't tell between 'large' and large." + expected = [string-dict: + "joe", 1, + "can't", 1, + "tell", 1, + "between", 1, + "large", 2, + "and", 1] + + word-count(input) is expected +end + +check "substrings from the beginning": + input = "Joe can't tell between app, apple and a." + expected = [string-dict: + "joe", 1, + "can't", 1, + "tell", 1, + "between", 1, + "app", 1, + "apple", 1, + "and", 1, + "a", 1] + + word-count(input) is expected +end + +check "multiple spaces not detected as a word": + input = " multiple whitespaces" + expected = [string-dict: "multiple", 1, "whitespaces", 1] + + word-count(input) is expected +end + +check "alternating word separators not detected as a word": + input = ",\n,one,\n ,two \n 'three'" + expected = [string-dict: "one", 1, "two", 1, "three", 1] + + word-count(input) is expected +end + +check "quotation for word with apostrophe": + input = "can, can't, 'can't'" + expected = [string-dict: "can", 1, "can't", 2] + + word-count(input) is expected +end + diff --git a/exercises/practice/word-count/word-count.arr b/exercises/practice/word-count/word-count.arr new file mode 100644 index 00000000..2f47a44a --- /dev/null +++ b/exercises/practice/word-count/word-count.arr @@ -0,0 +1,7 @@ +use context starter2024 + +provide: word-count end + +fun word-count(phrase): + raise("please implement the word-count function") +end diff --git a/exercises/shared/.docs/debug.md b/exercises/shared/.docs/debug.md new file mode 100644 index 00000000..f09b150e --- /dev/null +++ b/exercises/shared/.docs/debug.md @@ -0,0 +1,45 @@ +# Debug + +```pyret +fun print-forty-two(): + print(42) # 42 +end +``` + +```pyret +fun return-forty-two() block: + print(42) # 42 + 42 +end +``` + +[print][print] is an expression returning a value. +Pyret only allows a single expression within a given block of code unless the outer block is marked with `block`. +In the second example, adding `block` to the function declaration allows us to print 42 before returning it. + +## Spies + +```pyret +fun do-something(): + x = "foo" + y = "bar" + spy "strings": x, y end + a = 1 + b = 2 + spy "numbers": a, b end +end +do-something(); + +# Spying "strings" (at /example.arr:4:4-4:27) +# x: "foo" +# y: "bar" +# Spying "numbers" (at /example.arr:7:4-7:27) +# a: 1 +# b: 2 +``` + +A spy statement takes an optional label, one or more variables and reports each variable and its value. +Because it's not an expression, a spy can be used in a block without requiring the outer block to be marked with `block`. + +[print]: https://pyret.org/docs/latest/_global_.html#%28idx._%28gentag._57%29%29 +[spy]: https://pyret.org/docs/latest/s_spies.html diff --git a/exercises/shared/.docs/help.md b/exercises/shared/.docs/help.md index 45aa2d64..7df6b55f 100644 --- a/exercises/shared/.docs/help.md +++ b/exercises/shared/.docs/help.md @@ -1,21 +1,13 @@ # Help - +[official-docs]: https://pyret.org/docs/latest/ +[google-groups]: https://groups.google.com/g/pyret-discuss +[pyret-twitter]: https://twitter.com/pyretlang +[pyret-discord]: https://discord.com/invite/7aFMB3b6Mt diff --git a/exercises/shared/.docs/tests.md b/exercises/shared/.docs/tests.md index 1f197bf7..15ca76a7 100644 --- a/exercises/shared/.docs/tests.md +++ b/exercises/shared/.docs/tests.md @@ -1,15 +1,17 @@ # Tests - +[testing-docs]: https://pyret.org/docs/latest/testing.html +[pyret-npm]: https://www.npmjs.com/package/pyret-npm diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..72777e35 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1529 @@ +{ + "name": "@exercism/pyret", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@exercism/pyret", + "license": "MIT", + "dependencies": { + "pyret-npm": "^0.0.90" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ascii-table": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/ascii-table/-/ascii-table-0.0.9.tgz", + "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ==", + "license": "MIT" + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/canvas": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.2.tgz", + "integrity": "sha512-duEt4h1HHu9sJZyVKfLRXR6tsKPY7cEELzxSRJkwddOXYvQT3P/+es98SV384JA0zMOZ5s+9gatnGfM6sL4Drg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": "^18.12.0 || >= 20.9.0" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "license": "MIT", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz", + "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==", + "license": "MIT", + "dependencies": { + "array-back": "^2.0.0", + "chalk": "^2.4.1", + "table-layout": "^0.4.3", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", + "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", + "license": "ISC", + "dependencies": { + "commander": "7", + "d3-array": "1 - 3", + "d3-geo": "1.12.0 - 3" + }, + "bin": { + "geo2svg": "bin/geo2svg.js", + "geograticule": "bin/geograticule.js", + "geoproject": "bin/geoproject.js", + "geoquantize": "bin/geoquantize.js", + "geostitch": "bin/geostitch.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "license": "MIT", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "license": "MIT" + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pyret-npm": { + "version": "0.0.90", + "resolved": "https://registry.npmjs.org/pyret-npm/-/pyret-npm-0.0.90.tgz", + "integrity": "sha512-1qpmlR5cb9d8fjfv8ma0a+jUH8U+UPoxYMyqPmn0UvRPI4X56OGxuvSLDxh1Qz5+4sRb5AxNK+jNly8YJ/Arvw==", + "license": "Apache-2.0", + "dependencies": { + "ascii-table": "^0.0.9", + "canvas": "^3.1.0", + "command-line-args": "^5.0.2", + "command-line-usage": "^5.0.5", + "mkdirp": "^0.5.1", + "resolve": "^1.22.10", + "strip-ansi": "^4.0.0", + "vega": "^6.1.2", + "ws": "^5.2.1" + }, + "bin": { + "pyret": "pyret.js" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "license": "MIT", + "dependencies": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "license": "MIT", + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "license": "MIT" + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vega": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/vega/-/vega-6.2.0.tgz", + "integrity": "sha512-BIwalIcEGysJdQDjeVUmMWB3e50jPDNAMfLJscjEvpunU9bSt7X1OYnQxkg3uBwuRRI4nWfFZO9uIW910nLeGw==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-crossfilter": "~5.1.0", + "vega-dataflow": "~6.1.0", + "vega-encode": "~5.1.0", + "vega-event-selector": "~4.0.0", + "vega-expression": "~6.1.0", + "vega-force": "~5.1.0", + "vega-format": "~2.1.0", + "vega-functions": "~6.1.0", + "vega-geo": "~5.1.0", + "vega-hierarchy": "~5.1.0", + "vega-label": "~2.1.0", + "vega-loader": "~5.1.0", + "vega-parser": "~7.1.0", + "vega-projection": "~2.1.0", + "vega-regression": "~2.1.0", + "vega-runtime": "~7.1.0", + "vega-scale": "~8.1.0", + "vega-scenegraph": "~5.1.0", + "vega-statistics": "~2.0.0", + "vega-time": "~3.1.0", + "vega-transforms": "~5.1.0", + "vega-typings": "~2.1.0", + "vega-util": "~2.1.0", + "vega-view": "~6.1.0", + "vega-view-transforms": "~5.1.0", + "vega-voronoi": "~5.1.0", + "vega-wordcloud": "~5.1.0" + }, + "funding": { + "url": "https://app.hubspot.com/payments/GyPC972GD9Rt" + } + }, + "node_modules/vega-canvas": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vega-canvas/-/vega-canvas-2.0.0.tgz", + "integrity": "sha512-9x+4TTw/USYST5nx4yN272sy9WcqSRjAR0tkQYZJ4cQIeon7uVsnohvoPQK1JZu7K1QXGUqzj08z0u/UegBVMA==", + "license": "BSD-3-Clause" + }, + "node_modules/vega-crossfilter": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-crossfilter/-/vega-crossfilter-5.1.0.tgz", + "integrity": "sha512-EmVhfP3p6AM7o/lPan/QAoqjblI19BxWUlvl2TSs0xjQd8KbaYYbS4Ixt3cmEvl0QjRdBMF6CdJJ/cy9DTS4Fw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "vega-dataflow": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-dataflow": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vega-dataflow/-/vega-dataflow-6.1.0.tgz", + "integrity": "sha512-JxumGlODtFbzoQ4c/jQK8Tb/68ih0lrexlCozcMfTAwQ12XhTqCvlafh7MAKKTMBizjOfaQTHm4Jkyb1H5CfyQ==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-format": "^2.1.0", + "vega-loader": "^5.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-encode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-encode/-/vega-encode-5.1.0.tgz", + "integrity": "sha512-q26oI7B+MBQYcTQcr5/c1AMsX3FvjZLQOBi7yI0vV+GEn93fElDgvhQiYrgeYSD4Exi/jBPeUXuN6p4bLz16kA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-interpolate": "^3.0.1", + "vega-dataflow": "^6.1.0", + "vega-scale": "^8.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-event-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/vega-event-selector/-/vega-event-selector-4.0.0.tgz", + "integrity": "sha512-CcWF4m4KL/al1Oa5qSzZ5R776q8lRxCj3IafCHs5xipoEHrkgu1BWa7F/IH5HrDNXeIDnqOpSV1pFsAWRak4gQ==", + "license": "BSD-3-Clause" + }, + "node_modules/vega-expression": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-6.1.0.tgz", + "integrity": "sha512-hHgNx/fQ1Vn1u6vHSamH7lRMsOa/yQeHGGcWVmh8fZafLdwdhCM91kZD9p7+AleNpgwiwzfGogtpATFaMmDFYg==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/estree": "^1.0.8", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-force": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-force/-/vega-force-5.1.0.tgz", + "integrity": "sha512-wdnchOSeXpF9Xx8Yp0s6Do9F7YkFeOn/E/nENtsI7NOcyHpICJ5+UkgjUo9QaQ/Yu+dIDU+sP/4NXsUtq6SMaQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-force": "^3.0.0", + "vega-dataflow": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-format/-/vega-format-2.1.0.tgz", + "integrity": "sha512-i9Ht33IgqG36+S1gFDpAiKvXCPz+q+1vDhDGKK8YsgMxGOG4PzinKakI66xd7SdV4q97FgpR7odAXqtDN2wKqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-format": "^3.1.0", + "d3-time-format": "^4.1.0", + "vega-time": "^3.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-functions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vega-functions/-/vega-functions-6.1.1.tgz", + "integrity": "sha512-Due6jP0y0FfsGMTrHnzUGnEwXPu7VwE+9relfo+LjL/tRPYnnKqwWvzt7n9JkeBuZqjkgYjMzm/WucNn6Hkw5A==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-color": "^3.1.0", + "d3-geo": "^3.1.1", + "vega-dataflow": "^6.1.0", + "vega-expression": "^6.1.0", + "vega-scale": "^8.1.0", + "vega-scenegraph": "^5.1.0", + "vega-selections": "^6.1.0", + "vega-statistics": "^2.0.0", + "vega-time": "^3.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-geo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-geo/-/vega-geo-5.1.0.tgz", + "integrity": "sha512-H8aBBHfthc3rzDbz/Th18+Nvp00J73q3uXGAPDQqizioDm/CoXCK8cX4pMePydBY9S6ikBiGJrLKFDa80wI20g==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-color": "^3.1.0", + "d3-geo": "^3.1.1", + "vega-canvas": "^2.0.0", + "vega-dataflow": "^6.1.0", + "vega-projection": "^2.1.0", + "vega-statistics": "^2.0.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-hierarchy": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-hierarchy/-/vega-hierarchy-5.1.0.tgz", + "integrity": "sha512-rZlU8QJNETlB6o73lGCPybZtw2fBBsRIRuFE77aCLFHdGsh6wIifhplVarqE9icBqjUHRRUOmcEYfzwVIPr65g==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-hierarchy": "^3.1.2", + "vega-dataflow": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-label/-/vega-label-2.1.0.tgz", + "integrity": "sha512-/hgf+zoA3FViDBehrQT42Lta3t8In6YwtMnwjYlh72zNn1p3c7E3YUBwqmAqTM1x+tudgzMRGLYig+bX1ewZxQ==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-canvas": "^2.0.0", + "vega-dataflow": "^6.1.0", + "vega-scenegraph": "^5.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-loader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-loader/-/vega-loader-5.1.0.tgz", + "integrity": "sha512-GaY3BdSPbPNdtrBz8SYUBNmNd8mdPc3mtdZfdkFazQ0RD9m+Toz5oR8fKnTamNSk9fRTJX0Lp3uEqxrAlQVreg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-dsv": "^3.0.1", + "topojson-client": "^3.1.0", + "vega-format": "^2.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/vega-parser/-/vega-parser-7.1.0.tgz", + "integrity": "sha512-g0lrYxtmYVW8G6yXpIS4J3Uxt9OUSkc0bLu5afoYDo4rZmoOOdll3x3ebActp5LHPW+usZIE+p5nukRS2vEc7Q==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-dataflow": "^6.1.0", + "vega-event-selector": "^4.0.0", + "vega-functions": "^6.1.0", + "vega-scale": "^8.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-projection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-projection/-/vega-projection-2.1.0.tgz", + "integrity": "sha512-EjRjVSoMR5ibrU7q8LaOQKP327NcOAM1+eZ+NO4ANvvAutwmbNVTmfA1VpPH+AD0AlBYc39ND/wnRk7SieDiXA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-geo": "^3.1.1", + "d3-geo-projection": "^4.0.0", + "vega-scale": "^8.1.0" + } + }, + "node_modules/vega-regression": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-regression/-/vega-regression-2.1.0.tgz", + "integrity": "sha512-HzC7MuoEwG1rIxRaNTqgcaYF03z/ZxYkQR2D5BN0N45kLnHY1HJXiEcZkcffTsqXdspLjn47yLi44UoCwF5fxQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "vega-dataflow": "^6.1.0", + "vega-statistics": "^2.0.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-runtime": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/vega-runtime/-/vega-runtime-7.1.0.tgz", + "integrity": "sha512-mItI+WHimyEcZlZrQ/zYR3LwHVeyHCWwp7MKaBjkU8EwkSxEEGVceyGUY9X2YuJLiOgkLz/6juYDbMv60pfwYA==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-dataflow": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-scale": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vega-scale/-/vega-scale-8.1.0.tgz", + "integrity": "sha512-VEgDuEcOec8+C8+FzLcnAmcXrv2gAJKqQifCdQhkgnsLa978vYUgVfCut/mBSMMHbH8wlUV1D0fKZTjRukA1+A==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-scale-chromatic": "^3.1.0", + "vega-time": "^3.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-scenegraph": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-scenegraph/-/vega-scenegraph-5.1.0.tgz", + "integrity": "sha512-4gA89CFIxkZX+4Nvl8SZF2MBOqnlj9J5zgdPh/HPx+JOwtzSlUqIhxFpFj7GWYfwzr/PyZnguBLPihPw1Og/cA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "^3.1.0", + "d3-shape": "^3.2.0", + "vega-canvas": "^2.0.0", + "vega-loader": "^5.1.0", + "vega-scale": "^8.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-selections": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/vega-selections/-/vega-selections-6.1.2.tgz", + "integrity": "sha512-xJ+V4qdd46nk2RBdwIRrQm2iSTMHdlu/omhLz1pqRL3jZDrkqNBXimrisci2kIKpH2WBpA1YVagwuZEKBmF2Qw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "3.2.4", + "vega-expression": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-statistics": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vega-statistics/-/vega-statistics-2.0.0.tgz", + "integrity": "sha512-dGPfDXnBlgXbZF3oxtkb8JfeRXd5TYHx25Z/tIoaa9jWua4Vf/AoW2wwh8J1qmMy8J03/29aowkp1yk4DOPazQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4" + } + }, + "node_modules/vega-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vega-time/-/vega-time-3.1.0.tgz", + "integrity": "sha512-G93mWzPwNa6UYQRkr8Ujur9uqxbBDjDT/WpXjbDY0yygdSkRT+zXF+Sb4gjhW0nPaqdiwkn0R6kZcSPMj1bMNA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-time": "^3.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-transforms/-/vega-transforms-5.1.0.tgz", + "integrity": "sha512-mj/sO2tSuzzpiXX8JSl4DDlhEmVwM/46MTAzTNQUQzJPMI/n4ChCjr/SdEbfEyzlD4DPm1bjohZGjLc010yuMg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "vega-dataflow": "^6.1.0", + "vega-statistics": "^2.0.0", + "vega-time": "^3.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-typings": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-typings/-/vega-typings-2.1.0.tgz", + "integrity": "sha512-zdis4Fg4gv37yEvTTSZEVMNhp8hwyEl7GZ4X4HHddRVRKxWFsbyKvZx/YW5Z9Ox4sjxVA2qHzEbod4Fdx+SEJA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/geojson": "7946.0.16", + "vega-event-selector": "^4.0.0", + "vega-expression": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-util": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-2.1.0.tgz", + "integrity": "sha512-PGfp0m0QCufDmcxKJCWQy4Ov23FoF8DSXmoJwSezi3itQaa2hbxK0+xwsTMP2vy4PR16Pu25HMzgMwXVW1+33w==", + "license": "BSD-3-Clause" + }, + "node_modules/vega-view": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vega-view/-/vega-view-6.1.0.tgz", + "integrity": "sha512-hmHDm/zC65lb23mb9Tr9Gx0wkxP0TMS31LpMPYxIZpvInxvUn7TYitkOtz1elr63k2YZrgmF7ztdGyQ4iCQ5fQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^3.2.4", + "d3-timer": "^3.0.1", + "vega-dataflow": "^6.1.0", + "vega-format": "^2.1.0", + "vega-functions": "^6.1.0", + "vega-runtime": "^7.1.0", + "vega-scenegraph": "^5.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-view-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-5.1.0.tgz", + "integrity": "sha512-fpigh/xn/32t+An1ShoY3MLeGzNdlbAp2+HvFKzPpmpMTZqJEWkk/J/wHU7Swyc28Ta7W1z3fO+8dZkOYO5TWQ==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-dataflow": "^6.1.0", + "vega-scenegraph": "^5.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-voronoi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-voronoi/-/vega-voronoi-5.1.0.tgz", + "integrity": "sha512-uKdsoR9x60mz7eYtVG+NhlkdQXeVdMr6jHNAHxs+W+i6kawkUp5S9jp1xf1FmW/uZvtO1eqinHQNwATcDRsiUg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-delaunay": "^6.0.4", + "vega-dataflow": "^6.1.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/vega-wordcloud": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vega-wordcloud/-/vega-wordcloud-5.1.0.tgz", + "integrity": "sha512-sSdNmT8y2D7xXhM2h76dKyaYn3PA4eV49WUUkfYfqHz/vpcu10GSAoFxLhQQTkbZXR+q5ZB63tFUow9W2IFo6g==", + "license": "BSD-3-Clause", + "dependencies": { + "vega-canvas": "^2.0.0", + "vega-dataflow": "^6.1.0", + "vega-scale": "^8.1.0", + "vega-statistics": "^2.0.0", + "vega-util": "^2.1.0" + } + }, + "node_modules/wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "license": "MIT", + "dependencies": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.4.tgz", + "integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..a82f1dc5 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "@exercism/pyret", + "description": "Exercism exercises in Pyret.", + "author": "Katrina Owen", + "contributors": [ + "BNAndras" + ], + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/pyret" + }, + "dependencies": { + "pyret-npm": "^0.0.90" + } +}