diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ed8f4a43..234b07e7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,4 +6,4 @@ updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'daily' + interval: 'monthly' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e219afb3..e5fe3d79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,14 +8,14 @@ on: jobs: ci: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: purcell/setup-emacs@ddbe7a402671fcbad2a5e1a0f503d61dd95a0b7c + - uses: purcell/setup-emacs@7a92187aa5b5a3b854cbdfa47499fbd3d1207163 with: - version: 28.1 + version: 30.1 - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.0.0 - name: Run exercism/emacs-lisp ci (runs tests) for all exercises run: | diff --git a/.github/workflows/configlet-sync.yml b/.github/workflows/configlet-sync.yml index 457cbcf5..4787cbeb 100644 --- a/.github/workflows/configlet-sync.yml +++ b/.github/workflows/configlet-sync.yml @@ -9,10 +9,10 @@ jobs: configlet: if: github.repository_owner == 'exercism' # Stops this job from running on forks timeout-minutes: 30 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.0.0 - name: Fetch configlet uses: exercism/github-actions/configlet-ci@main @@ -34,9 +34,8 @@ jobs: - name: Create issue if: ${{ failure() }} - uses: JasonEtco/create-an-issue@e27dddc79c92bc6e4562f268fffa5ed752639abd # v2.9.1 + uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: filename: .github/configlet-sync-issue.md - update_existing: true 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/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/README.md b/README.md index 6de56924..8faa6ffd 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,124 @@ -# Exercism Emacs Lisp Track +# Exercism Emacs Lisp Track +[![Exercism Discourse Forum](https://img.shields.io/discourse/posts?server=https%3A%2F%2Fforum.exercism.org%2F)](https://forum.exercism.org/) +[![Exercism Discord Invite](https://img.shields.io/discord/854117591135027261)](https://exercism.org/r/discord) [![Configlet Status](https://github.com/exercism/emacs-lisp/workflows/Configlet/badge.svg)](https://github.com/exercism/emacs-lisp/workflows/Configlet/badge.svg) [![Exercise Test Status](https://github.com/exercism/emacs-lisp/workflows/emacs-lisp%20%2F%20main/badge.svg)](https://github.com/exercism/emacs-lisp/workflows/emacs-lisp%20%2F%20main/badge.svg) -Exercism problems in Emacs Lisp. +**`exercism/emacs-lisp`** is one of many programming language tracks you can study on [exercism.org][exercism-website]. +This repo holds all the instructions, tests, code, & support files for Emacs Lisp _exercises_ currently under development or implemented & available for students. +If you haven't already, you can check out and study the live language track [here][exercism-emacs-lisp-track]. -## Contributing to The Emacs Lisp Track +🌟   The test runner is currently running Emacs 30.1 -### Contributing Guide +Currently the Emacs Lisp track doesn't feature a syllabus, so it only contains practice exercises. Practice exercises are open-ended problems that allow you to build and test your knowledge of a programming language. You can find the practice exercises referenced in the [config.json][config-json] and the files in the [`exercises/practice`][emacs-lisp-exercises-practice-dir] directory. The practice exercises are shared between tracks. You can find the canonical problem description in the [problem specifications repository][problem-specifications-repository]. -Please see the [contributing guide](https://exercism.org/contributing) -This describes how all the language tracks are put together, -as well as details about the common metadata, and high-level -information about contributing to existing problems and adding new problems. +
-### Issues +
+ + + + -Feel free to file any issues on the [elisp issue tracker](https://github.com/exercism/elisp/issues) for problems of -any size. Feel free to report typographical errors or poor wording for -example. You can greatly help improve the quality of the exercises by -filing reports of invalid solutions that pass tests or of valid solutions -that fail tests. +🌟🌟  Please take a moment to read our [Code of Conduct][exercism-code-of-conduct] 🌟🌟  +It might also be helpful to look at [Being a Good Community Member][being-a-good-community-member] & [The words that we use][the-words-that-we-use]. -New exercises or changes to existing ones can be submitted via a pull -request. You will need a GitHub account and you will need to fork -[exercism/emacs-lisp](https://github.com/exercism/emacs-lisp) to your account. See GitHub Help if you are unfamiliar -with the process. +                         Some defined roles in our community: [Contributors][exercism-contributors] **|** [Mentors][exercism-mentors] **|** [Maintainers][exercism-track-maintainers] **|** [Admins][exercism-admins] +
-### GNU Emacs icon +
+ +

Contributing to The Emacs Lisp Track

+ + + +We 💙 our community and welcome contributions. +
+The following guide is intended to give you an overview about the different ways to help and to point you to the relevant documentation. + +
+There are several ways of contributing to the Emacs Lisp track. Examples would be + +- [reporting][emacs-lisp-new-issue] problems with the track or possible improvements +- working on [practice exercises][practice-exercises] +- working on track [tooling][emacs-lisp-tooling] and [CI][emacs-lisp-ci] +- working on the [test runner][emacs-lisp-test-runner] +- working on track documents +- contributing [approaches][exercism-approaches] +- contributing [articles][exercism-articles] + +Feel free to open an issue or open a pull request for any of those items. +Pull Requests should be focused on a single change. +They must pass the CI system and need a review by a maintainer before they can get merged. + +You can also start work on + +- a [syllabus][exercism-syllabus] / [concept exercises][exercism-concept-exercises-stories], used to teach a language step by step +- an [analyzer][exercism-analyzers], used to give automated feedback on student solutions +- a [representer][exercism-representers], used to give automated feedback on sutdent solutions + +If you want to start work on one of those items please first get in touch via [GitHub issues][emacs-lisp-github-issues] or the [discourse forum][discourse-forum-emacs-lisp]. + + + +✨ 🦄  _**Not sure how to do something? Check out the documentation!**_ +     [Structure][exercism-track-structure] **|** [Tasks][exercism-tasks] **|** [Concepts][exercism-concepts] **|** [Concept Exercises][concept-exercises] **|** [Practice Exercises][practice-exercises] **|** [Presentation][exercise-presentation] +     [Writing Style Guide][exercism-writing-style] **|** [Markdown Specification][exercism-markdown-specification] (_markdown guidelines for [contributing][website-contributing-section] on exercism.org_) + +
+
+ +## Related repositories + +- [Emacs Lisp Test Runner][emacs-lisp-test-runner] - used to test the solutions submitted by students +- [Website Copy][exercism-website-copy] - contains [mentoring notes](https://exercism.org/docs/building/product/mentoring-notes) + +## Exercism Emacs Lisp Track License + +This repository uses the [MIT License][license]. + +## GNU Emacs icon The GNU Emacs icon was designed by Andrew Zhilin and is licensed under the Creative Commons Attribution-ShareAlike 3.0 License. We have modified the GNU Emacs icon to create the Emacs Lisp icon for Exercism. + +[exercism-website]: https://exercism.org/ +[emacs-lisp-new-issue]: https://github.com/exercism/emacs-lisp/issues/new +[exercism-emacs-lisp-track]: https://exercism.org/tracks/emacs-lisp +[emacs-lisp-exercises-practice-dir]: exercises/practice +[emacs-lisp-tooling]: /bin +[emacs-lisp-ci]: .github/workflows +[emacs-lisp-github-issues]: https://github.com/exercism/emacs-lisp/issues +[emacs-lisp-test-runner]: https://github.com/exercism/emacs-lisp-test-runner +[discourse-forum-emacs-lisp]: https://forum.exercism.org/c/programming/emacs-lisp/86 +[config-json]: https://github.com/exercism/emacs-lisp/blob/main/config.json +[being-a-good-community-member]: https://github.com/exercism/docs/tree/main/community/good-member +[chestertons-fence]: https://github.com/exercism/docs/blob/main/community/good-member/chestertons-fence.md +[concept-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/concept-exercises.md +[exercise-presentation]: https://github.com/exercism/docs/blob/main/building/tracks/presentation.md +[exercism-admins]: https://github.com/exercism/docs/blob/main/community/administrators.md +[exercism-code-of-conduct]: https://exercism.org/docs/using/legal/code-of-conduct +[exercism-concepts]: https://github.com/exercism/docs/blob/main/building/tracks/concepts.md +[exercism-contributors]: https://github.com/exercism/docs/blob/main/community/contributors.md +[exercism-markdown-specification]: https://github.com/exercism/docs/blob/main/building/markdown/markdown.md +[exercism-mentors]: https://github.com/exercism/docs/tree/main/mentoring +[exercism-tasks]: https://exercism.org/docs/building/product/tasks +[exercism-track-maintainers]: https://github.com/exercism/docs/blob/main/community/maintainers.md +[exercism-track-structure]: https://github.com/exercism/docs/tree/main/building/tracks +[exercism-website]: https://exercism.org/ +[exercism-writing-style]: https://github.com/exercism/docs/blob/main/building/markdown/style-guide.md +[exercism-approaches]: https://exercism.org/docs/building/tracks/approaches +[exercism-articles]: https://exercism.org/docs/building/tracks/articles +[exercism-syllabus]: https://exercism.org/docs/building/tracks/syllabus +[exercism-concept-exercises-stories]: https://exercism.org/docs/building/tracks/stories +[exercism-analyzers]: https://exercism.org/docs/building/tooling/analyzers +[exercism-representers]: https://exercism.org/docs/building/tooling/representers +[practice-exercises]: https://exercism.org/docs/building/tracks/practice-exercises +[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md +[problem-specifications-repository]: https://github.com/exercism/problem-specifications/ +[suggesting-improvements]: https://github.com/exercism/docs/blob/main/community/good-member/suggesting-exercise-improvements.md +[the-words-that-we-use]: https://github.com/exercism/docs/blob/main/community/good-member/words.md +[website-contributing-section]: https://exercism.org/docs/building +[exercism-website-copy]: https://github.com/exercism/website-copy +[license]: /LICENSE 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/generate_practice_exercise b/bin/generate_practice_exercise new file mode 100755 index 00000000..6bc91fa5 --- /dev/null +++ b/bin/generate_practice_exercise @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Exit if anything fails. +set -euo pipefail + +# If argument not provided, print usage and exit +if [ -z "$1" ]; then + echo "Usage: bin/generate_practice_exercise " + exit 1 +fi + +if ! [ -x "$(command -v jq)" ]; then + echo "jq is required. Please install jq and make sure it is on the $PATH: https://stedolan.github.io/jq/download/" >&2 + exit 1 +fi + +if ! [ -x "$(command -v emacs)" ]; then + echo "Emacs is required. Please install Emacs and make sure it is on the $PATH: https://www.gnu.org/software/emacs/download.html" >&2 + exit 1 +fi + +SLUG="$1" +exercise_dir="exercises/practice/${SLUG}" + +download() { + file="$1" + url="$2" + curl --silent --show-error --fail --retry 3 --max-time 3 \ + --output "$file" "$url" +} + +# build configlet +echo "Fetching latest version of configlet..." +./bin/fetch-configlet + +# Preparing config.json +echo "Adding instructions and configuration files..." +./bin/configlet create --practice-exercise "$SLUG" + +pushd tools +emacs -batch -l install-packages.el -l practice-exercise-generator.el --eval "(exercism/generate-practice-exercise \"$SLUG\")" +popd + +echo "All stub files were created. After implementing the solution, tests and configuration, please run:" +echo " ./bin/configlet sync --update --tests --exercise ${SLUG}" +echo " ./bin/configlet fmt --update --yes --exercise ${SLUG}" diff --git a/bin/test-examples b/bin/test-examples index ee25cba5..5d2c1236 100755 --- a/bin/test-examples +++ b/bin/test-examples @@ -12,7 +12,7 @@ pushd exercises/practice > /dev/null for exercise in *; do pushd $exercise mv "${exercise}.el" "${exercise}.el.bak" - mv .meta/example.el "${exercise}.el" + cp .meta/example.el "${exercise}.el" emacs -batch -l ert -l "${exercise}-test.el" -f ert-run-tests-batch-and-exit let "err_cnt += $?" mv "${exercise}.el.bak" "${exercise}.el" diff --git a/config.json b/config.json index 0b3ef12e..8fc2bb5d 100644 --- a/config.json +++ b/config.json @@ -16,7 +16,7 @@ "highlightjs_language": "lisp" }, "test_runner": { - "average_run_time": 2.0 + "average_run_time": 2 }, "files": { "solution": [ @@ -26,106 +26,67 @@ "%{kebab_slug}-test.el" ], "example": [ - "example.el" + ".meta/example.el" ], "exemplar": [ ".meta/exemplar.el" ] }, "exercises": { - "concept": [], "practice": [ { - "slug": "hello-world", - "name": "Hello World", - "uuid": "c4fdc935-885b-44bd-84e4-fae4a09e8c39", - "practices": [], - "prerequisites": [], - "difficulty": 1, - "topics": [ - "strings" - ] - }, - { - "slug": "two-fer", - "name": "Two Fer", - "uuid": "36e5dc3a-2122-484a-ae82-beb7b813e2cd", + "slug": "acronym", + "name": "Acronym", + "uuid": "a252e137-8a63-4f26-b119-7264f12a257a", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ - "strings" + "strings", + "transforming" ] }, { - "slug": "leap", - "name": "Leap", - "uuid": "84eaff19-cb25-46af-a34e-1379e692e57b", + "slug": "allergies", + "name": "Allergies", + "uuid": "e0d7220f-5d3a-4f10-842e-3d49df714cac", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ + "bitwise_operations", "control_flow_conditionals", - "equality", "integers", - "logic" + "text_formatting" ] }, { - "slug": "anagram", - "name": "Anagram", - "uuid": "d8906a7e-9d7e-4e8e-aff0-da877291d8dc", + "slug": "armstrong-numbers", + "name": "Armstrong Numbers", + "uuid": "059141f0-f28d-4d10-a03a-7852dbfc2d2e", "practices": [], "prerequisites": [], "difficulty": 2, "topics": [ - "equality", - "filtering", - "strings" - ] - }, - { - "slug": "roman-numerals", - "name": "Roman Numerals", - "uuid": "c4875bc5-e295-4782-99c7-cb3370ac8e08", - "practices": [], - "prerequisites": [], - "difficulty": 1, - "topics": [ - "control_flow_conditionals", - "control_flow_loops", - "integers", - "strings", - "text_formatting" + "math" ] }, { - "slug": "hamming", - "name": "Hamming", - "uuid": "2bfb508e-7ceb-4ae1-8316-1e2daf771807", + "slug": "binary", + "name": "Binary", + "uuid": "7307e13e-7a18-4654-8df4-96951d4dccc6", "practices": [], "prerequisites": [], - "difficulty": 1, - "topics": [ - "control_flow_conditionals", - "control_flow_loops", - "equality", - "filtering", - "strings" - ] + "difficulty": 2, + "status": "deprecated" }, { - "slug": "rna-transcription", - "name": "RNA Transcription", - "uuid": "7b6767b9-2efc-414d-83f7-def9c371d63d", + "slug": "binary-search", + "name": "Binary Search", + "uuid": "fb36c27f-0ad5-42d1-90c0-6718f01e61a4", "practices": [], "prerequisites": [], - "difficulty": 1, - "topics": [ - "control_flow_conditionals", - "strings", - "transforming" - ] + "difficulty": 2 }, { "slug": "bob", @@ -133,7 +94,7 @@ "uuid": "c02c7392-9478-4cfd-81eb-0cb8ebe78fe5", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ "control_flow_conditionals", "parsing", @@ -141,79 +102,23 @@ ] }, { - "slug": "word-count", - "name": "Word Count", - "uuid": "1a3c64b1-8447-451f-985d-f7bd6d42bdb6", + "slug": "collatz-conjecture", + "name": "Collatz Conjecture", + "uuid": "1eadc0d0-2086-4ab4-a53c-386933bf30f8", "practices": [], "prerequisites": [], "difficulty": 2, - "topics": [ - "control_flow_conditionals", - "control_flow_loops", - "filtering", - "strings" - ] - }, - { - "slug": "acronym", - "name": "Acronym", - "uuid": "a252e137-8a63-4f26-b119-7264f12a257a", - "practices": [], - "prerequisites": [], - "difficulty": 1, - "topics": [ - "strings", - "transforming" - ] - }, - { - "slug": "allergies", - "name": "Allergies", - "uuid": "e0d7220f-5d3a-4f10-842e-3d49df714cac", - "practices": [], - "prerequisites": [], - "difficulty": 1, - "topics": [ - "bitwise_operations", - "control_flow_conditionals", - "integers", - "text_formatting" - ] - }, - { - "slug": "armstrong-numbers", - "name": "Armstrong Numbers", - "uuid": "059141f0-f28d-4d10-a03a-7852dbfc2d2e", - "practices": [], - "prerequisites": [], - "difficulty": 1, "topics": [ "math" ] }, { - "slug": "binary", - "name": "Binary", - "uuid": "7307e13e-7a18-4654-8df4-96951d4dccc6", - "practices": [], - "prerequisites": [], - "difficulty": 1, - "topics": null, - "status": "deprecated" - }, - { - "slug": "all-your-base", - "name": "All Your Base", - "uuid": "a101dd85-fe8b-4f3f-bece-bbd899a08ee7", + "slug": "darts", + "name": "Darts", + "uuid": "ca189b4e-713d-4722-b21e-c07d1b975b58", "practices": [], "prerequisites": [], - "difficulty": 3, - "topics": [ - "bitwise_operations", - "control_flow_conditionals", - "integers", - "math" - ] + "difficulty": 2 }, { "slug": "difference-of-squares", @@ -221,7 +126,7 @@ "uuid": "7b421b14-c33c-41a6-9e7e-4f9b6fbe6fcc", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ "control_flow_loops", "floating_point_numbers", @@ -230,17 +135,20 @@ ] }, { - "slug": "etl", - "name": "ETL", - "uuid": "d8f4c530-02f0-44d3-b5b8-858fec4b6a9d", + "slug": "dnd-character", + "name": "D&D Character", + "uuid": "79f913e1-9571-4b1e-81f5-f0f3160898cc", "practices": [], "prerequisites": [], - "difficulty": 1, - "topics": [ - "control_flow_loops", - "maps", - "transforming" - ] + "difficulty": 2 + }, + { + "slug": "eliuds-eggs", + "name": "Eliud's Eggs", + "uuid": "9e10bc23-bd92-4b67-9921-6ff7555d3774", + "practices": [], + "prerequisites": [], + "difficulty": 2 }, { "slug": "gigasecond", @@ -248,7 +156,7 @@ "uuid": "e9e27a75-143a-439f-98d0-a604724a7af2", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ "date", "integers", @@ -263,162 +171,256 @@ "uuid": "d219aab6-11ec-4e0c-b041-f06c8d523946", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ "integers", "variables" ] }, { - "slug": "nucleotide-count", - "name": "Nucleotide Count", - "uuid": "aaff28dd-718b-48d4-b507-a0b9b01bb6ad", + "slug": "hamming", + "name": "Hamming", + "uuid": "2bfb508e-7ceb-4ae1-8316-1e2daf771807", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ + "control_flow_conditionals", "control_flow_loops", - "maps", - "sorting", + "equality", + "filtering", "strings" ] }, { - "slug": "pangram", - "name": "Pangram", - "uuid": "42222c98-e8d2-41bd-9d95-2ace63864359", + "slug": "hello-world", + "name": "Hello World", + "uuid": "c4fdc935-885b-44bd-84e4-fae4a09e8c39", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ - "control_flow_loops", "strings" ] }, { - "slug": "perfect-numbers", - "name": "Perfect Numbers", - "uuid": "30971b02-79fc-40a1-aa70-ada3fec85548", + "slug": "isogram", + "name": "Isogram", + "uuid": "63fc7e59-2448-487b-9b66-06c72ab8d6b0", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ - "math", - "number_theory" + "control_flow_loops", + "strings" ] }, { - "slug": "raindrops", - "name": "Raindrops", - "uuid": "9a97c992-3da0-4f6d-b2f6-1f2683417e3b", + "slug": "kindergarten-garden", + "name": "Kindergarten Garden", + "uuid": "798056fc-11df-4705-b783-6eee56e6a1e2", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ - "control_flow_conditionals", - "filtering", - "integers" + "strings" ] }, { - "slug": "trinary", - "name": "Trinary", - "uuid": "c5239cf6-c5a3-4d81-a4d9-504c292ff11e", + "slug": "leap", + "name": "Leap", + "uuid": "84eaff19-cb25-46af-a34e-1379e692e57b", "practices": [], "prerequisites": [], - "difficulty": 1, + "difficulty": 2, "topics": [ - "math" + "control_flow_conditionals", + "equality", + "integers", + "logic" ] }, { - "slug": "atbash-cipher", - "name": "Atbash Cipher", - "uuid": "89125ff6-f94b-46a4-9a5d-84539674238d", + "slug": "list-ops", + "name": "List Ops", + "uuid": "883fdbef-ad97-4f08-8d51-d65bd91fc864", "practices": [], "prerequisites": [], "difficulty": 2, "topics": [ - "control_flow_conditionals", - "control_flow_loops", - "sequences", - "transforming" + "filtering", + "functional_programming", + "generics", + "lists", + "control_flow_loops" ] }, { - "slug": "crypto-square", - "name": "Crypto Square", - "uuid": "884cd761-e764-4c1e-8240-bb4454b43ea9", + "slug": "matching-brackets", + "name": "Matching Brackets", + "uuid": "c000a3e1-3488-41ba-9d4c-6f06a96bc892", "practices": [], "prerequisites": [], - "difficulty": 2, - "topics": [ - "cryptograpy", - "strings", - "transforming" - ] + "difficulty": 2 }, { - "slug": "phone-number", - "name": "Phone Number", - "uuid": "c211045e-da97-479d-9df4-5d812573e2d0", + "slug": "pangram", + "name": "Pangram", + "uuid": "42222c98-e8d2-41bd-9d95-2ace63864359", "practices": [], "prerequisites": [], "difficulty": 2, "topics": [ - "interfaces", - "parsing", + "control_flow_loops", "strings" ] }, { - "slug": "robot-name", - "name": "Robot Name", - "uuid": "55bc8c2a-1db3-4d26-b7c6-a778b61bf46b", + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "uuid": "30971b02-79fc-40a1-aa70-ada3fec85548", "practices": [], "prerequisites": [], "difficulty": 2, "topics": [ - "randomness", - "strings", - "variables" + "math", + "number_theory" ] }, { - "slug": "run-length-encoding", - "name": "Run-Length Encoding", - "uuid": "9e8f98f0-6fff-4389-a9a2-9ae43edefa21", + "slug": "phone-number", + "name": "Phone Number", + "uuid": "c211045e-da97-479d-9df4-5d812573e2d0", "practices": [], "prerequisites": [], - "difficulty": 2, - "topics": null + "difficulty": 2 }, { - "slug": "luhn", - "name": "Luhn", - "uuid": "bb10d7e4-6006-4dfd-81ae-45062a0ec999", + "slug": "queen-attack", + "name": "Queen Attack", + "uuid": "bfa1d997-f0bd-432b-aa29-7018607d772d", "practices": [], "prerequisites": [], - "difficulty": 3, - "topics": [ - "checksums", - "list_operations", - "strings" + "difficulty": 2 + }, + { + "slug": "robot-simulator", + "name": "Robot Simulator", + "uuid": "575a40bd-2ff9-4623-ae98-c8d9b9f279a8", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "rna-transcription", + "name": "RNA Transcription", + "uuid": "7b6767b9-2efc-414d-83f7-def9c371d63d", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "control_flow_conditionals", + "strings", + "transforming" ] }, { - "slug": "list-ops", - "name": "List Ops", - "uuid": "883fdbef-ad97-4f08-8d51-d65bd91fc864", + "slug": "raindrops", + "name": "Raindrops", + "uuid": "9a97c992-3da0-4f6d-b2f6-1f2683417e3b", "practices": [], "prerequisites": [], - "difficulty": 3, + "difficulty": 2, "topics": [ + "control_flow_conditionals", "filtering", - "functional_programming", - "generics", + "integers" + ] + }, + { + "slug": "resistor-color", + "name": "Resistor Color", + "uuid": "dfc38e27-4c5f-416a-804e-d229e94a15b5", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "resistor-color-duo", + "name": "Resistor Color Duo", + "uuid": "1c55189e-2c58-48b4-92f1-9a05a9b9c6d6", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "reverse-string", + "name": "Reverse String", + "uuid": "59c6319b-2799-4e4c-bef1-e28626878446", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "roman-numerals", + "name": "Roman Numerals", + "uuid": "c4875bc5-e295-4782-99c7-cb3370ac8e08", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "control_flow_conditionals", + "control_flow_loops", + "integers", + "strings", + "text_formatting" + ] + }, + { + "slug": "rotational-cipher", + "name": "Rotational Cipher", + "uuid": "4faa2bae-ecda-4c16-9f6d-148793117995", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "scrabble-score", + "name": "Scrabble Score", + "uuid": "fb45c509-d83d-4f71-a8ef-f218c5426838", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ "lists", - "loops" + "strings" + ] + }, + { + "slug": "simple-cipher", + "name": "Simple Cipher", + "uuid": "c3cc6d91-b633-44d6-b7e1-04abb33e712c", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "space-age", + "name": "Space Age", + "uuid": "183eb493-2141-4823-8fe6-0f01ecbcb27a", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "square-root", + "name": "Square Root", + "uuid": "d74d3b6d-43db-4f46-a25d-b1a86df3fb47", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "math" ] }, { @@ -437,20 +439,548 @@ "pattern-matching", "recursion" ] + }, + { + "slug": "sum-of-multiples", + "name": "Sum of Multiples", + "uuid": "3df22483-1a7c-445d-9eca-131a00c74231", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "math" + ] + }, + { + "slug": "triangle", + "name": "Triangle", + "uuid": "df52be2e-8709-4da5-a8c2-03a2fd137993", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "control_flow_conditionals" + ] + }, + { + "slug": "trinary", + "name": "Trinary", + "uuid": "c5239cf6-c5a3-4d81-a4d9-504c292ff11e", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "status": "deprecated", + "topics": [ + "math" + ] + }, + { + "slug": "two-fer", + "name": "Two-Fer", + "uuid": "36e5dc3a-2122-484a-ae82-beb7b813e2cd", + "practices": [], + "prerequisites": [], + "difficulty": 2, + "topics": [ + "strings" + ] + }, + { + "slug": "prime-factors", + "name": "Prime Factors", + "uuid": "cb79cb42-7efe-49fe-98c4-68dbdc32f2a3", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [ + "math" + ] + }, + { + "slug": "protein-translation", + "name": "Protein Translation", + "uuid": "3201c23c-e2eb-4e1a-8a44-b56acac572f5", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [ + "strings" + ] + }, + { + "slug": "secret-handshake", + "name": "Secret Handshake", + "uuid": "e55f5562-9126-4cc7-9f9e-a3cbdfd292b4", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, + { + "slug": "series", + "name": "Series", + "uuid": "2e10ca92-6232-418e-b9ea-506ac1eba195", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [ + "strings" + ] + }, + { + "slug": "sieve", + "name": "Sieve", + "uuid": "21172aa9-a155-493c-ad9f-ab4884596488", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [ + "control_flow_loops", + "math" + ] + }, + { + "slug": "diamond", + "name": "Diamond", + "uuid": "312bff7d-34f4-4ade-bbd3-479bce9bff58", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "largest-series-product", + "name": "Largest Series Product", + "uuid": "91b2ba1a-8995-4c5c-9682-1ef462cbcbf5", + "practices": [], + "prerequisites": [], + "difficulty": 4, + "topics": [ + "control_flow_loops", + "strings" + ] + }, + { + "slug": "nth-prime", + "name": "Nth Prime", + "uuid": "4dfd9ac8-d0e8-4db5-a68c-75e8647b19c4", + "practices": [], + "prerequisites": [], + "difficulty": 4, + "topics": [ + "math" + ] + }, + { + "slug": "pascals-triangle", + "name": "Pascal's Triangle", + "uuid": "cc71a6d5-efb2-4054-bf72-102b3e96e0bc", + "practices": [], + "prerequisites": [], + "difficulty": 4, + "topics": [ + "math" + ] + }, + { + "slug": "affine-cipher", + "name": "Affine Cipher", + "uuid": "7d966a40-3cf7-422a-99b1-c0bf05e151b2", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "all-your-base", + "name": "All Your Base", + "uuid": "a101dd85-fe8b-4f3f-bece-bbd899a08ee7", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "bitwise_operations", + "control_flow_conditionals", + "integers", + "math" + ] + }, + { + "slug": "anagram", + "name": "Anagram", + "uuid": "d8906a7e-9d7e-4e8e-aff0-da877291d8dc", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "equality", + "filtering", + "strings" + ] + }, + { + "slug": "atbash-cipher", + "name": "Atbash Cipher", + "uuid": "89125ff6-f94b-46a4-9a5d-84539674238d", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_conditionals", + "control_flow_loops", + "sequences", + "transforming" + ] + }, + { + "slug": "crypto-square", + "name": "Crypto Square", + "uuid": "884cd761-e764-4c1e-8240-bb4454b43ea9", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "cryptograpy", + "strings", + "transforming" + ] + }, + { + "slug": "etl", + "name": "ETL", + "uuid": "d8f4c530-02f0-44d3-b5b8-858fec4b6a9d", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_loops", + "maps", + "transforming" + ] + }, + { + "slug": "flatten-array", + "name": "Flatten Array", + "uuid": "66f266a8-9c5c-4aad-8a27-92810b288a2b", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "game-of-life", + "name": "Conway's Game of Life", + "uuid": "4e8130a9-bdde-46c5-bb50-e780c3af8670", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_loops", + "lists" + ] + }, + { + "slug": "luhn", + "name": "Luhn", + "uuid": "bb10d7e4-6006-4dfd-81ae-45062a0ec999", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "checksums", + "list_operations", + "strings" + ] + }, + { + "slug": "minesweeper", + "name": "Minesweeper", + "uuid": "58e8c0ba-6a1c-4285-bea3-4809a6987da0", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_loops" + ] + }, + { + "slug": "nucleotide-count", + "name": "Nucleotide Count", + "uuid": "aaff28dd-718b-48d4-b507-a0b9b01bb6ad", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_loops", + "maps", + "sorting", + "strings" + ] + }, + { + "slug": "pythagorean-triplet", + "name": "Pythagorean Triplet", + "uuid": "f33d976d-5189-4fcb-b0ac-42879a1067bf", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "math" + ] + }, + { + "slug": "rail-fence-cipher", + "name": "Rail Fence Cipher", + "uuid": "bcd4671e-4fb9-4abb-af98-315fc83daa52", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "robot-name", + "name": "Robot Name", + "uuid": "55bc8c2a-1db3-4d26-b7c6-a778b61bf46b", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "randomness", + "strings", + "variables" + ] + }, + { + "slug": "run-length-encoding", + "name": "Run-Length Encoding", + "uuid": "9e8f98f0-6fff-4389-a9a2-9ae43edefa21", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "spiral-matrix", + "name": "Spiral Matrix", + "uuid": "4ce2b869-140a-4d5d-90dd-6fc717ef7696", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "word-count", + "name": "Word Count", + "uuid": "1a3c64b1-8447-451f-985d-f7bd6d42bdb6", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "control_flow_conditionals", + "control_flow_loops", + "filtering", + "strings" + ] + }, + { + "slug": "isbn-verifier", + "name": "ISBN Verifier", + "uuid": "b0cbc3b5-1d6c-40b8-bdd1-5ba9089024aa", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "satellite", + "name": "Satellite", + "uuid": "02d23d01-c4c6-4fd1-9e0b-f6a457c402ed", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "41029de7-58a6-431b-9f82-6d4603604fb3", + "practices": [], + "prerequisites": [], + "difficulty": 7, + "topics": [ + "control_flow_loops" + ] + }, + { + "slug": "pig-latin", + "name": "Pig Latin", + "uuid": "3bee6827-8766-4d4d-9d0f-3facc3bfd3f4", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "food-chain", + "name": "Food Chain", + "uuid": "ac1c0246-0fed-46e7-819a-0b013b2b4585", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "circular-buffer", + "name": "Circular Buffer", + "uuid": "d2eaa3fa-42b3-4287-9b87-714a73ac0c65", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "bank-account", + "name": "Bank Account", + "uuid": "edc5fa4b-35b3-4313-b4c3-16153708f0ac", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, + { + "slug": "yacht", + "name": "Yacht", + "uuid": "5d06fd6e-2ca7-425d-a3dd-892cb3f7bf65", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "accumulate", + "name": "Accumulate", + "uuid": "093463bf-e8df-4dd9-b6e3-b5ec007cb781", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "grade-school", + "name": "Grade School", + "uuid": "e5ab14bd-9eeb-4c63-8143-55e1e56b4ac1", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "meetup", + "name": "Meetup", + "uuid": "80000759-8ca8-4ff8-a88f-34c4244ac6c5", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "two-bucket", + "name": "Two Bucket", + "uuid": "2eb39d6e-52de-4822-8cd1-ef789c1f5e22", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "zebra-puzzle", + "name": "Zebra Puzzle", + "uuid": "bd7c4195-2d89-436b-918f-24224a95610d", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "strain", + "name": "Strain", + "uuid": "daff70f7-504a-4ba5-89a5-1a2860dcd98e", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "proverb", + "name": "Proverb", + "uuid": "e946ec2a-749a-4018-877b-25c42d4ee5b0", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, + { + "slug": "say", + "name": "Say", + "uuid": "7a6c586a-c8fa-4313-8036-5ff21097702c", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "parallel-letter-frequency", + "name": "Parallel Letter Frequency", + "uuid": "3daf3903-1eb0-49b9-827a-76e6b7ca25fb", + "practices": [], + "prerequisites": [], + "difficulty": 10 + }, + { + "slug": "variable-length-quantity", + "name": "Variable Length Quantity", + "uuid": "5073fd82-3025-4370-ae93-37b661c17483", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "linked-list", + "name": "Linked List", + "uuid": "ede3b6fb-b399-44a8-8aee-7bdcabec408e", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "resistor-color-trio", + "name": "Resistor Color Trio", + "uuid": "e63a974b-1d30-4323-8059-98d131d52f6e", + "practices": [], + "prerequisites": [], + "difficulty": 2 } ] }, - "concepts": [], - "key_features": [], + "key_features": [ + { + "title": "Simple syntax", + "content": "Emacs Lisp's syntax is simple, expressive and easy to learn.", + "icon": "easy" + }, + { + "title": "S-expressions", + "content": "Both source code and data are expressed using nested lists.", + "icon": "homoiconic" + }, + { + "title": "Macros", + "content": "It's easy to customize the language with Emacs Lisp's macro system.", + "icon": "powerful" + }, + { + "title": "Multi-paradigm", + "content": "Emacs Lisp support imperative and functional programming styles.", + "icon": "multi-paradigm" + }, + { + "title": "Scripting", + "content": "Emacs Lisp can be used as scripting language by using Emacs' batch mode.", + "icon": "embeddable" + }, + { + "title": "Well Documented", + "content": "Emacs Lisp is well documented right inside the editor.", + "icon": "documentation" + } + ], "tags": [ - "paradigm/functional", - "typing/dynamic", - "typing/strong", "execution_mode/interpreted", - "platform/windows", - "platform/mac", + "paradigm/functional", "platform/linux", + "platform/mac", + "platform/windows", "runtime/language_specific", + "typing/dynamic", + "typing/strong", "used_for/scripts" ] } diff --git a/exercises/practice/accumulate/.docs/instructions.md b/exercises/practice/accumulate/.docs/instructions.md new file mode 100644 index 00000000..c25a03fa --- /dev/null +++ b/exercises/practice/accumulate/.docs/instructions.md @@ -0,0 +1,22 @@ +# Instructions + +Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection. + +Given the collection of numbers: + +- 1, 2, 3, 4, 5 + +And the operation: + +- square a number (`x => x * x`) + +Your code should be able to produce the collection of squares: + +- 1, 4, 9, 16, 25 + +Check out the test suite to see the expected function signature. + +## Restrictions + +Keep your hands off that collect/map/fmap/whatchamacallit functionality provided by your standard library! +Solve this one yourself using other basic tools instead. diff --git a/exercises/practice/accumulate/.meta/config.json b/exercises/practice/accumulate/.meta/config.json new file mode 100644 index 00000000..884c92ba --- /dev/null +++ b/exercises/practice/accumulate/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "accumulate.el" + ], + "test": [ + "accumulate-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection.", + "source": "Conversation with James Edward Gray II", + "source_url": "http://graysoftinc.com/" +} diff --git a/exercises/practice/accumulate/.meta/example.el b/exercises/practice/accumulate/.meta/example.el new file mode 100644 index 00000000..b0c8d080 --- /dev/null +++ b/exercises/practice/accumulate/.meta/example.el @@ -0,0 +1,15 @@ +;;; accumulate.el --- Accumulate (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun accumulate (lst op) + (cond + ((null lst) lst) + (t (cons (funcall op (car lst)) (accumulate (cdr lst) op))))) + + +(provide 'accumulate) +;;; accumulate.el ends here diff --git a/exercises/practice/accumulate/.meta/tests.toml b/exercises/practice/accumulate/.meta/tests.toml new file mode 100644 index 00000000..d7858e07 --- /dev/null +++ b/exercises/practice/accumulate/.meta/tests.toml @@ -0,0 +1,30 @@ +# 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. + +[64d97c14-36dd-44a8-9621-2cecebd6ed23] +description = "accumulate empty" + +[00008ed2-4651-4929-8c08-8b4dbd70872e] +description = "accumulate squares" + +[551016da-4396-4cae-b0ec-4c3a1a264125] +description = "accumulate upcases" + +[cdf95597-b6ec-4eac-a838-3480d13d0d05] +description = "accumulate reversed strings" + +[bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb] +description = "accumulate recursively" +include = false + +[0b357334-4cad-49e1-a741-425202edfc7c] +description = "accumulate recursively" +reimplements = "bee8e9b6-b16f-4cd2-be3b-ccf7457e50bb" diff --git a/exercises/practice/accumulate/accumulate-test.el b/exercises/practice/accumulate/accumulate-test.el new file mode 100644 index 00000000..25a8dc6b --- /dev/null +++ b/exercises/practice/accumulate/accumulate-test.el @@ -0,0 +1,43 @@ +;;; accumulate-test.el --- Accumulate (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "accumulate.el") +(declare-function accumulate "accumulate.el" (lst op)) + + +(ert-deftest accumulate-empty () + (let ((result (accumulate '() (lambda (x) (* x x))))) + (should (equal result '())))) + + +(ert-deftest accumulate-square () + (let ((result (accumulate '(1 2 3 4) (lambda (x) (* x x))))) + (should (equal result '(1 4 9 16))))) + + +(ert-deftest accumulate-upcases () + (let ((result (accumulate '("Hello" "world") 'upcase))) + (should (equal result '("HELLO" "WORLD"))))) + + +(ert-deftest accumulate-reversed-strings () + (let ((result (accumulate '("the" "quick" "brown" "fox" "etc") (lambda (x) (apply #'string (reverse (string-to-list x))))))) + (should (equal result '("eht" "kciuq" "nworb" "xof" "cte"))))) + + +(ert-deftest accumulate-recursively () + (let* ((inner-list '("1" "2" "3")) + (result (accumulate '("a" "b" "c") + (lambda (x) + (mapcar (lambda (y) (concat x y)) inner-list))))) + (should (equal result '(("a1" "a2" "a3") + ("b1" "b2" "b3") + ("c1" "c2" "c3")))))) + + +(provide 'accumulate-test) +;;; accumulate-test.el ends here diff --git a/exercises/practice/accumulate/accumulate.el b/exercises/practice/accumulate/accumulate.el new file mode 100644 index 00000000..098d060d --- /dev/null +++ b/exercises/practice/accumulate/accumulate.el @@ -0,0 +1,13 @@ +;;; accumulate.el --- Accumulate (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun accumulate (lst op) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'accumulate) +;;; accumulate.el ends here diff --git a/exercises/practice/acronym/.docs/instructions.md b/exercises/practice/acronym/.docs/instructions.md index c62fc3e8..133bd2cb 100644 --- a/exercises/practice/acronym/.docs/instructions.md +++ b/exercises/practice/acronym/.docs/instructions.md @@ -10,8 +10,8 @@ Punctuation is handled as follows: hyphens are word separators (like whitespace) For example: -|Input|Output| -|-|-| -|As Soon As Possible|ASAP| -|Liquid-crystal display|LCD| -|Thank George It's Friday!|TGIF| +| Input | Output | +| ------------------------- | ------ | +| As Soon As Possible | ASAP | +| Liquid-crystal display | LCD | +| Thank George It's Friday! | TGIF | diff --git a/exercises/practice/acronym/.meta/example.el b/exercises/practice/acronym/.meta/example.el index b38cf7b3..83241be0 100644 --- a/exercises/practice/acronym/.meta/example.el +++ b/exercises/practice/acronym/.meta/example.el @@ -7,7 +7,7 @@ (require 'cl-lib) (defun acronym (input) - (let ((words (split-string input "\\W+"))) + (let ((words (split-string (string-replace "'" "" input) "\\W+"))) (mapconcat (lambda (word) (upcase (substring word 0 1))) words ""))) (provide 'acronym) diff --git a/exercises/practice/acronym/acronym-test.el b/exercises/practice/acronym/acronym-test.el index aa9678bc..b9b98b7e 100644 --- a/exercises/practice/acronym/acronym-test.el +++ b/exercises/practice/acronym/acronym-test.el @@ -4,26 +4,53 @@ ;;; Code + (load-file "acronym.el") (declare-function acronym "acronym.el" (phrase)) + (ert-deftest basic () - (should (equal "PNG" (acronym "Portable Network Graphics")))) + (should (string= (acronym "Portable Network Graphics") + "PNG"))) + (ert-deftest lowercase-words () - (should (equal "ROR" (acronym "Ruby on Rails")))) + (should (string= (acronym "Ruby on Rails") + "ROR"))) + (ert-deftest punctuation () - (should (equal "FIFO" (acronym "First In, First Out")))) + (should (string= (acronym "First In, First Out") + "FIFO"))) + + +(ert-deftest all-caps-word () + (should (string= (acronym "GNU Image Manipulation Program") + "GIMP"))) + + +(ert-deftest punctuation-without-white-space () + (should (string= (acronym "Complementary metal-oxide semiconductor") + "CMOS"))) + + +(ert-deftest very-long-abbreviation () + (should (string= (acronym "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me") + "ROTFLSHTMDCOALM"))) + + +(ert-deftest consecutive-delimiters () + (should (string= (acronym "Something - I made up from thin air") + "SIMUFTA"))) + + +(ert-deftest apostrophes () + (should (string= (acronym "Halley's Comet") "HC"))) -(ert-deftest all-caps-words () - (should (equal "PHP" (acronym "PHP: Hypertext Preprocessor")))) -(ert-deftest non-acronym-all-caps-word () - (should (equal "GIMP" (acronym "GNU Image Manipulation Program")))) +(ert-deftest underscore-emphasis () + (should (string= (acronym "The Road _Not_ Taken") "TRNT"))) -(ert-deftest hyphenated () - (should (equal "CMOS" (acronym "Complementary metal-oxide semiconductor")))) (provide 'acronym-test) ;;; acronym-test.el ends here diff --git a/exercises/practice/acronym/acronym.el b/exercises/practice/acronym/acronym.el index d6216c4f..f04d7115 100644 --- a/exercises/practice/acronym/acronym.el +++ b/exercises/practice/acronym/acronym.el @@ -2,9 +2,11 @@ ;;; Commentary: -(defun acronym (phrase) ;;; Code: -) + + +(defun acronym (phrase) + (error "Delete this S-Expression and write your own implementation")) (provide 'acronym) ;;; acronym.el ends here diff --git a/exercises/practice/affine-cipher/.docs/instructions.md b/exercises/practice/affine-cipher/.docs/instructions.md new file mode 100644 index 00000000..1603dbbc --- /dev/null +++ b/exercises/practice/affine-cipher/.docs/instructions.md @@ -0,0 +1,74 @@ +# Instructions + +Create an implementation of the affine cipher, an ancient encryption system created in the Middle East. + +The affine cipher is a type of monoalphabetic substitution cipher. +Each character is mapped to its numeric equivalent, encrypted with a mathematical function and then converted to the letter relating to its new numeric value. +Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the Atbash cipher, because it has many more keys. + +[//]: # " monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic " + +## Encryption + +The encryption function is: + +```text +E(x) = (ai + b) mod m +``` + +Where: + +- `i` is the letter's index from `0` to the length of the alphabet - 1. +- `m` is the length of the alphabet. + For the Latin alphabet `m` is `26`. +- `a` and `b` are integers which make up the encryption key. + +Values `a` and `m` must be _coprime_ (or, _relatively prime_) for automatic decryption to succeed, i.e., they have number `1` as their only common factor (more information can be found in the [Wikipedia article about coprime integers][coprime-integers]). +In case `a` is not coprime to `m`, your program should indicate that this is an error. +Otherwise it should encrypt or decrypt with the provided key. + +For the purpose of this exercise, digits are valid input but they are not encrypted. +Spaces and punctuation characters are excluded. +Ciphertext is written out in groups of fixed length separated by space, the traditional group size being `5` letters. +This is to make it harder to guess encrypted text based on word boundaries. + +## Decryption + +The decryption function is: + +```text +D(y) = (a^-1)(y - b) mod m +``` + +Where: + +- `y` is the numeric value of an encrypted letter, i.e., `y = E(x)` +- it is important to note that `a^-1` is the modular multiplicative inverse (MMI) of `a mod m` +- the modular multiplicative inverse only exists if `a` and `m` are coprime. + +The MMI of `a` is `x` such that the remainder after dividing `ax` by `m` is `1`: + +```text +ax mod m = 1 +``` + +More information regarding how to find a Modular Multiplicative Inverse and what it means can be found in the [related Wikipedia article][mmi]. + +## General Examples + +- Encrypting `"test"` gives `"ybty"` with the key `a = 5`, `b = 7` +- Decrypting `"ybty"` gives `"test"` with the key `a = 5`, `b = 7` +- Decrypting `"ybty"` gives `"lqul"` with the wrong key `a = 11`, `b = 7` +- Decrypting `"kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx"` gives `"thequickbrownfoxjumpsoverthelazydog"` with the key `a = 19`, `b = 13` +- Encrypting `"test"` with the key `a = 18`, `b = 13` is an error because `18` and `26` are not coprime + +## Example of finding a Modular Multiplicative Inverse (MMI) + +Finding MMI for `a = 15`: + +- `(15 * x) mod 26 = 1` +- `(15 * 7) mod 26 = 1`, ie. `105 mod 26 = 1` +- `7` is the MMI of `15 mod 26` + +[mmi]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse +[coprime-integers]: https://en.wikipedia.org/wiki/Coprime_integers diff --git a/exercises/practice/affine-cipher/.meta/config.json b/exercises/practice/affine-cipher/.meta/config.json new file mode 100644 index 00000000..5738ea8f --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "affine-cipher.el" + ], + "test": [ + "affine-cipher-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Create an implementation of the Affine cipher, an ancient encryption algorithm from the Middle East.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Affine_cipher" +} diff --git a/exercises/practice/affine-cipher/.meta/example.el b/exercises/practice/affine-cipher/.meta/example.el new file mode 100644 index 00000000..be7b9cbf --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/example.el @@ -0,0 +1,95 @@ +;;; affine-cipher.el --- Affine Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'seq) + +(defconst letters-in-latin 26) + +(defun encode (phrase key) + (let ((a (cdr (assoc "a" key))) + (b (cdr (assoc "b" key))) + (m letters-in-latin) + (phrase-cleaned + (replace-regexp-in-string "[^a-z0-9]" "" (downcase phrase)))) + (unless (coprime-p a m) + (error "a and m must be coprime.")) + (mapconcat + 'identity + (seq-partition + (mapcar + (apply-partially 'exercism-encode-char a b m) phrase-cleaned) + 5) + " "))) + +(defun exercism-encode-char (a b m char) + (if (<= ?0 char ?9) + char + (+ (mod (+ (* a (- char ?a)) b) m) ?a))) + +(defun decode (phrase key) + (let ((a (cdr (assoc "a" key))) + (b (cdr (assoc "b" key))) + (m letters-in-latin) + (phrase-cleaned + (replace-regexp-in-string "[^a-z0-9]" "" (downcase phrase)))) + (unless (coprime-p a m) + (error "a and m must be coprime.")) + (seq-into + (mapcar + (apply-partially 'exercism-decode-char a b m) phrase-cleaned) + 'string))) + +(defun exercism-decode-char (a b m char) + (if (<= ?0 char ?9) + char + (+ (mod (* (mmi a m) (- (- char ?a) b)) m) ?a))) + +(defun mmi (a m) + "Find the modular multiplicative inverse (MMI) of A mod M using the extended Euclidean algorithm." + (let ((tt 0) ;; t is already used as constant for truth + (newtt 1) + (r m) + (newr a)) + (while (not (= newr 0)) + (let ((quotient (/ r newr)) + temp) + (setq temp newtt) + (setq newtt (- tt (* quotient newtt))) + (setq tt temp) + (setq temp newr) + (setq newr (- r (* quotient newr))) + (setq r temp))) + + (if (> r 1) + (error + "a is not invertible (provided numbers are not coprime)")) + (if (< tt 0) + (setq tt (+ tt m))) + tt)) + +(defun coprime-p (m n) + (= (gcd m n) 1)) + +(defun gcd (m n) + "Find greatest common divisor of M and N. +Uses the Euclidian Algorithm." + (let ((m + (if (< m n) + n + m)) + (n + (if (< m n) + m + n)) + r) + (while (not (= n 0)) + (setq r (mod m n)) + (setq m n) + (setq n r)) + m)) + +(provide 'affine-cipher) +;;; affine-cipher.el ends here diff --git a/exercises/practice/affine-cipher/.meta/tests.toml b/exercises/practice/affine-cipher/.meta/tests.toml new file mode 100644 index 00000000..07cce7c7 --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/tests.toml @@ -0,0 +1,58 @@ +# 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. + +[2ee1d9af-1c43-416c-b41b-cefd7d4d2b2a] +description = "encode -> encode yes" + +[785bade9-e98b-4d4f-a5b0-087ba3d7de4b] +description = "encode -> encode no" + +[2854851c-48fb-40d8-9bf6-8f192ed25054] +description = "encode -> encode OMG" + +[bc0c1244-b544-49dd-9777-13a770be1bad] +description = "encode -> encode O M G" + +[381a1a20-b74a-46ce-9277-3778625c9e27] +description = "encode -> encode mindblowingly" + +[6686f4e2-753b-47d4-9715-876fdc59029d] +description = "encode -> encode numbers" + +[ae23d5bd-30a8-44b6-afbe-23c8c0c7faa3] +description = "encode -> encode deep thought" + +[c93a8a4d-426c-42ef-9610-76ded6f7ef57] +description = "encode -> encode all the letters" + +[0673638a-4375-40bd-871c-fb6a2c28effb] +description = "encode -> encode with a not coprime to m" + +[3f0ac7e2-ec0e-4a79-949e-95e414953438] +description = "decode -> decode exercism" + +[241ee64d-5a47-4092-a5d7-7939d259e077] +description = "decode -> decode a sentence" + +[33fb16a1-765a-496f-907f-12e644837f5e] +description = "decode -> decode numbers" + +[20bc9dce-c5ec-4db6-a3f1-845c776bcbf7] +description = "decode -> decode all the letters" + +[623e78c0-922d-49c5-8702-227a3e8eaf81] +description = "decode -> decode with no spaces in input" + +[58fd5c2a-1fd9-4563-a80a-71cff200f26f] +description = "decode -> decode with too many spaces" + +[b004626f-c186-4af9-a3f4-58f74cdb86d5] +description = "decode -> decode with a not coprime to m" diff --git a/exercises/practice/affine-cipher/affine-cipher-test.el b/exercises/practice/affine-cipher/affine-cipher-test.el new file mode 100644 index 00000000..0c820fde --- /dev/null +++ b/exercises/practice/affine-cipher/affine-cipher-test.el @@ -0,0 +1,123 @@ +;;; affine-cipher-test.el --- Affine Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "affine-cipher.el") +(declare-function encode "affine-cipher.el" (phrase key)) +(declare-function decode "affine-cipher.el" (phrase key)) + + +(ert-deftest encode-yes () + (should (equal "xbt" (encode "yes" '(("a" . 5) ("b" . 7)))))) + + +(ert-deftest encode-no () + (should (equal "fu" (encode "no" '(("a" . 15) ("b" . 18)))))) + + +(ert-deftest encode-omg () + (should (equal "lvz" (encode "OMG" '(("a" . 21) ("b" . 3)))))) + + +(ert-deftest encode-o-m-g () + (should (equal "hjp" (encode "O M G" '(("a" . 25) ("b" . 47)))))) + + +(ert-deftest encode-mindblowingly () + (should + (equal + "rzcwa gnxzc dgt" + (encode "mindblowingly" '(("a" . 11) ("b" . 15)))))) + + +(ert-deftest encode-numbers () + (should + (equal + "jqgjc rw123 jqgjc rw" + (encode "Testing,1 2 3, testing." '(("a" . 3) ("b" . 4)))))) + + +(ert-deftest encode-deep-thought () + (should + (equal + "iynia fdqfb ifje" + (encode "Truth is fiction." '(("a" . 5) ("b" . 17)))))) + + +(ert-deftest encode-all-the-letters () + (should + (equal + "swxtj npvyk lruol iejdc blaxk swxmh qzglf" + (encode + "The quick brown fox jumps over the lazy dog." + '(("a" . 17) ("b" . 33)))))) + + +(ert-deftest encode-with-a-not-coprime-to-m () + (let ((error-data + (should-error + (encode "This is a test." '(("a" . 6) ("b" . 17)))))) + (should + (string= + "a and m must be coprime." (error-message-string error-data))))) + + +(ert-deftest decode-exercism () + (should + (equal "exercism" (decode "tytgn fjr" '(("a" . 3) ("b" . 7)))))) + + +(ert-deftest decode-a-sentence () + (should + (equal + "anobstacleisoftenasteppingstone" + (decode + "qdwju nqcro muwhn odqun oppmd aunwd o" + '(("a" . 19) ("b" . 16)))))) + + +(ert-deftest decode-numbers () + (should + (equal + "testing123testing" + (decode "odpoz ub123 odpoz ub" '(("a" . 25) ("b" . 7)))))) + + +(ert-deftest decode-all-the-letters () + (should + (equal + "thequickbrownfoxjumpsoverthelazydog" + (decode + "swxtj npvyk lruol iejdc blaxk swxmh qzglf" + '(("a" . 17) ("b" . 33)))))) + + +(ert-deftest decode-with-no-spaces-in-input () + (should + (equal + "thequickbrownfoxjumpsoverthelazydog" + (decode + "swxtjnpvyklruoliejdcblaxkswxmhqzglf" + '(("a" . 17) ("b" . 33)))))) + + +(ert-deftest decode-with-too-many-spaces () + (should + (equal + "jollygreengiant" + (decode "vszzm cly yd cg qdp" '(("a" . 15) ("b" . 16)))))) + + +(ert-deftest decode-with-a-not-coprime-to-m () + (let ((error-data + (should-error (decode "Test" '(("a" . 13) ("b" . 5)))))) + (should + (string= + "a and m must be coprime." (error-message-string error-data))))) + + +(provide 'affine-cipher-test) +;;; affine-cipher-test.el ends here diff --git a/exercises/practice/affine-cipher/affine-cipher.el b/exercises/practice/affine-cipher/affine-cipher.el new file mode 100644 index 00000000..c816f02c --- /dev/null +++ b/exercises/practice/affine-cipher/affine-cipher.el @@ -0,0 +1,18 @@ +;;; affine-cipher.el --- Affine Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun encode (phrase key) + (error + "Delete this S-Expression and write your own implementation")) + +(defun decode (phrase key) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'affine-cipher) +;;; affine-cipher.el ends here diff --git a/exercises/practice/all-your-base/.docs/instructions.md b/exercises/practice/all-your-base/.docs/instructions.md index d5a2cde6..1b688b69 100644 --- a/exercises/practice/all-your-base/.docs/instructions.md +++ b/exercises/practice/all-your-base/.docs/instructions.md @@ -1,33 +1,28 @@ # Instructions -Convert a number, represented as a sequence of digits in one base, to any other base. +Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number. -Implement general base conversion. -Given a number in base **a**, represented as a sequence of digits, convert it to base **b**. - -## Note - -- Try to implement the conversion yourself. - Do not use something else to perform the conversion for you. +~~~~exercism/note +Try to implement the conversion yourself. +Do not use something else to perform the conversion for you. +~~~~ ## About [Positional Notation][positional-notation] In positional notation, a number in base **b** can be understood as a linear combination of powers of **b**. -The number 42, *in base 10*, means: - -`(4 * 10^1) + (2 * 10^0)` +The number 42, _in base 10_, means: -The number 101010, *in base 2*, means: +`(4 × 10¹) + (2 × 10⁰)` -`(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)` +The number 101010, _in base 2_, means: -The number 1120, *in base 3*, means: +`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)` -`(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)` +The number 1120, _in base 3_, means: -I think you got the idea! +`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)` -*Yes. Those three numbers above are exactly the same. Congratulations!* +_Yes. Those three numbers above are exactly the same. Congratulations!_ [positional-notation]: https://en.wikipedia.org/wiki/Positional_notation diff --git a/exercises/practice/all-your-base/.docs/introduction.md b/exercises/practice/all-your-base/.docs/introduction.md new file mode 100644 index 00000000..68aaffbe --- /dev/null +++ b/exercises/practice/all-your-base/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You've just been hired as professor of mathematics. +Your first week went well, but something is off in your second week. +The problem is that every answer given by your students is wrong! +Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)! +Amazingly, it turns out that each week, the students use a different base. +To help you quickly verify the student answers, you'll be building a tool to translate between bases. diff --git a/exercises/practice/allergies/.docs/instructions.md b/exercises/practice/allergies/.docs/instructions.md index a1394920..daf8cfde 100644 --- a/exercises/practice/allergies/.docs/instructions.md +++ b/exercises/practice/allergies/.docs/instructions.md @@ -22,6 +22,6 @@ 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.). +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/allergies-test.el b/exercises/practice/allergies/allergies-test.el index 834728cb..219be04c 100644 --- a/exercises/practice/allergies/allergies-test.el +++ b/exercises/practice/allergies/allergies-test.el @@ -4,55 +4,227 @@ ;;; Code: + (load-file "allergies.el") (declare-function allergen-list "allergies.el" (score)) (declare-function allergic-to-p "allergies.el" (score allergen)) -(ert-deftest no-allergies-at-all () - (should (equal '() (allergen-list 0)))) -(ert-deftest allergic-to-just-eggs () - (should (equal '("eggs") (allergen-list 1)))) +(ert-deftest eggs-not-allergic-to-anything () + (should-not (allergic-to-p 0 "eggs"))) + + +(ert-deftest eggs-allergic-only-to-eggs () + (should (allergic-to-p 1 "eggs"))) + + +(ert-deftest eggs-allergic-to-eggs-and-something-else () + (should (allergic-to-p 3 "eggs"))) + + +(ert-deftest eggs-allergic-to-something-but-not-eggs () + (should-not (allergic-to-p 2 "eggs"))) + + +(ert-deftest eggs-allergic-to-everything () + (should (allergic-to-p 255 "eggs"))) + + +(ert-deftest peanuts-not-allergic-to-anything () + (should-not (allergic-to-p 0 "peanuts"))) + + +(ert-deftest peanuts-allergic-only-to-peanuts () + (should (allergic-to-p 2 "peanuts"))) + + +(ert-deftest peanuts-allergic-to-peanuts-and-something-else () + (should (allergic-to-p 7 "peanuts"))) + + +(ert-deftest peanuts-allergic-to-something-but-not-peanuts () + (should-not (allergic-to-p 5 "peanuts"))) + + +(ert-deftest peanuts-allergic-to-everything () + (should (allergic-to-p 255 "peanuts"))) + + +(ert-deftest shellfish-not-allergic-to-anything () + (should-not (allergic-to-p 0 "shellfish"))) + -(ert-deftest allergic-to-just-peanuts () - (should (equal '("peanuts") (allergen-list 2)))) +(ert-deftest shellfish-allergic-only-to-shellfish () + (should (allergic-to-p 4 "shellfish"))) -(ert-deftest allergic-to-just-strawberries () - (should (equal '("strawberries") (allergen-list 8)))) -(ert-deftest allergic-to-eggs-and-peanuts () - (should (equal '("eggs" "peanuts") (allergen-list 3)))) +(ert-deftest shellfish-allergic-to-shellfish-and-something-else () + (should (allergic-to-p 14 "shellfish"))) -(ert-deftest allergic-to-more-than-eggs-but-not-peanuts () - (should (equal '("eggs" "shellfish") (allergen-list 5)))) -(ert-deftest allergic-to-lots-of-stuff () - (should (equal '("strawberries" "tomatoes" "chocolate" "pollen" "cats") - (allergen-list 248)))) +(ert-deftest shellfish-allergic-to-something-but-not-shellfish () + (should-not (allergic-to-p 10 "shellfish"))) -(ert-deftest allergic-to-everything () - (should (equal '("eggs" "peanuts" "shellfish" "strawberries" "tomatoes" - "chocolate" "pollen" "cats") - (allergen-list 255)))) -(ert-deftest no-allergies-means-not-allergic () - (should-not (allergic-to-p 0 "peanuts")) - (should-not (allergic-to-p 0 "cats")) +(ert-deftest shellfish-allergic-to-everything () + (should (allergic-to-p 255 "shellfish"))) + + +(ert-deftest strawberries-not-allergic-to-anything () (should-not (allergic-to-p 0 "strawberries"))) -(ert-deftest is-allergic-to-eggs () - (should (allergic-to-p 1 "eggs"))) -(ert-deftest allergic-to-eggs-and-other-stuff () - (should (allergic-to-p 5 "eggs"))) +(ert-deftest strawberries-allergic-only-to-strawberries () + (should (allergic-to-p 8 "strawberries"))) + + +(ert-deftest strawberries-allergic-to-strawberries-and-something-else () + (should (allergic-to-p 28 "strawberries"))) + + +(ert-deftest strawberries-allergic-to-something-but-not-strawberries () + (should-not (allergic-to-p 20 "strawberries"))) + + +(ert-deftest strawberries-allergic-to-everything () + (should (allergic-to-p 255 "strawberries"))) + + +(ert-deftest tomatoes-not-allergic-to-anything () + (should-not (allergic-to-p 0 "tomatoes"))) + + +(ert-deftest tomatoes-allergic-only-to-tomatoes () + (should (allergic-to-p 16 "tomatoes"))) + + +(ert-deftest tomatoes-allergic-to-tomatoes-and-something-else () + (should (allergic-to-p 56 "tomatoes"))) + + +(ert-deftest tomatoes-allergic-to-something-but-not-tomatoes () + (should-not (allergic-to-p 40 "tomatoes"))) + + +(ert-deftest tomatoes-allergic-to-everything () + (should (allergic-to-p 255 "tomatoes"))) + + +(ert-deftest chocolate-not-allergic-to-anything () + (should-not (allergic-to-p 0 "chocolate"))) + + +(ert-deftest chocolate-allergic-only-to-chocolate () + (should (allergic-to-p 32 "chocolate"))) + + +(ert-deftest chocolate-allergic-to-chocolate-and-something-else () + (should (allergic-to-p 112 "chocolate"))) + + +(ert-deftest chocolate-allergic-to-something-but-not-chocolate () + (should-not (allergic-to-p 80 "chocolate"))) + + +(ert-deftest chocolate-allergic-to-everything () + (should (allergic-to-p 255 "chocolate"))) + + +(ert-deftest pollen-not-allergic-to-anything () + (should-not (allergic-to-p 0 "pollen"))) + + +(ert-deftest pollen-allergic-only-to-pollen () + (should (allergic-to-p 64 "pollen"))) + + +(ert-deftest pollen-allergic-to-pollen-and-something-else () + (should (allergic-to-p 224 "pollen"))) + + +(ert-deftest pollen-allergic-to-something-but-not-pollen () + (should-not (allergic-to-p 160 "pollen"))) + + +(ert-deftest pollen-allergic-to-everything () + (should (allergic-to-p 255 "pollen"))) + + +(ert-deftest cats-not-allergic-to-anything () + (should-not (allergic-to-p 0 "cats"))) + + +(ert-deftest cats-allergic-only-to-cats () + (should (allergic-to-p 128 "cats"))) + + +(ert-deftest cats-allergic-to-cats-and-something-else () + (should (allergic-to-p 192 "cats"))) + + +(ert-deftest cats-allergic-to-something-but-not-cats () + (should-not (allergic-to-p 64 "cats"))) + + +(ert-deftest cats-allergic-to-everything () + (should (allergic-to-p 255 "cats"))) + + +(ert-deftest list-when-no-allergies-at-all () + (should (equal (allergen-list 0) '()))) + + +(ert-deftest list-when-allergic-to-just-eggs () + (should (equal (allergen-list 1) '("eggs")))) + + +(ert-deftest list-when-allergic-to-just-peanuts () + (should (equal (allergen-list 2) '("peanuts")))) + + +(ert-deftest list-when-allergic-to-just-strawberries () + (should (equal (allergen-list 8) '("strawberries")))) + + +(ert-deftest list-when-allergic-to-eggs-and-peanuts () + (should (equal (allergen-list 3) '("eggs" "peanuts")))) + + +(ert-deftest list-when-allergic-to-more-than-eggs-but-not-peanuts () + (should (equal (allergen-list 5) '("eggs" "shellfish")))) + + +(ert-deftest list-when-allergic-to-lots-of-stuff () + (should (equal (allergen-list 248) + '("strawberries" "tomatoes" "chocolate" "pollen" "cats")))) + + +(ert-deftest list-when-allergic-to-everything () + (should (equal (allergen-list 255) + '("eggs" + "peanuts" + "shellfish" + "strawberries" + "tomatoes" + "chocolate" + "pollen" + "cats")))) + (ert-deftest ignore-non-allergen-score-parts () - (should (equal '("eggs" "shellfish" "strawberries" "tomatoes" - "chocolate" "pollen" "cats") - (allergen-list 509)))) + (should (equal (allergen-list 509) + '("eggs" + "shellfish" + "strawberries" + "tomatoes" + "chocolate" + "pollen" + "cats")))) + + +(ert-deftest list-when-no-allergen-score-parts-without-highest-valid-score () + (should (equal (allergen-list 257) '("eggs")))) -(ert-deftest no-allergen-score-parts-without-highest-valid-score () - (should (equal '("eggs") (allergen-list 257)))) -(provide 'allergies) +(provide 'allergies-tests) ;;; allergies-test.el ends here diff --git a/exercises/practice/allergies/allergies.el b/exercises/practice/allergies/allergies.el index 7d42a6e8..4de88fa9 100644 --- a/exercises/practice/allergies/allergies.el +++ b/exercises/practice/allergies/allergies.el @@ -2,15 +2,16 @@ ;;; Commentary: -(defun allergen-list (score) -"List all allergens with a given SCORE." ;;; Code: -) + + +(defun allergen-list (score) + (error "Delete this S-Expression and write your own implementation")) + (defun allergic-to-p (score allergen) -"Check if Allergic to allergen based on SCORE and ALLERGEN." -;;; Code: -) + (error "Delete this S-Expression and write your own implementation")) + (provide 'allergies) ;;; allergies.el ends here 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 index 7d1c8283..dca24f52 100644 --- a/exercises/practice/anagram/.docs/instructions.md +++ b/exercises/practice/anagram/.docs/instructions.md @@ -1,13 +1,12 @@ # Instructions -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"`. +Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target. -Given a target word and a set of candidate words, this exercise requests the anagram set: the subset of 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 and candidates are words 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 anagram set is the subset of the candidate set that are anagrams of the target (in any order). -Words in the anagram set should have the same letter case as in the candidate set. +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 candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`. +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/tests.toml b/exercises/practice/anagram/.meta/tests.toml index c271593a..b40a89e2 100644 --- a/exercises/practice/anagram/.meta/tests.toml +++ b/exercises/practice/anagram/.meta/tests.toml @@ -45,6 +45,11 @@ 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" @@ -72,3 +77,9 @@ 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" + +[fd3509e5-e3ba-409d-ac3d-a9ac84d13296] +description = "different characters may have the same bytes" diff --git a/exercises/practice/anagram/anagram-test.el b/exercises/practice/anagram/anagram-test.el index 923616b2..27fd230d 100644 --- a/exercises/practice/anagram/anagram-test.el +++ b/exercises/practice/anagram/anagram-test.el @@ -4,69 +4,99 @@ ;;; Code: + (load-file "anagram.el") (declare-function anagrams-for "anagram.el" (subject candidates)) + (ert-deftest no-matches () - (should (equal '() (anagrams-for - "diaper" - '("hello" "world" "zombies" "pants"))))) + (should (equal (anagrams-for "diaper" '("hello" "world" "zombies" "pants")) + '()))) + + +(ert-deftest detects-two-anagrams () + (should (equal (anagrams-for "solemn" '("lemons" "cherry" "melons")) + '("lemons" "melons")))) -(ert-deftest detect-simple-anagram () - (should (equal '("tan") (anagrams-for - "ant" - '("tan" "stand" "at"))))) -(ert-deftest does-not-confuse-different-duplicates () - (should (equal '() (anagrams-for - "galea" - '("eagle"))))) +(ert-deftest does-not-detect-anagram-subsets () + (should (equal (anagrams-for "good" '("dog" "goody")) + '()))) -(ert-deftest eliminate-anagram-subsets () - (should (equal '() (anagrams-for - "good" - '("dog" "goody"))))) (ert-deftest detect-anagram () - (should (equal '("inlets") (anagrams-for - "listen" - '("enlists" "google" "inlets" "banana"))))) - -(ert-deftest multiple-anagrams () - (should (equal '("gallery" "regally" "largely") - (anagrams-for - "allergy" - '("gallery" "ballerina" "regally" "clergy" "largely" "leading"))))) - -(ert-deftest case-insensitive-anagrams () - (should (equal '("Carthorse") - (anagrams-for - "Orchestra" - '("cashregister" "Carthorse" "radishes"))))) + (should (equal (anagrams-for "listen" '("enlists" "google" "inlets" "banana")) + '("inlets")))) + + +(ert-deftest detects-three-anagrams () + (should (equal (anagrams-for "allergy" + '("gallery" "ballerina" "regally" "clergy" "largely" "leading")) + '("gallery" "regally" "largely")))) + + +(ert-deftest detects-multiple-anagrams-with-different-case () + (should (equal (anagrams-for "nose" '("Eons" "ONES")) + '("Eons" "ONES")))) + + +(ert-deftest does-not-detect-non-anagram-with-identical-checksum () + (should (equal (anagrams-for "mass" '("last")) + '()))) + + +(ert-deftest detects-anagrams-case-insensitively () + (should (equal (anagrams-for "Orchestra" '("cashregister" "Carthorse" "radishes")) + '("Carthorse")))) + + +(ert-deftest detects-anagrams-using-case-insensitive-subject () + (should (equal (anagrams-for "Orchestra" '("cashregister" "carthorse" "radishes")) + '("carthorse")))) + + +(ert-deftest detects-anagrams-using-case-insensitive-possible-matches () + (should (equal (anagrams-for "orchestra" '("cashregister" "Carthorse" "radishes")) + '("Carthorse")))) + + +(ert-deftest does-not-detect-anagram-if-original-word-is-repeated () + (should (equal (anagrams-for "go" '("goGoGO")) + '()))) + + +(ert-deftest anagrams-must-use-all-letters-exactly-once () + (should (equal (anagrams-for "tapper" '("patter")) + '()))) + (ert-deftest word-is-not-own-anagram () - (should (equal '() - (anagrams-for - "BANANA" - '("BANANA"))))) + (should (equal (anagrams-for "BANANA" '("BANANA")) + '()))) + (ert-deftest word-is-not-own-anagram-if-letter-case-is-partially-different () - (should (equal '() - (anagrams-for - "BANANA" - '("Banana"))))) + (should (equal (anagrams-for "BANANA" '("Banana")) + '()))) + (ert-deftest word-is-not-own-anagram-if-letter-case-is-completely-different () - (should (equal '() - (anagrams-for - "BANANA" - '("banana"))))) + (should (equal (anagrams-for "BANANA" '("banana")) + '()))) + (ert-deftest words-other-than-themselves-can-be-anagrams () - (should (equal '("Silent") - (anagrams-for - "LISTEN" - '("Listen" "Silent" "LISTEN"))))) + (should (equal (anagrams-for "LISTEN" '("LISTEN" "Silent")) + '("Silent")))) + + +(ert-deftest test-handles-case-of-greek-letters () + (should (equal (anagrams-for "ΑΒΓ" '("ΒΓΑ" "ΒΓΔ" "γβα" "αβγ")) + '("ΒΓΑ" "γβα")))) + +(ert-deftest different-characters-may-have-the-same-bytes () + (should (equal (anagrams-for "a⬂" '("€a")) + '()))) (provide 'anagram-test) ;;; anagram-test.el ends here diff --git a/exercises/practice/anagram/anagram.el b/exercises/practice/anagram/anagram.el index dfd94bf5..b13f44f4 100644 --- a/exercises/practice/anagram/anagram.el +++ b/exercises/practice/anagram/anagram.el @@ -2,9 +2,12 @@ ;;; Commentary: -(defun anagrams-for (subject candidates) ;;; Code: -) + + +(defun anagrams-for (subject candidates) + (error "Delete this S-Expression and write your own implementation")) + (provide 'anagram) ;;; anagram.el ends here diff --git a/exercises/practice/armstrong-numbers/.docs/instructions.md b/exercises/practice/armstrong-numbers/.docs/instructions.md index 744cfbe7..5e56bbe4 100644 --- a/exercises/practice/armstrong-numbers/.docs/instructions.md +++ b/exercises/practice/armstrong-numbers/.docs/instructions.md @@ -5,9 +5,9 @@ An [Armstrong number][armstrong-number] is a number that is the sum of its own d 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` +- 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` +- 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. diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers-test.el b/exercises/practice/armstrong-numbers/armstrong-numbers-test.el index 3e5e7910..e96849dd 100644 --- a/exercises/practice/armstrong-numbers/armstrong-numbers-test.el +++ b/exercises/practice/armstrong-numbers/armstrong-numbers-test.el @@ -7,44 +7,58 @@ (load-file "armstrong-numbers.el") (declare-function armstrong-p "armstrong-numbers.el" (n)) +(ert-deftest armstrong-number-0 () + "Zero is an Armstrong number" + (should (armstrong-p 0))) + (ert-deftest armstrong-number-5 () - "Single digit numbers are Armstrong numbers" + "Single-digit numbers are Armstrong numbers" (should (armstrong-p 5))) + (ert-deftest not-armstrong-number-10 () - "There are no 2 digit Armstrong numbers" - (should (not (armstrong-p 10)))) + "There are no two-digit Armstrong numbers" + (should-not (armstrong-p 10))) + (ert-deftest armstrong-number-153 () - "Three digit number that should an Armstrong number" + "Three-digit number that is an Armstrong number" (should (armstrong-p 153))) + (ert-deftest not-armstrong-number-100 () - "Three digit number that should an Armstrong number" - (should (not (armstrong-p 100)))) + "Three-digit number that is not an Armstrong number" + (should-not (armstrong-p 100))) + (ert-deftest armstrong-number-9474 () - "Four digit number that should an Armstrong number" + "Four-digit number that is an Armstrong number" (should (armstrong-p 9474))) + (ert-deftest not-armstrong-number-9475 () - "Four digit number that should not an Armstrong number" - (should (not (armstrong-p 9476)))) + "Four-digit number that is not an Armstrong number" + (should-not (armstrong-p 9476))) + (ert-deftest armstrong-number-9926315 () - "Seven digit number that should an Armstrong number" + "Seven-digit number that is an Armstrong number" (should (armstrong-p 9926315))) + (ert-deftest not-armstrong-number-9926314 () - "Seven digit number that should not an Armstrong number" - (should (not (armstrong-p 9926314)))) + "Seven-digit number that is not an Armstrong number" + (should-not (armstrong-p 9926314))) + (ert-deftest armstrong-number-186709961001538790100634132976990 () "Armstrong number containing seven zeroes that should be an Armstrong number" (should (armstrong-p 186709961001538790100634132976990))) + (ert-deftest armstrong-number-115132219018763992565095597973971522401 () "The largest and last Armstrong number should be an Armstrong number" (should (armstrong-p 115132219018763992565095597973971522401))) +(provide 'armstrong-numbers-test) ;;; armstrong-numbers-test.el ends here diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers.el b/exercises/practice/armstrong-numbers/armstrong-numbers.el index 69bbb2cf..f9372602 100644 --- a/exercises/practice/armstrong-numbers/armstrong-numbers.el +++ b/exercises/practice/armstrong-numbers/armstrong-numbers.el @@ -2,9 +2,12 @@ ;;; Commentary: -(defun armstrong-p (n) ;;; Code: -) + + +(defun armstrong-p (n) + (error "Delete this S-Expression and write your own implementation")) + (provide 'armstrong-numbers) ;;; armstrong-numbers.el ends here diff --git a/exercises/practice/atbash-cipher/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md index 21ca2ce0..1e7627b1 100644 --- a/exercises/practice/atbash-cipher/.docs/instructions.md +++ b/exercises/practice/atbash-cipher/.docs/instructions.md @@ -1,6 +1,6 @@ # Instructions -Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East. +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. diff --git a/exercises/practice/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json index 1c24ea9a..1ea9a8f6 100644 --- a/exercises/practice/atbash-cipher/.meta/config.json +++ b/exercises/practice/atbash-cipher/.meta/config.json @@ -3,7 +3,8 @@ "canweriotnow" ], "contributors": [ - "vermiculus" + "vermiculus", + "kmarker1101" ], "files": { "solution": [ @@ -16,7 +17,7 @@ ".meta/example.el" ] }, - "blurb": "Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.", + "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.el b/exercises/practice/atbash-cipher/.meta/example.el index a133fcf4..3fb8eaba 100644 --- a/exercises/practice/atbash-cipher/.meta/example.el +++ b/exercises/practice/atbash-cipher/.meta/example.el @@ -12,6 +12,10 @@ "Encode PLAINTEXT to atbash-cipher encoding." (to-string (group (to-cipher-seq plaintext)))) +(defun decode (ciphertext) + "Decode CIPHERTEXT from atbash-cipher encoding." + (to-string (cleanup-ciphered-seq (cl-map 'list #'encipher ciphertext)))) + (defun to-string (seq) "Convert SEQ of characters to a string." (cl-concatenate 'string seq)) diff --git a/exercises/practice/atbash-cipher/atbash-cipher-test.el b/exercises/practice/atbash-cipher/atbash-cipher-test.el index 01f449ab..d923ba50 100644 --- a/exercises/practice/atbash-cipher/atbash-cipher-test.el +++ b/exercises/practice/atbash-cipher/atbash-cipher-test.el @@ -7,37 +7,57 @@ (require 'cl-lib) (load-file "atbash-cipher.el") + (declare-function encode "atbash-cipher.el" (plaintext)) +(declare-function decode "atbash-cipher.el" (plaintext)) (ert-deftest encode-no () - (should (equal "ml" (encode "no")))) + (should (string= "ml" (encode "no")))) (ert-deftest encode-yes () - (should (equal "bvh" (encode "yes")))) + (should (string= "bvh" (encode "yes")))) (ert-deftest encode-OMG () - (should (equal "lnt" (encode "OMG")))) + (should (string= "lnt" (encode "OMG")))) (ert-deftest encode-O-M-G () - (should (equal "lnt" (encode "O M G")))) + (should (string= "lnt" (encode "O M G")))) (ert-deftest encode-long-word () - (should (equal "nrmwy oldrm tob" + (should (string= "nrmwy oldrm tob" (encode "mindblowingly")))) (ert-deftest encode-numbers () - (should (equal "gvhgr mt123 gvhgr mt" + (should (string= "gvhgr mt123 gvhgr mt" (encode "Testing, 1 2 3, testing.")))) (ert-deftest encode-sentence () - (should (equal "gifgs rhurx grlm" + (should (string= "gifgs rhurx grlm" (encode "Truth is fiction.")))) (ert-deftest encode-all-the-things () (let ((plaintext "The quick brown fox jumps over the lazy dog.") (ciphertext "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt")) - (should (equal ciphertext + (should (string= ciphertext (encode plaintext))))) +(ert-deftest decode-exercism () + (should (string= "exercism" (decode "vcvix rhn")))) + +(ert-deftest decode-a-sentence () + (should (string= "anobstacleisoftenasteppingstone" (decode "zmlyh gzxov rhlug vmzhg vkkrm thglm v")))) + +(ert-deftest decode-numbers () + (should (string= "testing123testing" (decode "gvhgr mt123 gvhgr mt")))) + +(ert-deftest decode-all-the-letters () + (should (string= "thequickbrownfoxjumpsoverthelazydog" (decode "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt")))) + +(ert-deftest decode-with-two-many-spaces () + (should (string= "exercism" (decode "vc vix r hn")))) + +(ert-deftest decode-with-no-spaces () + (should (string= "anobstacleisoftenasteppingstone" (decode "zmlyhgzxovrhlugvmzhgvkkrmthglmv")))) + (provide 'atbash-cipher-test) ;;; atbash-cipher-test.el ends here diff --git a/exercises/practice/atbash-cipher/atbash-cipher.el b/exercises/practice/atbash-cipher/atbash-cipher.el index 3017d19d..e2148961 100644 --- a/exercises/practice/atbash-cipher/atbash-cipher.el +++ b/exercises/practice/atbash-cipher/atbash-cipher.el @@ -7,5 +7,11 @@ ;;; Code: ) +(defun decode (plaintext) + "Decode atbash-cipher encoding to PLAINTEXT." + ;;; Code: +) + + (provide 'atbash-cipher) ;;; atbash-cipher.el ends here 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..fef55850 --- /dev/null +++ b/exercises/practice/bank-account/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "bank-account.el" + ], + "test": [ + "bank-account-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/bank-account/.meta/example.el new file mode 100644 index 00000000..981aa6a9 --- /dev/null +++ b/exercises/practice/bank-account/.meta/example.el @@ -0,0 +1,94 @@ +;;; bank-account.el --- Bank Account (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'eieio) + +(define-error 'account-closed "account not open") +(define-error 'account-open "account already open") +(define-error 'account-overdraw "amount must be less than balance") +(define-error 'account-negative-transaction "amount must be greater than 0") + +(defclass bank-account () + ((open + :initarg :open + :initform nil + :type boolean + :documentation "Is the account open?") + (funds + :initarg :funds + :initform 0 + :type number + :documentation "Amount of funds in the account") + (lock + :initarg :lock + :initform (bank-account--make-semaphore) + :documentation "Semaphore lock for the account")) + "Class representing a bank account.") + +(defun bank-account--make-semaphore () + (list 0 (make-mutex))) + +(defun bank-account--semaphore-post (semaphore) + (let ((count (car semaphore)) + (mutex (cadr semaphore))) + (with-mutex mutex + (setcar semaphore (1+ count))))) + +(defun bank-account--semaphore-wait (semaphore) + (let ((count (car semaphore)) + (mutex (cadr semaphore))) + (with-mutex mutex + (while (<= count 0) + (setq count (car semaphore))) + (setcar semaphore (1- count))))) + +(cl-defmethod check-open ((account bank-account)) + (unless (oref account open) + (signal 'account-closed nil))) + +(cl-defmethod check-positive ((account bank-account) amount) + (unless (> amount 0) + (signal 'account-negative-transaction nil))) + +(cl-defmethod open-account ((account bank-account)) + (if (oref account open) + (signal 'account-open nil) + (oset account open t) + (oset account funds 0) + (bank-account--semaphore-post (oref account lock)))) + +(cl-defmethod close-account ((account bank-account)) + (check-open account) + (bank-account--semaphore-wait (oref account lock)) + (oset account open nil) + (bank-account--semaphore-post (oref account lock))) + +(cl-defmethod deposit ((account bank-account) amount) + (check-open account) + (check-positive account amount) + (bank-account--semaphore-wait (oref account lock)) + (oset account funds (+ (oref account funds) amount)) + (bank-account--semaphore-post (oref account lock))) + +(cl-defmethod withdraw ((account bank-account) amount) + (check-open account) + (check-positive account amount) + (if (> amount (oref account funds)) + (signal 'account-overdraw nil) + (bank-account--semaphore-wait (oref account lock)) + (oset account funds (- (oref account funds) amount)) + (bank-account--semaphore-post (oref account lock)))) + +(cl-defmethod balance ((account bank-account)) + (check-open account) + (oref account funds)) + +(defun make-new-bank-account () + (bank-account :open nil :funds 0 :lock (bank-account--make-semaphore))) + + +(provide 'bank-account) +;;; bank-account.el ends here diff --git a/exercises/practice/bank-account/.meta/tests.toml b/exercises/practice/bank-account/.meta/tests.toml new file mode 100644 index 00000000..4e42d4dc --- /dev/null +++ b/exercises/practice/bank-account/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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" diff --git a/exercises/practice/bank-account/bank-account-test.el b/exercises/practice/bank-account/bank-account-test.el new file mode 100644 index 00000000..efc588d7 --- /dev/null +++ b/exercises/practice/bank-account/bank-account-test.el @@ -0,0 +1,142 @@ +;;; bank-account-test.el --- Bank Account (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "bank-account.el") + +(require 'eieio) + +(ert-deftest newly-opened-account-has-zero-balance () + (let ((account (make-new-bank-account))) + (open-account account) + (should (= (balance account) 0)))) + + +(ert-deftest single-deposit () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (should (= (balance account) 100)))) + + +(ert-deftest multiple-deposits () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (deposit account 50) + (should (= (balance account) 150)))) + + +(ert-deftest withdraw-once () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (withdraw account 75) + (should (= (balance account) 25)))) + + +(ert-deftest withdraw-twice () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (withdraw account 80) + (withdraw account 20) + (should (= (balance account) 0)))) + + +(ert-deftest can-do-multiple-operations-sequentially () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (deposit account 110) + (withdraw account 200) + (deposit account 60) + (withdraw account 50) + (should (= (balance account) 20)))) + + +(ert-deftest cannot-check-balance-of-closed-account () + (let ((account (make-new-bank-account))) + (open-account account) + (close-account account) + (should-error (balance account) :type 'account-closed))) + + +(ert-deftest cannot-deposit-into-closed-account () + (let ((account (make-new-bank-account))) + (open-account account) + (close-account account) + (should-error (deposit account 50) :type 'account-closed))) + + +(ert-deftest cannot-deposit-into-unopened-account () + (let ((account (make-new-bank-account))) + (should-error (deposit account 50) :type 'account-closed))) + + +(ert-deftest cannot-withdraw-from-closed-account () + (let ((account (make-new-bank-account))) + (open-account account) + (close-account account) + (should-error (withdraw account 50) :type 'account-closed))) + + +(ert-deftest cannot-close-an-account-that-was-not-opened () + (let ((account (make-new-bank-account))) + (should-error (close-account account) :type 'account-closed))) + + +(ert-deftest cannot-open-an-already-opened-account () + (let ((account (make-new-bank-account))) + (open-account account) + (should-error (open-account account) :type 'account-open))) + + +(ert-deftest reopened-account-does-not-retain-balance () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 50) + (close-account account) + (open-account account) + (should (= (balance account) 0)))) + + +(ert-deftest cannot-withdraw-more-than-deposited () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 25) + (should-error (withdraw account 50) :type 'account-overdraw))) + + +(ert-deftest cannot-withdraw-negative () + (let ((account (make-new-bank-account))) + (open-account account) + (deposit account 100) + (should-error (withdraw account -50) :type 'account-negative-transaction))) + + +(ert-deftest cannot-deposit-negative () + (let ((account (make-new-bank-account))) + (open-account account) + (should-error (deposit account -50) :type 'account-negative-transaction))) + + +(ert-deftest can-handle-concurrent-transactions () + (let ((account (make-new-bank-account))) + (open-account account) + (defun concurrent-operation () + (deposit account 1) + (withdraw account 1)) + (let ((threads ())) + (dotimes (_ 1000) + (push (make-thread #'concurrent-operation) threads)) + (dolist (thread threads) + (thread-join thread))) + (should (= (balance account ) 0)))) + + +(provide 'bank-account-test) +;;; bank-account-test.el ends here diff --git a/exercises/practice/bank-account/bank-account.el b/exercises/practice/bank-account/bank-account.el new file mode 100644 index 00000000..d62e3211 --- /dev/null +++ b/exercises/practice/bank-account/bank-account.el @@ -0,0 +1,22 @@ +;;; bank-account.el --- Bank Account (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(define-error 'account-closed + (error "Delete this S-Expression and write your own implementation")) +(define-error 'account-open + (error "Delete this S-Expression and write your own implementation")) +(define-error 'account-overdraw + (error "Delete this S-Expression and write your own implementation")) +(define-error 'account-negative-transaction + (error "Delete this S-Expression and write your own implementation")) + +(defclass bank-account (operations) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'bank-account) +;;; bank-account.el ends here 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..af9941be --- /dev/null +++ b/exercises/practice/binary-search/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "binary-search.el" + ], + "test": [ + "binary-search-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/binary-search/.meta/example.el new file mode 100644 index 00000000..e63f5753 --- /dev/null +++ b/exercises/practice/binary-search/.meta/example.el @@ -0,0 +1,26 @@ +;;; binary-search.el --- Binary Search (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun find-binary (array value) + (let ((left 0) + (right (1- (length array))) + middle + index-of-value) + (while (and (not index-of-value) (<= left right)) + (setq middle (+ left (/ (- right left) 2))) + (cond + ((equal (aref array middle) value) + (setq index-of-value middle)) + ((< (aref array middle) value) + (setq left (1+ middle))) + ((> (aref array middle) value) + (setq right (1- middle))))) + index-of-value)) + + +(provide 'binary-search) +;;; binary-search.el ends here 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.el b/exercises/practice/binary-search/binary-search-test.el new file mode 100644 index 00000000..84981701 --- /dev/null +++ b/exercises/practice/binary-search/binary-search-test.el @@ -0,0 +1,59 @@ +;;; binary-search-test.el --- Binary Search (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "binary-search.el") +(declare-function find-binary "binary-search.el" (array value)) + + +(ert-deftest finds-a-value-in-an-array-with-one-element () + (should (= (find-binary [6] 6) 0))) + + +(ert-deftest finds-a-value-in-the-middle-of-an-array () + (should (= (find-binary [1 3 4 6 8 9 11] 6) 3))) + + +(ert-deftest finds-a-value-at-the-beginning-of-an-array () + (should (= (find-binary [1 3 4 6 8 9 11] 1) 0))) + + +(ert-deftest finds-a-value-at-the-end-of-an-array () + (should (= (find-binary [1 3 4 6 8 9 11] 11) 6))) + + +(ert-deftest finds-a-value-in-an-array-of-odd-length () + (should + (= (find-binary [1 3 5 8 13 21 34 55 89 144 233 377 634] 144) 9))) + + +(ert-deftest finds-a-value-in-an-array-of-even-length () + (should + (= (find-binary [1 3 5 8 13 21 34 55 89 144 233 377] 21) 5))) + + +(ert-deftest identifies-that-a-value-is-not-included-in-the-array () + (should (equal (find-binary [1 3 4 6 8 9 11] 7) nil))) + + +(ert-deftest a-value-smaller-than-the-array () + (should (equal (find-binary [1 3 4 6 8 9 11] 0) nil))) + + +(ert-deftest a-value-larger-than-the-array () + (should (equal (find-binary [1 3 4 6 8 9 11] 13) nil))) + + +(ert-deftest nothing-is-found-in-an-empty-array () + (should (equal (find-binary [] 1) nil))) + + +(ert-deftest nothing-is-found-when-the-left-and-right-bounds-cross () + (should (equal (find-binary [1 2] 0) nil))) + + +(provide 'binary-search-test) +;;; binary-search-test.el ends here diff --git a/exercises/practice/binary-search/binary-search.el b/exercises/practice/binary-search/binary-search.el new file mode 100644 index 00000000..081a0976 --- /dev/null +++ b/exercises/practice/binary-search/binary-search.el @@ -0,0 +1,14 @@ +;;; binary-search.el --- Binary Search (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun find-binary (array value) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'binary-search) +;;; binary-search.el ends here diff --git a/exercises/practice/bob/.docs/instructions.md b/exercises/practice/bob/.docs/instructions.md index 7888c9b7..bb702f7b 100644 --- a/exercises/practice/bob/.docs/instructions.md +++ b/exercises/practice/bob/.docs/instructions.md @@ -1,16 +1,19 @@ # Instructions -Bob is a lackadaisical teenager. -In conversation, his responses are very limited. +Your task is to determine what Bob will reply to someone when they say something to him or ask him a question. -Bob answers 'Sure.' if you ask him a question, such as "How are you?". +Bob only ever answers one of five things: -He answers 'Whoa, chill out!' if you YELL AT HIM (in all capitals). - -He answers 'Calm down, I know what I'm doing!' if you yell a question at him. - -He says 'Fine. Be that way!' if you address him without actually saying anything. - -He answers 'Whatever.' to anything else. - -Bob's conversational partner is a purist when it comes to written communication and always follows normal rules regarding sentence punctuation in English. +- **"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/tests.toml b/exercises/practice/bob/.meta/tests.toml index ea47d6bb..5299e289 100644 --- a/exercises/practice/bob/.meta/tests.toml +++ b/exercises/practice/bob/.meta/tests.toml @@ -71,6 +71,7 @@ description = "alternate silence" [66953780-165b-4e7e-8ce3-4bcb80b6385a] description = "multiple line question" +include = false [5371ef75-d9ea-4103-bcfa-2da973ddec1b] description = "starting with whitespace" @@ -83,3 +84,7 @@ 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.el b/exercises/practice/bob/bob-test.el index 2c8a2544..29bf758d 100644 --- a/exercises/practice/bob/bob-test.el +++ b/exercises/practice/bob/bob-test.el @@ -33,19 +33,19 @@ (ert-deftest responds-to-talking-forcefully () (should - (string= "Whatever." (response-for "Let's go make out behind the gym!")))) + (string= "Whatever." (response-for "Hi there!")))) (ert-deftest responds-to-using-acronyms-in-regular-speech () (should - (string= "Whatever." (response-for "It's OK if you don't want to go to the DMV.")))) + (string= "Whatever." (response-for "It's OK if you don't want to go work for NASA.")))) (ert-deftest responds-to-forceful-question () (should - (string= "Calm down, I know what I'm doing!" (response-for "WHAT THE HELL WERE YOU THINKING?")))) + (string= "Calm down, I know what I'm doing!" (response-for "WHAT'S GOING ON?")))) (ert-deftest responds-to-shouting-numbers () (should - (string= "Whoa, chill out!" (response-for "1, 2, 3, GO!")))) + (string= "Whoa, chill out!" (response-for "1, 2, 3 GO!")))) (ert-deftest responds-to-only-numbers () (should @@ -61,7 +61,7 @@ (ert-deftest responds-to-shouting-with-no-exclamation-mark () (should - (string= "Whoa, chill out!" (response-for "I HATE YOU")))) + (string= "Whoa, chill out!" (response-for "I HATE THE DENTIST")))) (ert-deftest responds-to-statement-containing-question-mark () (should @@ -89,7 +89,7 @@ (ert-deftest responds-to-multiple-line-question () (should - (string= "Whatever." (response-for "\nDoes this cryogenic chamber make me look fat?\nno")))) + (string= "Sure." (response-for "\nDoes this cryogenic chamber make\n me look fat?")))) (ert-deftest responds-to-starting-with-whitespace () (should diff --git a/exercises/practice/bob/bob.el b/exercises/practice/bob/bob.el index 92089eb4..d2e36b7d 100644 --- a/exercises/practice/bob/bob.el +++ b/exercises/practice/bob/bob.el @@ -1,10 +1,13 @@ -;;; bob.el --- Bob exercise (exercism) -*- lexical-binding: t; -*- +;;; bob.el --- Bob (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -(defun response-for (phrase) ;;; Code: -) + + +(defun response-for (phrase) + (error "Delete this S-Expression and write your own implementation")) + (provide 'bob) ;;; bob.el ends here 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..b840c6a0 --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "circular-buffer.el" + ], + "test": [ + "circular-buffer-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/circular-buffer/.meta/example.el new file mode 100644 index 00000000..96c1321f --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/example.el @@ -0,0 +1,74 @@ +;;; circular-buffer.el --- Circular Buffer (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(define-error 'empty-buffer-error "buffer is empty") + +(define-error 'full-buffer-error "buffer is full") + +(defclass circular-buffer () + ((capacity :initarg :capacity :initform 10) + (buf-modulus :initform 20) + (data :initform nil) + (front :initform 0) + (back :initform 0)) + :documentation "Circular buffer class") + + +(cl-defmethod initialize-instance :after ((buf circular-buffer) &rest _) + (with-slots (capacity buf-modulus data) buf + (setf buf-modulus (* 2 capacity) + data (make-vector capacity nil)))) + +(cl-defmethod buf-empty-p ((buf circular-buffer)) + (with-slots (front back) buf + (= front back))) + +(cl-defmethod buf-full-p ((buf circular-buffer)) + (with-slots (front back buf-modulus capacity) buf + (= (mod (- back front) buf-modulus) capacity))) + +(cl-defmethod next-spot ((buf circular-buffer) s) + (with-slots (buf-modulus) buf + (mod (1+ s) buf-modulus))) + +(cl-defmethod advance-front ((buf circular-buffer)) + (with-slots (front) buf + (setf front (next-spot buf front)))) + +(cl-defmethod advance-back ((buf circular-buffer)) + (with-slots (back) buf + (setf back (next-spot buf back)))) + +(cl-defmethod clear ((buf circular-buffer)) + (with-slots (front back) buf + (setf front back))) + +(cl-defmethod read-buff ((buf circular-buffer)) + (with-slots (front data capacity) buf + (if (buf-empty-p buf) + (signal 'empty-buffer-error nil) + (let ((v (aref data (mod front capacity)))) + (advance-front buf) + v)))) + +(cl-defmethod write ((buf circular-buffer) v) + (with-slots (back data capacity) buf + (if (buf-full-p buf) + (signal 'full-buffer-error nil) + (progn + (aset data (mod back capacity) v) + (advance-back buf))))) + +(cl-defmethod overwrite ((buf circular-buffer) v) + (with-slots (front) buf + (when (buf-full-p buf) + (advance-front buf)) + (write buf v))) + +(provide 'circular-buffer) +;;; circular-buffer.el ends here + 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.el b/exercises/practice/circular-buffer/circular-buffer-test.el new file mode 100644 index 00000000..96b7225a --- /dev/null +++ b/exercises/practice/circular-buffer/circular-buffer-test.el @@ -0,0 +1,132 @@ +;;; circular-buffer-test.el --- Circular Buffer (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'eieio) + +(load-file "circular-buffer.el") + +(defun create-test-buffer (capacity) + "Helper function to create a circular buffer with the given capacity." + (circular-buffer :capacity capacity)) + +(ert-deftest reading-empty-buffer-should-fail () + (let ((buf (create-test-buffer 1))) + (should-error (read-buff buf) :type 'empty-buffer-error))) + + +(ert-deftest can-read-an-item-just-written () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (should (equal (read-buff buf) 1)))) + + +(ert-deftest each-item-may-only-be-read-once () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (should (equal (read-buff buf) 1)) + (should-error (read-buff buf) :type 'empty-buffer-error))) + + +(ert-deftest items-are-read-in-the-order-they-are-written () + (let ((buf (create-test-buffer 2))) + (write buf 1) + (write buf 2) + (should (equal (read-buff buf) 1)) + (should (equal (read-buff buf) 2)))) + + +(ert-deftest full-buffer-cant-be-written-to () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (should-error (write buf 2) :type 'full-buffer-error))) + + +(ert-deftest a-read-frees-up-capacity-for-another-write () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (should (equal (read-buff buf) 1)) + (write buf 2) + (should (equal (read-buff buf) 2)))) + + +(ert-deftest read-position-is-maintained-even-across-multiple-writes () + (let ((buf (create-test-buffer 3))) + (write buf 1) + (write buf 2) + (should (equal (read-buff buf) 1)) + (write buf 3) + (should (equal (read-buff buf) 2)) + (should (equal (read-buff buf) 3)))) + + +(ert-deftest items-cleared-out-of-buffer-cant-be-read () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (clear buf) + (should-error (read-buff buf) :type 'empty-buffer-error))) + + +(ert-deftest clear-frees-up-capacity-for-another-write () + (let ((buf (create-test-buffer 1))) + (write buf 1) + (clear buf) + (write buf 2) + (should (equal (read-buff buf) 2)))) + + +(ert-deftest clear-does-nothing-on-empty-buffer () + (let ((buf (create-test-buffer 1))) + (clear buf) + (write buf 1) + (should (equal (read-buff buf) 1)))) + + +(ert-deftest overwrite-acts-like-write-on-non-full-buffer () + (let ((buf (create-test-buffer 2))) + (write buf 1) + (overwrite buf 2) + (should (equal (read-buff buf) 1)) + (should (equal (read-buff buf) 2)))) + + +(ert-deftest overwrite-replaces-the-oldest-item-on-full-buffer () + (let ((buf (create-test-buffer 2))) + (write buf 1) + (write buf 2) + (overwrite buf 3) + (should (equal (read-buff buf) 2)) + (should (equal (read-buff buf) 3)))) + + +(ert-deftest overwrite-replaces-the-oldest-item-remaining-in-buffer-following-a-read () + (let ((buf (create-test-buffer 3))) + (write buf 1) + (write buf 2) + (write buf 3) + (should (equal (read-buff buf) 1)) + (write buf 4) + (overwrite buf 5) + (should (equal (read-buff buf) 3)) + (should (equal (read-buff buf) 4)) + (should (equal (read-buff buf) 5)))) + + +(ert-deftest initial-clear-does-not-affect-wrapping-around () + (let ((buf (create-test-buffer 2))) + (clear buf) + (write buf 1) + (write buf 2) + (overwrite buf 3) + (overwrite buf 4) + (should (equal (read-buff buf) 3)) + (should (equal (read-buff buf) 4)) + (should-error (read-buff buf) :type 'empty-buffer-error))) + + +(provide 'circular-buffer-test) +;;; circular-buffer-test.el ends here + diff --git a/exercises/practice/circular-buffer/circular-buffer.el b/exercises/practice/circular-buffer/circular-buffer.el new file mode 100644 index 00000000..4e680241 --- /dev/null +++ b/exercises/practice/circular-buffer/circular-buffer.el @@ -0,0 +1,19 @@ +;;; circular-buffer.el --- Circular Buffer (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(define-error 'empty-buffer-error + (error "Delete this S-Expression and write your own implementation")) + +(define-error 'full-buffer-error + (error "Delete this S-Expression and write your own implementation")) + +(defclass circular-buffer () + (error "Delete this S-Expression and write your own implementation")) + +(provide 'circular-buffer) +;;; circular-buffer.el ends here + 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..e117ae5b --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "collatz-conjecture.el" + ], + "test": [ + "collatz-conjecture-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/collatz-conjecture/.meta/example.el new file mode 100644 index 00000000..391f47c0 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/example.el @@ -0,0 +1,21 @@ +;;; collatz-conjecture.el --- Collatz Conjecture (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun steps (number) + "Count the steps to reach 1 using the Collatz conjecture." + (unless (< 0 number) (error "Only positive integers are allowed")) + (cl-labels + ((recur (n count) + (cond + ((= 0 (mod n 2)) (funcall #'recur (/ n 2) (+ 1 count))) + ((< 1 n) (funcall #'recur (+ 1 (* 3 n)) (+ 1 count))) + (t count)))) + (funcall #'recur number 0))) + +(provide 'collatz-conjecture) +;;; collatz-conjecture.el ends here 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.el b/exercises/practice/collatz-conjecture/collatz-conjecture-test.el new file mode 100644 index 00000000..3c8197da --- /dev/null +++ b/exercises/practice/collatz-conjecture/collatz-conjecture-test.el @@ -0,0 +1,35 @@ +;;; collatz-conjecture-test.el --- Tests for Collatz Conjecture (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "collatz-conjecture.el") +(declare-function steps "collatz-conjecture.el" (number)) + + +(ert-deftest zero-steps-for-one () + (should (equal 0 (steps 1)))) + + +(ert-deftest divide-if-even () + (should (equal 4 (steps 16)))) + + +(ert-deftest even-and-odd-steps () + (should (equal 9 (steps 12)))) + + +(ert-deftest large-number-of-even-and-odd-steps () + (should (equal 152 (steps 1000000)))) + + +(ert-deftest zero-is-an-error () + (should-error (steps 0))) + + +(ert-deftest negative-value-is-an-error () + (should-error (steps -15))) + +(provide 'collatz-conjecture-test) +;;; collatz-conjecture-test.el ends here diff --git a/exercises/practice/collatz-conjecture/collatz-conjecture.el b/exercises/practice/collatz-conjecture/collatz-conjecture.el new file mode 100644 index 00000000..6d2b56df --- /dev/null +++ b/exercises/practice/collatz-conjecture/collatz-conjecture.el @@ -0,0 +1,12 @@ +;;; collatz-conjecture.el --- Collatz Conjecture (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defun steps (number) + "Count the steps to reach 1 using the Collatz conjecture." + (error "Delete this S-Expression and write your own implementation")) + +(provide 'collatz-conjecture) +;;; collatz-conjecture.el ends here diff --git a/exercises/practice/crypto-square/.meta/tests.toml b/exercises/practice/crypto-square/.meta/tests.toml index 085d142e..94ef0819 100644 --- a/exercises/practice/crypto-square/.meta/tests.toml +++ b/exercises/practice/crypto-square/.meta/tests.toml @@ -32,3 +32,8 @@ description = "8 character plaintext results in 3 chunks, the last one with a tr [fbcb0c6d-4c39-4a31-83f6-c473baa6af80] description = "54 character plaintext results in 7 chunks, the last two with trailing spaces" +include = false + +[33fd914e-fa44-445b-8f38-ff8fbc9fe6e6] +description = "54 character plaintext results in 8 chunks, the last two with trailing spaces" +reimplements = "fbcb0c6d-4c39-4a31-83f6-c473baa6af80" diff --git a/exercises/practice/crypto-square/crypto-square-test.el b/exercises/practice/crypto-square/crypto-square-test.el index f2c9ddcb..1160dbef 100644 --- a/exercises/practice/crypto-square/crypto-square-test.el +++ b/exercises/practice/crypto-square/crypto-square-test.el @@ -25,7 +25,7 @@ (ert-deftest 8-character-plaintext-results-in-3-chunks-the-last-one-with-a-trailing-space () (should (equal "clu hlt io " (encipher "Chill out.")))) -(ert-deftest 54-character-plaintext-results-in-7-chunks-the-last-two-with-trailing-spaces () +(ert-deftest 54-character-plaintext-results-in-8-chunks-the-last-two-with-trailing-spaces () (should (equal "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau " (encipher "If man was meant to stay on the ground, god would have given us roots.")))) diff --git a/exercises/practice/darts/.docs/instructions.md b/exercises/practice/darts/.docs/instructions.md new file mode 100644 index 00000000..6518201c --- /dev/null +++ b/exercises/practice/darts/.docs/instructions.md @@ -0,0 +1,31 @@ +# Instructions + +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. +- If the dart lands in the inner circle of the target, player earns 10 points. + +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). + +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 new file mode 100644 index 00000000..20df9b93 --- /dev/null +++ b/exercises/practice/darts/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "darts.el" + ], + "test": [ + "darts-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/darts/.meta/example.el new file mode 100644 index 00000000..b9264a29 --- /dev/null +++ b/exercises/practice/darts/.meta/example.el @@ -0,0 +1,22 @@ +;;; darts.el --- Darts (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun score (x y) + (let ((distance-to-center (sqrt (+ (expt x 2) (expt y 2))))) + (cond + ((<= distance-to-center 1) + 10) + ((<= distance-to-center 5) + 5) + ((<= distance-to-center 10) + 1) + (t + 0)))) + + +(provide 'darts) +;;; darts.el ends here diff --git a/exercises/practice/darts/.meta/tests.toml b/exercises/practice/darts/.meta/tests.toml new file mode 100644 index 00000000..fbe2976d --- /dev/null +++ b/exercises/practice/darts/.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. + +[9033f731-0a3a-4d9c-b1c0-34a1c8362afb] +description = "Missed target" + +[4c9f6ff4-c489-45fd-be8a-1fcb08b4d0ba] +description = "On the outer circle" + +[14378687-ee58-4c9b-a323-b089d5274be8] +description = "On the middle circle" + +[849e2e63-85bd-4fed-bc3b-781ae962e2c9] +description = "On the inner circle" + +[1c5ffd9f-ea66-462f-9f06-a1303de5a226] +description = "Exactly on center" + +[b65abce3-a679-4550-8115-4b74bda06088] +description = "Near the center" + +[66c29c1d-44f5-40cf-9927-e09a1305b399] +description = "Just within the inner circle" + +[d1012f63-c97c-4394-b944-7beb3d0b141a] +description = "Just outside the inner circle" + +[ab2b5666-b0b4-49c3-9b27-205e790ed945] +description = "Just within the middle circle" + +[70f1424e-d690-4860-8caf-9740a52c0161] +description = "Just outside the middle circle" + +[a7dbf8db-419c-4712-8a7f-67602b69b293] +description = "Just within the outer circle" + +[e0f39315-9f9a-4546-96e4-a9475b885aa7] +description = "Just outside the outer circle" + +[045d7d18-d863-4229-818e-b50828c75d19] +description = "Asymmetric position between the inner and middle circles" diff --git a/exercises/practice/darts/darts-test.el b/exercises/practice/darts/darts-test.el new file mode 100644 index 00000000..0b5d5a39 --- /dev/null +++ b/exercises/practice/darts/darts-test.el @@ -0,0 +1,105 @@ +;;; darts-test.el --- Darts (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "darts.el") +(declare-function score "darts.el" (x y)) + + +(ert-deftest missed-target () + ;; Function under test: score + ;; Input: {"x":-9,"y":9} + ;; Expected: 0 + (should (equal 0 (score -9 9)))) + + +(ert-deftest on-the-outer-circle () + ;; Function under test: score + ;; Input: {"x":0,"y":10} + ;; Expected: 1 + (should (equal 1 (score 0 10)))) + + +(ert-deftest on-the-middle-circle () + ;; Function under test: score + ;; Input: {"x":-5,"y":0} + ;; Expected: 5 + (should (equal 5 (score -5 0)))) + + +(ert-deftest on-the-inner-circle () + ;; Function under test: score + ;; Input: {"x":0,"y":-1} + ;; Expected: 10 + (should (equal 10 (score 0 -1)))) + + +(ert-deftest exactly-on-center () + ;; Function under test: score + ;; Input: {"x":0,"y":0} + ;; Expected: 10 + (should (equal 10 (score 0 0)))) + + +(ert-deftest near-the-center () + ;; Function under test: score + ;; Input: {"x":-0.1,"y":-0.1} + ;; Expected: 10 + (should (equal 10 (score -0.1 -0.1)))) + + +(ert-deftest just-within-the-inner-circle () + ;; Function under test: score + ;; Input: {"x":0.7,"y":0.7} + ;; Expected: 10 + (should (equal 10 (score 0.7 0.7)))) + + +(ert-deftest just-outside-the-inner-circle () + ;; Function under test: score + ;; Input: {"x":0.8,"y":-0.8} + ;; Expected: 5 + (should (equal 5 (score 0.8 0.8)))) + + +(ert-deftest just-within-the-middle-circle () + ;; Function under test: score + ;; Input: {"x":-3.5,"y":3.5} + ;; Expected: 5 + (should (equal 5 (score -3.5 3.5)))) + + +(ert-deftest just-outside-the-middle-circle () + ;; Function under test: score + ;; Input: {"x":-3.6,"y":-3.6} + ;; Expected: 1 + (should (equal 1 (score -3.6 -3.6)))) + + +(ert-deftest just-within-the-outer-circle () + ;; Function under test: score + ;; Input: {"x":-7.0,"y":7.0} + ;; Expected: 1 + (should (equal 1 (score -7.0 7.0)))) + + +(ert-deftest just-outside-the-outer-circle () + ;; Function under test: score + ;; Input: {"x":7.1,"y":-7.1} + ;; Expected: 0 + (should (equal 0 (score 7.1 -7.1)))) + + +(ert-deftest asymmetric-position-between-the-inner-and-middle-circles + () + ;; Function under test: score + ;; Input: {"x":0.5,"y":-4} + ;; Expected: 5 + (should (equal 5 (score 0.5 -4)))) + + +(provide 'darts-test) +;;; darts-test.el ends here diff --git a/exercises/practice/darts/darts.el b/exercises/practice/darts/darts.el new file mode 100644 index 00000000..4e079d1c --- /dev/null +++ b/exercises/practice/darts/darts.el @@ -0,0 +1,14 @@ +;;; darts.el --- Darts (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun score (x y) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'darts) +;;; darts.el ends here diff --git a/exercises/practice/diamond/.docs/instructions.md b/exercises/practice/diamond/.docs/instructions.md new file mode 100644 index 00000000..3034802f --- /dev/null +++ b/exercises/practice/diamond/.docs/instructions.md @@ -0,0 +1,52 @@ +# Instructions + +The diamond kata takes as its input a letter, and outputs it in a diamond shape. +Given a letter, it prints a diamond starting with 'A', with the supplied letter at the widest point. + +## Requirements + +- The first row contains one 'A'. +- The last row contains one 'A'. +- All rows, except the first and last, have exactly two identical letters. +- All rows have as many trailing spaces as leading spaces. (This might be 0). +- The diamond is horizontally symmetric. +- The diamond is vertically symmetric. +- The diamond has a square shape (width equals height). +- The letters form a diamond shape. +- The top half has the letters in ascending order. +- The bottom half has the letters in descending order. +- The four corners (containing the spaces) are triangles. + +## Examples + +In the following examples, spaces are indicated by `·` characters. + +Diamond for letter 'A': + +```text +A +``` + +Diamond for letter 'C': + +```text +··A·· +·B·B· +C···C +·B·B· +··A·· +``` + +Diamond for letter 'E': + +```text +····A···· +···B·B··· +··C···C·· +·D·····D· +E·······E +·D·····D· +··C···C·· +···B·B··· +····A···· +``` diff --git a/exercises/practice/diamond/.meta/config.json b/exercises/practice/diamond/.meta/config.json new file mode 100644 index 00000000..a60c3eb4 --- /dev/null +++ b/exercises/practice/diamond/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "diamond.el" + ], + "test": [ + "diamond-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a letter, print a diamond starting with 'A' with the supplied letter at the widest point.", + "source": "Seb Rose", + "source_url": "https://web.archive.org/web/20220807163751/http://claysnow.co.uk/recycling-tests-in-tdd/" +} diff --git a/exercises/practice/diamond/.meta/example.el b/exercises/practice/diamond/.meta/example.el new file mode 100644 index 00000000..2088c9e0 --- /dev/null +++ b/exercises/practice/diamond/.meta/example.el @@ -0,0 +1,27 @@ +;;; diamond.el --- Diamond (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun rows (letter) + (let* ((n (- letter ?A)) + (length (1+ (* 2 n))) + (result (make-vector length nil))) + (cl-labels + ((row (index) + (let ((str (make-string length 32))) + (aset str (- n index) (+ ?A index)) + (aset str (+ n index) (+ ?A index)) + str))) + (cl-loop for index to n + do (aset result index (funcall #'row index))) + (cl-loop for index below n + do (aset result (- length 1 index) (funcall #'row index))) + result))) + +(provide 'diamond) +;;; diamond.el ends here + diff --git a/exercises/practice/diamond/.meta/tests.toml b/exercises/practice/diamond/.meta/tests.toml new file mode 100644 index 00000000..4e7802ec --- /dev/null +++ b/exercises/practice/diamond/.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. + +[202fb4cc-6a38-4883-9193-a29d5cb92076] +description = "Degenerate case with a single 'A' row" + +[bd6a6d78-9302-42e9-8f60-ac1461e9abae] +description = "Degenerate case with no row containing 3 distinct groups of spaces" + +[af8efb49-14ed-447f-8944-4cc59ce3fd76] +description = "Smallest non-degenerate case with odd diamond side length" + +[e0c19a95-9888-4d05-86a0-fa81b9e70d1d] +description = "Smallest non-degenerate case with even diamond side length" + +[82ea9aa9-4c0e-442a-b07e-40204e925944] +description = "Largest possible diamond" diff --git a/exercises/practice/diamond/diamond-test.el b/exercises/practice/diamond/diamond-test.el new file mode 100644 index 00000000..3dc7adf2 --- /dev/null +++ b/exercises/practice/diamond/diamond-test.el @@ -0,0 +1,95 @@ +;;; diamond-test.el --- Tests for Diamond (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "diamond.el") +(declare-function rows "diamond.el" (letter)) + + +(ert-deftest degenerate-case-with-a-single-a-row () + (should (equal ["A"] (rows ?A)))) + + +(ert-deftest degenerate-case-with-no-row-containing-3-distinct-groups-of-spaces () + (should (equal [" A " + "B B" + " A "] (rows ?B)))) + + +(ert-deftest smallest-non-degenerate-case-with-odd-diamond-side-length () + (should (equal [" A " + " B B " + "C C" + " B B " + " A "] (rows ?C)))) + + +(ert-deftest smallest-non-degenerate-case-with-even-diamond-side-length () + (should (equal [" A " + " B B " + " C C " + "D D" + " C C " + " B B " + " A "] (rows ?D)))) + + +(ert-deftest largest-possible-diamond () + (should (equal [" A " + " B B " + " C C " + " D D " + " E E " + " F F " + " G G " + " H H " + " I I " + " J J " + " K K " + " L L " + " M M " + " N N " + " O O " + " P P " + " Q Q " + " R R " + " S S " + " T T " + " U U " + " V V " + " W W " + " X X " + " Y Y " + "Z Z" + " Y Y " + " X X " + " W W " + " V V " + " U U " + " T T " + " S S " + " R R " + " Q Q " + " P P " + " O O " + " N N " + " M M " + " L L " + " K K " + " J J " + " I I " + " H H " + " G G " + " F F " + " E E " + " D D " + " C C " + " B B " + " A "] (rows ?Z)))) + + +(provide 'diamond-test) +;;; diamond-test.el ends here diff --git a/exercises/practice/diamond/diamond.el b/exercises/practice/diamond/diamond.el new file mode 100644 index 00000000..d04eee02 --- /dev/null +++ b/exercises/practice/diamond/diamond.el @@ -0,0 +1,14 @@ +;;; diamond.el --- Diamond (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun rows (letter) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'diamond) +;;; diamond.el ends here + 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..1df1479e --- /dev/null +++ b/exercises/practice/dnd-character/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "dnd-character.el" + ], + "test": [ + "dnd-character-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/dnd-character/.meta/example.el new file mode 100644 index 00000000..5aa185ff --- /dev/null +++ b/exercises/practice/dnd-character/.meta/example.el @@ -0,0 +1,32 @@ +;;; dnd-character.el --- D&D Character (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun modifier (score) + (floor (- score 10) 2)) + + +(defun ability () + (let ((rolls '())) + (dotimes (i 4) + (push (+ 1 (random 6)) rolls)) + (apply '+ (cdr (sort rolls '<))))) + +(defun generate-dnd-character () + (let ((constitution (ability))) + (record 'dnd-character + (ability) ; strength + (ability) ; dexterity + constitution ; constitution + (ability) ; intelligence + (ability) ; wisdom + (ability) ; charisma + (+ 10 (modifier constitution))))) ; hitpoints + + +(provide 'dnd-character) +;;; dnd-character.el ends here + diff --git a/exercises/practice/dnd-character/.meta/tests.toml b/exercises/practice/dnd-character/.meta/tests.toml new file mode 100644 index 00000000..e945b5dc --- /dev/null +++ b/exercises/practice/dnd-character/.meta/tests.toml @@ -0,0 +1,74 @@ +# 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.el b/exercises/practice/dnd-character/dnd-character-test.el new file mode 100644 index 00000000..3cea20e4 --- /dev/null +++ b/exercises/practice/dnd-character/dnd-character-test.el @@ -0,0 +1,105 @@ +;;; dnd-character-test.el --- D&D Character (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "dnd-character.el") +(declare-function modifier "dnd-character.el" (score)) +(declare-function ability "dnd-character.el" ()) +(declare-function generate-dnd-character "dnd-character.el" ()) + + +(ert-deftest ability-modifier-for-score-3-is-negative-4 () + (should (= -4 (modifier 3)))) + + +(ert-deftest ability-modifier-for-score-4-is-negative-3 () + (should (= -3 (modifier 4)))) + + +(ert-deftest ability-modifier-for-score-5-is-negative-3 () + (should (= -3 (modifier 5)))) + + +(ert-deftest ability-modifier-for-score-6-is-negative-2 () + (should (= -2 (modifier 6)))) + + +(ert-deftest ability-modifier-for-score-7-is-negative-2 () + (should (= -2 (modifier 7)))) + + +(ert-deftest ability-modifier-for-score-8-is-negative-1 () + (should (= -1 (modifier 8)))) + + +(ert-deftest ability-modifier-for-score-9-is-negative-1 () + (should (= -1 (modifier 9)))) + + +(ert-deftest ability-modifier-for-score-10-is-0 () + (should (= 0 (modifier 10)))) + + +(ert-deftest ability-modifier-for-score-11-is-0 () + (should (= 0 (modifier 11)))) + + +(ert-deftest ability-modifier-for-score-12-is-positive-1 () + (should (= 1 (modifier 12)))) + + +(ert-deftest ability-modifier-for-score-13-is-positive-1 () + (should (= 1 (modifier 13)))) + + +(ert-deftest ability-modifier-for-score-14-is-positive-2 () + (should (= 2 (modifier 14)))) + + +(ert-deftest ability-modifier-for-score-15-is-positive-2 () + (should (= 2 (modifier 15)))) + + +(ert-deftest ability-modifier-for-score-16-is-positive-3 () + (should (= 3 (modifier 16)))) + + +(ert-deftest ability-modifier-for-score-17-is-positive-3 () + (should (= 3 (modifier 17)))) + + +(ert-deftest ability-modifier-for-score-18-is-positive-4 () + (should (= 4 (modifier 18)))) + + +(defun valid-range-p (score) + (and (<= 3 score) (>= 18 score))) + +(ert-deftest random-ability-is-within-range () + (should (valid-range-p (ability)))) + + +(ert-deftest random-character-is-valid () + (let* ((dnd-char (generate-dnd-character)) + (strength (aref dnd-char 1)) + (dexterity (aref dnd-char 2)) + (constitution (aref dnd-char 3)) + (intelligence (aref dnd-char 4)) + (wisdom (aref dnd-char 5)) + (charisma (aref dnd-char 6)) + (hitpoints (aref dnd-char 7))) + (should (and (recordp dnd-char) + (valid-range-p strength) + (valid-range-p dexterity) + (valid-range-p constitution) + (valid-range-p intelligence) + (valid-range-p wisdom) + (valid-range-p charisma))))) + + +(provide 'dnd-character-test) +;;; dnd-character-test.el ends here + diff --git a/exercises/practice/dnd-character/dnd-character.el b/exercises/practice/dnd-character/dnd-character.el new file mode 100644 index 00000000..ee4ce8fa --- /dev/null +++ b/exercises/practice/dnd-character/dnd-character.el @@ -0,0 +1,22 @@ +;;; dnd-character.el --- D&D Character (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun modifier (score) + (error "Delete this S-Expression and write your own implementation")) + + +(defun ability () + (error "Delete this S-Expression and write your own implementation")) + + +(defun generate-dnd-character () + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'dnd-character) +;;; dnd-character.el ends here + 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..a718d4de --- /dev/null +++ b/exercises/practice/eliuds-eggs/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "eliuds-eggs.el" + ], + "test": [ + "eliuds-eggs-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/eliuds-eggs/.meta/example.el new file mode 100644 index 00000000..b2c34603 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.meta/example.el @@ -0,0 +1,18 @@ +;;; eliuds-eggs.el --- Eliud's Eggs (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun egg-count (number) + (let ((eggs 0)) + (while (> number 0) + (setq eggs (+ eggs (% number 2)) + number (truncate number 2))) + eggs)) + + +(provide 'eliuds-eggs) +;;; eliuds-eggs.el ends here + 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.el b/exercises/practice/eliuds-eggs/eliuds-eggs-test.el new file mode 100644 index 00000000..1d36041c --- /dev/null +++ b/exercises/practice/eliuds-eggs/eliuds-eggs-test.el @@ -0,0 +1,29 @@ +;;; eliuds-eggs-test.el --- Eliud's Eggs (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "eliuds-eggs.el") +(declare-function egg-count "eliuds-eggs.el" (number)) + + +(ert-deftest 0-eggs () + (should (= 0 (egg-count 0)))) + +(ert-deftest 1-egg () + (should (= 1 (egg-count 16)))) + + +(ert-deftest 4-eggs () + (should (= 4 (egg-count 89)))) + + +(ert-deftest 13-eggs () + (should (= 13 (egg-count 2000000000)))) + + +(provide 'eliuds-eggs-test) +;;; eliuds-eggs-test.el ends here + diff --git a/exercises/practice/eliuds-eggs/eliuds-eggs.el b/exercises/practice/eliuds-eggs/eliuds-eggs.el new file mode 100644 index 00000000..73e76022 --- /dev/null +++ b/exercises/practice/eliuds-eggs/eliuds-eggs.el @@ -0,0 +1,14 @@ +;;; eliuds-eggs.el --- Eliud's Eggs (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun egg-count (number) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'eliuds-eggs) +;;; eliuds-eggs.el ends here + diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md index fffe64f2..802863b5 100644 --- a/exercises/practice/etl/.docs/instructions.md +++ b/exercises/practice/etl/.docs/instructions.md @@ -1,19 +1,8 @@ # Instructions -We are going to do the `Transform` step of an Extract-Transform-Load. +Your task is to change the data format of letters and their point values in the game. -## ETL - -Extract-Transform-Load (ETL) is a fancy way of saying, "We have some crufty, legacy data over in this system, and now we need it in this shiny new system over here, so we're going to migrate this." - -(Typically, this is followed by, "We're only going to need to run this once." -That's then typically followed by much forehead slapping and moaning about how stupid we could possibly be.) - -## The goal - -We're going to extract some Scrabble scores from a legacy system. - -The old system stored a list of letters per score: +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", @@ -23,18 +12,16 @@ The old system stored a list of letters per score: - 8 points: "J", "X", - 10 points: "Q", "Z", -The shiny new Scrabble system instead stores the score per letter, which makes it much faster and easier to calculate the score for a word. -It also stores the letters in lower-case regardless of the case of the input letters: +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. - -Your mission, should you choose to accept it, is to transform the legacy data format to the shiny new format. +- etc. -## Notes +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. -A final note about scoring, Scrabble is played around the world in a variety of languages, each with its own unique scoring table. -For example, an "E" is scored at 2 in the Māori-language version of the game while being scored at 4 in the Hawaiian-language version. +~~~~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 index c925d017..99011a7f 100644 --- a/exercises/practice/etl/.meta/config.json +++ b/exercises/practice/etl/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "canweriotnow", "vermiculus" @@ -15,7 +17,7 @@ ".meta/example.el" ] }, - "blurb": "We are going to do the `Transform` step of an Extract-Transform-Load.", - "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "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://turing.edu/" } 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..7ea1fb6d --- /dev/null +++ b/exercises/practice/flatten-array/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions Append + +The input and output are represented as lists. 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..e66ad273 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "flatten-array.el" + ], + "test": [ + "flatten-array-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/flatten-array/.meta/example.el new file mode 100644 index 00000000..97456c0c --- /dev/null +++ b/exercises/practice/flatten-array/.meta/example.el @@ -0,0 +1,13 @@ +;;; flatten-array.el --- Flatten Array (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun list-flatten (list) + (flatten-tree list)) + + +(provide 'flatten-array) +;;; flatten-array.el ends here 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.el b/exercises/practice/flatten-array/flatten-array-test.el new file mode 100644 index 00000000..391ec247 --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array-test.el @@ -0,0 +1,94 @@ +;;; flatten-array-test.el --- Tests for Flatten Array (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "flatten-array.el") +(declare-function list-flatten "flatten-array.el" (array)) + + +(ert-deftest empty () + (should + (equal + (list-flatten '()) + '()))) + + +(ert-deftest no-nesting () + (should + (equal + (list-flatten '(0 1 2)) + '(0 1 2)))) + + +(ert-deftest flattens-a-nested-array () + (should + (equal + (list-flatten '((()))) + '()))) + + +(ert-deftest flattens-array-with-just-integers-present () + (should + (equal + (list-flatten '(1 (2 3 4 5 6 7) 8)) + '(1 2 3 4 5 6 7 8)))) + + +(ert-deftest 5-level-nesting () + (should + (equal + (list-flatten '(0 2 ((2 3) 8 100 4 (((50)))) -2)) + '(0 2 2 3 8 100 4 50 -2)))) + + +(ert-deftest 6-level-nesting () + (should + (equal + (list-flatten '(1 (2 ((3)) (4 ((5))) 6 7) 8)) + '(1 2 3 4 5 6 7 8)))) + + +(ert-deftest null-values-are-omitted-from-the-final-result () + (should + (equal + (list-flatten '(1 2 nil)) + '(1 2)))) + + +(ert-deftest + consecutive-null-values-at-the-front-of-the-array-are-omitted-from-the-final-result + () + (should + (equal + (list-flatten '(nil nil 3)) + '(3)))) + + +(ert-deftest + consecutive-null-values-in-the-middle-of-the-array-are-omitted-from-the-final-result + () + (should + (equal + (list-flatten '(1 nil nil 4)) + '(1 4)))) + + +(ert-deftest 6-level-nested-array-with-null-values () + (should + (equal + (list-flatten '(0 2 ((2 3) 8 ((100)) nil ((nil))) -2)) + '(0 2 2 3 8 100 -2)))) + + +(ert-deftest all-values-in-nested-array-are-null () + (should + (equal + (list-flatten '(nil (((nil))) nil nil ((nil nil) nil) nil)) + '()))) + + +(provide 'flatten-array-test) +;;; flatten-array-test.el ends here diff --git a/exercises/practice/flatten-array/flatten-array.el b/exercises/practice/flatten-array/flatten-array.el new file mode 100644 index 00000000..d0f19e10 --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array.el @@ -0,0 +1,14 @@ +;;; flatten-array.el --- Flatten Array (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun list-flatten (list) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'flatten-array) +;;; flatten-array.el ends here diff --git a/exercises/practice/food-chain/.docs/instructions.md b/exercises/practice/food-chain/.docs/instructions.md new file mode 100644 index 00000000..125820e3 --- /dev/null +++ b/exercises/practice/food-chain/.docs/instructions.md @@ -0,0 +1,64 @@ +# Instructions + +Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'. + +While you could copy/paste the lyrics, or read them from a file, this problem is much more interesting if you approach it algorithmically. + +This is a [cumulative song][cumulative-song] of unknown origin. + +This is one of many common variants. + +```text +I know an old lady who swallowed a fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a spider. +It wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a bird. +How absurd to swallow a bird! +She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a cat. +Imagine that, to swallow a cat! +She swallowed the cat to catch the bird. +She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a dog. +What a hog, to swallow a dog! +She swallowed the dog to catch the cat. +She swallowed the cat to catch the bird. +She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a goat. +Just opened her throat and swallowed a goat! +She swallowed the goat to catch the dog. +She swallowed the dog to catch the cat. +She swallowed the cat to catch the bird. +She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a cow. +I don't know how she swallowed a cow! +She swallowed the cow to catch the goat. +She swallowed the goat to catch the dog. +She swallowed the dog to catch the cat. +She swallowed the cat to catch the bird. +She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her. +She swallowed the spider to catch the fly. +I don't know why she swallowed the fly. Perhaps she'll die. + +I know an old lady who swallowed a horse. +She's dead, of course! +``` + +[cumulative-song]: https://en.wikipedia.org/wiki/Cumulative_song diff --git a/exercises/practice/food-chain/.meta/config.json b/exercises/practice/food-chain/.meta/config.json new file mode 100644 index 00000000..cbd54696 --- /dev/null +++ b/exercises/practice/food-chain/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "food-chain.el" + ], + "test": [ + "food-chain-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly" +} diff --git a/exercises/practice/food-chain/.meta/example.el b/exercises/practice/food-chain/.meta/example.el new file mode 100644 index 00000000..579526b2 --- /dev/null +++ b/exercises/practice/food-chain/.meta/example.el @@ -0,0 +1,46 @@ +;;; food-chain.el --- Food Chain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar +creatures+ ["fly" "spider" "bird" "cat" "dog" "goat" "cow" "horse"]) +(defvar +unique-lines+ ["I don't know why she swallowed the fly. Perhaps she'll die." + "It wriggled and jiggled and tickled inside her." + "How absurd to swallow a bird!" + "Imagine that, to swallow a cat!" + "What a hog, to swallow a dog!" + "Just opened her throat and swallowed a goat!" + "I don't know how she swallowed a cow!" + "She's dead, of course!"]) + +(defun verse-end-p (index) + (or (= 7 index) (zerop index))) + +(defun add-lines (index) + (if (verse-end-p index) + (list (aref +unique-lines+ index)) + (let* ((spider (if (= 2 index) " that wriggled and jiggled and tickled inside her" "")) + (creature (aref +creatures+ index)) + (next-creature (aref +creatures+ (1- index))) + (line (format "She swallowed the %s to catch the %s%s." creature next-creature spider))) + (cons line (add-lines (1- index)))))) + +(defun single-verse (index) + (let* ((start-line (format "I know an old lady who swallowed a %s." (aref +creatures+ index))) + (second-line (unless (verse-end-p index) (aref +unique-lines+ index))) + (verse-block (add-lines index))) + (if second-line + (cons start-line (cons second-line verse-block)) + (cons start-line verse-block)))) + + +(defun recite (start-verse end-verse) + (let (output) + (cl-loop for index from (1- start-verse) below end-verse + do (setq output (append output (single-verse index) '("")))) + (butlast output 1))) ;; Remove the last empty string added + +(provide 'food-chain) +;;; food-chain.el ends here + diff --git a/exercises/practice/food-chain/.meta/tests.toml b/exercises/practice/food-chain/.meta/tests.toml new file mode 100644 index 00000000..30c5b980 --- /dev/null +++ b/exercises/practice/food-chain/.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. + +[751dce68-9412-496e-b6e8-855998c56166] +description = "fly" + +[6c56f861-0c5e-4907-9a9d-b2efae389379] +description = "spider" + +[3edf5f33-bef1-4e39-ae67-ca5eb79203fa] +description = "bird" + +[e866a758-e1ff-400e-9f35-f27f28cc288f] +description = "cat" + +[3f02c30e-496b-4b2a-8491-bc7e2953cafb] +description = "dog" + +[4b3fd221-01ea-46e0-825b-5734634fbc59] +description = "goat" + +[1b707da9-7001-4fac-941f-22ad9c7a65d4] +description = "cow" + +[3cb10d46-ae4e-4d2c-9296-83c9ffc04cdc] +description = "horse" + +[22b863d5-17e4-4d1e-93e4-617329a5c050] +description = "multiple verses" + +[e626b32b-745c-4101-bcbd-3b13456893db] +description = "full song" diff --git a/exercises/practice/food-chain/food-chain-test.el b/exercises/practice/food-chain/food-chain-test.el new file mode 100644 index 00000000..ad394759 --- /dev/null +++ b/exercises/practice/food-chain/food-chain-test.el @@ -0,0 +1,158 @@ +;;; food-chain-test.el --- Food Chain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "food-chain.el") +(declare-function recite "food-chain.el" (start-verse end-verse)) + + +(ert-deftest fly () + (let ((expected '("I know an old lady who swallowed a fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 1 1) expected)))) + +(ert-deftest spider () + (let ((expected '("I know an old lady who swallowed a spider." + "It wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 2 2) expected)))) + + +(ert-deftest bird () + (let ((expected '("I know an old lady who swallowed a bird." + "How absurd to swallow a bird!" + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 3 3) expected)))) + + +(ert-deftest cat () + (let ((expected '("I know an old lady who swallowed a cat." + "Imagine that, to swallow a cat!" + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 4 4) expected)))) + + +(ert-deftest dog () + (let ((expected '("I know an old lady who swallowed a dog." + "What a hog, to swallow a dog!" + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 5 5) expected)))) + + +(ert-deftest goat () + (let ((expected '("I know an old lady who swallowed a goat." + "Just opened her throat and swallowed a goat!" + "She swallowed the goat to catch the dog." + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 6 6) expected)))) + + +(ert-deftest cow () + (let ((expected '("I know an old lady who swallowed a cow." + "I don't know how she swallowed a cow!" + "She swallowed the cow to catch the goat." + "She swallowed the goat to catch the dog." + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 7 7) expected)))) + + +(ert-deftest horse () + (let ((expected '("I know an old lady who swallowed a horse." + "She's dead, of course!"))) + (should (equal (recite 8 8) expected)))) + + +(ert-deftest multiple-verses () + (let ((expected '("I know an old lady who swallowed a fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a spider." + "It wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a bird." + "How absurd to swallow a bird!" + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die."))) + (should (equal (recite 1 3) expected)))) + + +(ert-deftest full-song () + (let ((expected '("I know an old lady who swallowed a fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a spider." + "It wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a bird." + "How absurd to swallow a bird!" + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a cat." + "Imagine that, to swallow a cat!" + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a dog." + "What a hog, to swallow a dog!" + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a goat." + "Just opened her throat and swallowed a goat!" + "She swallowed the goat to catch the dog." + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a cow." + "I don't know how she swallowed a cow!" + "She swallowed the cow to catch the goat." + "She swallowed the goat to catch the dog." + "She swallowed the dog to catch the cat." + "She swallowed the cat to catch the bird." + "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her." + "She swallowed the spider to catch the fly." + "I don't know why she swallowed the fly. Perhaps she'll die." + "" + "I know an old lady who swallowed a horse." + "She's dead, of course!"))) + (should (equal (recite 1 8) expected)))) + + +(provide 'food-chain-test) +;;; food-chain-test.el ends here diff --git a/exercises/practice/food-chain/food-chain.el b/exercises/practice/food-chain/food-chain.el new file mode 100644 index 00000000..db25de5b --- /dev/null +++ b/exercises/practice/food-chain/food-chain.el @@ -0,0 +1,14 @@ +;;; food-chain.el --- Food Chain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun recite (start-verse end-verse) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'food-chain) +;;; food-chain.el ends here + diff --git a/exercises/practice/game-of-life/.docs/instructions.md b/exercises/practice/game-of-life/.docs/instructions.md new file mode 100644 index 00000000..49531406 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/instructions.md @@ -0,0 +1,11 @@ +# Instructions + +After each generation, the cells interact with their eight neighbors, which are cells adjacent horizontally, vertically, or diagonally. + +The following rules are applied to each cell: + +- Any live cell with two or three live neighbors lives on. +- Any dead cell with exactly three live neighbors becomes a live cell. +- All other cells die or stay dead. + +Given a matrix of 1s and 0s (corresponding to live and dead cells), apply the rules to each cell, and return the next generation. diff --git a/exercises/practice/game-of-life/.docs/introduction.md b/exercises/practice/game-of-life/.docs/introduction.md new file mode 100644 index 00000000..2347b936 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/introduction.md @@ -0,0 +1,9 @@ +# Introduction + +[Conway's Game of Life][game-of-life] is a fascinating cellular automaton created by the British mathematician John Horton Conway in 1970. + +The game consists of a two-dimensional grid of cells that can either be "alive" or "dead." + +After each generation, the cells interact with their eight neighbors via a set of rules, which define the new generation. + +[game-of-life]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life diff --git a/exercises/practice/game-of-life/.meta/config.json b/exercises/practice/game-of-life/.meta/config.json new file mode 100644 index 00000000..4ecf5937 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "game-of-life.el" + ], + "test": [ + "game-of-life-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement Conway's Game of Life.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" +} diff --git a/exercises/practice/game-of-life/.meta/example.el b/exercises/practice/game-of-life/.meta/example.el new file mode 100644 index 00000000..c1b9f875 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/example.el @@ -0,0 +1,41 @@ +;;; game-of-life.el --- Conway's Game of Life (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun tick (matrix) + (when matrix + (let* ((lines (vconcat (mapcar #'vconcat matrix))) + (num-rows (length lines)) + (num-columns (length (aref lines 0))) + (results nil)) + (cl-loop for row from (1- num-rows) downto 0 + for result = nil + for r-start = (max 0 (1- row)) + for r-end = (min num-rows (+ 2 row)) + do (cl-loop for column from (1- num-columns) downto 0 + for c-start = (max 0 (1- column)) + for c-end = (min num-columns (+ 2 column)) + for previous = (aref (aref lines row) column) + for count = (- previous) + do (cl-loop for r from r-start below r-end + for line = (aref lines r) + do (cl-loop for c from c-start below c-end + do (when (= 1 (aref line c)) + (setq count (1+ count))))) + for cell = (cl-case count + (3 1) + (2 previous) + (t 0)) + do (setq result (cons cell result))) + + do (setq results (cons result results))) + results))) + + +(provide 'game-of-life) +;;; game-of-life.el ends here + diff --git a/exercises/practice/game-of-life/.meta/tests.toml b/exercises/practice/game-of-life/.meta/tests.toml new file mode 100644 index 00000000..398cd454 --- /dev/null +++ b/exercises/practice/game-of-life/.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. + +[ae86ea7d-bd07-4357-90b3-ac7d256bd5c5] +description = "empty matrix" + +[4ea5ccb7-7b73-4281-954a-bed1b0f139a5] +description = "live cells with zero live neighbors die" + +[df245adc-14ff-4f9c-b2ae-f465ef5321b2] +description = "live cells with only one live neighbor die" + +[2a713b56-283c-48c8-adae-1d21306c80ae] +description = "live cells with two live neighbors stay alive" + +[86d5c5a5-ab7b-41a1-8907-c9b3fc5e9dae] +description = "live cells with three live neighbors stay alive" + +[015f60ac-39d8-4c6c-8328-57f334fc9f89] +description = "dead cells with three live neighbors become alive" + +[2ee69c00-9d41-4b8b-89da-5832e735ccf1] +description = "live cells with four or more neighbors die" + +[a79b42be-ed6c-4e27-9206-43da08697ef6] +description = "bigger matrix" diff --git a/exercises/practice/game-of-life/game-of-life-test.el b/exercises/practice/game-of-life/game-of-life-test.el new file mode 100644 index 00000000..93efc211 --- /dev/null +++ b/exercises/practice/game-of-life/game-of-life-test.el @@ -0,0 +1,99 @@ +;;; game-of-life-test.el --- Tests for Conway's Game of Life (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "game-of-life.el") +(declare-function tick "game-of-life.el" (matrix)) + + +(ert-deftest empty-matrix () + (should (equal '() + + (tick '())))) + + +(ert-deftest live-cells-with-zero-live-neighbors-die () + (should (equal '((0 0 0) + (0 0 0) + (0 0 0)) + + (tick '((0 0 0) + (0 1 0) + (0 0 0)))))) + + +(ert-deftest live-cells-with-only-one-live-neighbor-die () + (should (equal '((0 0 0) + (0 0 0) + (0 0 0)) + + (tick '((0 0 0) + (0 1 0) + (0 1 0)))))) + + +(ert-deftest live-cells-with-two-live-neighbors-stay-alive () + (should (equal '((0 0 0) + (1 0 1) + (0 0 0)) + + (tick '((1 0 1) + (1 0 1) + (1 0 1)))))) + + +(ert-deftest live-cells-with-three-live-neighbors-stay-alive () + (should (equal '((0 0 0) + (1 0 0) + (1 1 0)) + + (tick '((0 1 0) + (1 0 0) + (1 1 0)))))) + + +(ert-deftest dead-cells-with-three-live-neighbors-become-alive () + (should (equal '((0 0 0) + (1 1 0) + (0 0 0)) + + (tick '((1 1 0) + (0 0 0) + (1 0 0)))))) + + +(ert-deftest live-cells-with-four-or-more-neighbors-die () + (should (equal '((1 0 1) + (0 0 0) + (1 0 1)) + + (tick '((1 1 1) + (1 1 1) + (1 1 1)))))) + + +(ert-deftest bigger-matrix () + (should (equal '((1 1 0 1 1 0 0 0) + (0 0 0 0 0 1 1 0) + (1 0 1 1 1 1 0 1) + (1 0 0 0 0 0 0 1) + (1 1 0 0 1 0 0 1) + (1 1 0 1 0 0 0 1) + (1 0 0 0 0 0 0 0) + (0 0 0 0 0 0 1 1)) + + (tick '((1 1 0 1 1 0 0 0) + (1 0 1 1 0 0 0 0) + (1 1 1 0 0 1 1 1) + (0 0 0 0 0 1 1 0) + (1 0 0 0 1 1 0 0) + (1 1 0 0 0 1 1 1) + (0 0 1 0 1 0 0 1) + (1 0 0 0 0 0 1 1)))))) + + +(provide 'game-of-life-test) +;;; game-of-life-test.el ends here diff --git a/exercises/practice/game-of-life/game-of-life.el b/exercises/practice/game-of-life/game-of-life.el new file mode 100644 index 00000000..57f7fb22 --- /dev/null +++ b/exercises/practice/game-of-life/game-of-life.el @@ -0,0 +1,14 @@ +;;; game-of-life.el --- Conway's Game of Life (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun tick (matrix) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'game-of-life) +;;; game-of-life.el ends here + diff --git a/exercises/practice/gigasecond/.docs/instructions.md b/exercises/practice/gigasecond/.docs/instructions.md index 41a057c4..1e20f002 100644 --- a/exercises/practice/gigasecond/.docs/instructions.md +++ b/exercises/practice/gigasecond/.docs/instructions.md @@ -1,5 +1,8 @@ # Instructions -Given a moment, determine the moment that would be after a gigasecond has passed. +Your task is to determine the date and time one gigasecond after a certain date. -A gigasecond is 10^9 (1,000,000,000) seconds. +A gigasecond is one thousand million seconds. +That is a one with nine zeros after it. + +If you were born on _January 24th, 2015 at 22:00 (10:00:00pm)_, then you would be a gigasecond old on _October 2nd, 2046 at 23:46:40 (11:46:40pm)_. diff --git a/exercises/practice/gigasecond/.docs/introduction.md b/exercises/practice/gigasecond/.docs/introduction.md new file mode 100644 index 00000000..18a3dc20 --- /dev/null +++ b/exercises/practice/gigasecond/.docs/introduction.md @@ -0,0 +1,24 @@ +# Introduction + +The way we measure time is kind of messy. +We have 60 seconds in a minute, and 60 minutes in an hour. +This comes from ancient Babylon, where they used 60 as the basis for their number system. +We have 24 hours in a day, 7 days in a week, and how many days in a month? +Well, for days in a month it depends not only on which month it is, but also on what type of calendar is used in the country you live in. + +What if, instead, we only use seconds to express time intervals? +Then we can use metric system prefixes for writing large numbers of seconds in more easily comprehensible quantities. + +- A food recipe might explain that you need to let the brownies cook in the oven for two kiloseconds (that's two thousand seconds). +- Perhaps you and your family would travel to somewhere exotic for two megaseconds (that's two million seconds). +- And if you and your spouse were married for _a thousand million_ seconds, you would celebrate your one gigasecond anniversary. + +~~~~exercism/note +If we ever colonize Mars or some other planet, measuring time is going to get even messier. +If someone says "year" do they mean a year on Earth or a year on Mars? + +The idea for this exercise came from the science fiction novel ["A Deepness in the Sky"][vinge-novel] by author Vernor Vinge. +In it the author uses the metric system as the basis for time measurements. + +[vinge-novel]: https://www.tor.com/2017/08/03/science-fiction-with-something-for-everyone-a-deepness-in-the-sky-by-vernor-vinge/ +~~~~ diff --git a/exercises/practice/gigasecond/.meta/example.el b/exercises/practice/gigasecond/.meta/example.el index 57072a5c..7e905732 100644 --- a/exercises/practice/gigasecond/.meta/example.el +++ b/exercises/practice/gigasecond/.meta/example.el @@ -1,4 +1,4 @@ -;;; gigasecond.el --- Gigasecond exercise (exercism) -*- lexical-binding: t; -*- +;;; gigasecond.el --- Gigasecond (exercism) -*- lexical-binding: t; -*- ;;; Commentary: ;; Calculate the date one gigasecond (10^9 seconds) from the @@ -9,7 +9,7 @@ ;;; Code: -(defun from (second minute hour day month year) +(defun add (second minute hour day month year) "Calculate gigasecond from date given. Params are SECOND, MINUTE, HOUR, DAY, MONTH, and YEAR." (let ((gigasecond (seconds-to-time (expt 10 9))) @@ -19,8 +19,5 @@ Params are SECOND, MINUTE, HOUR, DAY, MONTH, and YEAR." (butlast end-date (- (length end-date) 6))))) - - - (provide 'gigasecond) ;;; gigasecond.el ends here diff --git a/exercises/practice/gigasecond/gigasecond-test.el b/exercises/practice/gigasecond/gigasecond-test.el index 6ca401a5..5d8e398b 100644 --- a/exercises/practice/gigasecond/gigasecond-test.el +++ b/exercises/practice/gigasecond/gigasecond-test.el @@ -1,47 +1,34 @@ -;;; gigasecond-test.el --- ERT tests for gigasecond (exercism) -*- lexical-binding: t; -*- +;;; gigasecond-test.el --- Gigasecond (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -;; -;; Tests ported from Common Lisp gigasecond: -;; https://github.com/exercism/xlisp/blob/master/gigasecond/gigasecond-test.lisp -;; -;; To run tests individually: M-x eval-buffer RET, M-x ert RET test-name. -;; If you're using helm or something similar, you should get a menu of test names. -;; -;; To run tests in batch mode, from the command line run: -;; emacs -batch -l ert -l gigasecond-test.el -f ert-run-tests-batch-and-exit ;;; Code: + (load-file "gigasecond.el") -(declare-function from "gigasecond.el" (second minute hour day month year)) +(declare-function add "gigasecond.el" (moment)) + + +(ert-deftest date-only-specification-of-time () + (should (equal '(40 46 1 1 1 2043) (add 0 0 0 25 4 2011)))) -(ert-deftest from-lisp-epoch () - (should - (equal '(40 46 1 10 9 1931) (from 0 0 0 1 1 1900)))) -(ert-deftest from-unix-epoch () - (should - (equal '(40 46 1 9 9 2001) (from 0 0 0 1 1 1970)))) +(ert-deftest second-test-for-date-only-specification-of-time () + (should (equal '(40 46 1 19 2 2009) (add 0 0 0 13 6 1977)))) -(ert-deftest from-20110425T120000Z () - (should - (equal '(40 46 13 1 1 2043) (from 0 0 12 25 4 2011)))) -(ert-deftest from-19770613T235959Z () - (should - (equal '(39 46 1 20 2 2009) (from 59 59 23 13 6 1977)))) +(ert-deftest third-test-for-date-only-specification-of-time () + (should (equal '(40 46 1 27 3 1991) (add 0 0 0 19 7 1959)))) -(ert-deftest from-19590719T123030Z () - (should - (equal '(10 17 14 27 3 1991) (from 30 30 12 19 7 1959)))) -; customize this test to test your birthday and find your gigasecond date: -; (ert-deftest your-birthday () -; (should -; (equal '(0 0 0 day2 month2 year2) (from 0 0 0 day1 month1 year1)))) +(ert-deftest full-time-specified () + (should (equal '(40 46 23 2 10 2046) (add 0 0 22 24 1 2015)))) +(ert-deftest full-time-with-day-roll-over () + (should (equal '(39 46 1 3 10 2046) (add 59 59 23 24 1 2015)))) + (provide 'gigasecond-test) ;;; gigasecond-test.el ends here + diff --git a/exercises/practice/gigasecond/gigasecond.el b/exercises/practice/gigasecond/gigasecond.el index ba92a10f..62978cdc 100644 --- a/exercises/practice/gigasecond/gigasecond.el +++ b/exercises/practice/gigasecond/gigasecond.el @@ -1,15 +1,14 @@ -;;; gigasecond.el --- Gigasecond exercise (exercism) -*- lexical-binding: t; -*- +;;; gigasecond.el --- Gigasecond (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -;; Calculate the date one gigasecond (10^9 seconds) from the -;; given date. -;; -;; NB: Pay attention to Emacs' handling of time zones and dst -;; in the encode-time and decode-time functions. -(defun from (second minute hour day month year) ;;; Code: -) + + +(defun add (second minute hour day month year) + (error "Delete this S-Expression and write your own implementation")) + (provide 'gigasecond) ;;; gigasecond.el ends here + diff --git a/exercises/practice/grade-school/.docs/instructions.md b/exercises/practice/grade-school/.docs/instructions.md new file mode 100644 index 00000000..3cb1b5d5 --- /dev/null +++ b/exercises/practice/grade-school/.docs/instructions.md @@ -0,0 +1,21 @@ +# Instructions + +Given students' names along with the grade they are in, create a roster for the school. + +In the end, you should be able to: + +- Add a student's name to the roster for a grade: + - "Add Jim to grade 2." + - "OK." +- Get a list of all students enrolled in a grade: + - "Which students are in grade 2?" + - "We've only got Jim right now." +- Get a sorted list of all students in all grades. + Grades should be sorted as 1, 2, 3, etc., and students within a grade should be sorted alphabetically by name. + - "Who is enrolled in school right now?" + - "Let me think. + We have Anna, Barb, and Charlie in grade 1, Alex, Peter, and Zoe in grade 2, and Jim in grade 5. + So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe, and Jim." + +Note that all our students only have one name (it's a small town, what do you want?), and each student cannot be added more than once to a grade or the roster. +If a test attempts to add the same student more than once, your implementation should indicate that this is incorrect. diff --git a/exercises/practice/grade-school/.meta/config.json b/exercises/practice/grade-school/.meta/config.json new file mode 100644 index 00000000..a11f7a13 --- /dev/null +++ b/exercises/practice/grade-school/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "grade-school.el" + ], + "test": [ + "grade-school-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given students' names along with the grade that they are in, create a roster for the school.", + "source": "A pairing session with Phil Battos at gSchool" +} diff --git a/exercises/practice/grade-school/.meta/example.el b/exercises/practice/grade-school/.meta/example.el new file mode 100644 index 00000000..9dc2f7c0 --- /dev/null +++ b/exercises/practice/grade-school/.meta/example.el @@ -0,0 +1,33 @@ +;;; grade-school.el --- Grade School (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'cl-lib) + +(cl-defstruct school (roster (make-hash-table :test 'equal))) + +(defun add (school name grade) + (let ((current-roster (school-roster school))) + (if (cl-loop for g being the hash-keys of current-roster + thereis (member name (gethash g current-roster))) + nil + (puthash grade (sort (cons name (gethash grade current-roster)) #'string<) current-roster) + t))) + +(defun roster (school) + (let ((grades-and-names (list))) + (maphash (lambda (grade names) (push (cons grade names) grades-and-names)) + (school-roster school)) + (apply #'append + (mapcar #'cdr + (sort grades-and-names (lambda (a b) (< (car a) (car b)))))))) + +(defun grade (school grade) + (gethash grade (school-roster school))) + + +(provide 'grade-school) +;;; grade-school.el ends here diff --git a/exercises/practice/grade-school/.meta/tests.toml b/exercises/practice/grade-school/.meta/tests.toml new file mode 100644 index 00000000..50c9e2e5 --- /dev/null +++ b/exercises/practice/grade-school/.meta/tests.toml @@ -0,0 +1,86 @@ +# 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. + +[a3f0fb58-f240-4723-8ddc-e644666b85cc] +description = "Roster is empty when no student is added" + +[9337267f-7793-4b90-9b4a-8e3978408824] +description = "Add a student" + +[6d0a30e4-1b4e-472e-8e20-c41702125667] +description = "Student is added to the roster" + +[73c3ca75-0c16-40d7-82f5-ed8fe17a8e4a] +description = "Adding multiple students in the same grade in the roster" + +[233be705-dd58-4968-889d-fb3c7954c9cc] +description = "Multiple students in the same grade are added to the roster" + +[87c871c1-6bde-4413-9c44-73d59a259d83] +description = "Cannot add student to same grade in the roster more than once" + +[c125dab7-2a53-492f-a99a-56ad511940d8] +description = "A student can't be in two different grades" +include = false + +[a0c7b9b8-0e89-47f8-8b4a-c50f885e79d1] +description = "A student can only be added to the same grade in the roster once" +include = false +reimplements = "c125dab7-2a53-492f-a99a-56ad511940d8" + +[d7982c4f-1602-49f6-a651-620f2614243a] +description = "Student not added to same grade in the roster more than once" +reimplements = "a0c7b9b8-0e89-47f8-8b4a-c50f885e79d1" + +[e70d5d8f-43a9-41fd-94a4-1ea0fa338056] +description = "Adding students in multiple grades" + +[75a51579-d1d7-407c-a2f8-2166e984e8ab] +description = "Students in multiple grades are added to the roster" + +[7df542f1-57ce-433c-b249-ff77028ec479] +description = "Cannot add same student to multiple grades in the roster" + +[6a03b61e-1211-4783-a3cc-fc7f773fba3f] +description = "A student cannot be added to more than one grade in the sorted roster" +include = false +reimplements = "c125dab7-2a53-492f-a99a-56ad511940d8" + +[c7ec1c5e-9ab7-4d3b-be5c-29f2f7a237c5] +description = "Student not added to multiple grades in the roster" +reimplements = "6a03b61e-1211-4783-a3cc-fc7f773fba3f" + +[d9af4f19-1ba1-48e7-94d0-dabda4e5aba6] +description = "Students are sorted by grades in the roster" + +[d9fb5bea-f5aa-4524-9d61-c158d8906807] +description = "Students are sorted by name in the roster" + +[180a8ff9-5b94-43fc-9db1-d46b4a8c93b6] +description = "Students are sorted by grades and then by name in the roster" + +[5e67aa3c-a3c6-4407-a183-d8fe59cd1630] +description = "Grade is empty if no students in the roster" + +[1e0cf06b-26e0-4526-af2d-a2e2df6a51d6] +description = "Grade is empty if no students in that grade" + +[2bfc697c-adf2-4b65-8d0f-c46e085f796e] +description = "Student not added to same grade more than once" + +[66c8e141-68ab-4a04-a15a-c28bc07fe6b9] +description = "Student not added to multiple grades" + +[c9c1fc2f-42e0-4d2c-b361-99271f03eda7] +description = "Student not added to other grade for multiple grades" + +[1bfbcef1-e4a3-49e8-8d22-f6f9f386187e] +description = "Students are sorted by name in a grade" diff --git a/exercises/practice/grade-school/grade-school-test.el b/exercises/practice/grade-school/grade-school-test.el new file mode 100644 index 00000000..1d275368 --- /dev/null +++ b/exercises/practice/grade-school/grade-school-test.el @@ -0,0 +1,171 @@ +;;; grade-school-test.el --- Grade School (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "grade-school.el") +(declare-function make-school "grade-school.el") +(declare-function roster "grade-school.el" (school)) +(declare-function add "grade-school.el" (school name grade)) +(declare-function grade "grade-school.el" (school grade)) + + +(ert-deftest roster-is-empty-when-no-student-is-added () + (let ((test-school (make-school))) + (should (equal (roster test-school) '())))) + + +(ert-deftest add-a-student () + (let ((test-school (make-school))) + (should (add test-school "Aimee" 2)))) + + +(ert-deftest student-is-added-to-the-roster () + (let ((test-school (make-school))) + (add test-school "Aimee" 2) + (should (equal (roster test-school) '("Aimee"))))) + + +(ert-deftest adding-multiple-students-in-the-same-grade-in-the-roster () + (let ((test-school (make-school))) + (should (add test-school "Blair" 2)) + (should (add test-school "James" 2)) + (should (add test-school "Paul" 2)))) + + +(ert-deftest multiple-students-in-the-same-grade-are-added-to-the-roster () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "Paul" 2) + (should (equal (roster test-school) '("Blair" "James" "Paul"))))) + + +(ert-deftest cannot-add-student-to-same-grade-in-the-roster-more-than-once () + (let ((test-school (make-school))) + (should (add test-school "Blair" 2)) + (should (add test-school "James" 2)) + (should-not (add test-school "James" 2)) + (should (add test-school "Paul" 2)))) + + +(ert-deftest student-not-added-to-same-grade-in-the-roster-more-than-once () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "James" 2) + (add test-school "Paul" 2) + (should (equal (roster test-school) '("Blair" "James" "Paul"))))) + + +(ert-deftest adding-students-in-multiple-grades () + (let ((test-school (make-school))) + (should (add test-school "Chelsea" 3)) + (should (add test-school "Loagan" 7)))) + + +(ert-deftest students-in-multiple-grades-are-added-to-the-roster () + (let ((test-school (make-school))) + (add test-school "Chelsea" 3) + (add test-school "Logan" 7) + (should (equal (roster test-school) '("Chelsea" "Logan"))))) + + +(ert-deftest cannot-add-same-student-to-multiple-grades-in-the-roster () + (let ((test-school (make-school))) + (should (add test-school "Blair" 2)) + (should (add test-school "James" 2)) + (should-not (add test-school "James" 3)) + (should (add test-school "Paul" 3)))) + + +(ert-deftest student-not-added-to-multiple-grades-in-the-roster () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "James" 3) + (add test-school "Paul" 3) + (should (equal (roster test-school) '("Blair" "James" "Paul"))))) + + +(ert-deftest students-are-sorted-by-grades-in-the-roster () + (let ((test-school (make-school))) + (add test-school "Jim" 3) + (add test-school "Peter" 2) + (add test-school "Anna" 1) + (should (equal (roster test-school) '("Anna" "Peter" "Jim"))))) + + +(ert-deftest students-are-sorted-by-name-in-the-roster () + (let ((test-school (make-school))) + (add test-school "Peter" 2) + (add test-school "Zoe" 2) + (add test-school "Alex" 2) + (should (equal (roster test-school) '("Alex" "Peter" "Zoe"))))) + + +(ert-deftest students-are-sorted-by-grades-and-then-by-name-in-the-roster () + (let ((test-school (make-school))) + (add test-school "Peter" 2) + (add test-school "Anna" 1) + (add test-school "Barb" 1) + (add test-school "Zoe" 2) + (add test-school "Alex" 2) + (add test-school "Jim" 3) + (add test-school "Charlie" 1) + (should (equal (roster test-school) '("Anna" "Barb" "Charlie" "Alex" "Peter" "Zoe" "Jim"))))) + + +(ert-deftest grade-is-empty-if-no-students-in-the-roster () + (let ((test-school (make-school))) + (should (equal (grade test-school 1) '())))) + + +(ert-deftest grade-is-empty-if-no-students-in-that-grade () + (let ((test-school (make-school))) + (add test-school "Peter" 2) + (add test-school "Zoe" 2) + (add test-school "Alex" 2) + (add test-school "Jim" 3) + (should (equal (grade test-school 1) '())))) + + +(ert-deftest student-not-added-to-same-grade-more-than-once () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "James" 2) + (add test-school "Paul" 2) + (should (equal (grade test-school 2) '("Blair" "James" "Paul"))))) + + +(ert-deftest student-not-added-to-multiple-grades () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "James" 3) + (add test-school "Paul" 3) + (should (equal (grade test-school 2) '("Blair" "James"))))) + + +(ert-deftest student-not-added-to-other-grade-for-multiple-grades () + (let ((test-school (make-school))) + (add test-school "Blair" 2) + (add test-school "James" 2) + (add test-school "James" 3) + (add test-school "Paul" 3) + (should (equal (grade test-school 3) '("Paul"))))) + + +(ert-deftest students-are-sorted-by-name-in-a-grade () + (let ((test-school (make-school))) + (add test-school "Franklin" 5) + (add test-school "Bradley" 5) + (add test-school "Jeff" 1) + (should (equal (grade test-school 5) '("Bradley" "Franklin"))))) + + +(provide 'grade-school-test) +;;; grade-school-test.el ends here diff --git a/exercises/practice/grade-school/grade-school.el b/exercises/practice/grade-school/grade-school.el new file mode 100644 index 00000000..9c52d21d --- /dev/null +++ b/exercises/practice/grade-school/grade-school.el @@ -0,0 +1,22 @@ +;;; grade-school.el --- Grade School (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defun make-school () + (error "Delete this S-Expression and write your own implementation")) + +(defun roster (school) + (error "Delete this S-Expression and write your own implementation")) + +(defun add (school name grade) + (error "Delete this S-Expression and write your own implementation")) + +(defun grade (school grade) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'grade-school) +;;; grade-school.el ends here + diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md index df479fc0..f5b752a8 100644 --- a/exercises/practice/grains/.docs/instructions.md +++ b/exercises/practice/grains/.docs/instructions.md @@ -1,15 +1,11 @@ # Instructions -Calculate the number of grains of wheat on a chessboard given that the number on each square doubles. +Calculate the number of grains of wheat on a chessboard. -There once was a wise servant who saved the life of a prince. -The king promised to pay whatever the servant could dream up. -Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. -One grain on the first square of a chess board, with the number of grains doubling on each successive square. +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. -There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). +Write code that calculates: -Write code that shows: - -- how many grains were on a given square, and +- 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 index 6bb32f2d..3586fed6 100644 --- a/exercises/practice/grains/.meta/config.json +++ b/exercises/practice/grains/.meta/config.json @@ -20,5 +20,5 @@ }, "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.", "source": "The CodeRanch Cattle Drive, Assignment 6", - "source_url": "https://coderanch.com/wiki/718824/Grains" + "source_url": "https://web.archive.org/web/20240908084142/https://coderanch.com/wiki/718824/Grains" } diff --git a/exercises/practice/hamming/.docs/instructions.md b/exercises/practice/hamming/.docs/instructions.md index 020fdd02..8f47a179 100644 --- a/exercises/practice/hamming/.docs/instructions.md +++ b/exercises/practice/hamming/.docs/instructions.md @@ -1,26 +1,15 @@ # Instructions -Calculate the Hamming Distance between two DNA strands. +Calculate the Hamming distance between two DNA strands. -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". - -We read DNA using the letters C,A,G and T. +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. - -The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) +They have 7 differences, and therefore the Hamming distance is 7. ## Implementation notes 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 index 59d29d00..44152c8d 100644 --- a/exercises/practice/hamming/.meta/config.json +++ b/exercises/practice/hamming/.meta/config.json @@ -18,7 +18,7 @@ ".meta/example.el" ] }, - "blurb": "Calculate the Hamming difference between two DNA strands.", + "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/hamming-test.el b/exercises/practice/hamming/hamming-test.el index 0cac98e6..9fb22e67 100644 --- a/exercises/practice/hamming/hamming-test.el +++ b/exercises/practice/hamming/hamming-test.el @@ -1,54 +1,50 @@ -;;; hamming-test.el --- Tests for hamming (exercism) -*- lexical-binding: t; -*- +;;; hamming-test.el --- Hamming (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -;; Common test data version: 2.0.1 f79dfd7 ;;; Code: + (load-file "hamming.el") (declare-function hamming-distance "hamming.el" (dna1 dna2)) + (ert-deftest empty-strands () (should (= 0 (hamming-distance "" "")))) -(ert-deftest identical-strands () + +(ert-deftest single-letter-identical-strands () (should (= 0 (hamming-distance "A" "A")))) + +(ert-deftest single-letter-different-strands () + (should (= 1 (hamming-distance "G" "T")))) + + (ert-deftest long-identical-strands () - (should (= 0 (hamming-distance "GGACTGA" "GGACTGA")))) + (should (= 0 (hamming-distance "GGACTGAAATCTG" "GGACTGAAATCTG")))) -(ert-deftest complete-distance-in-single-nucleotide-strands () - (should (= 1 (hamming-distance "A" "G")))) +(ert-deftest long-different-strands () + (should (= 9 (hamming-distance "GGACGGATTCTG" "AGGACGGATTCT")))) -(ert-deftest complete-distance-in-small-strands () - (should (= 2 (hamming-distance "AG" "CT")))) -(ert-deftest small-distance-in-small-strands () - (should (= 1 (hamming-distance "AT" "CT")))) +(ert-deftest disallow-first-strand-longer () + (should-error (hamming-distance "AATG" "AAA"))) -(ert-deftest small-distance () - (should (= 1 (hamming-distance "GGACG" "GGTCG")))) -(ert-deftest small-distance-in-long-strands () - (should (= 2 (hamming-distance "ACCAGGG" "ACTATGG")))) +(ert-deftest disallow-second-strand-longer () + (should-error (hamming-distance "ATA" "AGTG"))) -(ert-deftest non-unique-character-in-first-strand () - (should (= 1 (hamming-distance "AAA" "AAG")))) -(ert-deftest same-nucleotides-in-different-positions () - (should (= 2 (hamming-distance "TAG" "GAT")))) +(ert-deftest disallow-empty-first-strand () + (should-error (hamming-distance "" "G"))) -(ert-deftest large-distance () - (should (= 4 (hamming-distance "GATACA" "GCATAA")))) -(ert-deftest large-distance-in-off-by-one-strand () - (should (= 9 (hamming-distance "GGACGGATTCTG" "AGGACGGATTCT")))) -(ert-deftest disallow-first-strand-longer () - (should-error (hamming-distance "AATG" "AAA"))) +(ert-deftest disallow-empty-second-strand () + (should-error (hamming-distance "G" ""))) -(ert-deftest disallow-second-strand-longer () - (should-error (hamming-distance "ATA" "AGTG"))) (provide 'hamming-test) ;;; hamming-test.el ends here + diff --git a/exercises/practice/hamming/hamming.el b/exercises/practice/hamming/hamming.el index 6a0bc5d1..c14a52fe 100644 --- a/exercises/practice/hamming/hamming.el +++ b/exercises/practice/hamming/hamming.el @@ -2,9 +2,12 @@ ;;; Commentary: +;;; Code: + + (defun hamming-distance (dna1 dna2) -;;; Code: -) + (error "Delete this S-Expression and write your own implementation")) + (provide 'hamming) ;;; hamming.el ends here diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json index 1f39c965..606e41a4 100644 --- a/exercises/practice/hello-world/.meta/config.json +++ b/exercises/practice/hello-world/.meta/config.json @@ -19,7 +19,7 @@ ".meta/example.el" ] }, - "blurb": "The classical introductory exercise. Just say \"Hello, World!\".", + "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/isbn-verifier/.docs/instructions.md b/exercises/practice/isbn-verifier/.docs/instructions.md new file mode 100644 index 00000000..4a0244e5 --- /dev/null +++ b/exercises/practice/isbn-verifier/.docs/instructions.md @@ -0,0 +1,42 @@ +# Instructions + +The [ISBN-10 verification process][isbn-verification] is used to validate book identification numbers. +These normally contain dashes and look like: `3-598-21508-8` + +## ISBN + +The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). +In the case the check character is an X, this represents the value '10'. +These may be communicated with or without hyphens, and can be checked for their validity by the following formula: + +```text +(d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0 +``` + +If the result is 0, then it is a valid ISBN-10, otherwise it is invalid. + +## Example + +Let's take the ISBN-10 `3-598-21508-8`. +We plug it in to the formula, and get: + +```text +(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0 +``` + +Since the result is 0, this proves that our ISBN is valid. + +## Task + +Given a string the program should check if the provided string is a valid ISBN-10. +Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN. + +The program should be able to verify ISBN-10 both with and without separating dashes. + +## Caveats + +Converting from strings to numbers can be tricky in certain languages. +Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). +For instance `3-598-21507-X` is a valid ISBN-10. + +[isbn-verification]: https://en.wikipedia.org/wiki/International_Standard_Book_Number diff --git a/exercises/practice/isbn-verifier/.meta/config.json b/exercises/practice/isbn-verifier/.meta/config.json new file mode 100644 index 00000000..c013be1e --- /dev/null +++ b/exercises/practice/isbn-verifier/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "isbn-verifier.el" + ], + "test": [ + "isbn-verifier-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Check if a given string is a valid ISBN-10 number.", + "source": "Converting a string into a number and some basic processing utilizing a relatable real world example.", + "source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation" +} diff --git a/exercises/practice/isbn-verifier/.meta/example.el b/exercises/practice/isbn-verifier/.meta/example.el new file mode 100644 index 00000000..c0adb88d --- /dev/null +++ b/exercises/practice/isbn-verifier/.meta/example.el @@ -0,0 +1,23 @@ +;;; isbn-verifier.el --- ISBN Verifier (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun digit-value (c) + (if (= ?X c) 10 (- c ?0))) + +(defun validp (isbn) + (let ((total 0) + (weight 10) + (characters (remq ?- (cl-coerce isbn 'list)))) + (cl-loop for c in characters + always (or (cl-digit-char-p c) (and (= ?X c) (= weight 1))) + do (setq total (+ total (* weight (digit-value c)))) + do (setq weight (- weight 1)) + finally return (and (= 0 weight) (= 0 (% total 11)))))) + +(provide 'isbn-verifier) +;;; isbn-verifier.el ends here diff --git a/exercises/practice/isbn-verifier/.meta/tests.toml b/exercises/practice/isbn-verifier/.meta/tests.toml new file mode 100644 index 00000000..6d5a8459 --- /dev/null +++ b/exercises/practice/isbn-verifier/.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. + +[0caa3eac-d2e3-4c29-8df8-b188bc8c9292] +description = "valid isbn" + +[19f76b53-7c24-45f8-87b8-4604d0ccd248] +description = "invalid isbn check digit" + +[4164bfee-fb0a-4a1c-9f70-64c6a1903dcd] +description = "valid isbn with a check digit of 10" + +[3ed50db1-8982-4423-a993-93174a20825c] +description = "check digit is a character other than X" + +[9416f4a5-fe01-4b61-a07b-eb75892ef562] +description = "invalid check digit in isbn is not treated as zero" + +[c19ba0c4-014f-4dc3-a63f-ff9aefc9b5ec] +description = "invalid character in isbn is not treated as zero" + +[28025280-2c39-4092-9719-f3234b89c627] +description = "X is only valid as a check digit" + +[f6294e61-7e79-46b3-977b-f48789a4945b] +description = "valid isbn without separating dashes" + +[185ab99b-3a1b-45f3-aeec-b80d80b07f0b] +description = "isbn without separating dashes and X as check digit" + +[7725a837-ec8e-4528-a92a-d981dd8cf3e2] +description = "isbn without check digit and dashes" + +[47e4dfba-9c20-46ed-9958-4d3190630bdf] +description = "too long isbn and no dashes" + +[737f4e91-cbba-4175-95bf-ae630b41fb60] +description = "too short isbn" + +[5458a128-a9b6-4ff8-8afb-674e74567cef] +description = "isbn without check digit" + +[70b6ad83-d0a2-4ca7-a4d5-a9ab731800f7] +description = "check digit of X should not be used for 0" + +[94610459-55ab-4c35-9b93-ff6ea1a8e562] +description = "empty isbn" + +[7bff28d4-d770-48cc-80d6-b20b3a0fb46c] +description = "input is 9 characters" + +[ed6e8d1b-382c-4081-8326-8b772c581fec] +description = "invalid characters are not ignored after checking length" + +[daad3e58-ce00-4395-8a8e-e3eded1cdc86] +description = "invalid characters are not ignored before checking length" + +[fb5e48d8-7c03-4bfb-a088-b101df16fdc3] +description = "input is too long but contains a valid isbn" diff --git a/exercises/practice/isbn-verifier/isbn-verifier-test.el b/exercises/practice/isbn-verifier/isbn-verifier-test.el new file mode 100644 index 00000000..c11efa47 --- /dev/null +++ b/exercises/practice/isbn-verifier/isbn-verifier-test.el @@ -0,0 +1,88 @@ +;;; isbn-verifier-test.el --- Tests for Isbn Verifier (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "isbn-verifier.el") +(declare-function validp "isbn-verifier.el" (isbn)) + + +(ert-deftest valid-isbn () + (should (validp "3-598-21508-8"))) + + +(ert-deftest invalid-isbn-check-digit () + (should-not (validp "3-598-21508-9"))) + + +(ert-deftest valid-isbn-with-a-check-digit-of-10 () + (should (validp "3-598-21507-X"))) + + +(ert-deftest check-digit-is-a-character-other-than-x () + (should-not (validp "3-598-21507-A"))) + + +(ert-deftest invalid-check-digit-in-isbn-is-not-treated-as-zero () + (should-not (validp "4-598-21507-B"))) + + +(ert-deftest invalid-character-in-isbn-is-not-treated-as-zero () + (should-not (validp "3-598-P1581-X"))) + + +(ert-deftest x-is-only-valid-as-a-check-digit () + (should-not (validp "3-598-2X507-9"))) + + +(ert-deftest valid-isbn-without-separating-dashes () + (should (validp "3598215088"))) + + +(ert-deftest isbn-without-separating-dashes-and-x-as-check-digit () + (should (validp "359821507X"))) + + +(ert-deftest isbn-without-check-digit-and-dashes () + (should-not (validp "359821507"))) + + +(ert-deftest too-long-isbn-and-no-dashes () + (should-not (validp "3598215078X"))) + + +(ert-deftest too-short-isbn () + (should-not (validp "00"))) + + +(ert-deftest isbn-without-check-digit () + (should-not (validp "3-598-21507"))) + + +(ert-deftest check-digit-of-x-should-not-be-used-for-0 () + (should-not (validp "3-598-21515-X"))) + + +(ert-deftest empty-isbn () + (should-not (validp ""))) + + +(ert-deftest input-is-9-characters () + (should-not (validp "134456729"))) + + +(ert-deftest invalid-characters-are-not-ignored-after-checking-length () + (should-not (validp "3132P34035"))) + + +(ert-deftest invalid-characters-are-not-ignored-before-checking-length () + (should-not (validp "3598P215088"))) + + +(ert-deftest input-is-too-long-but-contains-a-valid-isbn () + (should-not (validp "98245726788"))) + + +(provide 'isbn-verifier-test) +;;; isbn-verifier-test.el ends here diff --git a/exercises/practice/isbn-verifier/isbn-verifier.el b/exercises/practice/isbn-verifier/isbn-verifier.el new file mode 100644 index 00000000..18e0f85a --- /dev/null +++ b/exercises/practice/isbn-verifier/isbn-verifier.el @@ -0,0 +1,13 @@ +;;; isbn-verifier.el --- ISBN Verifier (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun validp (isbn) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'isbn-verifier) +;;; isbn-verifier.el ends here 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..0a57ef77 --- /dev/null +++ b/exercises/practice/isogram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "isogram.el" + ], + "test": [ + "isogram-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/isogram/.meta/example.el new file mode 100644 index 00000000..d63dd40f --- /dev/null +++ b/exercises/practice/isogram/.meta/example.el @@ -0,0 +1,27 @@ +;;; isogram.el --- isogram (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun upper-char-p (c) + "Determine if char C is alphabetic and upper case." + (and + (>= c ?A) + (<= c ?Z))) + +(defun isogramp (phrase) + "Determine if a given phrase is an isogram." + (let* ((bitset 0) + (updated 0) + (characters (cl-coerce (upcase phrase) 'list)) + (letters (cl-remove-if-not 'upper-char-p characters))) + (cl-loop for c in letters + do (setq updated (logior bitset (lsh 1 (- c ?A)))) + always (> updated bitset) + do (setq bitset updated)))) + +(provide 'isogram) +;;; isogram.el ends here 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.el b/exercises/practice/isogram/isogram-test.el new file mode 100644 index 00000000..6fbdbd51 --- /dev/null +++ b/exercises/practice/isogram/isogram-test.el @@ -0,0 +1,68 @@ +;;; isogram-test.el --- Tests for Isogram (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "isogram.el") +(declare-function isogramp "isogram.el" (phrase)) + + +(ert-deftest empty-string () + (should (isogramp ""))) + + +(ert-deftest isogram-with-only-lower-case-characters () + (should (isogramp "isogram"))) + + +(ert-deftest word-with-one-duplicated-character () + (should-not (isogramp "eleven"))) + + +(ert-deftest word-with-one-duplicated-character-from-the-end-of-the-alphabet () + (should-not (isogramp "zzyzx"))) + + +(ert-deftest longest-reported-english-isogram () + (should (isogramp "subdermatoglyphic"))) + + +(ert-deftest word-with-duplicated-character-in-mixed-case () + (should-not (isogramp "Alphabet"))) + + +(ert-deftest word-with-duplicated-character-in-mixed-case-lowercase-first () + (should-not (isogramp "alphAbet"))) + + +(ert-deftest hypothetical-isogrammic-word-with-hyphen () + (should (isogramp "thumbscrew-japingly"))) + + +(ert-deftest hypothetical-word-with-duplicated-character-following-hyphen () + (should-not (isogramp "thumbscrew-jappingly"))) + + +(ert-deftest isogram-with-duplicated-hyphen () + (should (isogramp "six-year-old"))) + + +(ert-deftest made-up-name-that-is-an-isogram () + (should (isogramp "Emily Jung Schwartzkopf"))) + + +(ert-deftest duplicated-character-in-the-middle () + (should-not (isogramp "accentor"))) + + +(ert-deftest same-first-and-last-characters () + (should-not (isogramp "angola"))) + + +(ert-deftest word-with-duplicated-character-and-with-two-hyphens () + (should-not (isogramp "up-to-date"))) + + +(provide 'isogram-test) +;;; isogram-test.el ends here diff --git a/exercises/practice/isogram/isogram.el b/exercises/practice/isogram/isogram.el new file mode 100644 index 00000000..699f0685 --- /dev/null +++ b/exercises/practice/isogram/isogram.el @@ -0,0 +1,13 @@ +;;; isogram.el --- isogram (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun isogramp (phrase) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'isogram) +;;; isogram.el ends here diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md new file mode 100644 index 00000000..6fe11a58 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/instructions.md @@ -0,0 +1,56 @@ +# Instructions + +Your task is to, given a diagram, determine which plants each child in the kindergarten class is responsible for. + +There are 12 children in the class: + +- Alice, Bob, Charlie, David, Eve, Fred, Ginny, Harriet, Ileana, Joseph, Kincaid, and Larry. + +Four different types of seeds are planted: + +| Plant | Diagram encoding | +| ------ | ---------------- | +| Grass | G | +| Clover | C | +| Radish | R | +| Violet | V | + +Each child gets four cups, two on each row: + +```text +[window][window][window] +........................ # each dot represents a cup +........................ +``` + +Their teacher assigns cups to the children alphabetically by their names, which means that Alice comes first and Larry comes last. + +Here is an example diagram representing Alice's plants: + +```text +[window][window][window] +VR...................... +RG...................... +``` + +In the first row, nearest the windows, she has a violet and a radish. +In the second row she has a radish and some grass. + +Your program will be given the plants from left-to-right starting with the row nearest the windows. +From this, it should be able to determine which plants belong to each student. + +For example, if it's told that the garden looks like so: + +```text +[window][window][window] +VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV +``` + +Then if asked for Alice's plants, it should provide: + +- Violets, radishes, violets, radishes + +While asking for Bob's plants would yield: + +- Clover, grass, clover, clover diff --git a/exercises/practice/kindergarten-garden/.docs/introduction.md b/exercises/practice/kindergarten-garden/.docs/introduction.md new file mode 100644 index 00000000..5ad97d23 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +The kindergarten class is learning about growing plants. +The teacher thought it would be a good idea to give the class seeds to plant and grow in the dirt. +To this end, the children have put little cups along the window sills and planted one type of plant in each cup. +The children got to pick their favorites from four available types of seeds: grass, clover, radishes, and violets. diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json new file mode 100644 index 00000000..3c33e492 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "kindergarten-garden.el" + ], + "test": [ + "kindergarten-garden-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu/" +} diff --git a/exercises/practice/kindergarten-garden/.meta/example.el b/exercises/practice/kindergarten-garden/.meta/example.el new file mode 100644 index 00000000..0dc8de8e --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/example.el @@ -0,0 +1,25 @@ +;;; kindergarten-garden.el --- Kindergarten Garden (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun plants (diagram student) + (cl-flet ((plant (index) + (cl-case (aref diagram index) + (?G "grass") + (?C "clover") + (?R "radishes") + (?V "violets")))) + (let* ((midpoint (/ (1+ (length diagram)) 2)) + (student_index (- (aref student 0) ?A)) + (first (* 2 student_index)) + (second (1+ first)) + (third (+ midpoint first)) + (fourth (1+ third))) + (mapcar #'plant (list first second third fourth))))) + +(provide 'plants) +;;; kindergarten-garden.el ends here diff --git a/exercises/practice/kindergarten-garden/.meta/tests.toml b/exercises/practice/kindergarten-garden/.meta/tests.toml new file mode 100644 index 00000000..0cdd9ad6 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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. + +[1fc316ed-17ab-4fba-88ef-3ae78296b692] +description = "partial garden -> garden with single student" + +[acd19dc1-2200-4317-bc2a-08f021276b40] +description = "partial garden -> different garden with single student" + +[c376fcc8-349c-446c-94b0-903947315757] +description = "partial garden -> garden with two students" + +[2d620f45-9617-4924-9d27-751c80d17db9] +description = "partial garden -> multiple students for the same garden with three students -> second student's garden" + +[57712331-4896-4364-89f8-576421d69c44] +description = "partial garden -> multiple students for the same garden with three students -> third student's garden" + +[149b4290-58e1-40f2-8ae4-8b87c46e765b] +description = "full garden -> for Alice, first student's garden" + +[ba25dbbc-10bd-4a37-b18e-f89ecd098a5e] +description = "full garden -> for Bob, second student's garden" + +[566b621b-f18e-4c5f-873e-be30544b838c] +description = "full garden -> for Charlie" + +[3ad3df57-dd98-46fc-9269-1877abf612aa] +description = "full garden -> for David" + +[0f0a55d1-9710-46ed-a0eb-399ba8c72db2] +description = "full garden -> for Eve" + +[a7e80c90-b140-4ea1-aee3-f4625365c9a4] +description = "full garden -> for Fred" + +[9d94b273-2933-471b-86e8-dba68694c615] +description = "full garden -> for Ginny" + +[f55bc6c2-ade8-4844-87c4-87196f1b7258] +description = "full garden -> for Harriet" + +[759070a3-1bb1-4dd4-be2c-7cce1d7679ae] +description = "full garden -> for Ileana" + +[78578123-2755-4d4a-9c7d-e985b8dda1c6] +description = "full garden -> for Joseph" + +[6bb66df7-f433-41ab-aec2-3ead6e99f65b] +description = "full garden -> for Kincaid, second to last student's garden" + +[d7edec11-6488-418a-94e6-ed509e0fa7eb] +description = "full garden -> for Larry, last student's garden" diff --git a/exercises/practice/kindergarten-garden/kindergarten-garden-test.el b/exercises/practice/kindergarten-garden/kindergarten-garden-test.el new file mode 100644 index 00000000..6755de71 --- /dev/null +++ b/exercises/practice/kindergarten-garden/kindergarten-garden-test.el @@ -0,0 +1,80 @@ +;;; kindergarten-garden-test.el --- Tests for Kindergarten Garden (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "kindergarten-garden.el") +(declare-function plants "kindergarten-garden.el" (diagram student)) + + +(ert-deftest partial-garden-with-single-student () + (should (equal '("radishes" "clover" "grass" "grass") (plants "RC\nGG" "Alice")))) + + +(ert-deftest partial-garden-different-garden-with-single-student () + (should (equal '("violets" "clover" "radishes" "clover") (plants "VC\nRC" "Alice")))) + + +(ert-deftest partial-garden-with-two-students () + (should (equal '("clover" "grass" "radishes" "clover") (plants "VVCG\nVVRC" "Bob")))) + + +(ert-deftest partial-garden-multiple-students-for-the-same-garden-with-three-students-second-students-garden () + (should (equal '("clover" "clover" "clover" "clover") (plants "VVCCGG\nVVCCGG" "Bob")))) + + +(ert-deftest partial-garden-multiple-students-for-the-same-garden-with-three-students-third-students-garden () + (should (equal '("grass" "grass" "grass" "grass") (plants "VVCCGG\nVVCCGG" "Charlie")))) + + +(ert-deftest full-garden-for-alice-first-students-garden () + (should (equal '("violets" "radishes" "violets" "radishes") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Alice")))) + + +(ert-deftest full-garden-for-bob-second-students-garden () + (should (equal '("clover" "grass" "clover" "clover") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Bob")))) + + +(ert-deftest full-garden-for-charlie () + (should (equal '("violets" "violets" "clover" "grass") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Charlie")))) + + +(ert-deftest full-garden-for-david () + (should (equal '("radishes" "violets" "clover" "radishes") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "David")))) + + +(ert-deftest full-garden-for-eve () + (should (equal '("clover" "grass" "radishes" "grass") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Eve")))) + + +(ert-deftest full-garden-for-fred () + (should (equal '("grass" "clover" "violets" "clover") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Fred")))) + + +(ert-deftest full-garden-for-ginny () + (should (equal '("clover" "grass" "grass" "clover") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Ginny")))) + + +(ert-deftest full-garden-for-harriet () + (should (equal '("violets" "radishes" "radishes" "violets") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Harriet")))) + + +(ert-deftest full-garden-for-ileana () + (should (equal '("grass" "clover" "violets" "clover") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Ileana")))) + + +(ert-deftest full-garden-for-joseph () + (should (equal '("violets" "clover" "violets" "grass") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Joseph")))) + + +(ert-deftest full-garden-for-kincaid-second-to-last-students-garden () + (should (equal '("grass" "clover" "clover" "grass") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Kincaid")))) + + +(ert-deftest full-garden-for-larry-last-students-garden () + (should (equal '("grass" "violets" "clover" "violets") (plants "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" "Larry")))) + + +(provide 'kindergarten-garden-test) +;;; kindergarten-garden-test.el ends here diff --git a/exercises/practice/kindergarten-garden/kindergarten-garden.el b/exercises/practice/kindergarten-garden/kindergarten-garden.el new file mode 100644 index 00000000..575dc4fc --- /dev/null +++ b/exercises/practice/kindergarten-garden/kindergarten-garden.el @@ -0,0 +1,13 @@ +;;; kindergarten-garden.el --- Kindergarten Garden (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun plants (diagram student) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'plants) +;;; kindergarten-garden.el ends here diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 00000000..0ebf7914 --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to determine which items to take so that the total value of her selection is maximized, taking into account the knapsack's carrying capacity. + +Items will be represented as a list of items. +Each item will have a weight and value. +All values given will be strictly positive. +Lhakpa can take only one of each item. + +For example: + +```text +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Maximum Weight: 10 +``` + +For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on. +In this example, Lhakpa should take the second and fourth item to maximize her value, which, in this case, is 90. +She cannot get more than 90 as her knapsack has a weight limit of 10. diff --git a/exercises/practice/knapsack/.docs/introduction.md b/exercises/practice/knapsack/.docs/introduction.md new file mode 100644 index 00000000..9ac9df59 --- /dev/null +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +Lhakpa is a [Sherpa][sherpa] mountain guide and porter. +After months of careful planning, the expedition Lhakpa works for is about to leave. +She will be paid the value she carried to the base camp. + +In front of her are many items, each with a value and weight. +Lhakpa would gladly take all of the items, but her knapsack can only hold so much weight. + +[sherpa]: https://en.wikipedia.org/wiki/Sherpa_people#Mountaineering diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 00000000..edb6f556 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "knapsack.el" + ], + "test": [ + "knapsack-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Knapsack_problem" +} diff --git a/exercises/practice/knapsack/.meta/example.el b/exercises/practice/knapsack/.meta/example.el new file mode 100644 index 00000000..ebb56bb3 --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.el @@ -0,0 +1,22 @@ +;;; knapsack.el --- Knapsack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun maximum-value (maximum-weight items) + (let ((table (make-vector (1+ maximum-weight) 0))) + (dolist (item items) + (let ((weight (alist-get :weight item)) + (value (alist-get :value item))) + (cl-loop for index from maximum-weight downto weight + for new-value = (+ value (aref table (- index weight))) + do (when (> new-value (aref table index)) + (aset table index new-value))))) + (aref table maximum-weight))) + +(provide 'knapsack) +;;; knapsack.el ends here + diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 00000000..8e013ef1 --- /dev/null +++ b/exercises/practice/knapsack/.meta/tests.toml @@ -0,0 +1,36 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7] +description = "no items" +include = false + +[3993a824-c20e-493d-b3c9-ee8a7753ee59] +description = "no items" +reimplements = "a4d7d2f0-ad8a-460c-86f3-88ba709d41a7" + +[1d39e98c-6249-4a8b-912f-87cb12e506b0] +description = "one item, too heavy" + +[833ea310-6323-44f2-9d27-a278740ffbd8] +description = "five items (cannot be greedy by weight)" + +[277cdc52-f835-4c7d-872b-bff17bab2456] +description = "five items (cannot be greedy by value)" + +[81d8e679-442b-4f7a-8a59-7278083916c9] +description = "example knapsack" + +[f23a2449-d67c-4c26-bf3e-cde020f27ecc] +description = "8 items" + +[7c682ae9-c385-4241-a197-d2fa02c81a11] +description = "15 items" diff --git a/exercises/practice/knapsack/knapsack-test.el b/exercises/practice/knapsack/knapsack-test.el new file mode 100644 index 00000000..4692bba0 --- /dev/null +++ b/exercises/practice/knapsack/knapsack-test.el @@ -0,0 +1,73 @@ +;;; knapsack-test.el --- Tests for Knapsack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "knapsack.el") +(declare-function maximum-value "knapsack.el" (maximum-weight items)) + + +(ert-deftest no-items () + (should (= 0 (maximum-value 100 '())))) + + +(ert-deftest one-item-too-heavy () + (should (= 0 (maximum-value 10 '(((:weight . 100) (:value . 1))))))) + + +(ert-deftest five-items-cannot-be-greedy-by-weight () + (should (= 21 (maximum-value 10 '(((:weight . 2) (:value . 5)) + ((:weight . 2) (:value . 5)) + ((:weight . 2) (:value . 5)) + ((:weight . 2) (:value . 5)) + ((:weight . 10) (:value . 21))))))) + + +(ert-deftest five-items-cannot-be-greedy-by-value () + (should (= 80 (maximum-value 10 '(((:weight . 2) (:value . 20)) + ((:weight . 2) (:value . 20)) + ((:weight . 2) (:value . 20)) + ((:weight . 2) (:value . 20)) + ((:weight . 10) (:value . 50))))))) + + +(ert-deftest example-knapsack () + (should (= 90 (maximum-value 10 '(((:weight . 5) (:value . 10)) + ((:weight . 4) (:value . 40)) + ((:weight . 6) (:value . 30)) + ((:weight . 4) (:value . 50))))))) + + +(ert-deftest 8-items () + (should (= 900 (maximum-value 104 '(((:weight . 25) (:value . 350)) + ((:weight . 35) (:value . 400)) + ((:weight . 45) (:value . 450)) + ((:weight . 5) (:value . 20)) + ((:weight . 25) (:value . 70)) + ((:weight . 3) (:value . 8)) + ((:weight . 2) (:value . 5)) + ((:weight . 2) (:value . 5))))))) + + +(ert-deftest 15-items () + (should (= 1458 (maximum-value 750 '(((:weight . 70) (:value . 135)) + ((:weight . 73) (:value . 139)) + ((:weight . 77) (:value . 149)) + ((:weight . 80) (:value . 150)) + ((:weight . 82) (:value . 156)) + ((:weight . 87) (:value . 163)) + ((:weight . 90) (:value . 173)) + ((:weight . 94) (:value . 184)) + ((:weight . 98) (:value . 192)) + ((:weight . 106) (:value . 201)) + ((:weight . 110) (:value . 210)) + ((:weight . 113) (:value . 214)) + ((:weight . 115) (:value . 221)) + ((:weight . 118) (:value . 229)) + ((:weight . 120) (:value . 240))))))) + + +(provide 'knapsack-test) +;;; knapsack-test.el ends here diff --git a/exercises/practice/knapsack/knapsack.el b/exercises/practice/knapsack/knapsack.el new file mode 100644 index 00000000..7c0559dd --- /dev/null +++ b/exercises/practice/knapsack/knapsack.el @@ -0,0 +1,14 @@ +;;; knapsack.el --- Knapsack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun maximum-value (maximum-weight items) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'knapsack) +;;; knapsack.el ends here + diff --git a/exercises/practice/largest-series-product/.docs/instructions.md b/exercises/practice/largest-series-product/.docs/instructions.md new file mode 100644 index 00000000..f297b57f --- /dev/null +++ b/exercises/practice/largest-series-product/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to look for patterns in the long sequence of digits in the encrypted signal. + +The technique you're going to use here is called the largest series product. + +Let's define a few terms, first. + +- **input**: the sequence of digits that you need to analyze +- **series**: a sequence of adjacent digits (those that are next to each other) that is contained within the input +- **span**: how many digits long each series is +- **product**: what you get when you multiply numbers together + +Let's work through an example, with the input `"63915"`. + +- To form a series, take adjacent digits in the original input. +- If you are working with a span of `3`, there will be three possible series: + - `"639"` + - `"391"` + - `"915"` +- Then we need to calculate the product of each series: + - The product of the series `"639"` is 162 (`6 × 3 × 9 = 162`) + - The product of the series `"391"` is 27 (`3 × 9 × 1 = 27`) + - The product of the series `"915"` is 45 (`9 × 1 × 5 = 45`) +- 162 is bigger than both 27 and 45, so the largest series product of `"63915"` is from the series `"639"`. + So the answer is **162**. diff --git a/exercises/practice/largest-series-product/.docs/introduction.md b/exercises/practice/largest-series-product/.docs/introduction.md new file mode 100644 index 00000000..597bb5fa --- /dev/null +++ b/exercises/practice/largest-series-product/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +You work for a government agency that has intercepted a series of encrypted communication signals from a group of bank robbers. +The signals contain a long sequence of digits. +Your team needs to use various digital signal processing techniques to analyze the signals and identify any patterns that may indicate the planning of a heist. diff --git a/exercises/practice/largest-series-product/.meta/config.json b/exercises/practice/largest-series-product/.meta/config.json new file mode 100644 index 00000000..71fd6a42 --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "largest-series-product.el" + ], + "test": [ + "largest-series-product-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.", + "source": "A variation on Problem 8 at Project Euler", + "source_url": "https://projecteuler.net/problem=8" +} diff --git a/exercises/practice/largest-series-product/.meta/example.el b/exercises/practice/largest-series-product/.meta/example.el new file mode 100644 index 00000000..bebb4b07 --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/example.el @@ -0,0 +1,26 @@ +;;; largest-series-product.el --- Largest Series Product (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun largest-product (digits span) + (when (< span 0) + (error "span must not be negative")) + (when (> span (length digits)) + (error "span must not exceed string length")) + (unless (cl-every 'cl-digit-char-p (cl-coerce digits 'list)) + (error "digits input must only contain digits")) + (let ((largest 0)) + (dotimes (i (+ 1 (- (length digits) span))) + (let ((product 1)) + (dotimes (j span) + (setq product (* product (cl-digit-char-p (aref digits (+ i j)))))) + (when (< largest product) + (setq largest product)))) + largest)) + +(provide 'largest-series-product) +;;; largest-series-product.el ends here diff --git a/exercises/practice/largest-series-product/.meta/tests.toml b/exercises/practice/largest-series-product/.meta/tests.toml new file mode 100644 index 00000000..5a62d619 --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/tests.toml @@ -0,0 +1,70 @@ +# 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. + +[7c82f8b7-e347-48ee-8a22-f672323324d4] +description = "finds the largest product if span equals length" + +[88523f65-21ba-4458-a76a-b4aaf6e4cb5e] +description = "can find the largest product of 2 with numbers in order" + +[f1376b48-1157-419d-92c2-1d7e36a70b8a] +description = "can find the largest product of 2" + +[46356a67-7e02-489e-8fea-321c2fa7b4a4] +description = "can find the largest product of 3 with numbers in order" + +[a2dcb54b-2b8f-4993-92dd-5ce56dece64a] +description = "can find the largest product of 3" + +[673210a3-33cd-4708-940b-c482d7a88f9d] +description = "can find the largest product of 5 with numbers in order" + +[02acd5a6-3bbf-46df-8282-8b313a80a7c9] +description = "can get the largest product of a big number" + +[76dcc407-21e9-424c-a98e-609f269622b5] +description = "reports zero if the only digits are zero" + +[6ef0df9f-52d4-4a5d-b210-f6fae5f20e19] +description = "reports zero if all spans include zero" + +[5d81aaf7-4f67-4125-bf33-11493cc7eab7] +description = "rejects span longer than string length" +include = false + +[0ae1ce53-d9ba-41bb-827f-2fceb64f058b] +description = "rejects span longer than string length" +reimplements = "5d81aaf7-4f67-4125-bf33-11493cc7eab7" + +[06bc8b90-0c51-4c54-ac22-3ec3893a079e] +description = "reports 1 for empty string and empty product (0 span)" + +[3ec0d92e-f2e2-4090-a380-70afee02f4c0] +description = "reports 1 for nonempty string and empty product (0 span)" + +[6d96c691-4374-4404-80ee-2ea8f3613dd4] +description = "rejects empty string and nonzero span" +include = false + +[6cf66098-a6af-4223-aab1-26aeeefc7402] +description = "rejects empty string and nonzero span" +reimplements = "6d96c691-4374-4404-80ee-2ea8f3613dd4" + +[7a38f2d6-3c35-45f6-8d6f-12e6e32d4d74] +description = "rejects invalid character in digits" + +[5fe3c0e5-a945-49f2-b584-f0814b4dd1ef] +description = "rejects negative span" +include = false + +[c859f34a-9bfe-4897-9c2f-6d7f8598e7f0] +description = "rejects negative span" +reimplements = "5fe3c0e5-a945-49f2-b584-f0814b4dd1ef" diff --git a/exercises/practice/largest-series-product/largest-series-product-test.el b/exercises/practice/largest-series-product/largest-series-product-test.el new file mode 100644 index 00000000..ef629bbb --- /dev/null +++ b/exercises/practice/largest-series-product/largest-series-product-test.el @@ -0,0 +1,72 @@ +;;; largest-series-product-test.el --- Tests for Largest Series Product (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "largest-series-product.el") +(declare-function largest-product "largest-series-product.el" (digits span)) + + +(ert-deftest finds-the-largest-product-if-span-equals-length () + (should (equal 18 (largest-product "29" 2)))) + + +(ert-deftest can-find-the-largest-product-of-2-with-numbers-in-order () + (should (equal 72 (largest-product "0123456789" 2)))) + + +(ert-deftest can-find-the-largest-product-of-2 () + (should (equal 48 (largest-product "576802143" 2)))) + + +(ert-deftest can-find-the-largest-product-of-3-with-numbers-in-order () + (should (equal 504 (largest-product "0123456789" 3)))) + + +(ert-deftest can-find-the-largest-product-of-3 () + (should (equal 270 (largest-product "1027839564" 3)))) + + +(ert-deftest can-find-the-largest-product-of-5-with-numbers-in-order () + (should (equal 15120 (largest-product "0123456789" 5)))) + + +(ert-deftest can-get-the-largest-product-of-a-big-number () + (should (equal 23520 (largest-product "73167176531330624919225119674426574742355349194934" 6)))) + + +(ert-deftest reports-zero-if-the-only-digits-are-zero () + (should (equal 0 (largest-product "0000" 2)))) + + +(ert-deftest reports-zero-if-all-spans-include-zero () + (should (equal 0 (largest-product "99099" 3)))) + + +(ert-deftest rejects-span-longer-than-string-length () + (should-error (largest-product "123" 4))) + + +(ert-deftest reports-1-for-empty-string-and-empty-product-zero-span () + (should (equal 1 (largest-product "" 0)))) + + +(ert-deftest reports-1-for-nonempty-string-and-empty-product-zero-span () + (should (equal 1 (largest-product "123" 0)))) + + +(ert-deftest rejects-empty-string-and-nonzero-span () + (should-error (largest-product "" 1))) + + +(ert-deftest rejects-invalid-character-in-digits () + (should-error (largest-product "1234a5" 2))) + + +(ert-deftest rejects-negative-span () + (should-error (largest-product "12345" -1))) + + +(provide 'largest-series-product-test) +;;; largest-series-product-test.el ends here diff --git a/exercises/practice/largest-series-product/largest-series-product.el b/exercises/practice/largest-series-product/largest-series-product.el new file mode 100644 index 00000000..0d422755 --- /dev/null +++ b/exercises/practice/largest-series-product/largest-series-product.el @@ -0,0 +1,13 @@ +;;; largest-series-product.el --- Largest Series Product (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun largest-product (digits span) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'largest-series-product) +;;; largest-series-product.el ends here diff --git a/exercises/practice/leap/.docs/instructions.md b/exercises/practice/leap/.docs/instructions.md index a83826b2..b14f8565 100644 --- a/exercises/practice/leap/.docs/instructions.md +++ b/exercises/practice/leap/.docs/instructions.md @@ -1,22 +1,3 @@ # Instructions -Given a year, report if it is a leap year. - -The tricky thing here is that a leap year in the Gregorian calendar occurs: - -```text -on every year that is evenly divisible by 4 - except every year that is evenly divisible by 100 - unless the year is also evenly divisible by 400 -``` - -For example, 1997 is not a leap year, but 1996 is. -1900 is not a leap year, but 2000 is. - -## Notes - -Though our exercise adopts some very simple rules, there is more to learn! - -For a delightful, four minute explanation of the whole leap year phenomenon, go watch [this youtube video][video]. - -[video]: https://www.youtube.com/watch?v=xX96xng7sAE +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 index b9dc2928..960964d1 100644 --- a/exercises/practice/leap/.meta/config.json +++ b/exercises/practice/leap/.meta/config.json @@ -16,7 +16,7 @@ ".meta/example.el" ] }, - "blurb": "Given a year, report if it is a leap year.", + "blurb": "Determine whether a given year is a leap year.", "source": "CodeRanch Cattle Drive, Assignment 3", - "source_url": "https://coderanch.com/t/718816/Leap" + "source_url": "https://web.archive.org/web/20240907033714/https://coderanch.com/t/718816/Leap" } diff --git a/exercises/practice/linked-list/.docs/instructions.md b/exercises/practice/linked-list/.docs/instructions.md new file mode 100644 index 00000000..edf4055b --- /dev/null +++ b/exercises/practice/linked-list/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your team has decided to use a doubly linked list to represent each train route in the schedule. +Each station along the train's route will be represented by a node in the linked list. + +You don't need to worry about arrival and departure times at the stations. +Each station will simply be represented by a number. + +Routes can be extended, adding stations to the beginning or end of a route. +They can also be shortened by removing stations from the beginning or the end of a route. + +Sometimes a station gets closed down, and in that case the station needs to be removed from the route, even if it is not at the beginning or end of the route. + +The size of a route is measured not by how far the train travels, but by how many stations it stops at. + +~~~~exercism/note +The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures. +As the name suggests, it is a list of nodes that are linked together. +It is a list of "nodes", where each node links to its neighbor or neighbors. +In a **singly linked list** each node links only to the node that follows it. +In a **doubly linked list** each node links to both the node that comes before, as well as the node that comes after. + +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/linked-list/.docs/introduction.md b/exercises/practice/linked-list/.docs/introduction.md new file mode 100644 index 00000000..6e83ae7b --- /dev/null +++ b/exercises/practice/linked-list/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +You are working on a project to develop a train scheduling system for a busy railway network. + +You've been asked to develop a prototype for the train routes in the scheduling system. +Each route consists of a sequence of train stations that a given train stops at. diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json new file mode 100644 index 00000000..f914bcf2 --- /dev/null +++ b/exercises/practice/linked-list/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "linked-list.el" + ], + "test": [ + "linked-list-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement a doubly linked list.", + "source": "Classic computer science topic" +} diff --git a/exercises/practice/linked-list/.meta/example.el b/exercises/practice/linked-list/.meta/example.el new file mode 100644 index 00000000..566e0ebb --- /dev/null +++ b/exercises/practice/linked-list/.meta/example.el @@ -0,0 +1,88 @@ +;;; linked-list.el --- Linked List (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'cl-lib) + + +(cl-defstruct node + value prev next) + + +(cl-defstruct dll + head tail) + + +(defun dll-create () + (make-dll :head nil :tail nil)) + + +(defun dll-push (list value) + (let ((new-node (make-node :value value :prev (dll-tail list) :next nil))) + (if (dll-tail list) + (setf (node-next (dll-tail list)) new-node) + (setf (dll-head list) new-node)) + (setf (dll-tail list) new-node))) + + +(defun dll-pop (list) + (when (dll-tail list) + (let ((value (node-value (dll-tail list))) + (new-tail (node-prev (dll-tail list)))) + (if new-tail + (setf (node-next new-tail) nil) + (setf (dll-head list) nil)) + (setf (dll-tail list) new-tail) + value))) + + +(defun dll-unshift (list value) + (let ((new-node (make-node :value value :prev nil :next (dll-head list)))) + (if (dll-head list) + (setf (node-prev (dll-head list)) new-node) + (setf (dll-tail list) new-node)) + (setf (dll-head list) new-node))) + + +(defun dll-shift (list) + (when (dll-head list) + (let ((value (node-value (dll-head list))) + (new-head (node-next (dll-head list)))) + (if new-head + (setf (node-prev new-head) nil) + (setf (dll-tail list) nil)) + (setf (dll-head list) new-head) + value))) + + +(defun dll-count (list) + (let ((count 0) + (current (dll-head list))) + (while current + (setq count (1+ count)) + (setq current (node-next current))) + count)) + + +(defun dll-delete (list value) + (let ((current (dll-head list))) + (while current + (if (equal (node-value current) value) + (progn + (let ((prev-node (node-prev current)) + (next-node (node-next current))) + (if prev-node + (setf (node-next prev-node) next-node) + (setf (dll-head list) next-node)) + (if next-node + (setf (node-prev next-node) prev-node) + (setf (dll-tail list) prev-node))) + (setq current nil)) + (setq current (node-next current)))))) + + +(provide 'linked-list) +;;; linked-list.el ends here diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml new file mode 100644 index 00000000..96906d2c --- /dev/null +++ b/exercises/practice/linked-list/.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. + +[7f7e3987-b954-41b8-8084-99beca08752c] +description = "pop gets element from the list" + +[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885] +description = "push/pop respectively add/remove at the end of the list" + +[00ea24ce-4f5c-4432-abb4-cc6e85462657] +description = "shift gets an element from the list" + +[37962ee0-3324-4a29-b588-5a4c861e6564] +description = "shift gets first element from the list" + +[30a3586b-e9dc-43fb-9a73-2770cec2c718] +description = "unshift adds element at start of the list" + +[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d] +description = "pop, push, shift, and unshift can be used in any order" + +[88f65c0c-4532-4093-8295-2384fb2f37df] +description = "count an empty list" + +[fc055689-5cbe-4cd9-b994-02e2abbb40a5] +description = "count a list with items" + +[8272cef5-130d-40ea-b7f6-5ffd0790d650] +description = "count is correct after mutation" + +[229b8f7a-bd8a-4798-b64f-0dc0bb356d95] +description = "popping to empty doesn't break the list" + +[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad] +description = "shifting to empty doesn't break the list" + +[e8f7c600-d597-4f79-949d-8ad8bae895a6] +description = "deletes the only element" + +[fd65e422-51f3-45c0-9fd0-c33da638f89b] +description = "deletes the element with the specified value from the list" + +[59db191a-b17f-4ab7-9c5c-60711ec1d013] +description = "deletes the element with the specified value from the list, re-assigns tail" + +[58242222-5d39-415b-951d-8128247f8993] +description = "deletes the element with the specified value from the list, re-assigns head" + +[ee3729ee-3405-4bd2-9bad-de0d4aa5d647] +description = "deletes the first of two elements" + +[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb] +description = "deletes the second of two elements" + +[7b420958-f285-4922-b8f9-10d9dcab5179] +description = "delete does not modify the list if the element is not found" + +[7e04828f-6082-44e3-a059-201c63252a76] +description = "deletes only the first occurrence" diff --git a/exercises/practice/linked-list/linked-list-test.el b/exercises/practice/linked-list/linked-list-test.el new file mode 100644 index 00000000..d58fd786 --- /dev/null +++ b/exercises/practice/linked-list/linked-list-test.el @@ -0,0 +1,193 @@ +;;; linked-list-test.el --- Linked List (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "linked-list.el") +(declare-function dll-create "linked-list.el" ()) +(declare-function dll-push "linked-list.el" (list value)) +(declare-function dll-pop "linked-list.el" (list)) +(declare-function dll-unshift "linked-list.el" (list value)) +(declare-function dll-shift "linked-list.el" (list)) +(declare-function dll-count "linked-list.el" (list)) +(declare-function dll-delete "linked-list.el" (list value)) + + +(ert-deftest pop-gets-element-from-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 7) + (should (= (dll-pop test-list) 7)))) + + +(ert-deftest push/pop-respectively-add/remove-at-the-end-of-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 11) + (dll-push test-list 13) + (should (= (dll-pop test-list) 13)) + (should (= (dll-pop test-list) 11)))) + + +(ert-deftest shift-gets-an-element-from-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 17) + (should (= (dll-shift test-list) 17)))) + + +(ert-deftest shift-gets-first-element-from-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 23) + (dll-push test-list 5) + (should (= (dll-shift test-list) 23)) + (should (= (dll-shift test-list) 5)))) + + +(ert-deftest unshift-adds-element-at-start-of-the-list () + (let ((test-list (dll-create))) + (dll-unshift test-list 23) + (dll-unshift test-list 5) + (should (= (dll-shift test-list) 5)) + (should (= (dll-shift test-list) 23)))) + + +(ert-deftest pop-push-shift-and-unshift-can-be-used-in-any-order () + (let ((test-list (dll-create))) + (dll-push test-list 1) + (dll-push test-list 2) + (should (= (dll-pop test-list) 2)) + (dll-push test-list 3) + (should (= (dll-shift test-list) 1)) + (dll-unshift test-list 4) + (dll-push test-list 5) + (should (= (dll-shift test-list) 4)) + (should (= (dll-pop test-list) 5)) + (should (= (dll-shift test-list) 3)))) + + +(ert-deftest count-an-empty-list () + (let ((test-list (dll-create))) + (should (= (dll-count test-list) 0)))) + + +(ert-deftest count-a-list-with-items () + (let ((test-list (dll-create))) + (dll-push test-list 37) + (dll-push test-list 1) + (should (= (dll-count test-list) 2)))) + + +(ert-deftest count-is-correct-after-mutation () + (let ((test-list (dll-create))) + (dll-push test-list 31) + (should (= (dll-count test-list) 1)) + (dll-unshift test-list 43) + (should (= (dll-count test-list) 2)) + (dll-shift test-list) + (should (= (dll-count test-list) 1)) + (dll-pop test-list) + (should (= (dll-count test-list) 0)))) + + +(ert-deftest popping-to-empty-does-not-break-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 41) + (dll-push test-list 59) + (dll-pop test-list) + (dll-pop test-list) + (dll-push test-list 47) + (should (= (dll-count test-list) 1)) + (should (= (dll-pop test-list) 47)))) + + +(ert-deftest shifting-to-empty-does-not-break-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 41) + (dll-push test-list 59) + (dll-shift test-list) + (dll-shift test-list) + (dll-push test-list 47) + (should (= (dll-count test-list) 1)) + (should (= (dll-shift test-list) 47)))) + + +(ert-deftest deletes-the-only-element () + (let ((test-list (dll-create))) + (dll-push test-list 61) + (dll-delete test-list 61) + (should (= (dll-count test-list) 0)))) + + +(ert-deftest deletes-the-element-with-the-specified-value-from-the-list () + (let ((test-list (dll-create))) + (dll-push test-list 71) + (dll-push test-list 83) + (dll-push test-list 79) + (dll-delete test-list 83) + (should (= (dll-count test-list) 2)) + (should (= (dll-pop test-list) 79)) + (should (= (dll-shift test-list) 71)))) + + +(ert-deftest deletes-the-element-with-the-specified-value-from-the-list-re-assigns-tail () + (let ((test-list (dll-create))) + (dll-push test-list 71) + (dll-push test-list 83) + (dll-push test-list 79) + (dll-delete test-list 83) + (should (= (dll-count test-list) 2)) + (should (= (dll-pop test-list) 79)) + (should (= (dll-pop test-list) 71)))) + + +(ert-deftest deletes-the-element-with-the-specified-value-from-the-list-re-assigns-head () + (let ((test-list (dll-create))) + (dll-push test-list 71) + (dll-push test-list 83) + (dll-push test-list 79) + (dll-delete test-list 83) + (should (= (dll-count test-list) 2)) + (should (= (dll-shift test-list) 71)) + (should (= (dll-shift test-list) 79)))) + + +(ert-deftest deletes-the-first-of-two-elements () + (let ((test-list (dll-create))) + (dll-push test-list 97) + (dll-push test-list 101) + (dll-delete test-list 97) + (should (= (dll-count test-list) 1)) + (should (= (dll-pop test-list) 101)))) + + +(ert-deftest deletes-the-second-of-two-elements () + (let ((test-list (dll-create))) + (dll-push test-list 97) + (dll-push test-list 101) + (dll-delete test-list 101) + (should (= (dll-count test-list) 1)) + (should (= (dll-pop test-list) 97)))) + + +(ert-deftest delete-does-not-modify-the-list-if-the-element-is-not-found () + (let ((test-list (dll-create))) + (dll-push test-list 89) + (dll-delete test-list 103) + (should (= (dll-count test-list) 1)))) + + +(ert-deftest deletes-only-the-first-occurrence () + (let ((test-list (dll-create))) + (dll-push test-list 73) + (dll-push test-list 9) + (dll-push test-list 9) + (dll-push test-list 107) + (dll-delete test-list 9) + (should (= (dll-count test-list) 3)) + (should (= (dll-pop test-list) 107)) + (should (= (dll-pop test-list) 9)) + (should (= (dll-pop test-list) 73)))) + + +(provide 'linked-list-test) +;;; linked-list-test.el ends here diff --git a/exercises/practice/linked-list/linked-list.el b/exercises/practice/linked-list/linked-list.el new file mode 100644 index 00000000..4ad78995 --- /dev/null +++ b/exercises/practice/linked-list/linked-list.el @@ -0,0 +1,37 @@ +;;; linked-list.el --- Linked List (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun dll-create () + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-push (list value) + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-pop (list) + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-unshift (list value) + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-shift (list) + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-count (list) + (error "Delete this S-Expression and write your own implementation")) + + +(defun dll-delete (list value) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'linked-list) +;;; linked-list.el ends here diff --git a/exercises/practice/list-ops/.docs/instructions.append.md b/exercises/practice/list-ops/.docs/instructions.append.md index 92400777..771c11e5 100644 --- a/exercises/practice/list-ops/.docs/instructions.append.md +++ b/exercises/practice/list-ops/.docs/instructions.append.md @@ -1,4 +1,4 @@ -# Emacs Lisp Track specific functions +## Emacs Lisp track specific functions * `list-empty-p` (*given a list, return if the list is empty*) * `list-sum` (*given a list of numbers, return the sum of all elements*) diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md index d3453338..ebc5dffe 100644 --- a/exercises/practice/list-ops/.docs/instructions.md +++ b/exercises/practice/list-ops/.docs/instructions.md @@ -7,11 +7,13 @@ 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 using `function(accumulator, item)`*); -- `foldr` (*given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right using `function(item, accumulator)`*); -- `reverse` (*given a list, return a list with all the original items, but in reversed order*); +- `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/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md index 8cbe791f..df2e304a 100644 --- a/exercises/practice/luhn/.docs/instructions.md +++ b/exercises/practice/luhn/.docs/instructions.md @@ -1,64 +1,68 @@ # Instructions -Given a number determine whether or not it is valid per the Luhn formula. +Determine whether a number is valid according to the [Luhn formula][luhn]. -The [Luhn algorithm][luhn] is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. +The number will be provided as a string. -The task is to check if a given string is valid. - -## Validating a Number +## 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. -### Example 1: valid credit card number +## Examples -```text -4539 3195 0343 6467 -``` +### Valid credit card number -The first step of the Luhn algorithm is to double every second digit, starting from the right. -We will be doubling +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 -4_3_ 3_9_ 0_4_ 6_6_ +4539 3195 0343 6467 +↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these) ``` -If doubling the number results in a number greater than 9 then subtract 9 from the product. -The results of our doubling: +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 ``` -Then sum all of the digits: +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 +8 + 5 + 6 + 9 + 6 + 1 + 9 + 5 + 0 + 3 + 8 + 3 + 3 + 4 + 3 + 7 = 80 ``` -If the sum is evenly divisible by 10, then the number is valid. -This number is valid! +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 468`. -### Example 2: invalid credit card number +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 -8273 1232 7352 0569 +066 123 478 + ↑ ↑ ↑ ↑ (double these) ``` -Double the second digits, starting from the right +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: ```text -7253 2262 5312 0539 +036 226 458 ``` -Sum the digits +We sum the digits: ```text -7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57 +0 + 3 + 6 + 2 + 2 + 6 + 4 + 5 + 8 = 36 ``` -57 is not evenly divisible by 10, so this number is not valid. +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/tests.toml b/exercises/practice/luhn/.meta/tests.toml index c0be0c4d..98626ea0 100644 --- a/exercises/practice/luhn/.meta/tests.toml +++ b/exercises/practice/luhn/.meta/tests.toml @@ -68,9 +68,12 @@ 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" +include = false [f94cf191-a62f-4868-bc72-7253114aa157] description = "using ascii value for doubled non-digit isn't allowed" +include = false [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" +include = false diff --git a/exercises/practice/luhn/luhn.el b/exercises/practice/luhn/luhn.el index 039007f2..5801251f 100644 --- a/exercises/practice/luhn/luhn.el +++ b/exercises/practice/luhn/luhn.el @@ -1,10 +1,14 @@ -;;; luhn.el --- Luhn exercise (exercism) -*- lexical-binding: t; -*- +;;; luhn.el --- Luhn (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -(defun luhn-p (str) ;;; Code: -) + + +(defun luhn-p (str) + (error "Delete this S-Expression and write your own implementation")) + (provide 'luhn) ;;; luhn.el ends here + diff --git a/exercises/practice/matching-brackets/.docs/instructions.md b/exercises/practice/matching-brackets/.docs/instructions.md new file mode 100644 index 00000000..ea170842 --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/instructions.md @@ -0,0 +1,5 @@ +# Instructions + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly. +Any other characters should be ignored. +For example, `"{what is (42)}?"` is balanced and `"[text}"` is not. diff --git a/exercises/practice/matching-brackets/.docs/introduction.md b/exercises/practice/matching-brackets/.docs/introduction.md new file mode 100644 index 00000000..0618221b --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You're given the opportunity to write software for the Bracketeer™, an ancient but powerful mainframe. +The software that runs on it is written in a proprietary language. +Much of its syntax is familiar, but you notice _lots_ of brackets, braces and parentheses. +Despite the Bracketeer™ being powerful, it lacks flexibility. +If the source code has any unbalanced brackets, braces or parentheses, the Bracketeer™ crashes and must be rebooted. +To avoid such a scenario, you start writing code that can verify that brackets, braces, and parentheses are balanced before attempting to run it on the Bracketeer™. diff --git a/exercises/practice/matching-brackets/.meta/config.json b/exercises/practice/matching-brackets/.meta/config.json new file mode 100644 index 00000000..c33a2fd3 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "matching-brackets.el" + ], + "test": [ + "matching-brackets-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Make sure the brackets and braces all match.", + "source": "Ginna Baker" +} diff --git a/exercises/practice/matching-brackets/.meta/example.el b/exercises/practice/matching-brackets/.meta/example.el new file mode 100644 index 00000000..8911d25a --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/example.el @@ -0,0 +1,43 @@ +;;; matching-brackets.el --- Matching Brackets (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'seq) + +(defun is-paired (value) + (let ((brackets '())) + (and + (seq-every-p + (lambda (char) + (cond + ((= char ?\() + (push ?\) brackets)) + ((= char ?\[) + (push ?\] brackets)) + ((= char ?{) + (push ?} brackets)) + ((= char ?\)) + ;; need to use equal because = doesn't accept nil + (if (equal (car brackets) ?\)) + ;; make sure return is true when brackets gets set to nil / empty list + (or (setq brackets (cdr brackets)) t) + nil)) + ((= char ?\]) + (if (equal (car brackets) ?\]) + (or (setq brackets (cdr brackets)) t) + nil)) + ((= char ?}) + (if (equal (car brackets) ?}) + (or (setq brackets (cdr brackets)) t) + nil)) + (t + t))) + value) + (seq-empty-p brackets)))) + + +(provide 'matching-brackets) +;;; matching-brackets.el ends here diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml new file mode 100644 index 00000000..35a98a04 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/tests.toml @@ -0,0 +1,70 @@ +# 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. + +[81ec11da-38dd-442a-bcf9-3de7754609a5] +description = "paired square brackets" + +[287f0167-ac60-4b64-8452-a0aa8f4e5238] +description = "empty string" + +[6c3615a3-df01-4130-a731-8ef5f5d78dac] +description = "unpaired brackets" + +[9d414171-9b98-4cac-a4e5-941039a97a77] +description = "wrong ordered brackets" + +[f0f97c94-a149-4736-bc61-f2c5148ffb85] +description = "wrong closing bracket" + +[754468e0-4696-4582-a30e-534d47d69756] +description = "paired with whitespace" + +[ba84f6ee-8164-434a-9c3e-b02c7f8e8545] +description = "partially paired brackets" + +[3c86c897-5ff3-4a2b-ad9b-47ac3a30651d] +description = "simple nested brackets" + +[2d137f2c-a19e-4993-9830-83967a2d4726] +description = "several paired brackets" + +[2e1f7b56-c137-4c92-9781-958638885a44] +description = "paired and nested brackets" + +[84f6233b-e0f7-4077-8966-8085d295c19b] +description = "unopened closing brackets" + +[9b18c67d-7595-4982-b2c5-4cb949745d49] +description = "unpaired and nested brackets" + +[a0205e34-c2ac-49e6-a88a-899508d7d68e] +description = "paired and wrong nested brackets" + +[1d5c093f-fc84-41fb-8c2a-e052f9581602] +description = "paired and wrong nested brackets but innermost are correct" + +[ef47c21b-bcfd-4998-844c-7ad5daad90a8] +description = "paired and incomplete brackets" + +[a4675a40-a8be-4fc2-bc47-2a282ce6edbe] +description = "too many closing brackets" + +[a345a753-d889-4b7e-99ae-34ac85910d1a] +description = "early unexpected brackets" + +[21f81d61-1608-465a-b850-baa44c5def83] +description = "early mismatched brackets" + +[99255f93-261b-4435-a352-02bdecc9bdf2] +description = "math expression" + +[8e357d79-f302-469a-8515-2561877256a1] +description = "complex latex expression" diff --git a/exercises/practice/matching-brackets/matching-brackets-test.el b/exercises/practice/matching-brackets/matching-brackets-test.el new file mode 100644 index 00000000..2d94d4a5 --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets-test.el @@ -0,0 +1,97 @@ +;;; matching-brackets-test.el --- Matching Brackets (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "matching-brackets.el") +(declare-function is-paired "matching-brackets.el" (value)) + + +(ert-deftest paired-square-brackets () + (should (is-paired "[]"))) + + +(ert-deftest empty-string () + (should (is-paired "[]"))) + + +(ert-deftest unpaired-brackets () + (should (not (is-paired "[[")))) + + +(ert-deftest wrong-ordered-brackets () + (should (not (is-paired "}{")))) + + +(ert-deftest wrong-closing-bracket () + (should (not (is-paired "{]")))) + + +(ert-deftest paired-with-whitespace () + (should (is-paired "{ }"))) + + +(ert-deftest partially-paired-brackets () + (should (not (is-paired "{[])")))) + + +(ert-deftest simple-nested-brackets () + (should (is-paired "{[]}"))) + + +(ert-deftest several-paired-brackets () + (should (is-paired "{}[]"))) + + +(ert-deftest paired-and-nested-brackets () + (should (is-paired "[{}({}[])]"))) + + +(ert-deftest unopened-closing-brackets () + (should (not (is-paired "{[)][]}")))) + + +(ert-deftest unpaired-and-nested-brackets () + (should (not (is-paired "({])")))) + + +(ert-deftest paired-and-wrong-nested-brackets () + (should (not (is-paired "[({]})")))) + + +(ert-deftest + paired-and-wrong-nested-brackets-but-innermost-are-correct + () + (should (not (is-paired "[({}])")))) + + +(ert-deftest paired-and-incomplete-brackets () + (should (not (is-paired "{}[")))) + + +(ert-deftest too-many-closing-brackets () + (should (not (is-paired "[]]")))) + + +(ert-deftest early-unexpected-brackets () + (should (not (is-paired ")()")))) + + +(ert-deftest early-mismatched-brackets () + (should (not (is-paired "{)()")))) + + +(ert-deftest math-expression () + (should (is-paired "(((185 + 223.85) * 15) - 543)/2"))) + + +(ert-deftest complex-latex-expression () + (should + (is-paired + "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)"))) + + +(provide 'matching-brackets-test) +;;; matching-brackets-test.el ends here diff --git a/exercises/practice/matching-brackets/matching-brackets.el b/exercises/practice/matching-brackets/matching-brackets.el new file mode 100644 index 00000000..a9ba2438 --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets.el @@ -0,0 +1,14 @@ +;;; matching-brackets.el --- Matching Brackets (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun is-paired (value) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'matching-brackets) +;;; matching-brackets.el ends here diff --git a/exercises/practice/meetup/.docs/instructions.md b/exercises/practice/meetup/.docs/instructions.md new file mode 100644 index 00000000..8b1bda5e --- /dev/null +++ b/exercises/practice/meetup/.docs/instructions.md @@ -0,0 +1,34 @@ +# Instructions + +Your task is to find the exact date of a meetup, given a month, year, weekday and week. + +There are six week values to consider: `first`, `second`, `third`, `fourth`, `last`, `teenth`. + +For example, you might be asked to find the date for the meetup on the first Monday in January 2018 (January 1, 2018). + +Similarly, you might be asked to find: + +- the third Tuesday of August 2019 (August 20, 2019) +- the teenth Wednesday of May 2020 (May 13, 2020) +- the fourth Sunday of July 2021 (July 25, 2021) +- the last Thursday of November 2022 (November 24, 2022) +- the teenth Saturday of August 1953 (August 15, 1953) + +## Teenth + +The teenth week refers to the seven days in a month that end in '-teenth' (13th, 14th, 15th, 16th, 17th, 18th and 19th). + +If asked to find the teenth Saturday of August, 1953, we check its calendar: + +```plaintext + August 1953 +Su Mo Tu We Th Fr Sa + 1 + 2 3 4 5 6 7 8 + 9 10 11 12 13 14 15 +16 17 18 19 20 21 22 +23 24 25 26 27 28 29 +30 31 +``` + +From this we find that the teenth Saturday is August 15, 1953. diff --git a/exercises/practice/meetup/.docs/introduction.md b/exercises/practice/meetup/.docs/introduction.md new file mode 100644 index 00000000..29170ef1 --- /dev/null +++ b/exercises/practice/meetup/.docs/introduction.md @@ -0,0 +1,29 @@ +# Introduction + +Every month, your partner meets up with their best friend. +Both of them have very busy schedules, making it challenging to find a suitable date! +Given your own busy schedule, your partner always double-checks potential meetup dates with you: + +- "Can I meet up on the first Friday of next month?" +- "What about the third Wednesday?" +- "Maybe the last Sunday?" + +In this month's call, your partner asked you this question: + +- "I'd like to meet up on the teenth Thursday; is that okay?" + +Confused, you ask what a "teenth" day is. +Your partner explains that a teenth day, a concept they made up, refers to the days in a month that end in '-teenth': + +- 13th (thirteenth) +- 14th (fourteenth) +- 15th (fifteenth) +- 16th (sixteenth) +- 17th (seventeenth) +- 18th (eighteenth) +- 19th (nineteenth) + +As there are also seven weekdays, it is guaranteed that each day of the week has _exactly one_ teenth day each month. + +Now that you understand the concept of a teenth day, you check your calendar. +You don't have anything planned on the teenth Thursday, so you happily confirm the date with your partner. diff --git a/exercises/practice/meetup/.meta/config.json b/exercises/practice/meetup/.meta/config.json new file mode 100644 index 00000000..b1ca414e --- /dev/null +++ b/exercises/practice/meetup/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "meetup.el" + ], + "test": [ + "meetup-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Calculate the date of meetups.", + "source": "Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month" +} diff --git a/exercises/practice/meetup/.meta/example.el b/exercises/practice/meetup/.meta/example.el new file mode 100644 index 00000000..5ceeb9c0 --- /dev/null +++ b/exercises/practice/meetup/.meta/example.el @@ -0,0 +1,61 @@ +;;; meetup.el --- Meetup (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'calendar) + +(defconst +days-of-the-week+ + '((:sunday . 0) + (:monday . 1) + (:tuesday . 2) + (:wednesday . 3) + (:thursday . 4) + (:friday . 5) + (:saturday . 6))) + +(defconst +schedules+ + '((:first . 1) + (:second . 8) + (:third . 15) + (:teenth . 13) + (:fourth . 22) + (:last . -7))) + +(defun last-day-of (month year) + (calendar-last-day-of-month month year)) + +(defun find-dow-near-date (target-dow start-day month year direction) + (let* ((direction-factor (if (eq direction :before) -1 1)) + (current-day start-day) + (found-day nil)) + (while (and (not found-day) + (<= 1 current-day (last-day-of month year))) + (let ((current-dow (calendar-day-of-week (list month current-day year)))) + (when (= current-dow target-dow) + (setq found-day current-day))) + (setq current-day (+ current-day direction-factor))) + (list year month found-day))) + +(defun meetup (year month dayofweek schedule) + (let* ((target-dow (cdr (assoc dayofweek +days-of-the-week+))) + (first-day-of-month (calendar-day-of-week (list month 1 year))) + (schedule-offset (cdr (assoc schedule +schedules+))) + (first-target-dow (+ 1 (- target-dow first-day-of-month))) + (first-occurrence (if (>= first-target-dow 1) + first-target-dow + (+ first-target-dow 7))) + (start-day (cond + ((eq schedule :last) + (last-day-of month year)) + ((eq schedule :teenth) + 13) + (t + (+ first-occurrence (* (/ (1- schedule-offset) 7) 7)))))) + (if (eq schedule :last) + (find-dow-near-date target-dow start-day month year :before) + (find-dow-near-date target-dow start-day month year :after)))) + +(provide 'meetup) +;;; meetup.el ends here diff --git a/exercises/practice/meetup/.meta/tests.toml b/exercises/practice/meetup/.meta/tests.toml new file mode 100644 index 00000000..1e5b84d0 --- /dev/null +++ b/exercises/practice/meetup/.meta/tests.toml @@ -0,0 +1,295 @@ +# 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. + +[d7f8eadd-d4fc-46ee-8a20-e97bd3fd01c8] +description = "when teenth Monday is the 13th, the first day of the teenth week" + +[f78373d1-cd53-4a7f-9d37-e15bf8a456b4] +description = "when teenth Monday is the 19th, the last day of the teenth week" + +[8c78bea7-a116-425b-9c6b-c9898266d92a] +description = "when teenth Monday is some day in the middle of the teenth week" + +[cfef881b-9dc9-4d0b-8de4-82d0f39fc271] +description = "when teenth Tuesday is the 19th, the last day of the teenth week" + +[69048961-3b00-41f9-97ee-eb6d83a8e92b] +description = "when teenth Tuesday is some day in the middle of the teenth week" + +[d30bade8-3622-466a-b7be-587414e0caa6] +description = "when teenth Tuesday is the 13th, the first day of the teenth week" + +[8db4b58b-92f3-4687-867b-82ee1a04f851] +description = "when teenth Wednesday is some day in the middle of the teenth week" + +[6c27a2a2-28f8-487f-ae81-35d08c4664f7] +description = "when teenth Wednesday is the 13th, the first day of the teenth week" + +[008a8674-1958-45b5-b8e6-c2c9960d973a] +description = "when teenth Wednesday is the 19th, the last day of the teenth week" + +[e4abd5e3-57cb-4091-8420-d97e955c0dbd] +description = "when teenth Thursday is some day in the middle of the teenth week" + +[85da0b0f-eace-4297-a6dd-63588d5055b4] +description = "when teenth Thursday is the 13th, the first day of the teenth week" + +[ecf64f9b-8413-489b-bf6e-128045f70bcc] +description = "when teenth Thursday is the 19th, the last day of the teenth week" + +[ac4e180c-7d0a-4d3d-b05f-f564ebb584ca] +description = "when teenth Friday is the 19th, the last day of the teenth week" + +[b79101c7-83ad-4f8f-8ec8-591683296315] +description = "when teenth Friday is some day in the middle of the teenth week" + +[6ed38b9f-0072-4901-bd97-7c8b8b0ef1b8] +description = "when teenth Friday is the 13th, the first day of the teenth week" + +[dfae03ed-9610-47de-a632-655ab01e1e7c] +description = "when teenth Saturday is some day in the middle of the teenth week" + +[ec02e3e1-fc72-4a3c-872f-a53fa8ab358e] +description = "when teenth Saturday is the 13th, the first day of the teenth week" + +[d983094b-7259-4195-b84e-5d09578c89d9] +description = "when teenth Saturday is the 19th, the last day of the teenth week" + +[d84a2a2e-f745-443a-9368-30051be60c2e] +description = "when teenth Sunday is the 19th, the last day of the teenth week" + +[0e64bc53-92a3-4f61-85b2-0b7168c7ce5a] +description = "when teenth Sunday is some day in the middle of the teenth week" + +[de87652c-185e-4854-b3ae-04cf6150eead] +description = "when teenth Sunday is the 13th, the first day of the teenth week" + +[2cbfd0f5-ba3a-46da-a8cc-0fe4966d3411] +description = "when first Monday is some day in the middle of the first week" + +[a6168c7c-ed95-4bb3-8f92-c72575fc64b0] +description = "when first Monday is the 1st, the first day of the first week" + +[1bfc620f-1c54-4bbd-931f-4a1cd1036c20] +description = "when first Tuesday is the 7th, the last day of the first week" + +[12959c10-7362-4ca0-a048-50cf1c06e3e2] +description = "when first Tuesday is some day in the middle of the first week" + +[1033dc66-8d0b-48a1-90cb-270703d59d1d] +description = "when first Wednesday is some day in the middle of the first week" + +[b89185b9-2f32-46f4-a602-de20b09058f6] +description = "when first Wednesday is the 7th, the last day of the first week" + +[53aedc4d-b2c8-4dfb-abf7-a8dc9cdceed5] +description = "when first Thursday is some day in the middle of the first week" + +[b420a7e3-a94c-4226-870a-9eb3a92647f0] +description = "when first Thursday is another day in the middle of the first week" + +[61df3270-28b4-4713-bee2-566fa27302ca] +description = "when first Friday is the 1st, the first day of the first week" + +[cad33d4d-595c-412f-85cf-3874c6e07abf] +description = "when first Friday is some day in the middle of the first week" + +[a2869b52-5bba-44f0-a863-07bd1f67eadb] +description = "when first Saturday is some day in the middle of the first week" + +[3585315a-d0db-4ea1-822e-0f22e2a645f5] +description = "when first Saturday is another day in the middle of the first week" + +[c49e9bd9-8ccf-4cf2-947a-0ccd4e4f10b1] +description = "when first Sunday is some day in the middle of the first week" + +[1513328b-df53-4714-8677-df68c4f9366c] +description = "when first Sunday is the 7th, the last day of the first week" + +[49e083af-47ec-4018-b807-62ef411efed7] +description = "when second Monday is some day in the middle of the second week" + +[6cb79a73-38fe-4475-9101-9eec36cf79e5] +description = "when second Monday is the 8th, the first day of the second week" + +[4c39b594-af7e-4445-aa03-bf4f8effd9a1] +description = "when second Tuesday is the 14th, the last day of the second week" + +[41b32c34-2e39-40e3-b790-93539aaeb6dd] +description = "when second Tuesday is some day in the middle of the second week" + +[90a160c5-b5d9-4831-927f-63a78b17843d] +description = "when second Wednesday is some day in the middle of the second week" + +[23b98ce7-8dd5-41a1-9310-ef27209741cb] +description = "when second Wednesday is the 14th, the last day of the second week" + +[447f1960-27ca-4729-bc3f-f36043f43ed0] +description = "when second Thursday is some day in the middle of the second week" + +[c9aa2687-300c-4e79-86ca-077849a81bde] +description = "when second Thursday is another day in the middle of the second week" + +[a7e11ef3-6625-4134-acda-3e7195421c09] +description = "when second Friday is the 8th, the first day of the second week" + +[8b420e5f-9290-4106-b5ae-022f3e2a3e41] +description = "when second Friday is some day in the middle of the second week" + +[80631afc-fc11-4546-8b5f-c12aaeb72b4f] +description = "when second Saturday is some day in the middle of the second week" + +[e34d43ac-f470-44c2-aa5f-e97b78ecaf83] +description = "when second Saturday is another day in the middle of the second week" + +[a57d59fd-1023-47ad-b0df-a6feb21b44fc] +description = "when second Sunday is some day in the middle of the second week" + +[a829a8b0-abdd-4ad1-b66c-5560d843c91a] +description = "when second Sunday is the 14th, the last day of the second week" + +[501a8a77-6038-4fc0-b74c-33634906c29d] +description = "when third Monday is some day in the middle of the third week" + +[49e4516e-cf32-4a58-8bbc-494b7e851c92] +description = "when third Monday is the 15th, the first day of the third week" + +[4db61095-f7c7-493c-85f1-9996ad3012c7] +description = "when third Tuesday is the 21st, the last day of the third week" + +[714fc2e3-58d0-4b91-90fd-61eefd2892c0] +description = "when third Tuesday is some day in the middle of the third week" + +[b08a051a-2c80-445b-9b0e-524171a166d1] +description = "when third Wednesday is some day in the middle of the third week" + +[80bb9eff-3905-4c61-8dc9-bb03016d8ff8] +description = "when third Wednesday is the 21st, the last day of the third week" + +[fa52a299-f77f-4784-b290-ba9189fbd9c9] +description = "when third Thursday is some day in the middle of the third week" + +[f74b1bc6-cc5c-4bf1-ba69-c554a969eb38] +description = "when third Thursday is another day in the middle of the third week" + +[8900f3b0-801a-466b-a866-f42d64667abd] +description = "when third Friday is the 15th, the first day of the third week" + +[538ac405-a091-4314-9ccd-920c4e38e85e] +description = "when third Friday is some day in the middle of the third week" + +[244db35c-2716-4fa0-88ce-afd58e5cf910] +description = "when third Saturday is some day in the middle of the third week" + +[dd28544f-f8fa-4f06-9bcd-0ad46ce68e9e] +description = "when third Saturday is another day in the middle of the third week" + +[be71dcc6-00d2-4b53-a369-cbfae55b312f] +description = "when third Sunday is some day in the middle of the third week" + +[b7d2da84-4290-4ee6-a618-ee124ae78be7] +description = "when third Sunday is the 21st, the last day of the third week" + +[4276dc06-a1bd-4fc2-b6c2-625fee90bc88] +description = "when fourth Monday is some day in the middle of the fourth week" + +[ddbd7976-2deb-4250-8a38-925ac1a8e9a2] +description = "when fourth Monday is the 22nd, the first day of the fourth week" + +[eb714ef4-1656-47cc-913c-844dba4ebddd] +description = "when fourth Tuesday is the 28th, the last day of the fourth week" + +[16648435-7937-4d2d-b118-c3e38fd084bd] +description = "when fourth Tuesday is some day in the middle of the fourth week" + +[de062bdc-9484-437a-a8c5-5253c6f6785a] +description = "when fourth Wednesday is some day in the middle of the fourth week" + +[c2ce6821-169c-4832-8d37-690ef5d9514a] +description = "when fourth Wednesday is the 28th, the last day of the fourth week" + +[d462c631-2894-4391-a8e3-dbb98b7a7303] +description = "when fourth Thursday is some day in the middle of the fourth week" + +[9ff1f7b6-1b72-427d-9ee9-82b5bb08b835] +description = "when fourth Thursday is another day in the middle of the fourth week" + +[83bae8ba-1c49-49bc-b632-b7c7e1d7e35f] +description = "when fourth Friday is the 22nd, the first day of the fourth week" + +[de752d2a-a95e-48d2-835b-93363dac3710] +description = "when fourth Friday is some day in the middle of the fourth week" + +[eedd90ad-d581-45db-8312-4c6dcf9cf560] +description = "when fourth Saturday is some day in the middle of the fourth week" + +[669fedcd-912e-48c7-a0a1-228b34af91d0] +description = "when fourth Saturday is another day in the middle of the fourth week" + +[648e3849-ea49-44a5-a8a3-9f2a43b3bf1b] +description = "when fourth Sunday is some day in the middle of the fourth week" + +[f81321b3-99ab-4db6-9267-69c5da5a7823] +description = "when fourth Sunday is the 28th, the last day of the fourth week" + +[1af5e51f-5488-4548-aee8-11d7d4a730dc] +description = "last Monday in a month with four Mondays" + +[f29999f2-235e-4ec7-9dab-26f137146526] +description = "last Monday in a month with five Mondays" + +[31b097a0-508e-48ac-bf8a-f63cdcf6dc41] +description = "last Tuesday in a month with four Tuesdays" + +[8c022150-0bb5-4a1f-80f9-88b2e2abcba4] +description = "last Tuesday in another month with four Tuesdays" + +[0e762194-672a-4bdf-8a37-1e59fdacef12] +description = "last Wednesday in a month with five Wednesdays" + +[5016386a-f24e-4bd7-b439-95358f491b66] +description = "last Wednesday in a month with four Wednesdays" + +[12ead1a5-cdf9-4192-9a56-2229e93dd149] +description = "last Thursday in a month with four Thursdays" + +[7db89e11-7fbe-4e57-ae3c-0f327fbd7cc7] +description = "last Thursday in a month with five Thursdays" + +[e47a739e-b979-460d-9c8a-75c35ca2290b] +description = "last Friday in a month with five Fridays" + +[5bed5aa9-a57a-4e5d-8997-2cc796a5b0ec] +description = "last Friday in a month with four Fridays" + +[61e54cba-76f3-4772-a2b1-bf443fda2137] +description = "last Saturday in a month with four Saturdays" + +[8b6a737b-2fa9-444c-b1a2-80ce7a2ec72f] +description = "last Saturday in another month with four Saturdays" + +[0b63e682-f429-4d19-9809-4a45bd0242dc] +description = "last Sunday in a month with five Sundays" + +[5232307e-d3e3-4afc-8ba6-4084ad987c00] +description = "last Sunday in a month with four Sundays" + +[0bbd48e8-9773-4e81-8e71-b9a51711e3c5] +description = "when last Wednesday in February in a leap year is the 29th" + +[fe0936de-7eee-4a48-88dd-66c07ab1fefc] +description = "last Wednesday in December that is also the last day of the year" + +[2ccf2488-aafc-4671-a24e-2b6effe1b0e2] +description = "when last Sunday in February in a non-leap year is not the 29th" + +[00c3ce9f-cf36-4b70-90d8-92b32be6830e] +description = "when first Friday is the 7th, the last day of the first week" diff --git a/exercises/practice/meetup/meetup-test.el b/exercises/practice/meetup/meetup-test.el new file mode 100644 index 00000000..167857d2 --- /dev/null +++ b/exercises/practice/meetup/meetup-test.el @@ -0,0 +1,393 @@ +;;; meetup-test.el --- Meetup (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "meetup.el") +(declare-function meetup "meetup.el" (year month dayofweek schedule)) + + +(ert-deftest when-teenth-monday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 5 :monday :teenth) '(2013 5 13)))) + + +(ert-deftest when-teenth-monday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 8 :monday :teenth) '(2013 8 19)))) + + +(ert-deftest when-teenth-monday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 9 :monday :teenth) '(2013 9 16)))) + + +(ert-deftest when-teenth-tuesday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 3 :tuesday :teenth) '(2013 3 19)))) + + +(ert-deftest when-teenth-tuesday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 4 :tuesday :teenth) '(2013 4 16)))) + + +(ert-deftest when-teenth-tuesday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 8 :tuesday :teenth) '(2013 8 13)))) + + +(ert-deftest when-teenth-wednesday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 1 :wednesday :teenth) '(2013 1 16)))) + + +(ert-deftest when-teenth-wednesday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 2 :wednesday :teenth) '(2013 2 13)))) + + +(ert-deftest when-teenth-wednesday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 6 :wednesday :teenth) '(2013 6 19)))) + + +(ert-deftest when-teenth-thursday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 5 :thursday :teenth) '(2013 5 16)))) + + +(ert-deftest when-teenth-thursday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 6 :thursday :teenth) '(2013 6 13)))) + + +(ert-deftest when-teenth-thursday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 9 :thursday :teenth) '(2013 9 19)))) + + +(ert-deftest when-teenth-friday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 4 :friday :teenth) '(2013 4 19)))) + + +(ert-deftest when-teenth-friday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 8 :friday :teenth) '(2013 8 16)))) + + +(ert-deftest when-teenth-friday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 9 :friday :teenth) '(2013 9 13)))) + + +(ert-deftest when-teenth-saturday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 2 :saturday :teenth) '(2013 2 16)))) + + +(ert-deftest when-teenth-saturday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 4 :saturday :teenth) '(2013 4 13)))) + + +(ert-deftest when-teenth-saturday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 10 :saturday :teenth) '(2013 10 19)))) + + +(ert-deftest when-teenth-sunday-is-the-19th-the-last-day-of-the-teenth-week () + (should (equal (meetup 2013 5 :sunday :teenth) '(2013 5 19)))) + + +(ert-deftest when-teenth-sunday-is-some-day-in-the-middle-of-the-teenth-week () + (should (equal (meetup 2013 6 :sunday :teenth) '(2013 6 16)))) + + +(ert-deftest when-teenth-sunday-is-the-13th-the-first-day-of-the-teenth-week () + (should (equal (meetup 2013 10 :sunday :teenth) '(2013 10 13)))) + + +(ert-deftest when-first-monday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 3 :monday :first) '(2013 3 4)))) + + +(ert-deftest when-first-monday-is-the-1st-the-first-day-of-the-first-week () + (should (equal (meetup 2013 4 :monday :first) '(2013 4 1)))) + + +(ert-deftest when-first-tuesday-is-the-7th-the-last-day-of-the-first-week () + (should (equal (meetup 2013 5 :tuesday :first) '(2013 5 7)))) + + +(ert-deftest when-first-tuesday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 6 :tuesday :first) '(2013 6 4)))) + + +(ert-deftest when-first-wednesday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 7 :wednesday :first) '(2013 7 3)))) + + +(ert-deftest when-first-wednesday-is-the-7th-the-last-day-of-the-first-week () + (should (equal (meetup 2013 8 :wednesday :first) '(2013 8 7)))) + + +(ert-deftest when-first-thursday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 9 :thursday :first) '(2013 9 5)))) + + +(ert-deftest when-first-thursday-is-another-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 10 :thursday :first) '(2013 10 3)))) + + +(ert-deftest when-first-friday-is-the-1st-the-first-day-of-the-first-week () + (should (equal (meetup 2013 11 :friday :first) '(2013 11 1)))) + + +(ert-deftest when-first-friday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 12 :friday :first) '(2013 12 6)))) + + +(ert-deftest when-first-saturday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 1 :saturday :first) '(2013 1 5)))) + + +(ert-deftest when-first-saturday-is-another-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 2 :saturday :first) '(2013 2 2)))) + + +(ert-deftest when-first-sunday-is-some-day-in-the-middle-of-the-first-week () + (should (equal (meetup 2013 3 :sunday :first) '(2013 3 3)))) + + +(ert-deftest when-first-sunday-is-the-7th-the-last-day-of-the-first-week () + (should (equal (meetup 2013 4 :sunday :first) '(2013 4 7)))) + + +(ert-deftest when-second-monday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 3 :monday :second) '(2013 3 11)))) + + +(ert-deftest when-second-monday-is-the-8th-the-first-day-of-the-second-week () + (should (equal (meetup 2013 4 :monday :second) '(2013 4 8)))) + + +(ert-deftest when-second-tuesday-is-the-14th-the-last-day-of-the-second-week () + (should (equal (meetup 2013 5 :tuesday :second) '(2013 5 14)))) + + +(ert-deftest when-second-tuesday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 6 :tuesday :second) '(2013 6 11)))) + + +(ert-deftest when-second-wednesday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 7 :wednesday :second) '(2013 7 10)))) + + +(ert-deftest when-second-wednesday-is-the-14th-the-last-day-of-the-second-week () + (should (equal (meetup 2013 8 :wednesday :second) '(2013 8 14)))) + + +(ert-deftest when-second-thursday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 9 :thursday :second) '(2013 9 12)))) + + +(ert-deftest when-second-thursday-is-another-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 10 :thursday :second) '(2013 10 10)))) + + +(ert-deftest when-second-friday-is-the-8th-the-first-day-of-the-second-week () + (should (equal (meetup 2013 11 :friday :second) '(2013 11 8)))) + + +(ert-deftest when-second-friday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 12 :friday :second) '(2013 12 13)))) + + +(ert-deftest when-second-saturday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 1 :saturday :second) '(2013 1 12)))) + + +(ert-deftest when-second-saturday-is-another-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 2 :saturday :second) '(2013 2 9)))) + + +(ert-deftest when-second-sunday-is-some-day-in-the-middle-of-the-second-week () + (should (equal (meetup 2013 3 :sunday :second) '(2013 3 10)))) + + +(ert-deftest when-second-sunday-is-the-14th-the-last-day-of-the-second-week () + (should (equal (meetup 2013 4 :sunday :second) '(2013 4 14)))) + + +(ert-deftest when-third-monday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 3 :monday :third) '(2013 3 18)))) + + +(ert-deftest when-third-monday-is-the-15th-the-first-day-of-the-third-week () + (should (equal (meetup 2013 4 :monday :third) '(2013 4 15)))) + + +(ert-deftest when-third-tuesday-is-the-21st-the-last-day-of-the-third-week () + (should (equal (meetup 2013 5 :tuesday :third) '(2013 5 21)))) + + +(ert-deftest when-third-tuesday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 6 :tuesday :third) '(2013 6 18)))) + + +(ert-deftest when-third-wednesday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 7 :wednesday :third) '(2013 7 17)))) + + +(ert-deftest when-third-wednesday-is-the-21st-the-last-day-of-the-third-week () + (should (equal (meetup 2013 8 :wednesday :third) '(2013 8 21)))) + + +(ert-deftest when-third-thursday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 9 :thursday :third) '(2013 9 19)))) + + +(ert-deftest when-third-thursday-is-another-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 10 :thursday :third) '(2013 10 17)))) + + +(ert-deftest when-third-friday-is-the-15th-the-first-day-of-the-third-week () + (should (equal (meetup 2013 11 :friday :third) '(2013 11 15)))) + + +(ert-deftest when-third-friday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 12 :friday :third) '(2013 12 20)))) + + +(ert-deftest when-third-saturday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 1 :saturday :third) '(2013 1 19)))) + + +(ert-deftest when-third-saturday-is-another-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 2 :saturday :third) '(2013 2 16)))) + + +(ert-deftest when-third-sunday-is-some-day-in-the-middle-of-the-third-week () + (should (equal (meetup 2013 3 :sunday :third) '(2013 3 17)))) + + +(ert-deftest when-third-sunday-is-the-21st-the-last-day-of-the-third-week () + (should (equal (meetup 2013 4 :sunday :third) '(2013 4 21)))) + + +(ert-deftest when-fourth-monday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 3 :monday :fourth) '(2013 3 25)))) + + +(ert-deftest when-fourth-monday-is-the-22nd-the-first-day-of-the-fourth-week () + (should (equal (meetup 2013 4 :monday :fourth) '(2013 4 22)))) + + +(ert-deftest when-fourth-tuesday-is-the-28th-the-last-day-of-the-fourth-week () + (should (equal (meetup 2013 5 :tuesday :fourth) '(2013 5 28)))) + + +(ert-deftest when-fourth-tuesday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 6 :tuesday :fourth) '(2013 6 25)))) + + +(ert-deftest when-fourth-wednesday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 7 :wednesday :fourth) '(2013 7 24)))) + + +(ert-deftest when-fourth-wednesday-is-the-28th-the-last-day-of-the-fourth-week () + (should (equal (meetup 2013 8 :wednesday :fourth) '(2013 8 28)))) + + +(ert-deftest when-fourth-thursday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 9 :thursday :fourth) '(2013 9 26)))) + + +(ert-deftest when-fourth-thursday-is-another-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 10 :thursday :fourth) '(2013 10 24)))) + + +(ert-deftest when-fourth-friday-is-the-22nd-the-first-day-of-the-fourth-week () + (should (equal (meetup 2013 11 :friday :fourth) '(2013 11 22)))) + + +(ert-deftest when-fourth-friday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 12 :friday :fourth) '(2013 12 27)))) + + +(ert-deftest when-fourth-saturday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 1 :saturday :fourth) '(2013 1 26)))) + + +(ert-deftest when-fourth-saturday-is-another-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 2 :saturday :fourth) '(2013 2 23)))) + + +(ert-deftest when-fourth-sunday-is-some-day-in-the-middle-of-the-fourth-week () + (should (equal (meetup 2013 3 :sunday :fourth) '(2013 3 24)))) + + +(ert-deftest when-fourth-sunday-is-the-28th-the-last-day-of-the-fourth-week () + (should (equal (meetup 2013 4 :sunday :fourth) '(2013 4 28)))) + + +(ert-deftest last-monday-in-a-month-with-four-mondays () + (should (equal (meetup 2013 3 :monday :last) '(2013 3 25)))) + + +(ert-deftest last-monday-in-a-month-with-five-mondays () + (should (equal (meetup 2013 4 :monday :last) '(2013 4 29)))) + + +(ert-deftest last-tuesday-in-a-month-with-four-tuesdays () + (should (equal (meetup 2013 5 :tuesday :last) '(2013 5 28)))) + + +(ert-deftest last-tuesday-in-another-month-with-four-tuesdays () + (should (equal (meetup 2013 6 :tuesday :last) '(2013 6 25)))) + + +(ert-deftest last-wednesday-in-a-month-with-five-wednesdays () + (should (equal (meetup 2013 7 :wednesday :last) '(2013 7 31)))) + + +(ert-deftest last-wednesday-in-a-month-with-four-wednesdays () + (should (equal (meetup 2013 8 :wednesday :last) '(2013 8 28)))) + + +(ert-deftest last-thursday-in-a-month-with-four-thursdays () + (should (equal (meetup 2013 9 :thursday :last) '(2013 9 26)))) + + +(ert-deftest last-thursday-in-a-month-with-five-thursdays () + (should (equal (meetup 2013 10 :thursday :last) '(2013 10 31)))) + + +(ert-deftest last-friday-in-a-month-with-five-fridays () + (should (equal (meetup 2013 11 :friday :last) '(2013 11 29)))) + + +(ert-deftest last-friday-in-a-month-with-four-fridays () + (should (equal (meetup 2013 12 :friday :last) '(2013 12 27)))) + + +(ert-deftest last-saturday-in-a-month-with-four-saturdays () + (should (equal (meetup 2013 1 :saturday :last) '(2013 1 26)))) + + +(ert-deftest last-saturday-in-another-month-with-four-saturdays () + (should (equal (meetup 2013 2 :saturday :last) '(2013 2 23)))) + + +(ert-deftest last-sunday-in-a-month-with-five-sundays () + (should (equal (meetup 2013 3 :sunday :last) '(2013 3 31)))) + + +(ert-deftest last-sunday-in-a-month-with-four-sundays () + (should (equal (meetup 2013 4 :sunday :last) '(2013 4 28)))) + + +(ert-deftest when-last-wednesday-in-february-in-a-leap-year-is-the-29th () + (should (equal (meetup 2012 2 :wednesday :last) '(2012 2 29)))) + + +(ert-deftest last-wednesday-in-december-that-is-also-the-last-day-of-the-year () + (should (equal (meetup 2014 12 :wednesday :last) '(2014 12 31)))) + + +(ert-deftest when-last-sunday-in-february-in-a-non-leap-year-is-not-the-29th () + (should (equal (meetup 2015 2 :sunday :last) '(2015 2 22)))) + + +(ert-deftest when-first-friday-is-the-7th-the-last-day-of-the-first-week () + (should (equal (meetup 2012 12 :friday :first) '(2012 12 7)))) + + +(provide 'meetup-test) +;;; meetup-test.el ends here diff --git a/exercises/practice/meetup/meetup.el b/exercises/practice/meetup/meetup.el new file mode 100644 index 00000000..010f0c97 --- /dev/null +++ b/exercises/practice/meetup/meetup.el @@ -0,0 +1,14 @@ +;;; meetup.el --- Meetup (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun meetup (year month dayofweek schedule) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'meetup) +;;; meetup.el ends here + diff --git a/exercises/practice/minesweeper/.docs/instructions.md b/exercises/practice/minesweeper/.docs/instructions.md new file mode 100644 index 00000000..7c1df2e4 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add the mine counts to empty squares in a completed Minesweeper board. +The board itself is a rectangle composed of squares that are either empty (`' '`) or a mine (`'*'`). + +For each empty square, count the number of mines adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent mines, leave it empty. +Otherwise replace it with the adjacent mines count. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/minesweeper/.docs/introduction.md b/exercises/practice/minesweeper/.docs/introduction.md new file mode 100644 index 00000000..5f74a742 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +[Minesweeper][wikipedia] is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square. + +[wikipedia]: https://en.wikipedia.org/wiki/Minesweeper_(video_game) diff --git a/exercises/practice/minesweeper/.meta/config.json b/exercises/practice/minesweeper/.meta/config.json new file mode 100644 index 00000000..0bc81f7a --- /dev/null +++ b/exercises/practice/minesweeper/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "minesweeper.el" + ], + "test": [ + "minesweeper-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Add the numbers to a minesweeper board." +} diff --git a/exercises/practice/minesweeper/.meta/example.el b/exercises/practice/minesweeper/.meta/example.el new file mode 100644 index 00000000..f2cab1bf --- /dev/null +++ b/exercises/practice/minesweeper/.meta/example.el @@ -0,0 +1,36 @@ +;;; minesweeper.el --- Minesweeper (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun annotate (minefield) + (when minefield + (let* ((num-columns (length (car minefield))) + (lines (vconcat minefield)) + (num-rows (length lines)) + (results nil)) + (cl-loop for row from (1- num-rows) downto 0 + for result = (copy-sequence (aref lines row)) + for r-start = (max 0 (1- row)) + for r-end = (min num-rows (+ 2 row)) + do (cl-loop for column from 0 below num-columns + for c-start = (max 0 (1- column)) + for c-end = (min num-columns (+ 2 column)) + for count = 0 + do (unless (= ?* (aref result column)) + (cl-loop for r from r-start below r-end + for line = (aref lines r) + do (cl-loop for c from c-start below c-end + do (when (= ?* (aref line c)) + (setq count (1+ count))))) + (unless (= 0 count) + (aset result column (+ ?0 count))))) + do (setq results (cons result results))) + results))) + +(provide 'minesweeper) +;;; minesweeper.el ends here + diff --git a/exercises/practice/minesweeper/.meta/tests.toml b/exercises/practice/minesweeper/.meta/tests.toml new file mode 100644 index 00000000..2a142222 --- /dev/null +++ b/exercises/practice/minesweeper/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[0c5ec4bd-dea7-4138-8651-1203e1cb9f44] +description = "no rows" + +[650ac4c0-ad6b-4b41-acde-e4ea5852c3b8] +description = "no columns" + +[6fbf8f6d-a03b-42c9-9a58-b489e9235478] +description = "no mines" + +[61aff1c4-fb31-4078-acad-cd5f1e635655] +description = "minefield with only mines" + +[84167147-c504-4896-85d7-246b01dea7c5] +description = "mine surrounded by spaces" + +[cb878f35-43e3-4c9d-93d9-139012cccc4a] +description = "space surrounded by mines" + +[7037f483-ddb4-4b35-b005-0d0f4ef4606f] +description = "horizontal line" + +[e359820f-bb8b-4eda-8762-47b64dba30a6] +description = "horizontal line, mines at edges" + +[c5198b50-804f-47e9-ae02-c3b42f7ce3ab] +description = "vertical line" + +[0c79a64d-703d-4660-9e90-5adfa5408939] +description = "vertical line, mines at edges" + +[4b098563-b7f3-401c-97c6-79dd1b708f34] +description = "cross" + +[04a260f1-b40a-4e89-839e-8dd8525abe0e] +description = "large minefield" diff --git a/exercises/practice/minesweeper/minesweeper-test.el b/exercises/practice/minesweeper/minesweeper-test.el new file mode 100644 index 00000000..8659a963 --- /dev/null +++ b/exercises/practice/minesweeper/minesweeper-test.el @@ -0,0 +1,135 @@ +;;; minesweeper-test.el --- Tests for Minesweeper (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "minesweeper.el") +(declare-function annotate "minesweeper.el" (minefield)) + + +(ert-deftest no-rows () + (should (equal '() + + (annotate '())))) + + +(ert-deftest no-columns () + (should (equal '("") + + (annotate '(""))))) + + +(ert-deftest no-mines () + (should (equal '(" " + " " + " ") + + (annotate '(" " + " " + " "))))) + + +(ert-deftest minefield-with-only-mines () + (should (equal '("***" + "***" + "***") + + (annotate '("***" + "***" + "***"))))) + + +(ert-deftest mine-surrounded-by-spaces () + (should (equal '("111" + "1*1" + "111") + + (annotate '(" " + " * " + " "))))) + + +(ert-deftest space-surrounded-by-mines () + (should (equal '("***" + "*8*" + "***") + + (annotate '("***" + "* *" + "***"))))) + + +(ert-deftest horizontal-line () + (should (equal '("1*2*1") + + (annotate '(" * * "))))) + + +(ert-deftest horizontal-line-mines-at-edges () + (should (equal '("*1 1*") + + (annotate '("* *"))))) + + +(ert-deftest vertical-line () + (should (equal '("1" + "*" + "2" + "*" + "1") + + (annotate '(" " + "*" + " " + "*" + " "))))) + + +(ert-deftest vertical-line-mines-at-edges () + (should (equal '("*" + "1" + " " + "1" + "*") + + (annotate '("*" + " " + " " + " " + "*"))))) + + +(ert-deftest cross () + (should (equal '(" 2*2 " + "25*52" + "*****" + "25*52" + " 2*2 ") + + (annotate '(" * " + " * " + "*****" + " * " + " * "))))) + + +(ert-deftest large-minefield () + (should (equal '("1*22*1" + "12*322" + " 123*2" + "112*4*" + "1*22*2" + "111111") + + (annotate '(" * * " + " * " + " * " + " * *" + " * * " + " "))))) + + +(provide 'minesweeper-test) +;;; minesweeper-test.el ends here diff --git a/exercises/practice/minesweeper/minesweeper.el b/exercises/practice/minesweeper/minesweeper.el new file mode 100644 index 00000000..45a5c41f --- /dev/null +++ b/exercises/practice/minesweeper/minesweeper.el @@ -0,0 +1,14 @@ +;;; minesweeper.el --- Minesweeper (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun annotate (minefield) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'minesweeper) +;;; minesweeper.el ends here + diff --git a/exercises/practice/nth-prime/.docs/instructions.md b/exercises/practice/nth-prime/.docs/instructions.md new file mode 100644 index 00000000..065e323a --- /dev/null +++ b/exercises/practice/nth-prime/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Given a number n, determine what the nth prime is. + +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. + +If your language provides methods in the standard library to deal with prime numbers, pretend they don't exist and implement them yourself. diff --git a/exercises/practice/nth-prime/.meta/config.json b/exercises/practice/nth-prime/.meta/config.json new file mode 100644 index 00000000..33ae427a --- /dev/null +++ b/exercises/practice/nth-prime/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "nth-prime.el" + ], + "test": [ + "nth-prime-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a number n, determine what the nth prime is.", + "source": "A variation on Problem 7 at Project Euler", + "source_url": "https://projecteuler.net/problem=7" +} diff --git a/exercises/practice/nth-prime/.meta/example.el b/exercises/practice/nth-prime/.meta/example.el new file mode 100644 index 00000000..3f07c768 --- /dev/null +++ b/exercises/practice/nth-prime/.meta/example.el @@ -0,0 +1,42 @@ +;;; nth-prime.el --- Nth Prime (exercism) -*- lexical-binding: t; -*- + + +;;; Commentary: + +; We use the prime number theorem to over-estimate the nth prime +; as 1 + n (log2 n) + +; We use the Sieve of Eratosthenes to generate primes. + + +;;; Code: + +(require 'cl-lib) + +(defun over-estimate (number) + (cl-loop for count from 1 + while (< (ash 1 count) number) + finally return (1+ (* number count)))) + +(defun prime (number) + (when (= number 0) + (error "there is no zeroth prime")) + (let* ((limit (over-estimate number)) + (table (make-bool-vector (1+ limit) t)) + (count 0)) + (cl-loop for p from 2 + for psq = (* p p) + until (< limit psq) + do (when (aref table p) + (cl-loop for m from psq to limit by p + do (aset table m nil)))) + (cl-loop for p from 2 + do (when (aref table p) + (setq count (1+ count))) + until (= count number) + finally return p))) + + +(provide 'nth-prime) +;;; nth-prime.el ends here + diff --git a/exercises/practice/nth-prime/.meta/tests.toml b/exercises/practice/nth-prime/.meta/tests.toml new file mode 100644 index 00000000..daccec42 --- /dev/null +++ b/exercises/practice/nth-prime/.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. + +[75c65189-8aef-471a-81de-0a90c728160c] +description = "first prime" + +[2c38804c-295f-4701-b728-56dea34fd1a0] +description = "second prime" + +[56692534-781e-4e8c-b1f9-3e82c1640259] +description = "sixth prime" + +[fce1e979-0edb-412d-93aa-2c744e8f50ff] +description = "big prime" + +[bd0a9eae-6df7-485b-a144-80e13c7d55b2] +description = "there is no zeroth prime" diff --git a/exercises/practice/nth-prime/nth-prime-test.el b/exercises/practice/nth-prime/nth-prime-test.el new file mode 100644 index 00000000..e7c436be --- /dev/null +++ b/exercises/practice/nth-prime/nth-prime-test.el @@ -0,0 +1,33 @@ +;;; nth-prime-test.el --- Tests for Nth Prime (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "nth-prime.el") +(declare-function prime "nth-prime.el" (number)) + + +(ert-deftest first-prime () + (should (= 2 (prime 1)))) + + +(ert-deftest second-prime () + (should (= 3 (prime 2)))) + + +(ert-deftest sixth-prime () + (should (= 13 (prime 6)))) + + +(ert-deftest big-prime () + (should (= 104743 (prime 10001)))) + + +(ert-deftest there-is-no-zeroth-prime () + (should-error (prime 0))) + + +(provide 'nth-prime-test) +;;; nth-prime-test.el ends here diff --git a/exercises/practice/nth-prime/nth-prime.el b/exercises/practice/nth-prime/nth-prime.el new file mode 100644 index 00000000..1c09096a --- /dev/null +++ b/exercises/practice/nth-prime/nth-prime.el @@ -0,0 +1,14 @@ +;;; nth-prime.el --- Nth Prime (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun prime (number) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'nth-prime) +;;; nth-prime.el ends here + diff --git a/exercises/practice/nucleotide-count/.docs/hints.md b/exercises/practice/nucleotide-count/.docs/hints.md new file mode 100644 index 00000000..332002fc --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/hints.md @@ -0,0 +1,11 @@ +# Hints + +## General + +- The expected result is an `Association List` data structure (also called `alist`), not an string, or something else. You can find more information about it in this [link](https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html). So, for the correct example, the right output would be: + + ```text + "GATTACA" -> '((?A . 3) (?C . 1) (?G . 1) (?T . 2)) + ``` + +- The `?A` is the Emacs Lisp way to represent a character. You can find more information in the documentation: [link](https://www.gnu.org/software/emacs/manual/html_node/elisp/Basic-Char-Syntax.html). diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json index 8e874d3e..ccbd7a31 100644 --- a/exercises/practice/nucleotide-count/.meta/config.json +++ b/exercises/practice/nucleotide-count/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "canweriotnow" ], diff --git a/exercises/practice/nucleotide-count/nucleotide-count.el b/exercises/practice/nucleotide-count/nucleotide-count.el index 2dcb5c4f..bc12a923 100644 --- a/exercises/practice/nucleotide-count/nucleotide-count.el +++ b/exercises/practice/nucleotide-count/nucleotide-count.el @@ -2,9 +2,12 @@ ;;; Commentary: -(defun nucleotide-count (sequence) ;;; Code: -) + + +(defun nucleotide-count (sequence) + (error "Delete this S-Expression and write your own implementation")) + (provide 'nucleotide-count) ;;; nucleotide-count.el ends here diff --git a/exercises/practice/pangram/.docs/instructions.md b/exercises/practice/pangram/.docs/instructions.md index de83d54e..817c872d 100644 --- a/exercises/practice/pangram/.docs/instructions.md +++ b/exercises/practice/pangram/.docs/instructions.md @@ -1,9 +1,8 @@ # Instructions -Determine if a sentence is a pangram. -A pangram (Greek: παν γράμμα, pan gramma, "every letter") is a sentence using every letter of the alphabet at least once. -The best known English pangram is: +Your task is to figure out if a sentence is a pangram. -> The quick brown fox jumps over the lazy dog. +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`). -The alphabet used consists of letters `a` to `z`, inclusive, and is case insensitive. +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/example.el b/exercises/practice/pangram/.meta/example.el index adc020fd..7771374d 100644 --- a/exercises/practice/pangram/.meta/example.el +++ b/exercises/practice/pangram/.meta/example.el @@ -5,7 +5,7 @@ ;;; Code: (require 'cl-lib) -(defun is-pangram (phrase) +(defun pangramp (phrase) "Determine if a given phrase is a pangram." (let ((alphabet "abcdefghijklmnopqrstuvwxyz")) (cl-every diff --git a/exercises/practice/pangram/pangram-test.el b/exercises/practice/pangram/pangram-test.el index a6de6642..0486a060 100644 --- a/exercises/practice/pangram/pangram-test.el +++ b/exercises/practice/pangram/pangram-test.el @@ -1,42 +1,53 @@ -;;; pagram-test.el --- Tests for Pangram (exercism) -*- lexical-binding: t; -*- +;;; pangram-test.el --- Tests for Pangram (exercism) -*- lexical-binding: t; -*- ;;; Commentary: -;; Common test data version: 1.3.0 d79e13e ;;; Code: + (load-file "pangram.el") -(declare-function is-pangram "pangram.el" (phrase)) +(declare-function pangramp "pangram.el" (phrase)) + + +(ert-deftest empty-sentence () + (should-not (pangramp ""))) + + +(ert-deftest perfect-lower-case () + (should (pangramp "abcdefghijklmnopqrstuvwxyz"))) + + +(ert-deftest only-lower-case () + (should (pangramp "the quick brown fox jumps over the lazy dog"))) + + +(ert-deftest missing-the-letter-x () + (should-not (pangramp "a quick movement of the enemy will jeopardize five gunboats"))) + + +(ert-deftest missing-the-letter-h () + (should-not (pangramp "five boxing wizards jump quickly at it"))) + -(ert-deftest sentence-empty () - (should (equal nil (is-pangram "")))) +(ert-deftest with-underscores () + (should (pangramp "the_quick_brown_fox_jumps_over_the_lazy_dog"))) -(ert-deftest recognizes-a-perfect-lower-case-pangram () - (should (equal t (is-pangram "abcdefghijklmnopqrstuvwxyz")))) -(ert-deftest pangram-with-only-lower-case () - (should (equal t (is-pangram "the quick brown fox jumps over the lazy dog")))) +(ert-deftest with-numbers () + (should (pangramp "the 1 quick brown fox jumps over the 2 lazy dogs"))) -(ert-deftest missing-character-x () - (should (equal nil (is-pangram "a quick movement of the enemy will jeopardize five gunboats")))) -(ert-deftest missing-another-character-eg-h () - (should (equal nil (is-pangram "five boxing wizards jump quickly at it")))) +(ert-deftest missing-letters-replaced-by-numbers () + (should-not (pangramp "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"))) -(ert-deftest pangram-with-underscores () - (should (equal t (is-pangram "the_quick_brown_fox_jumps_over_the_lazy_dog")))) -(ert-deftest pangram-with-numbers () - (should (equal t (is-pangram "the 1 quick brown fox jumps over the 2 lazy dogs")))) +(ert-deftest mixed-case-and-punctuation () + (should (pangramp "\"Five quacking Zephyrs jolt my wax bed.\""))) -(ert-deftest missing-letters-replaced-by-numbers () - (should (equal nil (is-pangram "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")))) -(ert-deftest pangram-with-mixed-case-and-punctuation () - (should (equal t (is-pangram "\"Five quacking Zephyrs jolt my wax bed.\"")))) +(ert-deftest a-m-and-A-M-are-26-different-characters-but-not-a-pangram () + (should-not (pangramp "abcdefghijklm ABCDEFGHIJKLM"))) -(ert-deftest a-m-and-A-M-are-26-different-characters-but-not-a-pangram () - (should (equal nil (is-pangram "abcdefghijklm ABCDEFGHIJKLM")))) (provide 'pangram-test) -;;; pagram-test.el ends here +;;; pangram-test.el ends here diff --git a/exercises/practice/pangram/pangram.el b/exercises/practice/pangram/pangram.el index c7dd7a90..333a9ad8 100644 --- a/exercises/practice/pangram/pangram.el +++ b/exercises/practice/pangram/pangram.el @@ -2,9 +2,12 @@ ;;; Commentary: -(defun is-pangram (phrase) ;;; Code: -) + + +(defun pangramp (phrase) + (error "Delete this S-Expression and write your own implementation")) + (provide 'pangram) ;;; pangram.el ends here diff --git a/exercises/practice/parallel-letter-frequency/.docs/instructions.append.md b/exercises/practice/parallel-letter-frequency/.docs/instructions.append.md new file mode 100644 index 00000000..b58c60a0 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.docs/instructions.append.md @@ -0,0 +1,9 @@ +# Instructions append + +## Using Parallelism + +The goal of this exercise is to practice parallelism with Emacs Lisp. + +In Emacs Lisp this can be achieved by using [`asynchronous processes`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Asynchronous-Processes.html#:~:text=An%20asynchronous%20process%20is%20controlled,%2Dtype%20(see%20below)). + +You may also want to look at the documentation for [`batch mode`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Batch-Mode.html), [`sentinels`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Sentinels.html) and [`receiving output from processes`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Output-from-Processes.html). diff --git a/exercises/practice/parallel-letter-frequency/.docs/instructions.md b/exercises/practice/parallel-letter-frequency/.docs/instructions.md new file mode 100644 index 00000000..6147b90a --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Count the frequency of letters in texts using parallel computation. + +Parallelism is about doing things in parallel that can also be done sequentially. +A common example is counting the frequency of letters. +Employ parallelism to calculate the total frequency of each letter in a list of texts. diff --git a/exercises/practice/parallel-letter-frequency/.meta/config.json b/exercises/practice/parallel-letter-frequency/.meta/config.json new file mode 100644 index 00000000..4372e34f --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.meta/config.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "kmarker1101" + ], + "contributors": [ + "fapdash", + "BNAndras" + ], + "files": { + "solution": [ + "parallel-letter-frequency.el" + ], + "test": [ + "parallel-letter-frequency-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Count the frequency of letters in texts using parallel computation." +} diff --git a/exercises/practice/parallel-letter-frequency/.meta/example.el b/exercises/practice/parallel-letter-frequency/.meta/example.el new file mode 100644 index 00000000..11123bff --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.meta/example.el @@ -0,0 +1,77 @@ +;;; parallel-letter-frequency.el --- Parallel Letter Frequency (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + + +(defun clean-text (text) + (downcase (replace-regexp-in-string "[^[:alpha:]]" "" text))) + + +(defun combine-frequencies (freqs-list) + (let ((combined-freqs (make-hash-table :test 'equal))) + (dolist (freqs freqs-list) + (maphash (lambda (key value) + (puthash key (+ value (gethash key combined-freqs 0)) combined-freqs)) + freqs)) + combined-freqs)) + + +(defun calculate-frequencies (texts) + (let ((cleaned-texts (mapcar #'clean-text texts))) + (if (cl-every #'string-empty-p cleaned-texts) + (make-hash-table :test 'equal) + (let* ((num-processes (min (length cleaned-texts) (max 1 (string-to-number (shell-command-to-string "nproc"))))) + (texts-per-process (ceiling (/ (float (length cleaned-texts)) num-processes))) + (results (make-hash-table :test 'equal)) + (pending num-processes) + (final-result (make-hash-table :test 'equal)) + (processes nil)) + (dotimes (i num-processes) + (let* ((start-index (* i texts-per-process)) + (end-index (min (* (1+ i) texts-per-process) (length cleaned-texts))) + (process-texts (if (< start-index (length cleaned-texts)) + (cl-subseq cleaned-texts start-index end-index) + '()))) + (when (not (null process-texts)) + (let* ((command (prin1-to-string `(calculate-frequencies-in-subprocess ',process-texts))) + (process (make-process + :name (format "letter-freq-process-%d" i) + :buffer (generate-new-buffer (format " *letter-freq-process-%d*" i)) + :command (list "emacs" "--batch" "-l" "parallel-letter-frequency.el" "--eval" command) + :sentinel (lambda (proc _event) + (when (eq (process-status proc) 'exit) + (with-current-buffer (process-buffer proc) + (let ((result (read (buffer-string)))) + (maphash (lambda (key value) + (puthash key (+ value (gethash key results 0)) results)) + result)) + (setq pending (1- pending)) + (when (= pending 0) + (setq final-result (combine-frequencies (list results)))))))))) + (push process processes))))) + (while (> pending 0) + (sleep-for 0.1)) + final-result)))) + + +(defun calculate-frequencies-in-subprocess (texts) + (let ((freqs (make-hash-table :test 'equal))) + (dolist (text texts) + (let ((text-freqs (make-hash-table :test 'equal))) + (dolist (char (string-to-list text)) + (when (string-match-p "[[:alpha:]]" (char-to-string char)) + (puthash + char (1+ (gethash char text-freqs 0)) text-freqs))) + (maphash + (lambda (key value) + (puthash key (+ value (gethash key freqs 0)) freqs)) + text-freqs))) + (prin1 freqs))) + + +(provide 'parallel-letter-frequency) +;;; parallel-letter-frequency.el ends here diff --git a/exercises/practice/parallel-letter-frequency/.meta/tests.toml b/exercises/practice/parallel-letter-frequency/.meta/tests.toml new file mode 100644 index 00000000..0c974f7f --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.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. + +[c054d642-c1fa-4234-8007-9339f2337886] +description = "no texts" + +[818031be-49dc-4675-b2f9-c4047f638a2a] +description = "one text with one letter" + +[c0b81d1b-940d-4cea-9f49-8445c69c17ae] +description = "one text with multiple letters" + +[708ff1e0-f14a-43fd-adb5-e76750dcf108] +description = "two texts with one letter" + +[1b5c28bb-4619-4c9d-8db9-a4bb9c3bdca0] +description = "two texts with multiple letters" + +[6366e2b8-b84c-4334-a047-03a00a656d63] +description = "ignore letter casing" + +[92ebcbb0-9181-4421-a784-f6f5aa79f75b] +description = "ignore whitespace" + +[bc5f4203-00ce-4acc-a5fa-f7b865376fd9] +description = "ignore punctuation" + +[68032b8b-346b-4389-a380-e397618f6831] +description = "ignore numbers" + +[aa9f97ac-3961-4af1-88e7-6efed1bfddfd] +description = "Unicode letters" + +[7b1da046-701b-41fc-813e-dcfb5ee51813] +description = "combination of lower- and uppercase letters, punctuation and white space" + +[4727f020-df62-4dcf-99b2-a6e58319cb4f] +description = "large texts" + +[adf8e57b-8e54-4483-b6b8-8b32c115884c] +description = "many small texts" diff --git a/exercises/practice/parallel-letter-frequency/parallel-letter-frequency-test.el b/exercises/practice/parallel-letter-frequency/parallel-letter-frequency-test.el new file mode 100644 index 00000000..520254f7 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/parallel-letter-frequency-test.el @@ -0,0 +1,289 @@ +;;; parallel-letter-frequency-test.el --- Parallel Letter Frequency (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "parallel-letter-frequency.el") +(declare-function calculate-frequencies "parallel-letter-frequency.el" (texts)) + + +(defun hash-table-contains (expected actual) + (let ((result t)) + (maphash (lambda (key value) + (unless (equal (gethash key actual) value) + (setq result nil))) + expected) + result)) + + +(ert-deftest no-texts () + (let ((expected (make-hash-table :test 'equal))) + (should (hash-table-contains expected (calculate-frequencies '()))))) + + +(ert-deftest one-text-with-one-letter () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?a 1 expected) + (should (hash-table-contains expected (calculate-frequencies '("a")))))) + + +(ert-deftest one-text-with-multiple-letters () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?b 2 expected) + (puthash ?c 3 expected) + (puthash ?d 1 expected) + (should (hash-table-contains expected (calculate-frequencies '("bbcccd")))))) + + +(ert-deftest two-texts-with-one-letter () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?e 1 expected) + (puthash ?f 1 expected) + (should (hash-table-contains expected (calculate-frequencies '("e" "f")))))) + + +(ert-deftest two-texts-with-multiple-letters () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?g 2 expected) + (puthash ?h 3 expected) + (puthash ?i 1 expected) + (should (hash-table-contains expected (calculate-frequencies '("ggh" "hhi")))))) + + +(ert-deftest ignore-letter-casing () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?m 2 expected) + (should (hash-table-contains expected (calculate-frequencies '("m" "M")))))) + + +(ert-deftest ignore-whitespace () + (let ((expected (make-hash-table :test 'equal))) + (should (hash-table-contains expected (calculate-frequencies '(" " "\t" "\r\n")))))) + + +(ert-deftest ignore-punctuation () + (let ((expected (make-hash-table :test 'equal))) + (should (hash-table-contains expected (calculate-frequencies '("!" "?" ";" "," ".")))))) + + +(ert-deftest ignore-numbers () + (let ((expected (make-hash-table :test 'equal))) + (should (hash-table-contains expected (calculate-frequencies '("1" "2" "3" "4" "5" "6" "7" "8" "9")))))) + + +(ert-deftest unicode-letters () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?ø 1 expected) + (puthash ?φ 1 expected) + (puthash ?ほ 1 expected) + (puthash ?本 1 expected) + (should (hash-table-contains expected (calculate-frequencies '("ø" "φ" "ほ" "本")))))) + + +(ert-deftest combination-of-lower-and-uppercase-letters-punctuation-and-white-space () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?a 32 expected) + (puthash ?b 4 expected) + (puthash ?c 6 expected) + (puthash ?d 14 expected) + (puthash ?e 37 expected) + (puthash ?f 7 expected) + (puthash ?g 8 expected) + (puthash ?h 29 expected) + (puthash ?i 19 expected) + (puthash ?k 6 expected) + (puthash ?l 12 expected) + (puthash ?m 7 expected) + (puthash ?n 19 expected) + (puthash ?o 22 expected) + (puthash ?p 7 expected) + (puthash ?r 17 expected) + (puthash ?s 16 expected) + (puthash ?t 30 expected) + (puthash ?u 9 expected) + (puthash ?v 2 expected) + (puthash ?w 9 expected) + (puthash ?y 4 expected) + (should (hash-table-contains expected (calculate-frequencies '("There, peeping among the cloud-wrack above a dark tower high up in the mountains, Sam saw a white star twinkle for a while. The beauty of it smote his heart, as he looked up out of the forsaken land, and hope returned to him. For like a shaft, clear and cold, the thought pierced him that in the end, the shadow was only a small and passing thing: there was light and high beauty forever beyond its reach.")))))) + + +(defvar large-text + '("I am a sick man.... I am a spiteful man. I am an unattractive man." + "I believe my liver is diseased. However, I know nothing at all about my disease, and do not" + "know for certain what ails me. I don't consult a doctor for it," + "and never have, though I have a respect for medicine and doctors." + "Besides, I am extremely superstitious, sufficiently so to respect medicine," + "anyway (I am well-educated enough not to be superstitious, but I am superstitious)." + "No, I refuse to consult a doctor from spite." + "That you probably will not understand. Well, I understand it, though." + "Of course, I can't explain who it is precisely that I am mortifying in this case by my spite:" + "I am perfectly well aware that I cannot \"pay out\" the doctors by not consulting them;" + "I know better than anyone that by all this I am only injuring myself and no one else." + "But still, if I don't consult a doctor it is from spite." + "My liver is bad, well - let it get worse!" + "I have been going on like that for a long time - twenty years. Now I am forty." + "I used to be in the government service, but am no longer." + "I was a spiteful official. I was rude and took pleasure in being so." + "I did not take bribes, you see, so I was bound to find a recompense in that, at least." + "(A poor jest, but I will not scratch it out. I wrote it thinking it would sound very witty;" + "but now that I have seen myself that I only wanted to show off in a despicable way -" + "I will not scratch it out on purpose!) When petitioners used to come for" + "information to the table at which I sat, I used to grind my teeth at them," + "and felt intense enjoyment when I succeeded in making anybody unhappy." + "I almost did succeed. For the most part they were all timid people - of course," + "they were petitioners. But of the uppish ones there was one officer in particular" + "I could not endure. He simply would not be humble, and clanked his sword in a disgusting way." + "I carried on a feud with him for eighteen months over that sword. At last I got the better of him." + "He left off clanking it. That happened in my youth, though. But do you know," + "gentlemen, what was the chief point about my spite? Why, the whole point," + "the real sting of it lay in the fact that continually, even in the moment of the acutest spleen," + "I was inwardly conscious with shame that I was not only not a spiteful but not even an embittered man," + "that I was simply scaring sparrows at random and amusing myself by it." + "I might foam at the mouth, but bring me a doll to play with, give me a cup of tea with sugar in it," + "and maybe I should be appeased. I might even be genuinely touched," + "though probably I should grind my teeth at myself afterwards and lie awake at night with shame for" + "months after. That was my way. I was lying when I said just now that I was a spiteful official." + "I was lying from spite. I was simply amusing myself with the petitioners and with the officer," + "and in reality I never could become spiteful. I was conscious every moment in myself of many," + "very many elements absolutely opposite to that. I felt them positively swarming in me," + "these opposite elements. I knew that they had been swarming in me all my life and craving some outlet from me," + "but I would not let them, would not let them, purposely would not let them come out." + "They tormented me till I was ashamed: they drove me to convulsions and - sickened me, at last," + "how they sickened me!" + "Gentlemen, I am joking, and I know myself that my jokes are not brilliant" + "but you know one can take everything as a joke. I am, perhaps, jesting against the grain." + "Gentlemen, I am tormented by questions; answer them for me. You, for instance, want to cure men of their" + "old habits and reform their will in accordance with science and good sense." + "But how do you know, not only that it is possible, but also that it is" + "desirable to reform man in that way? And what leads you to the conclusion that man's" + "inclinations need reforming? In short, how do you know that such a reformation will be a benefit to man?" + "And to go to the root of the matter, why are you so positively convinced that not to act against" + "his real normal interests guaranteed by the conclusions of reason and arithmetic is certainly always" + "advantageous for man and must always be a law for mankind? So far, you know," + "this is only your supposition. It may be the law of logic, but not the law of humanity." + "You think, gentlemen, perhaps that I am mad? Allow me to defend myself. I agree that man" + "is pre-eminently a creative animal, predestined to strive consciously for an object and to engage in engineering -" + "that is, incessantly and eternally to make new roads, wherever" + "they may lead. But the reason why he wants sometimes to go off at a tangent may just be that he is" + "predestined to make the road, and perhaps, too, that however stupid the \"direct\"" + "practical man may be, the thought sometimes will occur to him that the road almost always does lead" + "somewhere, and that the destination it leads to is less important than the process" + "of making it, and that the chief thing is to save the well-conducted child from despising engineering," + "and so giving way to the fatal idleness, which, as we all know," + "is the mother of all the vices. Man likes to make roads and to create, that is a fact beyond dispute." + "But why has he such a passionate love for destruction and chaos also?" + "Tell me that! But on that point I want to say a couple of words myself. May it not be that he loves" + "chaos and destruction (there can be no disputing that he does sometimes love it)" + "because he is instinctively afraid of attaining his object and completing the edifice he is constructing?" + "Who knows, perhaps he only loves that edifice from a distance, and is by no means" + "in love with it at close quarters; perhaps he only loves building it and does not want to live in it," + "but will leave it, when completed, for the use of les animaux domestiques -" + "such as the ants, the sheep, and so on. Now the ants have quite a different taste." + "They have a marvellous edifice of that pattern which endures for ever - the ant-heap." + "With the ant-heap the respectable race of ants began and with the ant-heap they will probably end," + "which does the greatest credit to their perseverance and good sense. But man is a frivolous and" + "incongruous creature, and perhaps, like a chess player, loves the process of the game, not the end of it." + "And who knows (there is no saying with certainty), perhaps the only goal on earth" + "to which mankind is striving lies in this incessant process of attaining, in other words," + "in life itself, and not in the thing to be attained, which must always be expressed as a formula," + "as positive as twice two makes four, and such positiveness is not life, gentlemen," + "but is the beginning of death." + "But these are all golden dreams. Oh, tell me, who was it first announced," + "who was it first proclaimed, that man only does nasty things because he does not know his own interests;" + "and that if he were enlightened, if his eyes were opened to his real normal interests," + "man would at once cease to do nasty things, would at once become good and noble because," + "being enlightened and understanding his real advantage, he would see his own advantage in the" + "good and nothing else, and we all know that not one man can, consciously, act against his own interests," + "consequently, so to say, through necessity, he would begin doing good? Oh, the babe! Oh, the pure," + "innocent child! Why, in the first place, when in all these thousands of years has there been a time" + "when man has acted only from his own interest? What is to be done with the millions of facts that bear" + "witness that men, consciously, that is fully understanding their real interests, have left them in the" + "background and have rushed headlong on another path, to meet peril and danger," + "compelled to this course by nobody and by nothing, but, as it were, simply disliking the beaten track," + "and have obstinately, wilfully, struck out another difficult, absurd way, seeking it almost in the darkness." + "So, I suppose, this obstinacy and perversity were pleasanter to them than any advantage...." + "Advantage! What is advantage? And will you take it upon yourself to define with perfect accuracy in what the" + "advantage of man consists? And what if it so happens that a man's advantage, sometimes, not only may," + "but even must, consist in his desiring in certain cases what is harmful to himself and not advantageous." + "And if so, if there can be such a case, the whole principle falls into dust. What do you think -" + "are there such cases? You laugh; laugh away, gentlemen, but only answer me: have man's advantages been" + "reckoned up with perfect certainty? Are there not some which not only have not been included but cannot" + "possibly be included under any classification? You see, you gentlemen have, to the best of my knowledge," + "taken your whole register of human advantages from the averages of statistical figures and" + "politico-economical formulas. Your advantages are prosperity, wealth, freedom, peace - and so on, and so on." + "So that the man who should, for instance, go openly and knowingly in opposition to all that list would to your thinking," + "and indeed mine, too, of course, be an obscurantist or an absolute madman: would not he? But, you know, this is" + "what is surprising: why does it so happen that all these statisticians, sages and lovers of humanity," + "when they reckon up human advantages invariably leave out one? They don't even take it into their reckoning" + "in the form in which it should be taken, and the whole reckoning depends upon that. It would be no greater matter," + "they would simply have to take it, this advantage, and add it to the list. But the trouble is, that this strange" + "advantage does not fall under any classification and is not in place in any list. I have a friend for instance ..." + "Ech! gentlemen, but of course he is your friend, too; and indeed there is no one, no one to whom he is not a friend!" + "Yes, but here I come to a stop! Gentlemen, you must excuse me for being over-philosophical;" + "it's the result of forty years underground! Allow me to indulge my fancy. You see, gentlemen, reason is an excellent thing," + "there's no disputing that, but reason is nothing but reason and satisfies only the rational side of man's nature," + "while will is a manifestation of the whole life, that is, of the whole human life including reason and all the impulses." + "And although our life, in this manifestation of it, is often worthless, yet it is life and not simply extracting square roots." + "Here I, for instance, quite naturally want to live, in order to satisfy all my capacities for life, and not simply my capacity" + "for reasoning, that is, not simply one twentieth of my capacity for life. What does reason know? Reason only knows what it has" + "succeeded in learning (some things, perhaps, it will never learn; this is a poor comfort, but why not say so frankly?)" + "and human nature acts as a whole, with everything that is in it, consciously or unconsciously, and, even it if goes wrong, it lives." + "I suspect, gentlemen, that you are looking at me with compassion; you tell me again that an enlightened and developed man," + "such, in short, as the future man will be, cannot consciously desire anything disadvantageous to himself, that that can be proved mathematically." + "I thoroughly agree, it can - by mathematics. But I repeat for the hundredth time, there is one case, one only, when man may consciously, purposely," + "desire what is injurious to himself, what is stupid, very stupid - simply in order to have the right to desire for himself even what is very stupid" + "and not to be bound by an obligation to desire only what is sensible. Of course, this very stupid thing, this caprice of ours, may be in reality," + "gentlemen, more advantageous for us than anything else on earth, especially in certain cases. And in particular it may be more advantageous than" + "any advantage even when it does us obvious harm, and contradicts the soundest conclusions of our reason concerning our advantage -" + "for in any circumstances it preserves for us what is most precious and most important - that is, our personality, our individuality." + "Some, you see, maintain that this really is the most precious thing for mankind; choice can, of course, if it chooses, be in agreement" + "with reason; and especially if this be not abused but kept within bounds. It is profitable and some- times even praiseworthy." + "But very often, and even most often, choice is utterly and stubbornly opposed to reason ... and ... and ... do you know that that," + "too, is profitable, sometimes even praiseworthy? Gentlemen, let us suppose that man is not stupid. (Indeed one cannot refuse to suppose that," + "if only from the one consideration, that, if man is stupid, then who is wise?) But if he is not stupid, he is monstrously ungrateful!" + "Phenomenally ungrateful. In fact, I believe that the best definition of man is the ungrateful biped. But that is not all, that is not his worst defect;" + "his worst defect is his perpetual moral obliquity, perpetual - from the days of the Flood to the Schleswig-Holstein period.")) + + +(ert-deftest large-texts () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?a 845 expected) + (puthash ?b 155 expected) + (puthash ?c 278 expected) + (puthash ?d 359 expected) + (puthash ?e 1143 expected) + (puthash ?f 222 expected) + (puthash ?g 187 expected) + (puthash ?h 507 expected) + (puthash ?i 791 expected) + (puthash ?j 12 expected) + (puthash ?k 67 expected) + (puthash ?l 423 expected) + (puthash ?m 288 expected) + (puthash ?n 833 expected) + (puthash ?o 791 expected) + (puthash ?p 197 expected) + (puthash ?r 432 expected) + (puthash ?q 8 expected) + (puthash ?s 700 expected) + (puthash ?t 1043 expected) + (puthash ?u 325 expected) + (puthash ?v 111 expected) + (puthash ?w 223 expected) + (puthash ?x 7 expected) + (puthash ?y 251 expected) + (should (hash-table-contains expected (calculate-frequencies (split-string (mapconcat 'identity large-text "\n") "\n")))))) + + +(ert-deftest many-small-texts () + (let ((expected (make-hash-table :test 'equal))) + (puthash ?a 50 expected) + (puthash ?b 100 expected) + (puthash ?c 150 expected) + (should (hash-table-contains expected (calculate-frequencies '("abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc" "abbccc")))))) + + +(provide 'parallel-letter-frequency-test) +;;; parallel-letter-frequency-test.el ends here diff --git a/exercises/practice/parallel-letter-frequency/parallel-letter-frequency.el b/exercises/practice/parallel-letter-frequency/parallel-letter-frequency.el new file mode 100644 index 00000000..2bda046c --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/parallel-letter-frequency.el @@ -0,0 +1,14 @@ +;;; parallel-letter-frequency.el --- Parallel Letter Frequency (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun calculate-frequencies (texts) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'parallel-letter-frequency) +;;; parallel-letter-frequency.el ends here + diff --git a/exercises/practice/pascals-triangle/.docs/instructions.md b/exercises/practice/pascals-triangle/.docs/instructions.md new file mode 100644 index 00000000..0f58f006 --- /dev/null +++ b/exercises/practice/pascals-triangle/.docs/instructions.md @@ -0,0 +1,35 @@ +# Instructions + +Your task is to output the first N rows of Pascal's triangle. + +[Pascal's triangle][wikipedia] is a triangular array of positive integers. + +In Pascal's triangle, the number of values in a row is equal to its row number (which starts at one). +Therefore, the first row has one value, the second row has two values, and so on. + +The first (topmost) row has a single value: `1`. +Subsequent rows' values are computed by adding the numbers directly to the right and left of the current position in the previous row. + +If the previous row does _not_ have a value to the left or right of the current position (which only happens for the leftmost and rightmost positions), treat that position's value as zero (effectively "ignoring" it in the summation). + +## Example + +Let's look at the first 5 rows of Pascal's Triangle: + +```text + 1 + 1 1 + 1 2 1 + 1 3 3 1 +1 4 6 4 1 +``` + +The topmost row has one value, which is `1`. + +The leftmost and rightmost values have only one preceding position to consider, which is the position to its right respectively to its left. +With the topmost value being `1`, it follows from this that all the leftmost and rightmost values are also `1`. + +The other values all have two positions to consider. +For example, the fifth row's (`1 4 6 4 1`) middle value is `6`, as the values to its left and right in the preceding row are `3` and `3`: + +[wikipedia]: https://en.wikipedia.org/wiki/Pascal%27s_triangle diff --git a/exercises/practice/pascals-triangle/.docs/introduction.md b/exercises/practice/pascals-triangle/.docs/introduction.md new file mode 100644 index 00000000..eab454e5 --- /dev/null +++ b/exercises/practice/pascals-triangle/.docs/introduction.md @@ -0,0 +1,22 @@ +# Introduction + +With the weather being great, you're not looking forward to spending an hour in a classroom. +Annoyed, you enter the class room, where you notice a strangely satisfying triangle shape on the blackboard. +Whilst waiting for your math teacher to arrive, you can't help but notice some patterns in the triangle: the outer values are all ones, each subsequent row has one more value than its previous row and the triangle is symmetrical. +Weird! + +Not long after you sit down, your teacher enters the room and explains that this triangle is the famous [Pascal's triangle][wikipedia]. + +Over the next hour, your teacher reveals some amazing things hidden in this triangle: + +- It can be used to compute how many ways you can pick K elements from N values. +- It contains the Fibonacci sequence. +- If you color odd and even numbers differently, you get a beautiful pattern called the [Sierpiński triangle][wikipedia-sierpinski-triangle]. + +The teacher implores you and your classmates to look up other uses, and assures you that there are lots more! +At that moment, the school bell rings. +You realize that for the past hour, you were completely absorbed in learning about Pascal's triangle. +You quickly grab your laptop from your bag and go outside, ready to enjoy both the sunshine _and_ the wonders of Pascal's triangle. + +[wikipedia]: https://en.wikipedia.org/wiki/Pascal%27s_triangle +[wikipedia-sierpinski-triangle]: https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle diff --git a/exercises/practice/pascals-triangle/.meta/config.json b/exercises/practice/pascals-triangle/.meta/config.json new file mode 100644 index 00000000..ce634d22 --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "pascals-triangle.el" + ], + "test": [ + "pascals-triangle-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Compute Pascal's triangle up to a given number of rows.", + "source": "Pascal's Triangle at Wolfram Math World", + "source_url": "https://www.wolframalpha.com/input/?i=Pascal%27s+triangle" +} diff --git a/exercises/practice/pascals-triangle/.meta/example.el b/exercises/practice/pascals-triangle/.meta/example.el new file mode 100644 index 00000000..8e0afc2f --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/example.el @@ -0,0 +1,23 @@ +;;; pascals-triangle.el --- Pascal's Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun rows (count) + (let ((result (make-vector count nil)) + (current nil) + (previous nil)) + (cl-loop for r below count + do (setq current (make-vector (1+ r) 1)) + do (cl-loop for c from 1 below r + do (aset current c (+ (aref previous (1- c)) (aref previous c)))) + do (aset result r current) + do (setq previous current)) + result)) + +(provide 'pascals-triangle) +;;; pascals-triangle.el ends here + diff --git a/exercises/practice/pascals-triangle/.meta/tests.toml b/exercises/practice/pascals-triangle/.meta/tests.toml new file mode 100644 index 00000000..2db0ee52 --- /dev/null +++ b/exercises/practice/pascals-triangle/.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. + +[9920ce55-9629-46d5-85d6-4201f4a4234d] +description = "zero rows" + +[70d643ce-a46d-4e93-af58-12d88dd01f21] +description = "single row" + +[a6e5a2a2-fc9a-4b47-9f4f-ed9ad9fbe4bd] +description = "two rows" + +[97206a99-79ba-4b04-b1c5-3c0fa1e16925] +description = "three rows" + +[565a0431-c797-417c-a2c8-2935e01ce306] +description = "four rows" + +[06f9ea50-9f51-4eb2-b9a9-c00975686c27] +description = "five rows" + +[c3912965-ddb4-46a9-848e-3363e6b00b13] +description = "six rows" + +[6cb26c66-7b57-4161-962c-81ec8c99f16b] +description = "ten rows" diff --git a/exercises/practice/pascals-triangle/pascals-triangle-test.el b/exercises/practice/pascals-triangle/pascals-triangle-test.el new file mode 100644 index 00000000..969d987b --- /dev/null +++ b/exercises/practice/pascals-triangle/pascals-triangle-test.el @@ -0,0 +1,69 @@ +;;; pascals-triangle-test.el --- Tests for Pascal's Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "pascals-triangle.el") +(declare-function rows "pascals-triangle.el" (count)) + + +(ert-deftest zero-rows () + (should (equal [] (rows 0)))) + + +(ert-deftest single-row () + (should (equal [[1]] (rows 1)))) + + +(ert-deftest two-rows () + (should (equal [[1] + [1 1]] (rows 2)))) + + +(ert-deftest three-rows () + (should (equal [[1] + [1 1] + [1 2 1]] (rows 3)))) + + +(ert-deftest four-rows () + (should (equal [[1] + [1 1] + [1 2 1] + [1 3 3 1]] (rows 4)))) + + +(ert-deftest five-rows () + (should (equal [[1] + [1 1] + [1 2 1] + [1 3 3 1] + [1 4 6 4 1]] (rows 5)))) + + +(ert-deftest six-rows () + (should (equal [[1] + [1 1] + [1 2 1] + [1 3 3 1] + [1 4 6 4 1] + [1 5 10 10 5 1]] (rows 6)))) + + +(ert-deftest ten-rows () + (should (equal [[1] + [1 1] + [1 2 1] + [1 3 3 1] + [1 4 6 4 1] + [1 5 10 10 5 1] + [1 6 15 20 15 6 1] + [1 7 21 35 35 21 7 1] + [1 8 28 56 70 56 28 8 1] + [1 9 36 84 126 126 84 36 9 1]] (rows 10)))) + + +(provide 'pascals-triangle-test) +;;; pascals-triangle-test.el ends here diff --git a/exercises/practice/pascals-triangle/pascals-triangle.el b/exercises/practice/pascals-triangle/pascals-triangle.el new file mode 100644 index 00000000..9fd2baab --- /dev/null +++ b/exercises/practice/pascals-triangle/pascals-triangle.el @@ -0,0 +1,14 @@ +;;; pascals-triangle.el --- Pascal's Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun rows (count) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'pascals-triangle) +;;; pascals-triangle.el ends here + diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md index 0dae8867..b2bc82ca 100644 --- a/exercises/practice/perfect-numbers/.docs/instructions.md +++ b/exercises/practice/perfect-numbers/.docs/instructions.md @@ -1,24 +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**, **abundant**, or **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**: aliquot sum = number - - 6 is a perfect number because (1 + 2 + 3) = 6 - - 28 is a perfect number because (1 + 2 + 4 + 7 + 14) = 28 -- **Abundant**: aliquot sum > number - - 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**: aliquot sum < number - - 8 is a deficient number because (1 + 2 + 4) = 7 - - Prime numbers are deficient - -Implement a way to determine whether a given number is **perfect**. -Depending on your language track, you may also need to implement a way to determine whether a given number is **abundant** or **deficient**. +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 index cd62fae3..a30cdfdd 100644 --- a/exercises/practice/perfect-numbers/.meta/config.json +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "canweriotnow", "guygastineau", diff --git a/exercises/practice/phone-number/.docs/instructions.append.md b/exercises/practice/phone-number/.docs/instructions.append.md new file mode 100644 index 00000000..cb0a9b9a --- /dev/null +++ b/exercises/practice/phone-number/.docs/instructions.append.md @@ -0,0 +1,11 @@ +# Instructions Append + +In addition to the function, _numbers_, that cleans up phone numbers, you need to write two more functions, _area-code_ and _pprint_ in the Emacs Lisp track. + +Each of the extra functions takes the phone number as its only parameter. + +The first function _area-code_ extracts the area code from the input, while the second function _pprint_ transforms the input to a string in the format of +``` +(NXX) NXX-XXXX +``` +where `N` and `X` are as described above. diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md index 6d3275cd..5d4d3739 100644 --- a/exercises/practice/phone-number/.docs/instructions.md +++ b/exercises/practice/phone-number/.docs/instructions.md @@ -1,22 +1,24 @@ # Instructions -Clean up user-entered phone numbers so that they can be sent SMS messages. +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*. +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 +NXX NXX-XXXX ``` where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9. -Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code (1) if present. +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 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 index d91424ec..60881337 100644 --- a/exercises/practice/phone-number/.meta/config.json +++ b/exercises/practice/phone-number/.meta/config.json @@ -1,7 +1,10 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ - "yurrriq" + "yurrriq", + "borderite" ], "files": { "solution": [ diff --git a/exercises/practice/phone-number/.meta/example.el b/exercises/practice/phone-number/.meta/example.el index 03316fa2..79bf0bcf 100644 --- a/exercises/practice/phone-number/.meta/example.el +++ b/exercises/practice/phone-number/.meta/example.el @@ -2,32 +2,91 @@ ;;; Commentary: -;;; Code: +(define-error 'short-phone-num-error "must not be fewer than 10 digits") +(define-error 'long-phone-num-error "must not be greater than 11 digits") +(define-error 'letters-in-phone-num-error "letters not permitted") +(define-error 'punctuations-in-phone-num-error "punctuations not permitted") +(define-error 'country-code-error "country code must be 1") +(define-error 'area-code-starting-with-0-error "area code cannot start with zero") +(define-error 'area-code-starting-with-1-error "area code cannot start with one") +(define-error 'exchange-code-starting-with-0-error "exchange code cannot start with zero") +(define-error 'exchange-code-starting-with-1-error "exchange code cannot start with one") +(defun char-digit-p (x) + (<= ?0 x ?9)) -(defun numbers (num) - (let ((number (replace-regexp-in-string "[^0-9]+" "" num))) - (cond - ((= (length number) 10) number) - ((and (= (length number) 11) - (string-equal (substring number 0 1) "1")) (substring number 1)) - (t "0000000000")))) +(defun char-lowercase-p (x) + (<= ?a x ?z)) +(defun char-uppercase-p (x) + (<= ?A x ?Z)) -(defun area-code (num) - (substring (numbers num) 0 3)) - +(defun char-alphabetic-p (x) + (or (char-lowercase-p x) + (char-uppercase-p x))) -(defun prefix (num) - (substring (numbers num) 3 6)) +(defun char-punctuation-p (x) + (or (<= 33 x 39) + (= x 42) + (= x 44) + (= x 47) + (<= 58 x 64) + (<= 91 x 96) + (<= 123 x 126))) +(defun negate (pred) + (lambda (&rest args) (apply pred args))) -(defun line-number (num) - (substring (numbers num) 6)) +(defun string-remove (s pred) + (let* ((n (length s)) + (to-str (make-string n ?\0)) + (k 0)) + (dotimes (i n) + (let ((x (aref s i))) + (when (funcall pred x) + (setf (aref to-str k) x) + (cl-incf k)))) + (substring to-str 0 k))) +(defun numbers (num) + "Converts a num string into a string of digits." + (cond + ((cl-find-if #'char-alphabetic-p num) + (signal 'letters-in-phone-num-error num)) + ((cl-find-if #'char-punctuation-p num) + (signal 'punctuations-in-phone-num-error num))) + (let* ((digits (string-remove num (negate #'char-digit-p))) + (n (length digits))) + (cond + ((< n 10) (signal 'short-phone-num-error num)) + ((> n 11) (signal 'long-phone-num-error num))) + (if (= n 11) + (if (= (aref digits 0) ?1) + (setf digits (substring digits 1)) + (signal 'country-code-error num))) + (let ((y (aref digits 0))) + (cond + ((= y ?0) + (signal 'area-code-starting-with-0-error num)) + ((= y ?1) + (signal 'area-code-starting-with-1-error num))) + (let ((y (aref digits 3))) + (cond + ((= y ?0) + (signal 'exchange-code-starting-with-0-error num)) + ((= y ?1) + (signal 'exchange-code-starting-with-1-error num))))) + digits)) + +(defun area-code (num) + (let ((digits (numbers num))) + (substring digits 0 3))) (defun pprint (num) - (format "(%s) %s-%s" (area-code num) (prefix num) (line-number num))) + (let ((digits (numbers num))) + (format "(%s) %s-%s" (substring digits 0 3) + (substring digits 3 6) + (substring digits 6 10)))) (provide 'phone-number) diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml index 24dbf07a..4fe47d59 100644 --- a/exercises/practice/phone-number/.meta/tests.toml +++ b/exercises/practice/phone-number/.meta/tests.toml @@ -82,3 +82,4 @@ 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.el b/exercises/practice/phone-number/phone-number-test.el index b89eb82b..b1e66134 100644 --- a/exercises/practice/phone-number/phone-number-test.el +++ b/exercises/practice/phone-number/phone-number-test.el @@ -1,4 +1,4 @@ -;;; phone-number-test.el --- Tests for phone-number (exercism) -*- lexical-binding: t; -*- +;;; phone-number-test.el --- Phone Number (exercism) -*- lexical-binding: t; -*- ;;; Commentary: @@ -9,47 +9,71 @@ (declare-function area-code "phone-number.el" (num)) (declare-function pprint "phone-number.el" (num)) -(ert-deftest cleans-number-test () - (should (equal (numbers "(123) 456-7890") "1234567890"))) +(ert-deftest cleans-the-number () + (should (string= (numbers "(223) 456-7890") "2234567890"))) +(ert-deftest cleans-numbers-with-dots () + (should (string= (numbers "223.456.7890") "2234567890"))) -(ert-deftest cleans-numbers-with-dots-test () - (should (equal (numbers "123.456.7890") "1234567890"))) +(ert-deftest cleans-numbers-with-multiple-spaces () + (should (string= (numbers "223 456 7890 ") "2234567890"))) +(ert-deftest invalid-when-9-digits () + (should-error (numbers "123456789") :type 'short-phone-num-error)) -(ert-deftest valid-when-11-digits-and-first-is-1-test () - (should (equal (numbers "11234567890") "1234567890"))) +(ert-deftest invalid-when-11-digits-does-not-start-with-a-1 () + (should-error (numbers "22234567890") :type 'country-code-error)) +(ert-deftest valid-when-11-digits-and-starting-with-1 () + (should (string= (numbers "12234567890") "2234567890"))) -(ert-deftest invalid-when-11-digits-test () - (should (equal (numbers "21234567890") "0000000000"))) +(ert-deftest valid-when-11-digits-and-starting-with-1-even-with-punctuation () + (should (string= (numbers "+1 (223) 456-7890") "2234567890"))) +(ert-deftest invalid-when-more-than-11-digits () + (should-error (numbers "321234567890") :type 'long-phone-num-error)) -(ert-deftest invalid-when-9-digits-test () - (should (equal (numbers "123456789") "0000000000"))) +(ert-deftest invalid-with-letters () + (should-error (numbers "523-abc-7890") :type 'letters-in-phone-num-error)) -(ert-deftest invalid-when-more-than-11-digits-test () - (should (equal (numbers "321234567890") "0000000000"))) +(ert-deftest invalid-with-punctuations () + (should-error (numbers "523-@:!-7890") :type 'punctuations-in-phone-num-error)) -(ert-deftest invalid-with-letters () - (should (equal (numbers "523-abc-7890") "0000000000"))) +(ert-deftest invalid-if-area-code-starts-with-0 () + (should-error (numbers "(023) 456-7890") :type 'area-code-starting-with-0-error)) +(ert-deftest invalid-if-area-code-starts-with-1 () + (should-error (numbers "(123) 456-7890") :type 'area-code-starting-with-1-error)) -(ert-deftest invalid-with-punctuations () - (should (equal (numbers "523-@:!-7890") "0000000000"))) +(ert-deftest invalid-if-exchange-code-starts-with-0 () + (should-error (numbers "(223) 056-7890") :type 'exchange-code-starting-with-0-error)) + +(ert-deftest invalid-if-exchange-code-starts-with-1 () + (should-error (numbers "(223) 156-7890") :type 'exchange-code-starting-with-1-error)) +(ert-deftest invalid-if-area-code-starts-with-0-on-valid-11-digit-number () + (should-error (numbers "1 (023) 456-7890") :type 'area-code-starting-with-0-error)) + +(ert-deftest invalid-if-area-code-starts-with-1-on-valid-11-digit-number () + (should-error (numbers "1 (123) 456-7890") :type 'area-code-starting-with-1-error)) + +(ert-deftest invalid-if-exchange-code-starts-with-0-on-valid-11-digit-number () + (should-error (numbers "1 (223) 056-7890") :type 'exchange-code-starting-with-0-error)) + +(ert-deftest invalid-if-exchange-code-starts-with-1-on-valid-11-digit-number () + (should-error (numbers "1 (223) 156-7890") :type 'exchange-code-starting-with-1-error)) (ert-deftest area-code-test () - (should (equal (area-code "1234567890") "123"))) + (should (equal (area-code "2234567890") "223"))) (ert-deftest pprint-test () - (should (equal (pprint "1234567890") "(123) 456-7890"))) + (should (equal (pprint "2234567890") "(223) 456-7890"))) (ert-deftest pprint-full-us-phone-number-test () - (should (equal (pprint "11234567890") "(123) 456-7890"))) + (should (equal (pprint "12234567890") "(223) 456-7890"))) - -(provide 'phone-number) +(provide 'phone-number-test) ;;; phone-number-test.el ends here + diff --git a/exercises/practice/phone-number/phone-number.el b/exercises/practice/phone-number/phone-number.el index 61cac595..a08eee0c 100644 --- a/exercises/practice/phone-number/phone-number.el +++ b/exercises/practice/phone-number/phone-number.el @@ -1,4 +1,4 @@ -;;; phone-number.el --- phone-number Exercise (exercism) -*- lexical-binding: t; -*- +;;; phone-number.el --- phone-number (exercism) -*- lexical-binding: t; -*- ;;; Commentary: diff --git a/exercises/practice/pig-latin/.docs/instructions.md b/exercises/practice/pig-latin/.docs/instructions.md new file mode 100644 index 00000000..a9645ac2 --- /dev/null +++ b/exercises/practice/pig-latin/.docs/instructions.md @@ -0,0 +1,46 @@ +# Instructions + +Your task is to translate text from English to Pig Latin. +The translation is defined using four rules, which look at the pattern of vowels and consonants at the beginning of a word. +These rules look at each word's use of vowels and consonants: + +- vowels: the letters `a`, `e`, `i`, `o`, and `u` +- consonants: the other 21 letters of the English alphabet + +## Rule 1 + +If a word begins with a vowel, or starts with `"xr"` or `"yt"`, add an `"ay"` sound to the end of the word. + +For example: + +- `"apple"` -> `"appleay"` (starts with vowel) +- `"xray"` -> `"xrayay"` (starts with `"xr"`) +- `"yttria"` -> `"yttriaay"` (starts with `"yt"`) + +## Rule 2 + +If a word begins with one or more consonants, first move those consonants to the end of the word and then add an `"ay"` sound to the end of the word. + +For example: + +- `"pig"` -> `"igp"` -> `"igpay"` (starts with single consonant) +- `"chair"` -> `"airch"` -> `"airchay"` (starts with multiple consonants) +- `"thrush"` -> `"ushthr"` -> `"ushthray"` (starts with multiple consonants) + +## Rule 3 + +If a word starts with zero or more consonants followed by `"qu"`, first move those consonants (if any) and the `"qu"` part to the end of the word, and then add an `"ay"` sound to the end of the word. + +For example: + +- `"quick"` -> `"ickqu"` -> `"ickquay"` (starts with `"qu"`, no preceding consonants) +- `"square"` -> `"aresqu"` -> `"aresquay"` (starts with one consonant followed by `"qu`") + +## Rule 4 + +If a word starts with one or more consonants followed by `"y"`, first move the consonants preceding the `"y"`to the end of the word, and then add an `"ay"` sound to the end of the word. + +Some examples: + +- `"my"` -> `"ym"` -> `"ymay"` (starts with single consonant followed by `"y"`) +- `"rhythm"` -> `"ythmrh"` -> `"ythmrhay"` (starts with multiple consonants followed by `"y"`) diff --git a/exercises/practice/pig-latin/.docs/introduction.md b/exercises/practice/pig-latin/.docs/introduction.md new file mode 100644 index 00000000..04baa475 --- /dev/null +++ b/exercises/practice/pig-latin/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +Your parents have challenged you and your sibling to a game of two-on-two basketball. +Confident they'll win, they let you score the first couple of points, but then start taking over the game. +Needing a little boost, you start speaking in [Pig Latin][pig-latin], which is a made-up children's language that's difficult for non-children to understand. +This will give you the edge to prevail over your parents! + +[pig-latin]: https://en.wikipedia.org/wiki/Pig_latin diff --git a/exercises/practice/pig-latin/.meta/config.json b/exercises/practice/pig-latin/.meta/config.json new file mode 100644 index 00000000..86d6dc4d --- /dev/null +++ b/exercises/practice/pig-latin/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "pig-latin.el" + ], + "test": [ + "pig-latin-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement a program that translates from English to Pig Latin.", + "source": "The Pig Latin exercise at Test First Teaching by Ultrasaurus", + "source_url": "https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/" +} diff --git a/exercises/practice/pig-latin/.meta/example.el b/exercises/practice/pig-latin/.meta/example.el new file mode 100644 index 00000000..32322d0b --- /dev/null +++ b/exercises/practice/pig-latin/.meta/example.el @@ -0,0 +1,52 @@ +;;; pig-latin.el --- Pig Latin (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defvar +vowels+ '("a" "e" "i" "o" "u" "xr" "yt")) + +(defun custom-split-string (str) + "Split STR into a list of words." + (if (zerop (length str)) + nil + (let* ((split-at (string-match " " str)) + (word (if split-at (substring str 0 split-at) str)) + (rest-str (if split-at (substring str (1+ split-at)) ""))) + (cons word (custom-split-string rest-str))))) + +(defun starts-with-p (word search-list) + "Check if WORD starts with any prefix in SEARCH-LIST." + (if search-list + (if (string-prefix-p (car search-list) word) + t + (starts-with-p word (cdr search-list))))) + +(defun stop-p (word index) + "Determine if processing should stop based on WORD and INDEX." + (or (starts-with-p word +vowels+) + (and (char-equal ?y (aref word 0)) (= index 1)))) + +(defun take-consonants (word &optional index) + "Calculate the number of consonants at the start of WORD. +INDEX is used to keep track of the current position." + (unless index + (setq index 0)) + (cond + ((stop-p word index) index) + ((starts-with-p word '("qu")) (take-consonants (substring word 2) (+ 2 index))) + (t (take-consonants (substring word 1) (1+ index))))) + +(defun translate-word (word) + "Translate a single WORD into Pig Latin." + (let ((index (take-consonants word))) + (concat (substring word index) (substring word 0 index) "ay"))) + +(defun translate (phrase) + "Translate a PHRASE into Pig Latin." + (mapconcat #'translate-word (split-string phrase) " ")) + + +(provide 'pig-latin) +;;; pig-latin.el ends here + diff --git a/exercises/practice/pig-latin/.meta/tests.toml b/exercises/practice/pig-latin/.meta/tests.toml new file mode 100644 index 00000000..d524305b --- /dev/null +++ b/exercises/practice/pig-latin/.meta/tests.toml @@ -0,0 +1,79 @@ +# 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. + +[11567f84-e8c6-4918-aedb-435f0b73db57] +description = "ay is added to words that start with vowels -> word beginning with a" + +[f623f581-bc59-4f45-9032-90c3ca9d2d90] +description = "ay is added to words that start with vowels -> word beginning with e" + +[7dcb08b3-23a6-4e8a-b9aa-d4e859450d58] +description = "ay is added to words that start with vowels -> word beginning with i" + +[0e5c3bff-266d-41c8-909f-364e4d16e09c] +description = "ay is added to words that start with vowels -> word beginning with o" + +[614ba363-ca3c-4e96-ab09-c7320799723c] +description = "ay is added to words that start with vowels -> word beginning with u" + +[bf2538c6-69eb-4fa7-a494-5a3fec911326] +description = "ay is added to words that start with vowels -> word beginning with a vowel and followed by a qu" + +[e5be8a01-2d8a-45eb-abb4-3fcc9582a303] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with p" + +[d36d1e13-a7ed-464d-a282-8820cb2261ce] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with k" + +[d838b56f-0a89-4c90-b326-f16ff4e1dddc] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with x" + +[bce94a7a-a94e-4e2b-80f4-b2bb02e40f71] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with q without a following u" + +[e59dbbe8-ccee-4619-a8e9-ce017489bfc0] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with consonant and vowel containing qu" + +[c01e049a-e3e2-451c-bf8e-e2abb7e438b8] +description = "some letter clusters are treated like a single consonant -> word beginning with ch" + +[9ba1669e-c43f-4b93-837a-cfc731fd1425] +description = "some letter clusters are treated like a single consonant -> word beginning with qu" + +[92e82277-d5e4-43d7-8dd3-3a3b316c41f7] +description = "some letter clusters are treated like a single consonant -> word beginning with qu and a preceding consonant" + +[79ae4248-3499-4d5b-af46-5cb05fa073ac] +description = "some letter clusters are treated like a single consonant -> word beginning with th" + +[e0b3ae65-f508-4de3-8999-19c2f8e243e1] +description = "some letter clusters are treated like a single consonant -> word beginning with thr" + +[20bc19f9-5a35-4341-9d69-1627d6ee6b43] +description = "some letter clusters are treated like a single consonant -> word beginning with sch" + +[54b796cb-613d-4509-8c82-8fbf8fc0af9e] +description = "some letter clusters are treated like a single vowel -> word beginning with yt" + +[8c37c5e1-872e-4630-ba6e-d20a959b67f6] +description = "some letter clusters are treated like a single vowel -> word beginning with xr" + +[a4a36d33-96f3-422c-a233-d4021460ff00] +description = "position of y in a word determines if it is a consonant or a vowel -> y is treated like a consonant at the beginning of a word" + +[adc90017-1a12-4100-b595-e346105042c7] +description = "position of y in a word determines if it is a consonant or a vowel -> y is treated like a vowel at the end of a consonant cluster" + +[29b4ca3d-efe5-4a95-9a54-8467f2e5e59a] +description = "position of y in a word determines if it is a consonant or a vowel -> y as second letter in two letter word" + +[44616581-5ce3-4a81-82d0-40c7ab13d2cf] +description = "phrases are translated -> a whole phrase" diff --git a/exercises/practice/pig-latin/pig-latin-test.el b/exercises/practice/pig-latin/pig-latin-test.el new file mode 100644 index 00000000..93996c21 --- /dev/null +++ b/exercises/practice/pig-latin/pig-latin-test.el @@ -0,0 +1,106 @@ +;;; pig-latin-test.el --- Pig Latin (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "pig-latin.el") +(declare-function translate "pig-latin.el" (phrase)) + + +(ert-deftest word-beginning-with-a () + (should (string= (translate "apple") "appleay"))) + + +(ert-deftest word-beginning-with-e () + (should (string= (translate "ear") "earay"))) + + +(ert-deftest word-beginning-with-i () + (should (string= (translate "igloo") "iglooay"))) + + +(ert-deftest word-beginning-with-o () + (should (string= (translate "object") "objectay"))) + + +(ert-deftest word-beginning-with-u () + (should (string= (translate "under") "underay"))) + + +(ert-deftest word-beginning-with-a-vowel-and-followed-by-a-qu () + (should (string= (translate "equal") "equalay"))) + + +(ert-deftest word-beginning-with-p () + (should (string= (translate "pig") "igpay"))) + + +(ert-deftest word-beginning-with-k () + (should (string= (translate "koala") "oalakay"))) + + +(ert-deftest word-beginning-with-x () + (should (string= (translate "xenon") "enonxay"))) + + +(ert-deftest word-beginning-with-q-without-a-following-u () + (should (string= (translate "qat") "atqay"))) + + +(ert-deftest word-beginning-with-consonant-and-vowel-containing-qu () + (should (string= (translate "liquid") "iquidlay"))) + + +(ert-deftest word-beginning-with-ch () + (should (string= (translate "chair") "airchay"))) + + +(ert-deftest word-beginning-with-qu () + (should (string= (translate "queen") "eenquay"))) + + +(ert-deftest word-beginning-with-qu-and-a-preceding-consonant () + (should (string= (translate "square") "aresquay"))) + + +(ert-deftest word-beginning-with-th () + (should (string= (translate "therapy") "erapythay"))) + + +(ert-deftest word-beginning-with-thr () + (should (string= (translate "thrush") "ushthray"))) + + +(ert-deftest word-beginning-with-sch () + (should (string= (translate "school") "oolschay"))) + + +(ert-deftest word-beginning-with-yt () + (should (string= (translate "yttria") "yttriaay"))) + + +(ert-deftest word-beginning-with-xr () + (should (string= (translate "xray") "xrayay"))) + + +(ert-deftest y-is-treated-like-a-consonant-at-the-beginning-of-a-word () + (should (string= (translate "yellow") "ellowyay"))) + + +(ert-deftest y-is-treated-like-a-vowel-at-the-end-of-a-consonant-cluster () + (should (string= (translate "rhythm") "ythmrhay"))) + + +(ert-deftest y-as-second-letter-in-two-letter-word () + (should (string= (translate "my") "ymay"))) + + +(ert-deftest a-whole-phrase () + (should (string= (translate "quick fast run") "ickquay astfay unray"))) + + +(provide 'pig-latin-test) +;;; pig-latin-test.el ends here + diff --git a/exercises/practice/pig-latin/pig-latin.el b/exercises/practice/pig-latin/pig-latin.el new file mode 100644 index 00000000..5824d6e0 --- /dev/null +++ b/exercises/practice/pig-latin/pig-latin.el @@ -0,0 +1,14 @@ +;;; pig-latin.el --- Pig Latin (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun translate (phrase) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'pig-latin) +;;; pig-latin.el ends here + diff --git a/exercises/practice/prime-factors/.docs/instructions.md b/exercises/practice/prime-factors/.docs/instructions.md new file mode 100644 index 00000000..252cc8ee --- /dev/null +++ b/exercises/practice/prime-factors/.docs/instructions.md @@ -0,0 +1,36 @@ +# Instructions + +Compute the prime factors of a given natural number. + +A prime number is only evenly divisible by itself and 1. + +Note that 1 is not a prime number. + +## Example + +What are the prime factors of 60? + +- Our first divisor is 2. + 2 goes into 60, leaving 30. +- 2 goes into 30, leaving 15. + - 2 doesn't go cleanly into 15. + So let's move on to our next divisor, 3. +- 3 goes cleanly into 15, leaving 5. + - 3 does not go cleanly into 5. + The next possible factor is 4. + - 4 does not go cleanly into 5. + The next possible factor is 5. +- 5 does go cleanly into 5. +- We're left only with 1, so now, we're done. + +Our successful divisors in that computation represent the list of prime factors of 60: 2, 2, 3, and 5. + +You can check this yourself: + +```text +2 * 2 * 3 * 5 += 4 * 15 += 60 +``` + +Success! diff --git a/exercises/practice/prime-factors/.meta/config.json b/exercises/practice/prime-factors/.meta/config.json new file mode 100644 index 00000000..2758856f --- /dev/null +++ b/exercises/practice/prime-factors/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "prime-factors.el" + ], + "test": [ + "prime-factors-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Compute the prime factors of a given natural number.", + "source": "The Prime Factors Kata by Uncle Bob", + "source_url": "https://web.archive.org/web/20221026171801/http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata" +} diff --git a/exercises/practice/prime-factors/.meta/example.el b/exercises/practice/prime-factors/.meta/example.el new file mode 100644 index 00000000..146f34a2 --- /dev/null +++ b/exercises/practice/prime-factors/.meta/example.el @@ -0,0 +1,19 @@ +;;; prime-factors.el --- Prime Factors (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun factors-from (start value) + (unless (= value 1) + (cl-loop for p from start + until (= 0 (% value p)) + finally return (cons p (factors-from p (/ value p)))))) + +(defun factors (value) + (factors-from 2 value)) + +(provide 'prime-factors) +;;; prime-factors.el ends here diff --git a/exercises/practice/prime-factors/.meta/tests.toml b/exercises/practice/prime-factors/.meta/tests.toml new file mode 100644 index 00000000..6f9cc8ce --- /dev/null +++ b/exercises/practice/prime-factors/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[924fc966-a8f5-4288-82f2-6b9224819ccd] +description = "no factors" + +[17e30670-b105-4305-af53-ddde182cb6ad] +description = "prime number" + +[238d57c8-4c12-42ef-af34-ae4929f94789] +description = "another prime number" + +[f59b8350-a180-495a-8fb1-1712fbee1158] +description = "square of a prime" + +[756949d3-3158-4e3d-91f2-c4f9f043ee70] +description = "product of first prime" + +[bc8c113f-9580-4516-8669-c5fc29512ceb] +description = "cube of a prime" + +[7d6a3300-a4cb-4065-bd33-0ced1de6cb44] +description = "product of second prime" + +[073ac0b2-c915-4362-929d-fc45f7b9a9e4] +description = "product of third prime" + +[6e0e4912-7fb6-47f3-a9ad-dbcd79340c75] +description = "product of first and second prime" + +[00485cd3-a3fe-4fbe-a64a-a4308fc1f870] +description = "product of primes and non-primes" + +[02251d54-3ca1-4a9b-85e1-b38f4b0ccb91] +description = "product of primes" + +[070cf8dc-e202-4285-aa37-8d775c9cd473] +description = "factors include a large prime" diff --git a/exercises/practice/prime-factors/prime-factors-test.el b/exercises/practice/prime-factors/prime-factors-test.el new file mode 100644 index 00000000..c361ef78 --- /dev/null +++ b/exercises/practice/prime-factors/prime-factors-test.el @@ -0,0 +1,60 @@ +;;; prime-factors-test.el --- Tests for Prime Factors (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "prime-factors.el") +(declare-function factors "prime-factors.el" (value)) + + +(ert-deftest no-factors () + (should (equal '() (factors 1)))) + + +(ert-deftest prime-number () + (should (equal '(2) (factors 2)))) + + +(ert-deftest another-prime-number () + (should (equal '(3) (factors 3)))) + + +(ert-deftest square-of-a-prime () + (should (equal '(3 3) (factors 9)))) + + +(ert-deftest product-of-first-prime () + (should (equal '(2 2) (factors 4)))) + + +(ert-deftest cube-of-a-prime () + (should (equal '(2 2 2) (factors 8)))) + + +(ert-deftest product-of-second-prime () + (should (equal '(3 3 3) (factors 27)))) + + +(ert-deftest product-of-third-prime () + (should (equal '(5 5 5 5) (factors 625)))) + + +(ert-deftest product-of-first-and-second-prime () + (should (equal '(2 3) (factors 6)))) + + +(ert-deftest product-of-primes-and-non-primes () + (should (equal '(2 2 3) (factors 12)))) + + +(ert-deftest product-of-primes () + (should (equal '(5 17 23 461) (factors 901255)))) + + +(ert-deftest factors-include-a-large-prime () + (should (equal '(11 9539 894119) (factors 93819012551)))) + + +(provide 'prime-factors-test) +;;; prime-factors-test.el ends here diff --git a/exercises/practice/prime-factors/prime-factors.el b/exercises/practice/prime-factors/prime-factors.el new file mode 100644 index 00000000..076c9578 --- /dev/null +++ b/exercises/practice/prime-factors/prime-factors.el @@ -0,0 +1,13 @@ +;;; prime-factors.el --- Prime Factors (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun factors (value) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'prime-factors) +;;; prime-factors.el ends here diff --git a/exercises/practice/protein-translation/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md new file mode 100644 index 00000000..44880802 --- /dev/null +++ b/exercises/practice/protein-translation/.docs/instructions.md @@ -0,0 +1,45 @@ +# Instructions + +Translate RNA sequences into proteins. + +RNA can be broken into three-nucleotide sequences called codons, and then translated to a protein like so: + +RNA: `"AUGUUUUCU"` => translates to + +Codons: `"AUG", "UUU", "UCU"` +=> which become a protein with the following sequence => + +Protein: `"Methionine", "Phenylalanine", "Serine"` + +There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise. +If it works for one codon, the program should work for all of them. +However, feel free to expand the list in the test suite to include them all. + +There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated. + +All subsequent codons after are ignored, like this: + +RNA: `"AUGUUUUCUUAAAUG"` => + +Codons: `"AUG", "UUU", "UCU", "UAA", "AUG"` => + +Protein: `"Methionine", "Phenylalanine", "Serine"` + +Note the stop codon `"UAA"` terminates the translation and the final methionine is not translated into the protein sequence. + +Below are the codons and resulting amino acids needed for the exercise. + +| 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 | + +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..bef5c3b6 --- /dev/null +++ b/exercises/practice/protein-translation/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "protein-translation.el" + ], + "test": [ + "protein-translation-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Translate RNA sequences into proteins.", + "source": "Tyler Long" +} diff --git a/exercises/practice/protein-translation/.meta/example.el b/exercises/practice/protein-translation/.meta/example.el new file mode 100644 index 00000000..255b7aff --- /dev/null +++ b/exercises/practice/protein-translation/.meta/example.el @@ -0,0 +1,44 @@ +;;; protein-translation.el --- Protein Translation (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun translate (codon) + (pcase codon + ("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" nil) + ("UAG" nil) + ("UGA" nil) + (otherwise (error "Invalid codon")))) + +(defun proteins (strand) + (let* ((len (length strand)) + (stop (- len (% len 3)))) + (cl-labels + ((recur (index) + (if (= index stop) + (unless (= index len) (error "Invalid codon")) + (let* ((next (+ index 3)) + (protein (translate (substring strand index next)))) + (when protein (cons protein (recur next))))))) + (recur 0)))) + +(provide 'protein-translation) +;;; protein-translation.el ends here + diff --git a/exercises/practice/protein-translation/.meta/tests.toml b/exercises/practice/protein-translation/.meta/tests.toml new file mode 100644 index 00000000..de680e39 --- /dev/null +++ b/exercises/practice/protein-translation/.meta/tests.toml @@ -0,0 +1,105 @@ +# 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" +include = false + +[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.el b/exercises/practice/protein-translation/protein-translation-test.el new file mode 100644 index 00000000..3e435098 --- /dev/null +++ b/exercises/practice/protein-translation/protein-translation-test.el @@ -0,0 +1,161 @@ +;;; protein-translation-test.el --- Tests for Protein Translation (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "protein-translation.el") +(declare-function proteins "protein-translation.el" (strand)) + + +(ert-deftest empty-rna-sequence-results-in-no-proteins () + (should (equal '() + (proteins "")))) + + +(ert-deftest methionine-rna-sequence () + (should (equal '("Methionine") + (proteins "AUG")))) + + +(ert-deftest phenylalanine-rna-sequence-1 () + (should (equal '("Phenylalanine") + (proteins "UUU")))) + + +(ert-deftest phenylalanine-rna-sequence-2 () + (should (equal '("Phenylalanine") + (proteins "UUC")))) + + +(ert-deftest leucine-rna-sequence-1 () + (should (equal '("Leucine") + (proteins "UUA")))) + + +(ert-deftest leucine-rna-sequence-2 () + (should (equal '("Leucine") + (proteins "UUG")))) + + +(ert-deftest serine-rna-sequence-1 () + (should (equal '("Serine") + (proteins "UCU")))) + + +(ert-deftest serine-rna-sequence-2 () + (should (equal '("Serine") + (proteins "UCC")))) + + +(ert-deftest serine-rna-sequence-3 () + (should (equal '("Serine") + (proteins "UCA")))) + + +(ert-deftest serine-rna-sequence-4 () + (should (equal '("Serine") + (proteins "UCG")))) + + +(ert-deftest tyrosine-rna-sequence-1 () + (should (equal '("Tyrosine") + (proteins "UAU")))) + + +(ert-deftest tyrosine-rna-sequence-2 () + (should (equal '("Tyrosine") + (proteins "UAC")))) + + +(ert-deftest cysteine-rna-sequence-1 () + (should (equal '("Cysteine") + (proteins "UGU")))) + + +(ert-deftest cysteine-rna-sequence-2 () + (should (equal '("Cysteine") + (proteins "UGC")))) + + +(ert-deftest tryptophan-rna-sequence () + (should (equal '("Tryptophan") + (proteins "UGG")))) + + +(ert-deftest stop-codon-rna-sequence-1 () + (should (equal '() + (proteins "UAA")))) + + +(ert-deftest stop-codon-rna-sequence-2 () + (should (equal '() + (proteins "UAG")))) + + +(ert-deftest stop-codon-rna-sequence-3 () + (should (equal '() + (proteins "UGA")))) + + +(ert-deftest sequence-of-two-protein-codons-translates-into-proteins () + (should (equal '("Phenylalanine" "Phenylalanine") + (proteins "UUUUUU")))) + + +(ert-deftest sequence-of-two-different-protein-codons-translates-into-proteins () + (should (equal '("Leucine" "Leucine") + (proteins "UUAUUG")))) + + +(ert-deftest translate-rna-strand-into-correct-protein-list () + (should (equal '("Methionine" "Phenylalanine" "Tryptophan") + (proteins "AUGUUUUGG")))) + + +(ert-deftest translation-stops-if-stop-codon-at-beginning-of-sequence () + (should (equal '() + (proteins "UAGUGG")))) + + +(ert-deftest translation-stops-if-stop-codon-at-end-of-two-codon-sequence () + (should (equal '("Tryptophan") + (proteins "UGGUAG")))) + + +(ert-deftest translation-stops-if-stop-codon-at-end-of-three-codon-sequence () + (should (equal '("Methionine" "Phenylalanine") + (proteins "AUGUUUUAA")))) + + +(ert-deftest translation-stops-if-stop-codon-in-middle-of-three-codon-sequence () + (should (equal '("Tryptophan") + (proteins "UGGUAGUGG")))) + + +(ert-deftest translation-stops-if-stop-codon-in-middle-of-six-codon-sequence () + (should (equal '("Tryptophan" "Cysteine" "Tyrosine") + (proteins "UGGUGUUAUUAAUGGUUU")))) + + +(ert-deftest sequence-of-two-non-stop-codons-does-not-translate-to-a-stop-codon () + (should (equal '("Methionine" "Methionine") + (proteins "AUGAUG")))) + + +(ert-deftest unknown-amino-acids-not-part-of-a-codon-cant-translate () + (should-error (proteins "XYZ"))) + + +(ert-deftest incomplete-rna-sequence-cant-translate () + (should-error (proteins "AUGU"))) + + +(ert-deftest incomplete-rna-sequence-can-translate-if-valid-until-a-stop-codon () + (should (equal '("Phenylalanine" "Phenylalanine") + (proteins "UUCUUCUAAUGGU")))) + + +(provide 'protein-translation-test) +;;; protein-translation-test.el ends here diff --git a/exercises/practice/protein-translation/protein-translation.el b/exercises/practice/protein-translation/protein-translation.el new file mode 100644 index 00000000..1d5f5366 --- /dev/null +++ b/exercises/practice/protein-translation/protein-translation.el @@ -0,0 +1,14 @@ +;;; protein-translation.el --- Protein Translation (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun proteins (strand) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'protein-translation) +;;; protein-translation.el ends here + 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..ff212aa9 --- /dev/null +++ b/exercises/practice/proverb/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "proverb.el" + ], + "test": [ + "proverb-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/proverb/.meta/example.el new file mode 100644 index 00000000..083270bd --- /dev/null +++ b/exercises/practice/proverb/.meta/example.el @@ -0,0 +1,19 @@ +;;; proverb.el --- Proverb (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun recite (strings) + (let ((n (length strings))) + (cond ((= n 0) '()) + ((= n 1) (list (format "And all for the want of a %s." (car strings)))) + (t (append (cl-loop for i from 0 to (- n 2) + collect (format "For want of a %s the %s was lost." (nth i strings) (nth (1+ i) strings))) + (list (format "And all for the want of a %s." (car strings)))))))) + + +(provide 'proverb) +;;; proverb.el ends here + 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.el b/exercises/practice/proverb/proverb-test.el new file mode 100644 index 00000000..8f5f1412 --- /dev/null +++ b/exercises/practice/proverb/proverb-test.el @@ -0,0 +1,48 @@ +;;; proverb-test.el --- Proverb (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "proverb.el") +(declare-function recite "proverb.el" (strings)) + + +(ert-deftest zero-pieces () + (should (equal (recite '()) '()))) + + +(ert-deftest one-piece () + (should (equal (recite '("nail")) '("And all for the want of a nail.")))) + + +(ert-deftest two-pieces () + (should (equal (recite '("nail" "shoe")) '("For want of a nail the shoe was lost." "And all for the want of a nail.")))) + + +(ert-deftest three-pieces () + (should (equal (recite '("nail" "shoe" "horse")) '("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.")))) + + +(ert-deftest full-proverb () + (should (equal (recite '("nail" "shoe" "horse" "rider" "message" "battle" "kingdom")) + '("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.")))) + + +(ert-deftest four-pieces-modernized () + (should (equal (recite '("pin" "gun" "soldier" "battle")) + '("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.")))) + + +(provide 'proverb-test) +;;; proverb-test.el ends here diff --git a/exercises/practice/proverb/proverb.el b/exercises/practice/proverb/proverb.el new file mode 100644 index 00000000..dd9ae8b8 --- /dev/null +++ b/exercises/practice/proverb/proverb.el @@ -0,0 +1,14 @@ +;;; proverb.el --- Proverb (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun recite (strings) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'proverb) +;;; proverb.el ends here + diff --git a/exercises/practice/pythagorean-triplet/.docs/instructions.md b/exercises/practice/pythagorean-triplet/.docs/instructions.md new file mode 100644 index 00000000..ced833d7 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.docs/instructions.md @@ -0,0 +1,23 @@ +# Description + +A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for which, + +```text +a² + b² = c² +``` + +and such that, + +```text +a < b < c +``` + +For example, + +```text +3² + 4² = 5². +``` + +Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`. + +For example, with N = 1000, there is exactly one Pythagorean triplet for which `a + b + c = 1000`: `{200, 375, 425}`. diff --git a/exercises/practice/pythagorean-triplet/.docs/introduction.md b/exercises/practice/pythagorean-triplet/.docs/introduction.md new file mode 100644 index 00000000..3453c6ed --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.docs/introduction.md @@ -0,0 +1,19 @@ +# Introduction + +You are an accomplished problem-solver, known for your ability to tackle the most challenging mathematical puzzles. +One evening, you receive an urgent letter from an inventor called the Triangle Tinkerer, who is working on a groundbreaking new project. +The letter reads: + +> Dear Mathematician, +> +> I need your help. +> I am designing a device that relies on the unique properties of Pythagorean triplets — sets of three integers that satisfy the equation a² + b² = c². +> This device will revolutionize navigation, but for it to work, I must program it with every possible triplet where the sum of a, b, and c equals a specific number, N. +> Calculating these triplets by hand would take me years, but I hear you are more than up to the task. +> +> Time is of the essence. +> The future of my invention — and perhaps even the future of mathematical innovation — rests on your ability to solve this problem. + +Motivated by the importance of the task, you set out to find all Pythagorean triplets that satisfy the condition. +Your work could have far-reaching implications, unlocking new possibilities in science and engineering. +Can you rise to the challenge and make history? diff --git a/exercises/practice/pythagorean-triplet/.meta/config.json b/exercises/practice/pythagorean-triplet/.meta/config.json new file mode 100644 index 00000000..6ed6cc09 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "pythagorean-triplet.el" + ], + "test": [ + "pythagorean-triplet-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given an integer N, find all Pythagorean triplets for which a + b + c = N.", + "source": "A variation of Problem 9 from Project Euler", + "source_url": "https://projecteuler.net/problem=9" +} diff --git a/exercises/practice/pythagorean-triplet/.meta/example.el b/exercises/practice/pythagorean-triplet/.meta/example.el new file mode 100644 index 00000000..51f1ecb8 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.meta/example.el @@ -0,0 +1,30 @@ +;;; pythagorean-triplet.el --- Pythagorean Triplet (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +; For every Pythagorean triplet with total a + b + c = n, +; a² + b² = c² +; <=> a² + b² = (n - a - b)², substituting c +; <=> 0 = n² - 2*n*a - 2*n*b + 2*a*b +; <=> (2*n - 2*a) b = (n² - 2*n*a) +; <=> b = (n² - 2*n*a) / (2*n - 2*a) + +;;; Code: + +(require 'cl-lib) + +(defun triplets-with-sum (n) + (cl-labels + ((recur (start) + (cl-loop for a from start + for numerator = (* n (- n a a)) + for denominator = (* 2 (- n a)) + for b = (/ numerator denominator) + always (< a b) + until (= 0 (% numerator denominator)) + finally return (cons (list a b (- n a b)) (recur (1+ a)))))) + (unless (< n 2) + (recur 1)))) + +(provide 'pythagorean-triplet) +;;; pythagorean-triplet.el ends here diff --git a/exercises/practice/pythagorean-triplet/.meta/tests.toml b/exercises/practice/pythagorean-triplet/.meta/tests.toml new file mode 100644 index 00000000..719620a9 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.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. + +[a19de65d-35b8-4480-b1af-371d9541e706] +description = "triplets whose sum is 12" + +[48b21332-0a3d-43b2-9a52-90b2a6e5c9f5] +description = "triplets whose sum is 108" + +[dffc1266-418e-4daa-81af-54c3e95c3bb5] +description = "triplets whose sum is 1000" + +[5f86a2d4-6383-4cce-93a5-e4489e79b186] +description = "no matching triplets for 1001" + +[bf17ba80-1596-409a-bb13-343bdb3b2904] +description = "returns all matching triplets" + +[9d8fb5d5-6c6f-42df-9f95-d3165963ac57] +description = "several matching triplets" + +[f5be5734-8aa0-4bd1-99a2-02adcc4402b4] +description = "triplets for large number" diff --git a/exercises/practice/pythagorean-triplet/pythagorean-triplet-test.el b/exercises/practice/pythagorean-triplet/pythagorean-triplet-test.el new file mode 100644 index 00000000..81dbebf8 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/pythagorean-triplet-test.el @@ -0,0 +1,53 @@ +;;; pythagorean-triplet-test.el --- Tests for Pythagorean Triplet (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "pythagorean-triplet.el") +(declare-function triplets-with-sum "pythagorean-triplet.el" (n)) + + +(ert-deftest triplets-whose-sum-is-12 () + (should (equal '((3 4 5)) (triplets-with-sum 12)))) + + +(ert-deftest triplets-whose-sum-is-108 () + (should (equal '((27 36 45)) (triplets-with-sum 108)))) + + +(ert-deftest triplets-whose-sum-is-1000 () + (should (equal '((200 375 425)) (triplets-with-sum 1000)))) + + +(ert-deftest no-matching-triplets-for-1001 () + (should (equal '() (triplets-with-sum 1001)))) + + +(ert-deftest returns-all-matching-triplets () + (should (equal '((9 40 41) (15 36 39)) (triplets-with-sum 90)))) + + +(ert-deftest several-matching-triplets () + (should (equal '((40 399 401) + (56 390 394) + (105 360 375) + (120 350 370) + (140 336 364) + (168 315 357) + (210 280 350) + (240 252 348)) + (triplets-with-sum 840)))) + + +(ert-deftest triplets-for-large-number () + (should (equal '((1200 14375 14425) + (1875 14000 14125) + (5000 12000 13000) + (6000 11250 12750) + (7500 10000 12500)) + (triplets-with-sum 30000)))) + + +(provide 'pythagorean-triplet-test) +;;; pythagorean-triplet-test.el ends here diff --git a/exercises/practice/pythagorean-triplet/pythagorean-triplet.el b/exercises/practice/pythagorean-triplet/pythagorean-triplet.el new file mode 100644 index 00000000..19ad38d9 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/pythagorean-triplet.el @@ -0,0 +1,13 @@ +;;; pythagorean-triplet.el --- Pythagorean Triplet (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun triplets-with-sum (n) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'pythagorean-triplet) +;;; pythagorean-triplet.el ends here 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..442addae --- /dev/null +++ b/exercises/practice/queen-attack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "queen-attack.el" + ], + "test": [ + "queen-attack-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/queen-attack/.meta/example.el new file mode 100644 index 00000000..2f31b58f --- /dev/null +++ b/exercises/practice/queen-attack/.meta/example.el @@ -0,0 +1,23 @@ +;;; queen-attack.el --- Queen Attack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun valid-position-p (queen) + (and (<= 0 (car queen) 7) (<= 0 (cdr queen) 7))) + +(defun can-attack-p (white-queen black-queen) + (let ((row-white (car white-queen)) + (column-white (cdr white-queen)) + (row-black (car black-queen)) + (column-black (cdr black-queen))) + (or (= row-white row-black) + (= column-white column-black) + (= (- row-white column-white) (- row-black column-black)) + (= (+ row-white column-white) (+ row-black column-black))))) + + +(provide 'queen-attack) +;;; queen-attack.el ends here 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.el b/exercises/practice/queen-attack/queen-attack-test.el new file mode 100644 index 00000000..4f15b34b --- /dev/null +++ b/exercises/practice/queen-attack/queen-attack-test.el @@ -0,0 +1,69 @@ +;;; queen-attack-test.el --- Queen Attack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "queen-attack.el") +(declare-function valid-position-p "queen-attack.el" (queen)) +(declare-function can-attack-p "queen-attack.el" + (white-queen black-queen)) + + +(ert-deftest queen-with-a-valid-position () + (should (valid-position-p '(2 . 2)))) + + +(ert-deftest queen-must-have-positive-row () + (should (not (valid-position-p '(-2 . 2))))) + + +(ert-deftest queen-must-have-row-on-board () + (should (not (valid-position-p '(8 . 4))))) + + +(ert-deftest queen-must-have-positive-column () + (should (not (valid-position-p '(2 . -2))))) + + +(ert-deftest queen-must-have-column-on-board () + (should (not (valid-position-p '(4 . 8))))) + + +(ert-deftest cannot-attack () + (should (not (can-attack-p '(2 . 4) '(6 . 6))))) + + +(ert-deftest can-attack-on-same-row () + (should (can-attack-p '(2 . 4) '(2 . 6)))) + + +(ert-deftest can-attack-on-same-column () + (should (can-attack-p '(4 . 5) '(2 . 5)))) + + +(ert-deftest can-attack-on-first-diagonal () + (should (can-attack-p '(2 . 2) '(0 . 4)))) + + +(ert-deftest can-attack-on-second-diagonal () + (should (can-attack-p '(2 . 2) '(3 . 1)))) + + +(ert-deftest can-attack-on-third-diagonal () + (should (can-attack-p '(2 . 2) '(1 . 1)))) + + +(ert-deftest can-attack-on-fourth-diagonal () + (should (can-attack-p '(1 . 7) '(0 . 6)))) + + +(ert-deftest + cannot-attack-if-falling-diagonals-are-only-the-same-when-reflected-across-the-longest-falling-diagonal + () + (should (not (can-attack-p '(4 . 1) '(2 . 5))))) + + +(provide 'queen-attack-test) +;;; queen-attack-test.el ends here diff --git a/exercises/practice/queen-attack/queen-attack.el b/exercises/practice/queen-attack/queen-attack.el new file mode 100644 index 00000000..26cff306 --- /dev/null +++ b/exercises/practice/queen-attack/queen-attack.el @@ -0,0 +1,18 @@ +;;; queen-attack.el --- Queen Attack (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun valid-position-p (queen) + (error + "Delete this S-Expression and write your own implementation")) + +(defun can-attack-p (white-queen black-queen) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'queen-attack) +;;; queen-attack.el ends here diff --git a/exercises/practice/rail-fence-cipher/.docs/instructions.md b/exercises/practice/rail-fence-cipher/.docs/instructions.md new file mode 100644 index 00000000..e311de6c --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +Implement encoding and decoding for the rail fence cipher. + +The Rail Fence cipher is a form of transposition cipher that gets its name from the way in which it's encoded. +It was already used by the ancient Greeks. + +In the Rail Fence cipher, the message is written downwards on successive "rails" of an imaginary fence, then moving up when we get to the bottom (like a zig-zag). +Finally the message is then read off in rows. + +For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", the cipherer writes out: + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +Then reads off: + +```text +WECRLTEERDSOEEFEAOCAIVDEN +``` + +To decrypt a message you take the zig-zag shape and fill the ciphertext along the rows. + +```text +? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ? +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +The first row has seven spots that can be filled with "WECRLTE". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Now the 2nd row takes "ERDSOEEFEAOC". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Leaving "AIVDEN" for the last row. + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +If you now read along the zig-zag shape you can read the original message. diff --git a/exercises/practice/rail-fence-cipher/.meta/config.json b/exercises/practice/rail-fence-cipher/.meta/config.json new file mode 100644 index 00000000..e8e48811 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "rail-fence-cipher.el" + ], + "test": [ + "rail-fence-cipher-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement encoding and decoding for the rail fence cipher.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher" +} diff --git a/exercises/practice/rail-fence-cipher/.meta/example.el b/exercises/practice/rail-fence-cipher/.meta/example.el new file mode 100644 index 00000000..83e345dd --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/example.el @@ -0,0 +1,67 @@ +;;; rail-fence-cipher.el --- Rail Fence Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'seq) +(require 'cl-lib) + +(defun encode (message rails) + (let ((rail-list (make-list rails "")) + (index 0) + (direction 'down)) + (seq-do + (lambda (char) + (setf (nth index rail-list) + (concat (char-to-string char) (nth index rail-list))) + (cond + ((>= index (1- rails)) + (setq direction 'down)) + ((<= index 0) + (setq direction 'up))) + (if (equal direction 'up) + (cl-incf index) + (cl-decf index))) + message) + (mapconcat 'nreverse rail-list ""))) + +(defun decode (message rails) + (let* ((message-length (length message)) + (cycle-length (* 2 (1- rails))) + (cycles (/ message-length cycle-length)) + (decoded (make-vector message-length nil))) + ;; format: off + (cl-loop + for rail-index below rails + with message-index = -1 + with decoded-index + do + (cl-loop + for cycle-index upto cycles + do (setf decoded-index (+ rail-index (* cycle-index cycle-length))) + while (< decoded-index message-length) + do + (setf (elt decoded decoded-index) + (elt message (cl-incf message-index))) + and + if (is-middle-rail rail-index rails) + ;; middle rails consume two characters per cycle + do + (setf decoded-index + (+ (- cycle-length rail-index) (* cycle-index cycle-length))) + and + if (< decoded-index message-length) + do + (setf (elt decoded decoded-index) + (elt message (cl-incf message-index))))) + ;; format: on + (seq-into decoded 'string))) + +(defun is-middle-rail (rail-index rails) + (< 0 rail-index (1- rails))) + + +(provide 'rail-fence-cipher) +;;; rail-fence-cipher.el ends here diff --git a/exercises/practice/rail-fence-cipher/.meta/tests.toml b/exercises/practice/rail-fence-cipher/.meta/tests.toml new file mode 100644 index 00000000..dfc5e16b --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.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. + +[46dc5c50-5538-401d-93a5-41102680d068] +description = "encode -> encode with two rails" + +[25691697-fbd8-4278-8c38-b84068b7bc29] +description = "encode -> encode with three rails" + +[384f0fea-1442-4f1a-a7c4-5cbc2044002c] +description = "encode -> encode with ending in the middle" + +[cd525b17-ec34-45ef-8f0e-4f27c24a7127] +description = "decode -> decode with three rails" + +[dd7b4a98-1a52-4e5c-9499-cbb117833507] +description = "decode -> decode with five rails" + +[93e1ecf4-fac9-45d9-9cd2-591f47d3b8d3] +description = "decode -> decode with six rails" diff --git a/exercises/practice/rail-fence-cipher/rail-fence-cipher-test.el b/exercises/practice/rail-fence-cipher/rail-fence-cipher-test.el new file mode 100644 index 00000000..6086adb7 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/rail-fence-cipher-test.el @@ -0,0 +1,49 @@ +;;; rail-fence-cipher-test.el --- Rail Fence Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "rail-fence-cipher.el") +(declare-function encode "rail-fence-cipher.el" (message rails)) +(declare-function decode "rail-fence-cipher.el" (message rails)) + + +(ert-deftest encode-with-two-rails () + (should + (string= (encode "XOXOXOXOXOXOXOXOXO" 2) "XXXXXXXXXOOOOOOOOO"))) + + +(ert-deftest encode-with-three-rails () + (should + (string= + (encode "WEAREDISCOVEREDFLEEATONCE" 3) + "WECRLTEERDSOEEFEAOCAIVDEN"))) + + +(ert-deftest encode-with-ending-in-the-middle () + (should (string= (encode "EXERCISES" 4) "ESXIEECSR"))) + + +(ert-deftest decode-with-three-rails () + (should + (string= + (decode "TEITELHDVLSNHDTISEIIEA" 3) "THEDEVILISINTHEDETAILS"))) + + +(ert-deftest decode-with-five-rails () + (should + (string= (decode "EIEXMSMESAORIWSCE" 5) "EXERCISMISAWESOME"))) + + +(ert-deftest decode-with-six-rails () + (should + (string= + (decode + "133714114238148966225439541018335470986172518171757571896261" 6) + "112358132134558914423337761098715972584418167651094617711286"))) + + +(provide 'rail-fence-cipher-test) +;;; rail-fence-cipher-test.el ends here diff --git a/exercises/practice/rail-fence-cipher/rail-fence-cipher.el b/exercises/practice/rail-fence-cipher/rail-fence-cipher.el new file mode 100644 index 00000000..cfdc87d9 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/rail-fence-cipher.el @@ -0,0 +1,18 @@ +;;; rail-fence-cipher.el --- Rail Fence Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun encode (message rails) + (error + "Delete this S-Expression and write your own implementation")) + +(defun decode (message rails) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'rail-fence-cipher) +;;; rail-fence-cipher.el ends here diff --git a/exercises/practice/raindrops/.docs/instructions.md b/exercises/practice/raindrops/.docs/instructions.md index fc61d36e..df644107 100644 --- a/exercises/practice/raindrops/.docs/instructions.md +++ b/exercises/practice/raindrops/.docs/instructions.md @@ -1,20 +1,24 @@ # Instructions -Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. -A factor is a number that evenly divides into another number, leaving no remainder. -The simplest way to test if one number is a factor of another is to use the [modulo operation][modulo]. +Your task is to convert a number into its corresponding raindrop sounds. -The rules of `raindrops` are that if a given number: +If a given number: -- has 3 as a factor, add 'Pling' to the result. -- has 5 as a factor, add 'Plang' to the result. -- has 7 as a factor, add 'Plong' to the result. -- _does not_ have any of 3, 5, or 7 as a factor, the result should be the digits of the 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 has 7 as a factor, but not 3 or 5, so the result would be "Plong". -- 30 has both 3 and 5 as factors, but not 7, so the result would be "PlingPlang". -- 34 is not factored by 3, 5, or 7, so the result would be "34". +- 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 index 95c82dd3..d47b14f0 100644 --- a/exercises/practice/raindrops/.meta/config.json +++ b/exercises/practice/raindrops/.meta/config.json @@ -16,7 +16,7 @@ ".meta/example.el" ] }, - "blurb": "Convert a number to a string, the content of which depends on the number's factors.", + "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/raindrops-test.el b/exercises/practice/raindrops/raindrops-test.el index b04c71aa..6fb0ccc7 100644 --- a/exercises/practice/raindrops/raindrops-test.el +++ b/exercises/practice/raindrops/raindrops-test.el @@ -4,68 +4,82 @@ ;;; Code: + (load-file "raindrops.el") (declare-function convert "raindrops.el" (n)) -(ert-deftest test-1 () - (should (equal "1" - (convert 1)))) -(ert-deftest test-3 () - (should (equal "Pling" - (convert 3)))) +(ert-deftest the-sound-for-1-is-1 () + (should (string= (convert 1) "1"))) + + +(ert-deftest the-sound-for-3-is-Pling () + (should (string= (convert 3) "Pling"))) + + +(ert-deftest the-sound-for-5-is-Plang () + (should (string= (convert 5) "Plang"))) + + +(ert-deftest the-sound-for-7-is-Plong () + (should (string= (convert 7) "Plong"))) + + +(ert-deftest the-sound-for-6-is-Pling () + (should (string= (convert 6) "Pling" ))) + + +(ert-deftest the-sound-for-8-is-8 () + (should (string= (convert 8) "8"))) + + +(ert-deftest the-sound-for-9-is-Pling () + (should (string= (convert 9) "Pling" ))) + + +(ert-deftest the-sound-for-10-is-Plang () + (should (string= (convert 10) "Plang"))) + + +(ert-deftest the-sound-for-14-is-Plong () + (should (string= (convert 14) "Plong"))) + + +(ert-deftest the-sound-for-15-is-PlingPlong () + (should (string= (convert 15) "PlingPlang"))) + + +(ert-deftest the-sound-for-21-is-PlingPlong () + (should (string= (convert 21) "PlingPlong"))) + + +(ert-deftest the-sound-for-25-is-Plang () + (should (string= (convert 25) "Plang"))) -(ert-deftest test-5 () - (should (equal "Plang" - (convert 5)))) -(ert-deftest test-7 () - (should (equal "Plong" - (convert 7)))) +(ert-deftest the-sound-for-27-is-Pling () + (should (string= (convert 27) "Pling"))) -(ert-deftest test-6 () - (should (equal "Pling" - (convert 6)))) -(ert-deftest test-9 () - (should (equal "Pling" - (convert 9)))) +(ert-deftest the-sound-for-35-is-PlangPlong () + (should (string= (convert 35) "PlangPlong"))) -(ert-deftest test-10 () - (should (equal "Plang" - (convert 10)))) -(ert-deftest test-15 () - (should (equal "PlingPlang" - (convert 15)))) +(ert-deftest the-sound-for-49-is-Plong () + (should (string= (convert 49) "Plong"))) -(ert-deftest test-21 () - (should (equal "PlingPlong" - (convert 21)))) -(ert-deftest test-25 () - (should (equal "Plang" - (convert 25)))) +(ert-deftest the-sound-for-52-is-52 () + (should (string= (convert 52) "52"))) -(ert-deftest test-35 () - (should (equal "PlangPlong" - (convert 35)))) -(ert-deftest test-49 () - (should (equal "Plong" - (convert 49)))) +(ert-deftest the-sound-for-105-is-PlingPlangPlong () + (should (string= (convert 105) "PlingPlangPlong"))) -(ert-deftest test-52 () - (should (equal "52" - (convert 52)))) -(ert-deftest test-105 () - (should (equal "PlingPlangPlong" - (convert 105)))) +(ert-deftest the-sound-for-3125-is-Plang () + (should (string= (convert 3125) "Plang"))) -(ert-deftest test-12121 () - (should (equal "12121" - (convert 12121)))) (provide 'raindrops-test) ;;; raindrops-test.el ends here diff --git a/exercises/practice/raindrops/raindrops.el b/exercises/practice/raindrops/raindrops.el index a8a4e179..73c97951 100644 --- a/exercises/practice/raindrops/raindrops.el +++ b/exercises/practice/raindrops/raindrops.el @@ -2,10 +2,12 @@ ;;; Commentary: -(defun convert (n) - "Convert integer N to its raindrops string." ;;; Code: -) + + +(defun convert (n) + (error "Delete this S-Expression and write your own implementation")) + (provide 'raindrops) ;;; raindrops.el ends here 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..fe85c3ab --- /dev/null +++ b/exercises/practice/resistor-color-duo/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "resistor-color-duo.el" + ], + "test": [ + "resistor-color-duo-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/resistor-color-duo/.meta/example.el new file mode 100644 index 00000000..3d8a98a4 --- /dev/null +++ b/exercises/practice/resistor-color-duo/.meta/example.el @@ -0,0 +1,27 @@ +;;; resistor-color-duo.el --- Resistor Color Duo (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(setq band-values +'(("black" . 0) + ("brown" . 1) + ("red" . 2) + ("orange" . 3) + ("yellow" . 4) + ("green" . 5) + ("blue" . 6) + ("violet" . 7) + ("grey" . 8) + ("white" . 9))) + +(defun value (colors) + (+ (* (get-value (nth 0 colors)) 10) + (get-value (nth 1 colors)))) + +(defun get-value (color) + (cdr (assoc color band-values))) + +(provide 'resistor-color-duo) +;;; resistor-color-duo.el ends here 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.el b/exercises/practice/resistor-color-duo/resistor-color-duo-test.el new file mode 100644 index 00000000..fa60d529 --- /dev/null +++ b/exercises/practice/resistor-color-duo/resistor-color-duo-test.el @@ -0,0 +1,41 @@ +;;; resistor-color-duo-test.el --- Resistor Color Duo (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "resistor-color-duo.el") +(declare-function value "resistor-color-duo.el" (colors)) + + +(ert-deftest brown-and-black () + (should (equal (value '("brown" "black")) 10))) + + +(ert-deftest blue-and-grey () + (should (equal (value '("blue" "grey")) 68))) + + +(ert-deftest yellow-and-violet () + (should (equal (value '("yellow" "violet")) 47))) + + +(ert-deftest white-and-red () + (should (equal (value '("white" "red")) 92))) + + +(ert-deftest orange-and-orange () + (should (equal (value '("orange" "orange")) 33))) + + +(ert-deftest ignore-additional-colors () + (should (equal (value '("green" "brown" "orange")) 51))) + + +(ert-deftest black-and-brown-one-digit () + (should (equal (value '("black" "brown")) 1))) + + +(provide 'resistor-color-duo-test) +;;; resistor-color-duo-test.el ends here diff --git a/exercises/practice/resistor-color-duo/resistor-color-duo.el b/exercises/practice/resistor-color-duo/resistor-color-duo.el new file mode 100644 index 00000000..2641c2df --- /dev/null +++ b/exercises/practice/resistor-color-duo/resistor-color-duo.el @@ -0,0 +1,13 @@ +;;; resistor-color-duo.el --- Resistor Color Duo (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun value (colors) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'resistor-color-duo) +;;; resistor-color-duo.el ends here diff --git a/exercises/practice/resistor-color-trio/.docs/instructions.append.md b/exercises/practice/resistor-color-trio/.docs/instructions.append.md new file mode 100644 index 00000000..de70a112 --- /dev/null +++ b/exercises/practice/resistor-color-trio/.docs/instructions.append.md @@ -0,0 +1,5 @@ +# Instructions append + +An input of `"orange", "orange", "green"` should return: + +> "3.3 megaohms" 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..035b71db --- /dev/null +++ b/exercises/practice/resistor-color-trio/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "resistor-color-trio.el" + ], + "test": [ + "resistor-color-trio-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/resistor-color-trio/.meta/example.el new file mode 100644 index 00000000..8c3eff3b --- /dev/null +++ b/exercises/practice/resistor-color-trio/.meta/example.el @@ -0,0 +1,41 @@ +;;; resistor-color-trio.el --- Resistor Color Trio (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(setq band-values + '(("black" . 0) + ("brown" . 1) + ("red" . 2) + ("orange" . 3) + ("yellow" . 4) + ("green" . 5) + ("blue" . 6) + ("violet" . 7) + ("grey" . 8) + ("white" . 9))) + + +(defun label (colors) + (let* ((color-1 (nth 0 colors)) + (color-2 (nth 1 colors)) + (color-3 (nth 2 colors)) + (value (+ (* (cdr (assoc color-1 band-values)) 10) + (cdr (assoc color-2 band-values)))) + (resistance (* value (expt 10 (cdr (assoc color-3 band-values)))))) + (string-replace ".0" "" + (cond + ((< resistance 1000) + (format "%d ohms" resistance)) + ((< resistance 1000000) + (format "%.1f kiloohms" (/ resistance 1000.0))) + ((< resistance 1000000000) + (format "%.1f megaohms" (/ resistance 1000000.0))) + (t + (format "%.1f gigaohms" (/ resistance 1000000000.0))))))) + + +(provide 'resistor-color-trio) +;;; resistor-color-trio.el ends here 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.el b/exercises/practice/resistor-color-trio/resistor-color-trio-test.el new file mode 100644 index 00000000..6a6e3e0d --- /dev/null +++ b/exercises/practice/resistor-color-trio/resistor-color-trio-test.el @@ -0,0 +1,69 @@ +;;; resistor-color-trio-test.el --- Resistor Color Trio (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "resistor-color-trio.el") +(declare-function label "resistor-color-trio.el" (colors)) + + +(ert-deftest orange-and-orange-and-black () + (should (string= (label '("orange" "orange" "black")) + "33 ohms"))) + +(ert-deftest blue-and-grey-and-brown () + (should (string= (label '("blue" "grey" "brown")) + "680 ohms"))) + +(ert-deftest red-and-black-and-red () + (should (string= (label '("red" "black" "red")) + "2 kiloohms"))) + +(ert-deftest green-and-brown-and-orange () + (should (string= (label '("green" "brown" "orange")) + "51 kiloohms"))) + +(ert-deftest yellow-and-violet-and-yellow () + (should (string= (label '("yellow" "violet" "yellow")) + "470 kiloohms"))) + +(ert-deftest blue-and-violet-and-blue () + (should (string= (label '("blue" "violet" "blue")) + "67 megaohms"))) + +(ert-deftest minimum-possible-value () + (should (string= (label '("black" "black" "black")) + "0 ohms"))) + +(ert-deftest maximum-possible-value () + (should (string= (label '("white" "white" "white")) + "99 gigaohms"))) + +(ert-deftest first-two-colors-make-an-invalid-octal-number () + (should (string= (label '("black" "grey" "black")) + "8 ohms"))) + +(ert-deftest ignore-extra-colors () + (should (string= (label '("blue" "green" "yellow" "orange")) + "650 kiloohms"))) + +(ert-deftest orange-and-orange-and-red () + (should (string= (label '("orange" "orange" "red")) + "3.3 kiloohms"))) + +(ert-deftest orange-and-orange-and-green () + (should (string= (label '("orange" "orange" "green")) + "3.3 megaohms"))) + +(ert-deftest white-and-white-and-violet () + (should (string= (label '("white" "white" "violet")) + "990 megaohms"))) + +(ert-deftest white-and-white-and-grey () + (should (string= (label '("white" "white" "grey")) + "9.9 gigaohms"))) + +(provide 'resistor-color-trio-test) +;;; resistor-color-trio-test.el ends here diff --git a/exercises/practice/resistor-color-trio/resistor-color-trio.el b/exercises/practice/resistor-color-trio/resistor-color-trio.el new file mode 100644 index 00000000..44aafd33 --- /dev/null +++ b/exercises/practice/resistor-color-trio/resistor-color-trio.el @@ -0,0 +1,14 @@ +;;; resistor-color-trio.el --- Resistor Color Trio (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun label (colors) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'resistor-color-trio) +;;; resistor-color-trio.el ends here + 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..d6bb98cb --- /dev/null +++ b/exercises/practice/resistor-color/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "resistor-color.el" + ], + "test": [ + "resistor-color-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/resistor-color/.meta/example.el new file mode 100644 index 00000000..15236b26 --- /dev/null +++ b/exercises/practice/resistor-color/.meta/example.el @@ -0,0 +1,29 @@ +;;; resistor-color.el --- Resistor Color (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(setq band-values + '(("black" . 0) + ("brown" . 1) + ("red" . 2) + ("orange" . 3) + ("yellow" . 4) + ("green" . 5) + ("blue" . 6) + ("violet" . 7) + ("grey" . 8) + ("white" . 9))) + +(defun color-code (color) + (cdr (assoc color band-values))) + +(defun colors () + (mapcar #'car band-values)) + + +(provide 'resistor-color) +;;; resistor-color.el ends here + 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.el b/exercises/practice/resistor-color/resistor-color-test.el new file mode 100644 index 00000000..6ee31443 --- /dev/null +++ b/exercises/practice/resistor-color/resistor-color-test.el @@ -0,0 +1,32 @@ +;;; resistor-color-test.el --- Resistor Color (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "resistor-color.el") +(declare-function color-code "resistor-color.el" (color)) +(declare-function colors "resistor-color.el" ()) + + +(ert-deftest black () + (should (= 0 (color-code "black")))) + + +(ert-deftest white () + (should (= 9 (color-code "white")))) + + +(ert-deftest orange () + (should (= 3 (color-code "orange")))) + + +(ert-deftest colors () + (should (equal '("black" "brown" "red" "orange" "yellow" "green" "blue" "violet" "grey" "white") + (colors)))) + + +(provide 'resistor-color-test) +;;; resistor-color-test.el ends here + diff --git a/exercises/practice/resistor-color/resistor-color.el b/exercises/practice/resistor-color/resistor-color.el new file mode 100644 index 00000000..05895c14 --- /dev/null +++ b/exercises/practice/resistor-color/resistor-color.el @@ -0,0 +1,17 @@ +;;; resistor-color.el --- Resistor Color (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun color-code (color) + (error "Delete this S-Expression and write your own implementation")) + +(defun colors () + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'resistor-color) +;;; resistor-color.el ends here + 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..94ff37fe --- /dev/null +++ b/exercises/practice/reverse-string/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "fapdash" + ], + "contributors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "reverse-string.el" + ], + "test": [ + "reverse-string-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Reverse a given string.", + "source": "Introductory challenge to reverse an input string", + "source_url": "https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb" +} diff --git a/exercises/practice/reverse-string/.meta/example.el b/exercises/practice/reverse-string/.meta/example.el new file mode 100644 index 00000000..16465ba6 --- /dev/null +++ b/exercises/practice/reverse-string/.meta/example.el @@ -0,0 +1,37 @@ +;;; reverse-string.el --- Reverse String (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(require 'cl-lib) +(require 'ucs-normalize) + +(defun combining-mark-p (char) + (let ((category (get-char-code-property char 'general-category))) + (or (string= category "Mn") ; Nonspacing_Mark + (string= category "Mc") ; Spacing_Mark + (string= category "Me")))) ; Enclosing_Mark + +(defun split-grapheme-clusters (str) + (let ((clusters '()) + (i 0) + (len (length str))) + (while (< i len) + (let ((start i) + (char (aref str i))) + (setq i (1+ i)) + (while (and (< i len) + (combining-mark-p (aref str i))) + (setq i (1+ i))) + (push (substring str start i) clusters))) + (nreverse clusters))) + +(defun reverse-string (value) + (let* ((decomposed (ucs-normalize-NFD-string value)) + (clusters (split-grapheme-clusters decomposed))) + (apply #'concat (reverse clusters)))) + +(provide 'reverse-string) +;;; reverse-string.el ends here diff --git a/exercises/practice/reverse-string/.meta/tests.toml b/exercises/practice/reverse-string/.meta/tests.toml new file mode 100644 index 00000000..0c313cc5 --- /dev/null +++ b/exercises/practice/reverse-string/.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. + +[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" + +[93d7e1b8-f60f-4f3c-9559-4056e10d2ead] +description = "grapheme cluster with pre-combined form" + +[1028b2c1-6763-4459-8540-2da47ca512d9] +description = "grapheme clusters" diff --git a/exercises/practice/reverse-string/reverse-string-test.el b/exercises/practice/reverse-string/reverse-string-test.el new file mode 100644 index 00000000..41e4093c --- /dev/null +++ b/exercises/practice/reverse-string/reverse-string-test.el @@ -0,0 +1,46 @@ +;;; reverse-string-test.el --- Reverse String (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "reverse-string.el") +(declare-function reverse-string "reverse-string.el" (value)) + + +(ert-deftest an-empty-string () + (should (string= (reverse-string "") ""))) + + +(ert-deftest a-word () + (should (string= (reverse-string "robot") "tobor"))) + + +(ert-deftest a-capitalized-word () + (should (string= (reverse-string "Ramen") "nemaR"))) + + +(ert-deftest a-sentence-with-punctuation () + (should (string= (reverse-string "I'm hungry!") "!yrgnuh m'I"))) + + +(ert-deftest a-palindrome () + (should (string= (reverse-string "racecar") "racecar"))) + + +(ert-deftest an-even-sized-word () + (should (string= (reverse-string "drawer") "reward"))) + +(ert-deftest wide-characters () + (should (string= (reverse-string "子猫") "猫子"))) + +(ert-deftest grapheme-cluster-with-pre-combined-form () + (should (string= (reverse-string "Würstchenstand") "dnatsnehctsrüW"))) + +(ert-deftest grapheme-cluster () + (should (string= (reverse-string "ผู้เขียนโปรแกรม") "มรกแรปโนยขีเผู้"))) + + +(provide 'reverse-string-test) +;;; reverse-string-test.el ends here diff --git a/exercises/practice/reverse-string/reverse-string.el b/exercises/practice/reverse-string/reverse-string.el new file mode 100644 index 00000000..aca6ca2c --- /dev/null +++ b/exercises/practice/reverse-string/reverse-string.el @@ -0,0 +1,14 @@ +;;; reverse-string.el --- Reverse String (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun reverse-string (value) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'reverse-string) +;;; reverse-string.el ends here diff --git a/exercises/practice/rna-transcription/.docs/instructions.md b/exercises/practice/rna-transcription/.docs/instructions.md index 851bdb49..4dbfd3a2 100644 --- a/exercises/practice/rna-transcription/.docs/instructions.md +++ b/exercises/practice/rna-transcription/.docs/instructions.md @@ -1,12 +1,12 @@ # Instructions -Given a DNA strand, return its RNA complement (per RNA transcription). +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 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**). +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: @@ -14,3 +14,7 @@ Given a DNA strand, its transcribed RNA strand is formed by replacing each nucle - `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 index b5e32eab..b45df897 100644 --- a/exercises/practice/rna-transcription/.meta/config.json +++ b/exercises/practice/rna-transcription/.meta/config.json @@ -16,7 +16,7 @@ ".meta/example.el" ] }, - "blurb": "Given a DNA strand, return its RNA Complement Transcription.", + "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/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..a5ce9004 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "robot-simulator.el" + ], + "test": [ + "robot-simulator-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Write a robot simulator.", + "source": "Inspired by an interview question at a famous company." +} diff --git a/exercises/practice/robot-simulator/.meta/example.el b/exercises/practice/robot-simulator/.meta/example.el new file mode 100644 index 00000000..0cc1c767 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/example.el @@ -0,0 +1,48 @@ +;;; robot-simulator.el --- robot-simulator Exercise (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defconst directions [north east south west]) + + +(defconst steps '((north . (0 1)) + (east . (1 0)) + (south . (0 -1)) + (west . (-1 0)))) + + +(defun create-robot (x y direction) + (record 'robot x y direction)) + + +(defun move (robot instructions) + (let ((state robot)) + (seq-doseq (instruction instructions) + (cond ((equal ?L instruction) + (rotate state -1)) + ((equal ?R instruction) + (rotate state 1)) + ((equal ?A instruction) + (advance state)))) + state)) + + +(defun rotate (robot offset) + (let* ((old-index (cl-position (aref robot 3) directions)) + (new-index (mod (+ old-index offset) 4))) + (aset robot 3 (aref directions new-index)))) + + +(defun advance (robot) + (let* ((delta (assq (aref robot 3) steps)) + (delta-x (cadr delta)) + (delta-y (caddr delta))) + (aset robot 1 (+ delta-x (aref robot 1))) + (aset robot 2 (+ delta-y (aref robot 2))))) + + +(provide 'robot-simulator) +;;; robot-simulator.el ends here 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.el b/exercises/practice/robot-simulator/robot-simulator-test.el new file mode 100644 index 00000000..022a89d2 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator-test.el @@ -0,0 +1,104 @@ +;;; robot-simulator-test.el --- Tests for robot-simulator (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "robot-simulator.el") +(declare-function create-robot "robot-simulator.el" (x y direction)) +(declare-function move "robot-simulator.el" (robot instructions)) + + +(ert-deftest create-robot-at-origin-facing-north () + (should (equal #s(robot 0 0 north) + (create-robot 0 0 'north)))) + + +(ert-deftest create-robot-at-negative-position-facing-south () + (should (equal #s(robot -1 -1 south) + (create-robot -1 -1 'south)))) + + +(ert-deftest rotating-clockwise-changes-north-to-east () + (should (equal #s(robot 0 0 east) + (move (create-robot 0 0 'north) "R")))) + + +(ert-deftest rotating-clockwise-changes-east-to-south () + (should (equal #s(robot 0 0 south) + (move (create-robot 0 0 'east) "R")))) + + +(ert-deftest rotating-clockwise-changes-south-to-west () + (should (equal #s(robot 0 0 west) + (move (create-robot 0 0 'south) "R")))) + + +(ert-deftest rotating-clockwise-changes-west-to-north () + (should (equal #s(robot 0 0 north) + (move (create-robot 0 0 'west) "R")))) + + +(ert-deftest rotating-counter-clockwise-changes-north-to-west () + (should (equal #s(robot 0 0 west) + (move (create-robot 0 0 'north) "L")))) + + +(ert-deftest rotating-counter-clockwise-changes-west-to-south () + (should (equal #s(robot 0 0 south) + (move (create-robot 0 0 'west) "L")))) + + +(ert-deftest rotating-counter-clockwise-changes-south-to-east () + (should (equal #s(robot 0 0 east) + (move (create-robot 0 0 'south) "L")))) + + +(ert-deftest rotating-counter-clockwise-changes-east-to-north () + (should (equal #s(robot 0 0 north) + (move (create-robot 0 0 'east) "L")))) + + +(ert-deftest moving-forward-one-facing-north-increments-y () + (should (equal #s(robot 0 1 north) + (move (create-robot 0 0 'north) "A")))) + + +(ert-deftest moving-forward-one-facing-south-decrements-y () + (should (equal #s(robot 0 -1 south) + (move (create-robot 0 0 'south) "A")))) + + +(ert-deftest moving-forward-one-facing-east-increments-x () + (should (equal #s(robot 1 0 east) + (move (create-robot 0 0 'east) "A")))) + + +(ert-deftest moving-forward-one-facing-west-decrements-x () + (should (equal #s(robot -1 0 west) + (move (create-robot 0 0 'west) "A")))) + + +(ert-deftest follow-series-of-instructions-moving-east-and-north-from-README () + (should (equal #s(robot 9 4 west) + (move (create-robot 7 3 'north) "RAALAL")))) + + +(ert-deftest follow-series-of-instructions-moving-west-and-north () + (should (equal #s(robot -4 1 west) + (move (create-robot 0 0 'north) "LAAARALA")))) + + +(ert-deftest follow-series-of-instructions-moving-west-and-south () + (should (equal #s(robot -3 -8 south) + (move (create-robot 2 -7 'east) "RRAAAAALA")))) + + +(ert-deftest follow-series-of-instructions-moving-east-and-north () + (should (equal #s(robot 11 5 north) + (move (create-robot 8 4 'south) "LAAARRRALLLL")))) + + +(provide 'robot-simulator-test) +;;; robot-simulator-test.el ends here diff --git a/exercises/practice/robot-simulator/robot-simulator.el b/exercises/practice/robot-simulator/robot-simulator.el new file mode 100644 index 00000000..18e308f2 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator.el @@ -0,0 +1,17 @@ +;;; robot-simulator.el --- robot-simulator (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun create-robot (x y direction) + (error "Delete this S-Expression and write your own implementation")) + + +(defun move (robot instructions) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'robot-simulator) +;;; robot-simulator.el ends here diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md index bb7e909d..50e2f5bf 100644 --- a/exercises/practice/roman-numerals/.docs/instructions.md +++ b/exercises/practice/roman-numerals/.docs/instructions.md @@ -1,41 +1,12 @@ -# Instructions +# Introduction -Write a function to convert from normal numbers to Roman Numerals. +Your task is to convert a number from Arabic numerals to Roman numerals. -The Romans were a clever bunch. -They conquered most of Europe and ruled it for hundreds of years. -They invented concrete and straight roads and even bikinis. -One thing they never discovered though was the number zero. -This made writing and dating extensive histories of their exploits slightly more challenging, but the system of numbers they came up with is still in use today. -For example the BBC uses Roman numerals to date their programs. +For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999). -The Romans wrote numbers using letters - I, V, X, L, C, D, M. -(notice these letters have lots of straight lines and are hence easy to hack into stone tablets). +~~~~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. -```text - 1 => I -10 => X - 7 => VII -``` - -The maximum number supported by this notation is 3,999. -(The Romans themselves didn't tend to go any higher) - -Wikipedia says: Modern Roman numerals ... are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero. - -To see this in practice, consider the example of 1990. - -In Roman numerals 1990 is MCMXC: - -1000=M -900=CM -90=XC - -2008 is written as MMVIII: - -2000=MM -8=VIII - -Learn more about [Roman numberals on Wikipedia][roman-numerals]. - -[roman-numerals]: https://wiki.imperivm-romanvm.com/wiki/Roman_Numerals +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 index a31b48bd..502e22e7 100644 --- a/exercises/practice/roman-numerals/.meta/config.json +++ b/exercises/practice/roman-numerals/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "bakhti", "canweriotnow", @@ -16,7 +18,7 @@ ".meta/example.el" ] }, - "blurb": "Write a function to convert from normal numbers to Roman Numerals.", + "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/tests.toml b/exercises/practice/roman-numerals/.meta/tests.toml index ca142e9f..709011b5 100644 --- a/exercises/practice/roman-numerals/.meta/tests.toml +++ b/exercises/practice/roman-numerals/.meta/tests.toml @@ -30,6 +30,9 @@ 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" @@ -42,6 +45,9 @@ 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" @@ -51,38 +57,35 @@ 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" -[bb550038-d4eb-4be2-a9ce-f21961ac3bc6] -description = "3000 is MMM" - -[6d1d82d5-bf3e-48af-9139-87d7165ed509] -description = "16 is XVI" - -[4465ffd5-34dc-44f3-ada5-56f5007b6dad] -description = "66 is LXVI" - -[902ad132-0b4d-40e3-8597-ba5ed611dd8d] -description = "166 is CLXVI" - -[dacb84b9-ea1c-4a61-acbb-ce6b36674906] -description = "666 is DCLXVI" - [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.el b/exercises/practice/roman-numerals/roman-numerals-test.el index 7ca08586..5e83dc92 100644 --- a/exercises/practice/roman-numerals/roman-numerals-test.el +++ b/exercises/practice/roman-numerals/roman-numerals-test.el @@ -4,83 +4,118 @@ ;;; Code: + (load-file "roman-numerals.el") (declare-function to-roman "roman-numerals.el" (value)) -(ert-deftest to-roman-1 () - (should (equal (to-roman 1) "I"))) -(ert-deftest to-roman-2 () - (should (equal (to-roman 2) "II"))) +(ert-deftest 1-is-I () + (should (string= "I" (to-roman 1)))) + + +(ert-deftest 2-is-II () + (should (string= "II" (to-roman 2)))) + + +(ert-deftest 3-is-III () + (should (string= "III" (to-roman 3)))) + + +(ert-deftest 4-is-IV () + (should (string= "IV" (to-roman 4)))) + + +(ert-deftest 5-is-V () + (should (string= "V" (to-roman 5)))) + + +(ert-deftest 6-is-VI () + (should (string= "VI" (to-roman 6)))) + + +(ert-deftest 9-is-IX () + (should (string= "IX" (to-roman 9)))) + + +(ert-deftest 16-is-XVI () + (should (string= "XVI" (to-roman 16)))) + + +(ert-deftest 27-is-XXVII () + (should (string= "XXVII" (to-roman 27)))) + + +(ert-deftest 48-is-XLVIII () + (should (string= "XLVIII" (to-roman 48)))) + + +(ert-deftest 49-is-XLIX () + (should (string= "XLIX" (to-roman 49)))) + + +(ert-deftest 59-is-LIX () + (should (string= "LIX" (to-roman 59)))) + + +(ert-deftest 66-is-LXVI () + (should (string= "LXVI" (to-roman 66)))) + + +(ert-deftest 93-is-XCIII () + (should (string= "XCIII" (to-roman 93)))) + + +(ert-deftest 141-is-CXLI () + (should (string= "CXLI" (to-roman 141)))) + + +(ert-deftest 163-is-CLXIII () + (should (string= "CLXIII" (to-roman 163)))) -(ert-deftest to-roman-3 () - (should (equal (to-roman 3) "III"))) -(ert-deftest to-roman-4 () - (should (equal (to-roman 4) "IV"))) +(ert-deftest 166-is-CLXVI () + (should (string= (to-roman 166) "CLXVI"))) -(ert-deftest to-roman-5 () - (should (equal (to-roman 5) "V"))) -(ert-deftest to-roman-6 () - (should (equal (to-roman 6) "VI"))) +(ert-deftest 402-is-CDII () + (should (string= "CDII" (to-roman 402)))) -(ert-deftest to-roman-9 () - (should (equal (to-roman 9) "IX"))) -(ert-deftest to-roman-16 () - (should (equal (to-roman 16) "XVI"))) +(ert-deftest 575-is-DLXXV () + (should (string= "DLXXV" (to-roman 575)))) -(ert-deftest to-roman-27 () - (should (equal (to-roman 27) "XXVII"))) -(ert-deftest to-roman-48 () - (should (equal (to-roman 48) "XLVIII"))) +(ert-deftest 666-is-DCLXVI () + (should (string= "DCLXVI" (to-roman 666)))) -(ert-deftest to-roman-59 () - (should (equal (to-roman 59) "LIX"))) -(ert-deftest to-roman-66 () - (should (equal (to-roman 66) "LXVI"))) +(ert-deftest 911-is-CMXI () + (should (string= "CMXI" (to-roman 911)))) -(ert-deftest to-roman-93 () - (should (equal (to-roman 93) "XCIII"))) -(ert-deftest to-roman-141 () - (should (equal (to-roman 141) "CXLI"))) +(ert-deftest 1024-is-MXXIV () + (should (string= "MXXIV" (to-roman 1024)))) -(ert-deftest to-roman-163 () - (should (equal (to-roman 163) "CLXIII"))) -(ert-deftest to-roman-166 () - (should (equal (to-roman 166) "CLXVI"))) +(ert-deftest 1666-is-MDCLXVI () + (should (string= "MDCLXVI" (to-roman 1666)))) -(ert-deftest to-roman-402 () - (should (equal (to-roman 402) "CDII"))) -(ert-deftest to-roman-575 () - (should (equal (to-roman 575) "DLXXV"))) +(ert-deftest 3000-is-MMM () + (should (string= "MMM" (to-roman 3000)))) -(ert-deftest to-roman-666 () - (should (equal (to-roman 666) "DCLXVI"))) -(ert-deftest to-roman-911 () - (should (equal (to-roman 911) "CMXI"))) +(ert-deftest 3001-is-MMMI () + (should (string= "MMMI" (to-roman 3001)))) -(ert-deftest to-roman-1024 () - (should (equal (to-roman 1024) "MXXIV"))) -(ert-deftest to-roman-1666 () - (should (equal (to-roman 1666) "MDCLXVI"))) +(ert-deftest 3888-is-MMMDCCCLXXXVIII () + (should (string= "MMMDCCCLXXXVIII" (to-roman 3888)))) -(ert-deftest to-roman-3000 () - (should (equal (to-roman 3000) "MMM"))) -(ert-deftest to-roman-3001 () - (should (equal (to-roman 3001) "MMMI"))) +(ert-deftest 3999-is-MMMCMXCIX () + (should (string= "MMMCMXCIX" (to-roman 3999 )))) -(ert-deftest to-roman-3999 () - (should (equal (to-roman 3999) "MMMCMXCIX"))) -(provide 'roman-numerals) +(provide 'roman-numerals-test) ;;; roman-numerals-test.el ends here diff --git a/exercises/practice/roman-numerals/roman-numerals.el b/exercises/practice/roman-numerals/roman-numerals.el index 0a6884bb..ccba2def 100644 --- a/exercises/practice/roman-numerals/roman-numerals.el +++ b/exercises/practice/roman-numerals/roman-numerals.el @@ -2,9 +2,12 @@ ;;; Commentary: -(defun to-roman (value) ;;; Code: -) + + +(defun to-roman (value) + (error "Delete this S-Expression and write your own implementation")) + (provide 'roman-numerals) ;;; roman-numerals.el ends here diff --git a/exercises/practice/rotational-cipher/.docs/instructions.md b/exercises/practice/rotational-cipher/.docs/instructions.md new file mode 100644 index 00000000..4bf64ca1 --- /dev/null +++ b/exercises/practice/rotational-cipher/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Create an implementation of the rotational cipher, also sometimes called the Caesar cipher. + +The Caesar cipher is a simple shift cipher that relies on transposing all the letters in the alphabet using an integer key between `0` and `26`. +Using a key of `0` or `26` will always yield the same output due to modular arithmetic. +The letter is shifted for as many values as the value of the key. + +The general notation for rotational ciphers is `ROT + `. +The most commonly used rotational cipher is `ROT13`. + +A `ROT13` on the Latin alphabet would be as follows: + +```text +Plain: abcdefghijklmnopqrstuvwxyz +Cipher: nopqrstuvwxyzabcdefghijklm +``` + +It is stronger than the Atbash cipher because it has 27 possible keys, and 25 usable keys. + +Ciphertext is written out in the same formatting as the input including spaces and punctuation. + +## Examples + +- ROT5 `omg` gives `trl` +- ROT0 `c` gives `c` +- ROT26 `Cool` gives `Cool` +- ROT13 `The quick brown fox jumps over the lazy dog.` gives `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` +- ROT13 `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` gives `The quick brown fox jumps over the lazy dog.` diff --git a/exercises/practice/rotational-cipher/.meta/config.json b/exercises/practice/rotational-cipher/.meta/config.json new file mode 100644 index 00000000..bf003b29 --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "rotational-cipher.el" + ], + "test": [ + "rotational-cipher-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Caesar_cipher" +} diff --git a/exercises/practice/rotational-cipher/.meta/example.el b/exercises/practice/rotational-cipher/.meta/example.el new file mode 100644 index 00000000..2e8e1894 --- /dev/null +++ b/exercises/practice/rotational-cipher/.meta/example.el @@ -0,0 +1,25 @@ +;;; rotational-cipher.el --- Rotational Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defconst shift-max 26) + +(defun rotate (text shift-key) + (let ((normalized-shift-key (mod shift-key shift-max))) + (concat + (mapcar + (lambda (char) + (cond + ((<= ?A char ?Z) + (+ (mod (+ (- char ?A) normalized-shift-key) shift-max) ?A)) + ((<= ?a char ?z) + (+ (mod (+ (- char ?a) normalized-shift-key) shift-max) ?a)) + (t + char))) + text)))) + + +(provide 'rotational-cipher) +;;; rotational-cipher.el ends here diff --git a/exercises/practice/rotational-cipher/.meta/tests.toml b/exercises/practice/rotational-cipher/.meta/tests.toml new file mode 100644 index 00000000..53441ed2 --- /dev/null +++ b/exercises/practice/rotational-cipher/.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. + +[74e58a38-e484-43f1-9466-877a7515e10f] +description = "rotate a by 0, same output as input" + +[7ee352c6-e6b0-4930-b903-d09943ecb8f5] +description = "rotate a by 1" + +[edf0a733-4231-4594-a5ee-46a4009ad764] +description = "rotate a by 26, same output as input" + +[e3e82cb9-2a5b-403f-9931-e43213879300] +description = "rotate m by 13" + +[19f9eb78-e2ad-4da4-8fe3-9291d47c1709] +description = "rotate n by 13 with wrap around alphabet" + +[a116aef4-225b-4da9-884f-e8023ca6408a] +description = "rotate capital letters" + +[71b541bb-819c-4dc6-a9c3-132ef9bb737b] +description = "rotate spaces" + +[ef32601d-e9ef-4b29-b2b5-8971392282e6] +description = "rotate numbers" + +[32dd74f6-db2b-41a6-b02c-82eb4f93e549] +description = "rotate punctuation" + +[9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9] +description = "rotate all letters" diff --git a/exercises/practice/rotational-cipher/rotational-cipher-test.el b/exercises/practice/rotational-cipher/rotational-cipher-test.el new file mode 100644 index 00000000..854e4b81 --- /dev/null +++ b/exercises/practice/rotational-cipher/rotational-cipher-test.el @@ -0,0 +1,60 @@ +;;; rotational-cipher-test.el --- Rotational Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "rotational-cipher.el") +(declare-function rotate "rotational-cipher.el" (text shift-key)) + + +(ert-deftest rotate-a-by-0-same-output-as-input () + (should (string= (rotate "a" 0) "a"))) + + +(ert-deftest rotate-a-by-1 () + (should (string= (rotate "a" 1) "b"))) + + +(ert-deftest rotate-a-by-26-same-output-as-input () + (should (string= (rotate "a" 26) "a"))) + + +(ert-deftest rotate-m-by-13 () + (should (string= (rotate "m" 13) "z"))) + + +(ert-deftest rotate-n-by-13-with-wrap-around-alphabet () + (should (string= (rotate "n" 13) "a"))) + + +(ert-deftest rotate-capital-letters () + (should (string= (rotate "OMG" 5) "TRL"))) + + +(ert-deftest rotate-spaces () + (should (string= (rotate "O M G" 5) "T R L"))) + + +(ert-deftest rotate-numbers () + (should + (string= + (rotate "Testing 1 2 3 testing" 4) "Xiwxmrk 1 2 3 xiwxmrk"))) + + +(ert-deftest rotate-punctuation () + (should + (string= (rotate "Let's eat, Grandma!" 21) "Gzo'n zvo, Bmviyhv!"))) + + +(ert-deftest rotate-all-letters () + (should + (string= + (rotate + "The quick brown fox jumps over the lazy dog." 13) + "Gur dhvpx oebja sbk whzcf bire gur ynml qbt."))) + + +(provide 'rotational-cipher-test) +;;; rotational-cipher-test.el ends here diff --git a/exercises/practice/rotational-cipher/rotational-cipher.el b/exercises/practice/rotational-cipher/rotational-cipher.el new file mode 100644 index 00000000..dd0df423 --- /dev/null +++ b/exercises/practice/rotational-cipher/rotational-cipher.el @@ -0,0 +1,14 @@ +;;; rotational-cipher.el --- Rotational Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun rotate (text shift-key) + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'rotational-cipher) +;;; rotational-cipher.el ends here diff --git a/exercises/practice/run-length-encoding/.meta/config.json b/exercises/practice/run-length-encoding/.meta/config.json index 99c946d9..ea4711b8 100644 --- a/exercises/practice/run-length-encoding/.meta/config.json +++ b/exercises/practice/run-length-encoding/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "benreyn" ], diff --git a/exercises/practice/satellite/.docs/instructions.md b/exercises/practice/satellite/.docs/instructions.md new file mode 100644 index 00000000..fbbf14f4 --- /dev/null +++ b/exercises/practice/satellite/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Imagine you need to transmit a binary tree to a satellite approaching Alpha Centauri and you have limited bandwidth. +Since the tree has no repeating items it can be uniquely represented by its [pre-order and in-order traversals][wiki]. + +Write the software for the satellite to rebuild the tree from the traversals. + +A pre-order traversal reads the value of the current node before (hence "pre") reading the left subtree in pre-order. +Afterwards the right subtree is read in pre-order. + +An in-order traversal reads the left subtree in-order then the current node and finally the right subtree in-order. +So in order from left to right. + +For example the pre-order traversal of this tree is [a, i, x, f, r]. +The in-order traversal of this tree is [i, a, f, x, r] + +```text + a + / \ +i x + / \ + f r +``` + +Note: the first item in the pre-order traversal is always the root. + +[wiki]: https://en.wikipedia.org/wiki/Tree_traversal diff --git a/exercises/practice/satellite/.meta/config.json b/exercises/practice/satellite/.meta/config.json new file mode 100644 index 00000000..d7503331 --- /dev/null +++ b/exercises/practice/satellite/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "satellite.el" + ], + "test": [ + "satellite-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Rebuild binary trees from pre-order and in-order traversals." +} diff --git a/exercises/practice/satellite/.meta/example.el b/exercises/practice/satellite/.meta/example.el new file mode 100644 index 00000000..b4fb6ac8 --- /dev/null +++ b/exercises/practice/satellite/.meta/example.el @@ -0,0 +1,31 @@ +;;; satellite.el --- Satellite (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) +(require 'seq) + +(defun tree-from-traversals (preorder inorder) + (when (/= (length preorder) (length inorder)) + (error "traversals must have the same length")) + (when (or (/= (length preorder) (length (seq-uniq preorder))) + (/= (length inorder) (length (seq-uniq inorder)))) + (error "traversals must contain unique items")) + (cl-labels + ((traverse (successor) + (cond + ((and successor (equal (car inorder) successor)) nil) + ((null preorder) (error "traversals must have the same elements")) + (t (let ((v (car preorder))) + (setq preorder (cdr preorder)) + (let ((l (funcall #'traverse v))) + (setq inorder (cdr inorder)) + (let ((r (and inorder (funcall #'traverse successor)))) + (list (cons :v v) (cons :l l) (cons :r r))))))))) + (and preorder (funcall #'traverse nil)))) + +(provide 'satellite) +;;; satellite.el ends here + diff --git a/exercises/practice/satellite/.meta/tests.toml b/exercises/practice/satellite/.meta/tests.toml new file mode 100644 index 00000000..b32dc3b1 --- /dev/null +++ b/exercises/practice/satellite/.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. + +[8df3fa26-811a-4165-9286-ff9ac0850d19] +description = "Empty tree" + +[f945ccfc-05e3-47d7-825b-0270559d43ad] +description = "Tree with one item" + +[a0121d5f-37b0-48dd-9c64-cba4c4464135] +description = "Tree with many items" + +[6074041f-4891-4d81-a128-401050c2a3b0] +description = "Reject traversals of different length" + +[27916ce4-45f3-4d8b-8528-496fedc157ca] +description = "Reject inconsistent traversals of same length" + +[d86a3d72-76a9-43b5-9d3a-e64cb1216035] +description = "Reject traversals with repeated items" diff --git a/exercises/practice/satellite/satellite-test.el b/exercises/practice/satellite/satellite-test.el new file mode 100644 index 00000000..bfa19d13 --- /dev/null +++ b/exercises/practice/satellite/satellite-test.el @@ -0,0 +1,52 @@ +;;; satellite-test.el --- Tests for Satellite (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "satellite.el") +(declare-function tree-from-traversals "satellite.el" (preorder inorder)) + + +(ert-deftest empty-tree () + (should (equal nil + (tree-from-traversals '() '())))) + + +(ert-deftest tree-with-one-item () + (should (equal '((:v . "a") + (:l . nil) + (:r . nil)) + (tree-from-traversals '("a") '("a"))))) + + +(ert-deftest tree-with-many-items () + (should (equal '((:v . "a") + (:l . ((:v . "i") + (:l . nil) + (:r . nil))) + (:r . ((:v . "x") + (:l . ((:v . "f") + (:l . nil) + (:r . nil))) + (:r . ((:v . "r") + (:l . nil) + (:r . nil)))))) + (tree-from-traversals '("a" "i" "x" "f" "r") '("i" "a" "f" "x" "r"))))) + + +(ert-deftest reject-traversals-of-different-length () + (should-error (tree-from-traversals '("a" "b") '("b" "a" "r")))) + + +(ert-deftest reject-inconsistent-traversals-of-same-length () + (should-error (tree-from-traversals '("x" "y" "z") '("a" "b" "c")))) + + +(ert-deftest reject-traversals-with-repeated-items () + (should-error (tree-from-traversals '("a" "b" "a") '("b" "a" "a")))) + + +(provide 'satellite-test) +;;; satellite-test.el ends here diff --git a/exercises/practice/satellite/satellite.el b/exercises/practice/satellite/satellite.el new file mode 100644 index 00000000..ad0e3447 --- /dev/null +++ b/exercises/practice/satellite/satellite.el @@ -0,0 +1,14 @@ +;;; satellite.el --- Satellite (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun tree-from-traversals (preorder inorder) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'satellite) +;;; satellite.el ends here + diff --git a/exercises/practice/say/.docs/instructions.md b/exercises/practice/say/.docs/instructions.md new file mode 100644 index 00000000..ad3d3477 --- /dev/null +++ b/exercises/practice/say/.docs/instructions.md @@ -0,0 +1,48 @@ +# Instructions + +Given a number from 0 to 999,999,999,999, spell out that number in English. + +## Step 1 + +Handle the basic case of 0 through 99. + +If the input to the program is `22`, then the output should be `'twenty-two'`. + +Your program should complain loudly if given a number outside the blessed range. + +Some good test cases for this program are: + +- 0 +- 14 +- 50 +- 98 +- -1 +- 100 + +### Extension + +If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud. +If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`. + +## Step 2 + +Implement breaking a number up into chunks of thousands. + +So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0. + +## Step 3 + +Now handle inserting the appropriate scale word between those chunks. + +So `1234567890` should yield `'1 billion 234 million 567 thousand 890'` + +The program must also report any values that are out of range. +It's fine to stop at "trillion". + +## Step 4 + +Put it all together to get nothing but plain English. + +`12345` should give `twelve thousand three hundred forty-five`. + +The program must also report any values that are out of range. diff --git a/exercises/practice/say/.meta/config.json b/exercises/practice/say/.meta/config.json new file mode 100644 index 00000000..278dbf8b --- /dev/null +++ b/exercises/practice/say/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "say.el" + ], + "test": [ + "say-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.", + "source": "A variation on the JavaRanch CattleDrive, Assignment 4", + "source_url": "https://web.archive.org/web/20240907035912/https://coderanch.com/wiki/718804" +} diff --git a/exercises/practice/say/.meta/example.el b/exercises/practice/say/.meta/example.el new file mode 100644 index 00000000..8b560a97 --- /dev/null +++ b/exercises/practice/say/.meta/example.el @@ -0,0 +1,54 @@ +;;; say.el --- Say (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(define-error 'out-of-range "input out of range") + +(defun say (number) + (when (or (< number 0) (>= number (expt 10 12))) + (signal 'out-of-range 'nil)) + (let ((ones '("zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine")) + (teens '("ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen")) + (tens '("" "" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety")) + (powers '("" "thousand" "million" "billion"))) + + (defun number-to-words (n) + (let ((parts '())) + (while (> n 0) + (let* ((power (if (> n 999999999) 3 (if (> n 999999) 2 (if (> n 999) 1 0)))) + (unit (expt 1000 power)) + (quotient (/ n unit)) + (remainder (% n unit))) + (if (> quotient 0) + (push (concat (number-to-words-1000 quotient) (if (> power 0) (concat " " (nth power powers)) "")) parts)) + (setq n remainder))) + (string-join (reverse parts) " "))) + + (defun number-to-words-1000 (n) + (cond + ((< n 10) (nth n ones)) + ((< n 20) (nth (- n 10) teens)) + ((< n 100) + (let ((ten (nth (/ n 10) tens)) + (one (nth (% n 10) ones))) + (if (= (% n 10) 0) + ten + (concat ten "-" one)))) + ((< n 1000) + (let ((hundred (nth (/ n 100) ones)) + (remainder (% n 100))) + (if (= remainder 0) + (concat hundred " hundred") + (concat hundred " hundred " (number-to-words-1000 remainder))))))) + + (when (and (>= number 0) (< number (expt 10 12))) + (if (= number 0) + "zero" + (string-trim (number-to-words number)))))) + + +(provide 'say) +;;; say.el ends here + diff --git a/exercises/practice/say/.meta/tests.toml b/exercises/practice/say/.meta/tests.toml new file mode 100644 index 00000000..a5532e9e --- /dev/null +++ b/exercises/practice/say/.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. + +[5d22a120-ba0c-428c-bd25-8682235d83e8] +description = "zero" + +[9b5eed77-dbf6-439d-b920-3f7eb58928f6] +description = "one" + +[7c499be1-612e-4096-a5e1-43b2f719406d] +description = "fourteen" + +[f541dd8e-f070-4329-92b4-b7ce2fcf06b4] +description = "twenty" + +[d78601eb-4a84-4bfa-bf0e-665aeb8abe94] +description = "twenty-two" + +[f010d4ca-12c9-44e9-803a-27789841adb1] +description = "thirty" + +[738ce12d-ee5c-4dfb-ad26-534753a98327] +description = "ninety-nine" + +[e417d452-129e-4056-bd5b-6eb1df334dce] +description = "one hundred" + +[d6924f30-80ba-4597-acf6-ea3f16269da8] +description = "one hundred twenty-three" + +[2f061132-54bc-4fd4-b5df-0a3b778959b9] +description = "two hundred" + +[feed6627-5387-4d38-9692-87c0dbc55c33] +description = "nine hundred ninety-nine" + +[3d83da89-a372-46d3-b10d-de0c792432b3] +description = "one thousand" + +[865af898-1d5b-495f-8ff0-2f06d3c73709] +description = "one thousand two hundred thirty-four" + +[b6a3f442-266e-47a3-835d-7f8a35f6cf7f] +description = "one million" + +[2cea9303-e77e-4212-b8ff-c39f1978fc70] +description = "one million two thousand three hundred forty-five" + +[3e240eeb-f564-4b80-9421-db123f66a38f] +description = "one billion" + +[9a43fed1-c875-4710-8286-5065d73b8a9e] +description = "a big number" + +[49a6a17b-084e-423e-994d-a87c0ecc05ef] +description = "numbers below zero are out of range" + +[4d6492eb-5853-4d16-9d34-b0f61b261fd9] +description = "numbers above 999,999,999,999 are out of range" diff --git a/exercises/practice/say/say-test.el b/exercises/practice/say/say-test.el new file mode 100644 index 00000000..36770824 --- /dev/null +++ b/exercises/practice/say/say-test.el @@ -0,0 +1,89 @@ +;;; say-test.el --- Say (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "say.el") +(declare-function say "say.el" (number)) + + +(ert-deftest zero () + (should (string= (say 0) "zero"))) + + +(ert-deftest one () + (should (string= (say 1) "one"))) + + +(ert-deftest fourteen () + (should (string= (say 14) "fourteen"))) + + +(ert-deftest twenty () + (should (string= (say 20) "twenty"))) + + +(ert-deftest twenty-two () + (should (string= (say 22) "twenty-two"))) + + +(ert-deftest thirty () + (should (string= (say 30) "thirty"))) + + +(ert-deftest ninety-nine () + (should (string= (say 99) "ninety-nine"))) + + +(ert-deftest one-hundred () + (should (string= (say 100) "one hundred"))) + + +(ert-deftest one-hundred-twenty-three () + (should (string= (say 123) "one hundred twenty-three"))) + + +(ert-deftest two-hundred () + (should (string= (say 200) "two hundred"))) + + +(ert-deftest nine-hundred-ninety-nine () + (should (string= (say 999) "nine hundred ninety-nine"))) + + +(ert-deftest one-thousand () + (should (string= (say 1000) "one thousand"))) + + +(ert-deftest one-thousand-two-hundred-thirty-four () + (should (string= (say 1234) "one thousand two hundred thirty-four"))) + + +(ert-deftest one-million () + (should (string= (say 1000000) "one million"))) + + +(ert-deftest one-million-two-thousand-three-hundred-forty-five () + (should (string= (say 1002345) "one million two thousand three hundred forty-five"))) + + +(ert-deftest one-billion () + (should (string= (say 1000000000) "one billion"))) + + +(ert-deftest a-big-number () + (should (string= (say 987654321123) "nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three"))) + + +(ert-deftest numbers-below-zero-are-out-of-range () + (should-error (say -1) :type 'out-of-range)) + + +(ert-deftest numbers-above-999999999999-are-out-of-range () + (should-error (say 1000000000000) :type 'out-of-range)) + + +(provide 'say-test) +;;; say-test.el ends here diff --git a/exercises/practice/say/say.el b/exercises/practice/say/say.el new file mode 100644 index 00000000..8c8e3167 --- /dev/null +++ b/exercises/practice/say/say.el @@ -0,0 +1,18 @@ +;;; say.el --- Say (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(define-error 'out-of-range + (error "Delete this S-Expression and write your own implementation")) + + +(defun say (number) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'say) +;;; say.el ends here + 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..e78d541a --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "scrabble-score.el" + ], + "test": [ + "scrabble-score-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/scrabble-score/.meta/example.el new file mode 100644 index 00000000..6e3e0ef6 --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/example.el @@ -0,0 +1,36 @@ +;;; scrabble-score.el --- Scrabble Score (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defvar letters->score '( + ("AEIOULNRST" . 1) + ("DG" . 2) + ("BCMP" . 3) + ("FHVWY" . 4) + ("K" . 5) + ("JX" . 8) + ("QZ" . 10) +)) + +(defun letter-score (letter) + "The Scrabble score for letter" + (let ((table letters->score)) + (while + (not (cl-find letter (caar table))) + (setq table (cdr table))) + (cdar table))) + +(defun score (word) + "The Scrabble score for word" + (let ((total 0) + (letters (cl-coerce (upcase word) 'list))) + (dolist (letter letters) + (setq total (+ total (letter-score letter)))) + total)) + +(provide 'scrabble-score) +;;; scrabble-score.el ends here 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.el b/exercises/practice/scrabble-score/scrabble-score-test.el new file mode 100644 index 00000000..94fddd9f --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score-test.el @@ -0,0 +1,55 @@ +;;; scrabble-score-test.el --- Tests for Scrabble Score (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "scrabble-score.el") +(declare-function score "scrabble-score.el" (word)) + + +(ert-deftest lowercase-letter () + (should (equal 1 (score "a")))) + + +(ert-deftest uppercase-letter () + (should (equal 1 (score "A")))) + + +(ert-deftest valuable-letter () + (should (equal 4 (score "f")))) + + +(ert-deftest short-word () + (should (equal 2 (score "at")))) + + +(ert-deftest short-valuable-word () + (should (equal 12 (score "zoo")))) + + +(ert-deftest medium-word () + (should (equal 6 (score "street")))) + + +(ert-deftest medium-valuable-word () + (should (equal 22 (score "quirky")))) + + +(ert-deftest long-mixed-case-word () + (should (equal 41 (score "OxyphenButazone")))) + + +(ert-deftest english-like-word () + (should (equal 8 (score "pinata")))) + + +(ert-deftest empty-input () + (should (equal 0 (score "")))) + + +(ert-deftest entire-alphabet-available () + (should (equal 87 (score "abcdefghijklmnopqrstuvwxyz")))) + +(provide 'scrabble-score-test) +;;; scrabble-score-test.el ends here diff --git a/exercises/practice/scrabble-score/scrabble-score.el b/exercises/practice/scrabble-score/scrabble-score.el new file mode 100644 index 00000000..2f603452 --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score.el @@ -0,0 +1,11 @@ +;;; scrabble-score.el --- Scrabble Score (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(defun score (word) + (error "Delete this S-Expression and write your own implementation")) + +(provide 'scrabble-score) +;;; scrabble-score.el ends here 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..ec07121c --- /dev/null +++ b/exercises/practice/secret-handshake/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "secret-handshake.el" + ], + "test": [ + "secret-handshake-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/secret-handshake/.meta/example.el new file mode 100644 index 00000000..8271b79f --- /dev/null +++ b/exercises/practice/secret-handshake/.meta/example.el @@ -0,0 +1,26 @@ +;;; secret-handshake.el --- Secret Handshake (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun commands (number) + (let ((all-commands '("wink" "double blink" "close your eyes" "jump")) + (result nil)) + (cl-labels + ((extract (action shift pending) + (cl-loop until (null pending) + if (/= (logand number action) 0) + do (setq result (cons (car pending) result)) + do (setq pending (cdr pending)) + do (setq action (lsh action shift)) + finally return result))) + (if (= (logand number 16) 0) + (funcall #'extract 8 -1 (reverse all-commands)) + (funcall #'extract 1 1 all-commands))))) + +(provide 'secret-handshake) +;;; secret-handshake.el ends here + 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.el b/exercises/practice/secret-handshake/secret-handshake-test.el new file mode 100644 index 00000000..9ef4e020 --- /dev/null +++ b/exercises/practice/secret-handshake/secret-handshake-test.el @@ -0,0 +1,57 @@ +;;; secret-handshake-test.el --- Tests for Secret Handshake (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "secret-handshake.el") +(declare-function commands "secret-handshake.el" (number)) + + +(ert-deftest wink-for-1 () + (should (equal '("wink") (commands 1)))) + + +(ert-deftest double-blink-for-10 () + (should (equal '("double blink") (commands 2)))) + + +(ert-deftest close-your-eyes-for-100 () + (should (equal '("close your eyes") (commands 4)))) + + +(ert-deftest jump-for-1000 () + (should (equal '("jump") (commands 8)))) + + +(ert-deftest combine-two-actions () + (should (equal '("wink" "double blink") (commands 3)))) + + +(ert-deftest reverse-two-actions () + (should (equal '("double blink" "wink") (commands 19)))) + + +(ert-deftest reversing-one-action-gives-the-same-action () + (should (equal '("jump") (commands 24)))) + + +(ert-deftest reversing-no-actions-still-gives-no-actions () + (should (equal '() (commands 16)))) + + +(ert-deftest all-possible-actions () + (should (equal '("wink" "double blink" "close your eyes" "jump") (commands 15)))) + + +(ert-deftest reverse-all-possible-actions () + (should (equal '("jump" "close your eyes" "double blink" "wink") (commands 31)))) + + +(ert-deftest do-nothing-for-zero () + (should (equal '() (commands 0)))) + + +(provide 'secret-handshake-test) +;;; secret-handshake-test.el ends here diff --git a/exercises/practice/secret-handshake/secret-handshake.el b/exercises/practice/secret-handshake/secret-handshake.el new file mode 100644 index 00000000..d6dbf7ea --- /dev/null +++ b/exercises/practice/secret-handshake/secret-handshake.el @@ -0,0 +1,14 @@ +;;; secret-handshake.el --- Secret Handshake (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun commands (number) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'secret-handshake) +;;; secret-handshake.el ends here + 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..d7b49651 --- /dev/null +++ b/exercises/practice/series/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "series.el" + ], + "test": [ + "series-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/series/.meta/example.el new file mode 100644 index 00000000..e6675e98 --- /dev/null +++ b/exercises/practice/series/.meta/example.el @@ -0,0 +1,22 @@ +;;; series.el --- Series (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + + +(defun slices (series slice-length) + (cond + ((equal "" series) (error "series cannot be empty")) + ((> slice-length (length series)) (error "slice length cannot be greater than series length")) + ((= slice-length 0) (error "slice length cannot be zero")) + ((< slice-length 0) (error "slice length cannot be negative")) + (t (mapcar (lambda (start) + (substring series start (+ start slice-length))) + (number-sequence 0 (- (length series) slice-length)))))) + + +(provide 'series) +;;; series.el ends here + 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.el b/exercises/practice/series/series-test.el new file mode 100644 index 00000000..615531e6 --- /dev/null +++ b/exercises/practice/series/series-test.el @@ -0,0 +1,64 @@ +;;; series-test.el --- Tests for Series (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "series.el") +(declare-function slices "series.el" (series slice-length)) + + +(ert-deftest slices-of-one-from-one () + (should (equal '("1") (slices "1" 1)))) + + +(ert-deftest slices-of-one-from-two () + (should (equal '("1" "2") (slices "12" 1)))) + + +(ert-deftest slices-of-two () + (should (equal '("35") (slices "35" 2)))) + + +(ert-deftest slices-of-two-overlap () + (should (equal '("91" "14" "42") (slices "9142" 2)))) + + +(ert-deftest slices-can-include-duplicates () + (should (equal '("777" "777" "777" "777") (slices "777777" 3)))) + + +(ert-deftest slices-of-a-long-series () + (should (equal '("91849" + "18493" + "84939" + "49390" + "93904" + "39042" + "90424" + "04243") + (slices "918493904243" 5)))) + + +(ert-deftest slice-length-is-too-large () + (should-error (slices "12345" 6))) + + +(ert-deftest slice-length-is-way-too-large () + (should-error (slices "12345" 42))) + + +(ert-deftest slice-length-cannot-be-zero () + (should-error (slices "12345" 0))) + + +(ert-deftest slice-length-cannot-be-negative () + (should-error (slices "123" -1))) + + +(ert-deftest empty-series-is-invalid () + (should-error (slices "" 1))) + + +(provide 'series-test) +;;; series-test.el ends here diff --git a/exercises/practice/series/series.el b/exercises/practice/series/series.el new file mode 100644 index 00000000..2b298f22 --- /dev/null +++ b/exercises/practice/series/series.el @@ -0,0 +1,14 @@ +;;; series.el --- Series (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun slices (series slice-length) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'series) +;;; series.el ends here + 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..c98915f1 --- /dev/null +++ b/exercises/practice/sieve/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "sieve.el" + ], + "test": [ + "sieve-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/sieve/.meta/example.el new file mode 100644 index 00000000..fdd454ed --- /dev/null +++ b/exercises/practice/sieve/.meta/example.el @@ -0,0 +1,25 @@ +;;; sieve.el --- Sieve (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun primes (limit) + (let ((table (make-bool-vector (1+ limit) t)) + (result nil)) + (cl-loop for p from 2 + for psq = (* p p) + until (< limit psq) + do (when (aref table p) + (cl-loop for m from psq to limit by p + do (aset table m nil)))) + (cl-loop for p from limit downto 2 + do (when (aref table p) + (setq result (cons p result)))) + result)) + +(provide 'sieve) +;;; sieve.el ends here + 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.el b/exercises/practice/sieve/sieve-test.el new file mode 100644 index 00000000..eb96b595 --- /dev/null +++ b/exercises/practice/sieve/sieve-test.el @@ -0,0 +1,43 @@ +;;; sieve-test.el --- Tests for Sieve (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "sieve.el") +(declare-function primes "sieve.el" (limit)) + + +(ert-deftest no-primes-under-two () + (should (equal '() (primes 1)))) + + +(ert-deftest find-first-prime () + (should (equal '(2) (primes 2)))) + + +(ert-deftest find-primes-up-to-10 () + (should (equal '(2 3 5 7) (primes 10)))) + + +(ert-deftest limit-is-prime () + (should (equal '(2 3 5 7 11 13) (primes 13)))) + + +(ert-deftest find-primes-up-to-1000 () + (should (equal '(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) + (primes 1000)))) + + +(provide 'sieve-test) +;;; sieve-test.el ends here diff --git a/exercises/practice/sieve/sieve.el b/exercises/practice/sieve/sieve.el new file mode 100644 index 00000000..89858dda --- /dev/null +++ b/exercises/practice/sieve/sieve.el @@ -0,0 +1,14 @@ +;;; sieve.el --- Sieve (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun primes (limit) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'sieve) +;;; sieve.el ends here + diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md new file mode 100644 index 00000000..33785744 --- /dev/null +++ b/exercises/practice/simple-cipher/.docs/instructions.md @@ -0,0 +1,66 @@ +# Instructions + +Implement a simple shift cipher like Caesar and a more secure substitution cipher. + +## Step 1 + +"If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out. +If anyone wishes to decipher these, and get at their meaning, he must substitute the fourth letter of the alphabet, namely D, for A, and so with the others." +—Suetonius, Life of Julius Caesar + +Ciphers are very straight-forward algorithms that allow us to render text less readable while still allowing easy deciphering. +They are vulnerable to many forms of cryptanalysis, but Caesar was lucky that his enemies were not cryptanalysts. + +The Caesar cipher was used for some messages from Julius Caesar that were sent afield. +Now Caesar knew that the cipher wasn't very good, but he had one ally in that respect: almost nobody could read well. +So even being a couple letters off was sufficient so that people couldn't recognize the few words that they did know. + +Your task is to create a simple shift cipher like the Caesar cipher. +This image is a great example of the Caesar cipher: + +![Caesar cipher][img-caesar-cipher] + +For example: + +Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". +Obscure enough to keep our message secret in transit. + +When "ldpdsdqgdehdu" is put into the decode function it would return the original "iamapandabear" letting your friend read your original message. + +## Step 2 + +Shift ciphers quickly cease to be useful when the opposition commander figures them out. +So instead, let's try using a substitution cipher. +Try amending the code to allow us to specify a key and use that for the shift distance. + +Here's an example: + +Given the key "aaaaaaaaaaaaaaaaaa", encoding the string "iamapandabear" +would return the original "iamapandabear". + +Given the key "ddddddddddddddddd", encoding our string "iamapandabear" +would return the obscured "ldpdsdqgdehdu" + +In the example above, we've set a = 0 for the key value. +So when the plaintext is added to the key, we end up with the same message coming out. +So "aaaa" is not an ideal key. +But if we set the key to "dddd", we would get the same thing as the Caesar cipher. + +## Step 3 + +The weakest link in any cipher is the human being. +Let's make your substitution cipher a little more fault tolerant by providing a source of randomness and ensuring that the key contains only lowercase letters. + +If someone doesn't submit a key at all, generate a truly random key of at least 100 lowercase characters in length. + +## Extensions + +Shift ciphers work by making the text slightly odd, but are vulnerable to frequency analysis. +Substitution ciphers help that, but are still very vulnerable when the key is short or if spaces are preserved. +Later on you'll see one solution to this problem in the exercise "crypto-square". + +If you want to go farther in this field, the questions begin to be about how we can exchange keys in a secure way. +Take a look at [Diffie-Hellman on Wikipedia][dh] for one of the first implementations of this scheme. + +[img-caesar-cipher]: https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Caesar_cipher_left_shift_of_3.svg/320px-Caesar_cipher_left_shift_of_3.svg.png +[dh]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange diff --git a/exercises/practice/simple-cipher/.meta/config.json b/exercises/practice/simple-cipher/.meta/config.json new file mode 100644 index 00000000..74ea7671 --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "fapdash" + ], + "files": { + "solution": [ + "simple-cipher.el" + ], + "test": [ + "simple-cipher-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.", + "source": "Substitution Cipher at Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Substitution_cipher" +} diff --git a/exercises/practice/simple-cipher/.meta/example.el b/exercises/practice/simple-cipher/.meta/example.el new file mode 100644 index 00000000..c22ed0c9 --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/example.el @@ -0,0 +1,52 @@ +;;; simple-cipher.el --- Simple Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'seq) +(require 'cl-lib) + +(defconst alphabet-for-random-letter "abcdefghijklmnopqrstuvwxyz") + +(defun encode (plaintext key) + (simple-cipher plaintext key '+)) + +(defun decode (ciphertext key) + (simple-cipher ciphertext key '-)) + +(defun simple-cipher (text key fun) + (let ((calc-key + (if (<= (length text) (length key)) + key + (dotimes (_index (/ (length text) (length key)) key) + (let ((key-copy key)) + (setq key (concat key key-copy))))))) + (mapconcat + 'identity + (cl-mapcar (lambda (char1 char2) + (char-to-string + (+ ?a + (mod + (funcall fun (- char1 ?a) (- char2 ?a)) + (length alphabet-for-random-letter))))) + text + calc-key) + ""))) + +(defun generate-key () + (let ((key "")) + (dotimes (_i 100 key) + (setq + key + (concat + key + (char-to-string + (seq-elt + alphabet-for-random-letter + ;; NOT cryptographically secure! + ;; see https://emacs.stackexchange.com/questions/35615/secure-random-numbers + (random (length alphabet-for-random-letter))))))))) + +(provide 'simple-cipher) +;;; simple-cipher.el ends here diff --git a/exercises/practice/simple-cipher/.meta/tests.toml b/exercises/practice/simple-cipher/.meta/tests.toml new file mode 100644 index 00000000..77e6571e --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[b8bdfbe1-bea3-41bb-a999-b41403f2b15d] +description = "Random key cipher -> Can encode" + +[3dff7f36-75db-46b4-ab70-644b3f38b81c] +description = "Random key cipher -> Can decode" + +[8143c684-6df6-46ba-bd1f-dea8fcb5d265] +description = "Random key cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method" + +[defc0050-e87d-4840-85e4-51a1ab9dd6aa] +description = "Random key cipher -> Key is made only of lowercase letters" + +[565e5158-5b3b-41dd-b99d-33b9f413c39f] +description = "Substitution cipher -> Can encode" + +[d44e4f6a-b8af-4e90-9d08-fd407e31e67b] +description = "Substitution cipher -> Can decode" + +[70a16473-7339-43df-902d-93408c69e9d1] +description = "Substitution cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method" + +[69a1458b-92a6-433a-a02d-7beac3ea91f9] +description = "Substitution cipher -> Can double shift encode" + +[21d207c1-98de-40aa-994f-86197ae230fb] +description = "Substitution cipher -> Can wrap on encode" + +[a3d7a4d7-24a9-4de6-bdc4-a6614ced0cb3] +description = "Substitution cipher -> Can wrap on decode" + +[e31c9b8c-8eb6-45c9-a4b5-8344a36b9641] +description = "Substitution cipher -> Can encode messages longer than the key" + +[93cfaae0-17da-4627-9a04-d6d1e1be52e3] +description = "Substitution cipher -> Can decode messages longer than the key" diff --git a/exercises/practice/simple-cipher/simple-cipher-test.el b/exercises/practice/simple-cipher/simple-cipher-test.el new file mode 100644 index 00000000..80246cac --- /dev/null +++ b/exercises/practice/simple-cipher/simple-cipher-test.el @@ -0,0 +1,76 @@ +;;; simple-cipher-test.el --- Simple Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "simple-cipher.el") +(declare-function encode "simple-cipher.el" (plaintext key)) +(declare-function decode "simple-cipher.el" (ciphertext key)) +(declare-function generate-key "simple-cipher.el" ()) + + +(ert-deftest can-encode-random-key () + (let ((key (generate-key))) + (should + (string= + (substring key 0 (length "aaaaaaaaaa")) + (encode "aaaaaaaaaa" key))))) + + +(ert-deftest can-decode-random-key () + (let ((key (generate-key))) + (should + (string= + "aaaaaaaaaa" + (decode (substring key 0 (length "aaaaaaaaaa")) key))))) + + +(ert-deftest is-reversible-random-key () + (let* ((key (generate-key)) + (encoded (encode "abcdefghij" key))) + (should (string= "abcdefghij" (decode encoded key))))) + + +(ert-deftest key-is-made-only-of-lowercase-letters () + (should (string-match "^[a-z]+$" (generate-key)))) + + +(ert-deftest can-encode () + (should (string= "abcdefghij" (encode "aaaaaaaaaa" "abcdefghij")))) + + +(ert-deftest can-decode () + (should (string= "aaaaaaaaaa" (decode "abcdefghij" "abcdefghij")))) + + +(ert-deftest is-reversible () + (let ((encoded (encode "abcdefghij" "abcdefghij"))) + (should (string= "abcdefghij" (decode encoded "abcdefghij"))))) + + +(ert-deftest can-double-shift-encode () + (should + (string= + "qayaeaagaciai" (encode "iamapandabear" "iamapandabear")))) + + +(ert-deftest can-wrap-on-encode () + (should (string= "zabcdefghi" (encode "zzzzzzzzzz" "abcdefghij")))) + + +(ert-deftest can-wrap-on-decode () + (should (string= "zzzzzzzzzz" (decode "zabcdefghi" "abcdefghij")))) + + +(ert-deftest can-encode-messages-longer-than-the-key () + (should (string= "iboaqcnecbfcr" (encode "iamapandabear" "abc")))) + + +(ert-deftest can-decode-messages-longer-than-the-key () + (should (string= "iamapandabear" (decode "iboaqcnecbfcr" "abc")))) + + +(provide 'simple-cipher-test) +;;; simple-cipher-test.el ends here diff --git a/exercises/practice/simple-cipher/simple-cipher.el b/exercises/practice/simple-cipher/simple-cipher.el new file mode 100644 index 00000000..b38942db --- /dev/null +++ b/exercises/practice/simple-cipher/simple-cipher.el @@ -0,0 +1,22 @@ +;;; simple-cipher.el --- Simple Cipher (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun encode (plaintext key) + (error + "Delete this S-Expression and write your own implementation")) + +(defun decode (ciphertext key) + (error + "Delete this S-Expression and write your own implementation")) + +(defun generate-key () + (error + "Delete this S-Expression and write your own implementation")) + + +(provide 'simple-cipher) +;;; simple-cipher.el ends here 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..94990597 --- /dev/null +++ b/exercises/practice/space-age/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "space-age.el" + ], + "test": [ + "space-age-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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/?Chapter=01" +} diff --git a/exercises/practice/space-age/.meta/example.el b/exercises/practice/space-age/.meta/example.el new file mode 100644 index 00000000..86a4835b --- /dev/null +++ b/exercises/practice/space-age/.meta/example.el @@ -0,0 +1,26 @@ +;;; space-age.el --- Space Age (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun orbital-period (planet) + (pcase planet + (:mercury 0.2408467) + (:venus 0.61519726) + (:earth 1.0) + (:mars 1.8808158) + (:jupiter 11.862615) + (:saturn 29.447498) + (:uranus 84.016846) + (:neptune 164.79132) + (_ (error "not a planet")))) + +(defun age (planet seconds) + (/ seconds (* (orbital-period planet) 31557600))) + + +(provide 'space-age) +;;; space-age.el ends here + 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.el b/exercises/practice/space-age/space-age-test.el new file mode 100644 index 00000000..8e99d48f --- /dev/null +++ b/exercises/practice/space-age/space-age-test.el @@ -0,0 +1,53 @@ +;;; space-age-test.el --- Tests for Space Age (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "space-age.el") +(declare-function age "space-age.el" (planet seconds)) + + +(defmacro should-approximate (expected actual) + (list 'should (list '< (- expected 0.01) actual (+ expected 0.01)))) + + +(ert-deftest age-on-earth () + (should-approximate 31.69 (age :earth 1000000000))) + + +(ert-deftest age-on-mercury () + (should-approximate 280.88 (age :mercury 2134835688))) + + +(ert-deftest age-on-venus () + (should-approximate 9.78 (age :venus 189839836))) + + +(ert-deftest age-on-mars () + (should-approximate 35.88 (age :mars 2129871239))) + + +(ert-deftest age-on-jupiter () + (should-approximate 2.41 (age :jupiter 901876382))) + + +(ert-deftest age-on-saturn () + (should-approximate 2.15 (age :saturn 2000000000))) + + +(ert-deftest age-on-uranus () + (should-approximate 0.46 (age :uranus 1210123456))) + + +(ert-deftest age-on-neptune () + (should-approximate 0.35 (age :neptune 1821023456))) + + +(ert-deftest invalid-planet-causes-error () + (should-error (age :sun 680804807))) + + +(provide 'space-age-test) +;;; space-age-test.el ends here diff --git a/exercises/practice/space-age/space-age.el b/exercises/practice/space-age/space-age.el new file mode 100644 index 00000000..502a22ff --- /dev/null +++ b/exercises/practice/space-age/space-age.el @@ -0,0 +1,14 @@ +;;; space-age.el --- Space Age (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun age (planet seconds) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'space-age) +;;; space-age.el ends here + diff --git a/exercises/practice/spiral-matrix/.docs/instructions.md b/exercises/practice/spiral-matrix/.docs/instructions.md new file mode 100644 index 00000000..01e8a77f --- /dev/null +++ b/exercises/practice/spiral-matrix/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +Your task is to return a square matrix of a given size. + +The matrix should be filled with natural numbers, starting from 1 in the top-left corner, increasing in an inward, clockwise spiral order, like these examples: + +## Examples + +### Spiral matrix of size 3 + +```text +1 2 3 +8 9 4 +7 6 5 +``` + +### Spiral matrix of size 4 + +```text + 1 2 3 4 +12 13 14 5 +11 16 15 6 +10 9 8 7 +``` diff --git a/exercises/practice/spiral-matrix/.docs/introduction.md b/exercises/practice/spiral-matrix/.docs/introduction.md new file mode 100644 index 00000000..25c7eb59 --- /dev/null +++ b/exercises/practice/spiral-matrix/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +In a small village near an ancient forest, there was a legend of a hidden treasure buried deep within the woods. +Despite numerous attempts, no one had ever succeeded in finding it. +This was about to change, however, thanks to a young explorer named Elara. +She had discovered an old document containing instructions on how to locate the treasure. +Using these instructions, Elara was able to draw a map that revealed the path to the treasure. + +To her surprise, the path followed a peculiar clockwise spiral. +It was no wonder no one had been able to find the treasure before! +With the map in hand, Elara embarks on her journey to uncover the hidden treasure. diff --git a/exercises/practice/spiral-matrix/.meta/config.json b/exercises/practice/spiral-matrix/.meta/config.json new file mode 100644 index 00000000..20a3b7fb --- /dev/null +++ b/exercises/practice/spiral-matrix/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "spiral-matrix.el" + ], + "test": [ + "spiral-matrix-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given the size, return a square matrix of numbers in spiral order.", + "source": "Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension.", + "source_url": "https://web.archive.org/web/20230607064729/https://old.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/" +} diff --git a/exercises/practice/spiral-matrix/.meta/example.el b/exercises/practice/spiral-matrix/.meta/example.el new file mode 100644 index 00000000..dfd6ddc9 --- /dev/null +++ b/exercises/practice/spiral-matrix/.meta/example.el @@ -0,0 +1,41 @@ +;;; spiral-matrix.el --- Spiral Matrix (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun spiral-matrix (size) + (let* ((result (make-vector size nil)) + (value 1) + (row 0) + (column 0) + (side (1- size))) + (cl-loop for r below size + do (aset result r (make-vector size (* size size)))) + (while (>= side 1) + (cl-loop repeat side + do (aset (aref result row) column value) + do (setq value (1+ value)) + do (setq column (1+ column))) + (cl-loop repeat side + do (aset (aref result row) column value) + do (setq value (1+ value)) + do (setq row (1+ row))) + (cl-loop repeat side + do (aset (aref result row) column value) + do (setq value (1+ value)) + do (setq column (1- column))) + (cl-loop repeat side + do (aset (aref result row) column value) + do (setq value (1+ value)) + do (setq row (1- row))) + (setq row (1+ row)) + (setq column (1+ column)) + (setq side (- side 2))) + result)) + +(provide 'spiral-matrix) +;;; spiral-matrix.el ends here + diff --git a/exercises/practice/spiral-matrix/.meta/tests.toml b/exercises/practice/spiral-matrix/.meta/tests.toml new file mode 100644 index 00000000..9ac5baca --- /dev/null +++ b/exercises/practice/spiral-matrix/.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. + +[8f584201-b446-4bc9-b132-811c8edd9040] +description = "empty spiral" + +[e40ae5f3-e2c9-4639-8116-8a119d632ab2] +description = "trivial spiral" + +[cf05e42d-eb78-4098-a36e-cdaf0991bc48] +description = "spiral of size 2" + +[1c475667-c896-4c23-82e2-e033929de939] +description = "spiral of size 3" + +[05ccbc48-d891-44f5-9137-f4ce462a759d] +description = "spiral of size 4" + +[f4d2165b-1738-4e0c-bed0-c459045ae50d] +description = "spiral of size 5" diff --git a/exercises/practice/spiral-matrix/spiral-matrix-test.el b/exercises/practice/spiral-matrix/spiral-matrix-test.el new file mode 100644 index 00000000..ee9f6fd5 --- /dev/null +++ b/exercises/practice/spiral-matrix/spiral-matrix-test.el @@ -0,0 +1,47 @@ +;;; spiral-matrix-test.el --- Tests for Spiral Matrix (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "spiral-matrix.el") +(declare-function spiral-matrix "spiral-matrix.el" (size)) + + +(ert-deftest empty-spiral () + (should (equal [] (spiral-matrix 0)))) + + +(ert-deftest trivial-spiral () + (should (equal [[1]] (spiral-matrix 1)))) + + +(ert-deftest spiral-of-size-2 () + (should (equal [[1 2] + [4 3]] (spiral-matrix 2)))) + + +(ert-deftest spiral-of-size-3 () + (should (equal [[1 2 3] + [8 9 4] + [7 6 5]] (spiral-matrix 3)))) + + +(ert-deftest spiral-of-size-4 () + (should (equal [[1 2 3 4] + [12 13 14 5] + [11 16 15 6] + [10 9 8 7]] (spiral-matrix 4)))) + + +(ert-deftest spiral-of-size-5 () + (should (equal [[1 2 3 4 5] + [16 17 18 19 6] + [15 24 25 20 7] + [14 23 22 21 8] + [13 12 11 10 9]] (spiral-matrix 5)))) + + +(provide 'spiral-matrix-test) +;;; spiral-matrix-test.el ends here diff --git a/exercises/practice/spiral-matrix/spiral-matrix.el b/exercises/practice/spiral-matrix/spiral-matrix.el new file mode 100644 index 00000000..efefe513 --- /dev/null +++ b/exercises/practice/spiral-matrix/spiral-matrix.el @@ -0,0 +1,14 @@ +;;; spiral-matrix.el --- Spiral Matrix (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun spiral-matrix (size) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'spiral-matrix) +;;; spiral-matrix.el ends here + 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..e93a880b --- /dev/null +++ b/exercises/practice/square-root/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "square-root.el" + ], + "test": [ + "square-root-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/square-root/.meta/example.el new file mode 100644 index 00000000..9cfdd7b7 --- /dev/null +++ b/exercises/practice/square-root/.meta/example.el @@ -0,0 +1,15 @@ +;;; square-root.el --- Square Root (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun square-root (radicand) + (cl-loop for num from 0 + until (<= radicand (* num num)) + finally return num)) + +(provide 'square-root) +;;; square-root.el ends here 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.el b/exercises/practice/square-root/square-root-test.el new file mode 100644 index 00000000..681b9592 --- /dev/null +++ b/exercises/practice/square-root/square-root-test.el @@ -0,0 +1,36 @@ +;;; square-root-test.el --- Tests for Square Root (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "square-root.el") +(declare-function square-root "square-root.el" (radicand)) + + +(ert-deftest root-of-1 () + (should (= 1 (square-root 1)))) + + +(ert-deftest root-of-4 () + (should (= 2 (square-root 4)))) + + +(ert-deftest root-of-25 () + (should (= 5 (square-root 25)))) + + +(ert-deftest root-of-81 () + (should (= 9 (square-root 81)))) + + +(ert-deftest root-of-196 () + (should (= 14 (square-root 196)))) + + +(ert-deftest root-of-65025 () + (should (= 255 (square-root 65025)))) + + +(provide 'square-root-test) +;;; square-root-test.el ends here diff --git a/exercises/practice/square-root/square-root.el b/exercises/practice/square-root/square-root.el new file mode 100644 index 00000000..43f56534 --- /dev/null +++ b/exercises/practice/square-root/square-root.el @@ -0,0 +1,13 @@ +;;; square-root.el --- Square Root (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun square-root (radicand) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'square-root) +;;; square-root.el ends here 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..2165d6b3 --- /dev/null +++ b/exercises/practice/strain/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "strain.el" + ], + "test": [ + "strain-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/strain/.meta/example.el new file mode 100644 index 00000000..7f7f0a41 --- /dev/null +++ b/exercises/practice/strain/.meta/example.el @@ -0,0 +1,24 @@ +;;; strain.el --- Strain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun keep (predicate list) + "Return a list of elements in COLLECTION for which PREDICATE returns true." + (let (result) + (dolist (element list (nreverse result)) + (when (funcall predicate element) + (push element result))))) + +(defun discard (predicate list) + "Return a list of elements in COLLECTION for which PREDICATE returns false." + (let (result) + (dolist (element list (nreverse result)) + (unless (funcall predicate element) + (push element result))))) + + +(provide 'strain) +;;; strain.el ends here 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.el b/exercises/practice/strain/strain-test.el new file mode 100644 index 00000000..b73d08e5 --- /dev/null +++ b/exercises/practice/strain/strain-test.el @@ -0,0 +1,86 @@ +;;; strain-test.el --- Strain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "strain.el") +(declare-function keep "strain.el" (predicate list)) +(declare-function discard "strain.el" (predicate list)) + + +(defun starts-with (string prefix) + (string-prefix-p prefix string)) + + +(defun contains (list element) + (member element list)) + + +(ert-deftest keep-on-empty-list-returns-empty-list () + (should (equal (keep (lambda (x) t) '()) '()))) + + +(ert-deftest keeps-everything () + (should (equal (keep (lambda (x) t) '(1 3 5)) '(1 3 5)))) + + +(ert-deftest keeps-nothing () + (should (equal (keep (lambda (x) nil) '(1 3 5)) '()))) + + +(ert-deftest keeps-first-and-last () + (should (equal (keep (lambda (x) (= 1 (% x 2))) '(1 2 3)) '(1 3)))) + + +(ert-deftest keeps-neither-first-nor-last () + (should (equal (keep (lambda (x) (= 0 (% x 2))) '(1 2 3)) '(2)))) + + +(ert-deftest keeps-strings () + (should (equal (keep (lambda (x) (starts-with x "z")) + '("apple" "zebra" "banana" "zombies" "cherimoya" "zealot")) + '("zebra" "zombies" "zealot")))) + + +(ert-deftest keeps-lists () + (should (equal (keep (lambda (x) (contains x 5)) + '((1 2 3) (5 5 5) (5 1 2) (2 1 2) (1 5 2) (2 2 1) (1 2 5))) + '((5 5 5) (5 1 2) (1 5 2) (1 2 5))))) + + +(ert-deftest discard-on-empty-list-returns-empty-list () + (should (equal (discard (lambda (x) t) '()) '()))) + + +(ert-deftest discards-everything () + (should (equal (discard (lambda (x) t) '(1 3 5)) '()))) + + +(ert-deftest discards-nothing () + (should (equal (discard (lambda (x) nil) '(1 3 5)) '(1 3 5)))) + + +(ert-deftest discards-first-and-last () + (should (equal (discard (lambda (x) (= (mod x 2) 1)) '(1 2 3)) '(2)))) + + +(ert-deftest discards-neither-first-nor-last () + (should (equal (discard (lambda (x) (= (mod x 2) 0)) '(1 2 3)) '(1 3)))) + + +(ert-deftest discards-strings () + (should (equal (discard (lambda (x) (starts-with x "z")) + '("apple" "zebra" "banana" "zombies" "cherimoya" "zealot")) + '("apple" "banana" "cherimoya")))) + + +(ert-deftest discards-lists () + (should (equal (discard (lambda (x) (contains x 5)) + '((1 2 3) (5 5 5) (5 1 2) (2 1 2) (1 5 2) (2 2 1) (1 2 5))) + '((1 2 3) (2 1 2) (2 2 1))))) + + +(provide 'strain-test) +;;; strain-test.el ends here diff --git a/exercises/practice/strain/strain.el b/exercises/practice/strain/strain.el new file mode 100644 index 00000000..457ccbb3 --- /dev/null +++ b/exercises/practice/strain/strain.el @@ -0,0 +1,17 @@ +;;; strain.el --- Strain (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun keep (predicate list) + (error "Delete this S-Expression and write your own implementation")) + +(defun discard (predicate list) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'strain) +;;; strain.el ends here + diff --git a/exercises/practice/sublist/.docs/instructions.md b/exercises/practice/sublist/.docs/instructions.md index 7535931a..8228edc6 100644 --- a/exercises/practice/sublist/.docs/instructions.md +++ b/exercises/practice/sublist/.docs/instructions.md @@ -8,8 +8,8 @@ Given any two lists `A` and `B`, determine if: - None of the above is true, thus lists `A` and `B` are unequal Specifically, list `A` is equal to list `B` if both lists have the same values in the same order. -List `A` is a superlist of `B` if `A` contains a sub-sequence of values equal to `B`. -List `A` is a sublist of `B` if `B` contains a sub-sequence of values equal to `A`. +List `A` is a superlist of `B` if `A` contains a contiguous sub-sequence of values equal to `B`. +List `A` is a sublist of `B` if `B` contains a contiguous sub-sequence of values equal to `A`. Examples: diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md new file mode 100644 index 00000000..d69f890e --- /dev/null +++ b/exercises/practice/sum-of-multiples/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to write the code that calculates the energy points that get awarded to players when they complete a level. + +The points awarded depend on two things: + +- The level (a number) that the player completed. +- The base value of each magical item collected by the player during that level. + +The energy points are awarded according to the following rules: + +1. For each magical item, take the base value and find all the multiples of that value that are less than the level number. +2. Combine the sets of numbers. +3. Remove any duplicates. +4. Calculate the sum of all the numbers that are left. + +Let's look at an example: + +**The player completed level 20 and found two magical items with base values of 3 and 5.** + +To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than level 20. + +- Multiples of 3 less than 20: `{3, 6, 9, 12, 15, 18}` +- Multiples of 5 less than 20: `{5, 10, 15}` +- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}` +- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78` +- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5. diff --git a/exercises/practice/sum-of-multiples/.docs/introduction.md b/exercises/practice/sum-of-multiples/.docs/introduction.md new file mode 100644 index 00000000..69cabeed --- /dev/null +++ b/exercises/practice/sum-of-multiples/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +You work for a company that makes an online, fantasy-survival game. + +When a player finishes a level, they are awarded energy points. +The amount of energy awarded depends on which magical items the player found while exploring that level. diff --git a/exercises/practice/sum-of-multiples/.meta/config.json b/exercises/practice/sum-of-multiples/.meta/config.json new file mode 100644 index 00000000..eb31d82f --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "sum-of-multiples.el" + ], + "test": [ + "sum-of-multiples-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.", + "source": "A variation on Problem 1 at Project Euler", + "source_url": "https://projecteuler.net/problem=1" +} diff --git a/exercises/practice/sum-of-multiples/.meta/example.el b/exercises/practice/sum-of-multiples/.meta/example.el new file mode 100644 index 00000000..20b08480 --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/example.el @@ -0,0 +1,21 @@ +;;; sum-of-multiples.el --- Sum of Multiples (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun sum (factors limit) + (let* ((set (make-hash-table)) + (total 0)) + (dolist (factor (remove 0 factors)) + (cl-loop for multiple from factor below limit by factor + do (puthash multiple t set))) + (maphash (lambda (key value) (setq total (+ total key))) + set) + total)) + +(provide 'sum-of-multiples) +;;; sum-of-multiples.el ends here + diff --git a/exercises/practice/sum-of-multiples/.meta/tests.toml b/exercises/practice/sum-of-multiples/.meta/tests.toml new file mode 100644 index 00000000..1e9b1241 --- /dev/null +++ b/exercises/practice/sum-of-multiples/.meta/tests.toml @@ -0,0 +1,58 @@ +# 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. + +[54aaab5a-ce86-4edc-8b40-d3ab2400a279] +description = "no multiples within limit" + +[361e4e50-c89b-4f60-95ef-5bc5c595490a] +description = "one factor has multiples within limit" + +[e644e070-040e-4ae0-9910-93c69fc3f7ce] +description = "more than one multiple within limit" + +[607d6eb9-535c-41ce-91b5-3a61da3fa57f] +description = "more than one factor with multiples within limit" + +[f47e8209-c0c5-4786-b07b-dc273bf86b9b] +description = "each multiple is only counted once" + +[28c4b267-c980-4054-93e9-07723db615ac] +description = "a much larger limit" + +[09c4494d-ff2d-4e0f-8421-f5532821ee12] +description = "three factors" + +[2d0d5faa-f177-4ad6-bde9-ebb865083751] +description = "factors not relatively prime" + +[ece8f2e8-96aa-4166-bbb7-6ce71261e354] +description = "some pairs of factors relatively prime and some not" + +[624fdade-6ffb-400e-8472-456a38c171c0] +description = "one factor is a multiple of another" + +[949ee7eb-db51-479c-b5cb-4a22b40ac057] +description = "much larger factors" + +[41093673-acbd-482c-ab80-d00a0cbedecd] +description = "all numbers are multiples of 1" + +[1730453b-baaa-438e-a9c2-d754497b2a76] +description = "no factors means an empty sum" + +[214a01e9-f4bf-45bb-80f1-1dce9fbb0310] +description = "the only multiple of 0 is 0" + +[c423ae21-a0cb-4ec7-aeb1-32971af5b510] +description = "the factor 0 does not affect the sum of multiples of other factors" + +[17053ba9-112f-4ac0-aadb-0519dd836342] +description = "solutions using include-exclude must extend to cardinality greater than 3" diff --git a/exercises/practice/sum-of-multiples/sum-of-multiples-test.el b/exercises/practice/sum-of-multiples/sum-of-multiples-test.el new file mode 100644 index 00000000..cdee87cd --- /dev/null +++ b/exercises/practice/sum-of-multiples/sum-of-multiples-test.el @@ -0,0 +1,77 @@ +;;; sum-of-multiples-test.el --- Tests for Sum of Multiples (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "sum-of-multiples.el") +(declare-function sum "sum-of-multiples.el" (factors limit)) + + +(ert-deftest no-multiples-within-limit () + (should (= 0 (sum '(3 5) 1)))) + + +(ert-deftest one-factor-has-multiples-within-limit () + (should (= 3 (sum '(3 5) 4)))) + + +(ert-deftest more-than-one-multiple-within-limit () + (should (= 9 (sum '(3) 7)))) + + +(ert-deftest more-than-one-factor-with-multiples-within-limit () + (should (= 23 (sum '(3 5) 10)))) + + +(ert-deftest each-multiple-is-only-counted-once () + (should (= 2318 (sum '(3 5) 100)))) + + +(ert-deftest a-much-larger-limit () + (should (= 233168 (sum '(3 5) 1000)))) + + +(ert-deftest three-factors () + (should (= 51 (sum '(7 13 17) 20)))) + + +(ert-deftest factors-not-relatively-prime () + (should (= 30 (sum '(4 6) 15)))) + + +(ert-deftest some-pairs-of-factors-relatively-prime-and-some-not () + (should (= 4419 (sum '(5 6 8) 150)))) + + +(ert-deftest one-factor-is-a-multiple-of-another () + (should (= 275 (sum '(5 25) 51)))) + + +(ert-deftest much-larger-factors () + (should (= 2203160 (sum '(43 47) 10000)))) + + +(ert-deftest all-numbers-are-multiples-of-1 () + (should (= 4950 (sum '(1) 100)))) + + +(ert-deftest no-factors-means-an-empty-sum () + (should (= 0 (sum '() 10000)))) + + +(ert-deftest the-only-multiple-of-0-is-0 () + (should (= 0 (sum '(0) 1)))) + + +(ert-deftest the-factor-0-does-not-affect-the-sum-of-multiples-of-other-factors () + (should (= 3 (sum '(3 0) 4)))) + + +(ert-deftest solutions-using-include-exclude-must-extend-to-cardinality-greater-than-3 () + (should (= 39614537 (sum '(2 3 5 7 11) 10000)))) + + +(provide 'sum-of-multiples-test) +;;; sum-of-multiples-test.el ends here diff --git a/exercises/practice/sum-of-multiples/sum-of-multiples.el b/exercises/practice/sum-of-multiples/sum-of-multiples.el new file mode 100644 index 00000000..9426de56 --- /dev/null +++ b/exercises/practice/sum-of-multiples/sum-of-multiples.el @@ -0,0 +1,13 @@ +;;; sum-of-multiples.el --- Sum of Multiples (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun sum (factors limit) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'sum-of-multiples) +;;; sum-of-multiples.el ends here diff --git a/exercises/practice/triangle/.docs/instructions.md b/exercises/practice/triangle/.docs/instructions.md new file mode 100644 index 00000000..ac390087 --- /dev/null +++ b/exercises/practice/triangle/.docs/instructions.md @@ -0,0 +1,29 @@ +# 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. + +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..d402d560 --- /dev/null +++ b/exercises/practice/triangle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "triangle.el" + ], + "test": [ + "triangle-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Determine if a triangle is equilateral, isosceles, or scalene.", + "source": "The Ruby Koans triangle project, parts 1 & 2", + "source_url": "https://web.archive.org/web/20220831105330/http://rubykoans.com" +} diff --git a/exercises/practice/triangle/.meta/example.el b/exercises/practice/triangle/.meta/example.el new file mode 100644 index 00000000..4b114b0b --- /dev/null +++ b/exercises/practice/triangle/.meta/example.el @@ -0,0 +1,50 @@ +;;; triangle.el --- Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun validp (sides) + "Return non-nil if sides are positive and triangle inequality is satisfied" + (let ((a (car sides)) + (b (cadr sides)) + (c (caddr sides))) + (and + (< 0 a) + (< 0 b) + (< 0 c) + (<= a (+ b c)) + (<= b (+ a c)) + (<= c (+ a b))))) + +(defun equilateralp (sides) + "Return non-nil if sides represent a valid equilateral triangle" + (let ((a (car sides)) + (b (cadr sides)) + (c (caddr sides))) + (and + (validp sides) + (= a b) + (= a c)))) + +(defun isoscelesp (sides) + "Return non-nil if sides represent a valid isosceles triangle" + (let ((a (car sides)) + (b (cadr sides)) + (c (caddr sides))) + (and + (validp sides) + (or + (= a b) + (= a c) + (= b c))))) + +(defun scalenep (sides) + "Return non-nil if sides represent a valid scalene triangle" + (and + (validp sides) + (not (isoscelesp sides)))) + +(provide 'triangle) +;;; triangle.el ends here 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.el b/exercises/practice/triangle/triangle-test.el new file mode 100644 index 00000000..8628f696 --- /dev/null +++ b/exercises/practice/triangle/triangle-test.el @@ -0,0 +1,97 @@ +;;; triangle-test.el --- Tests for Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(load-file "triangle.el") +(declare-function equilateralp "triangle.el" (sides)) +(declare-function isoscelesp "triangle.el" (sides)) +(declare-function scalenep "triangle.el" (sides)) + + +(ert-deftest all-sides-are-equal-1 () + (should (equilateralp '(2 2 2)))) + + +(ert-deftest any-side-is-unequal () + (should-not (equilateralp '(2 3 2)))) + + +(ert-deftest no-sides-are-equal-1 () + (should-not (equilateralp '(5 4 6)))) + + +(ert-deftest all-zero-sides-is-not-a-triangle () + (should-not (equilateralp '(0 0 0)))) + + +(ert-deftest sides-may-be-floats-1 () + (should (equilateralp '(0.5 0.5 0.5)))) + + +(ert-deftest last-two-sides-are-equal () + (should (isoscelesp '(3 4 4)))) + + +(ert-deftest first-two-sides-are-equal () + (should (isoscelesp '(4 4 3)))) + + +(ert-deftest first-and-last-sides-are-equal () + (should (isoscelesp '(4 3 4)))) + + +(ert-deftest equilateral-triangles-are-also-isosceles () + (should (isoscelesp '(4 4 4)))) + + +(ert-deftest no-sides-are-equal-2 () + (should-not (isoscelesp '(2 3 4)))) + + +(ert-deftest first-triangle-inequality-violation () + (should-not (isoscelesp '(1 1 3)))) + + +(ert-deftest second-triangle-inequality-violation () + (should-not (isoscelesp '(1 3 1)))) + + +(ert-deftest third-triangle-inequality-violation () + (should-not (isoscelesp '(3 1 1)))) + + +(ert-deftest sides-may-be-floats-2 () + (should (isoscelesp '(0.5 0.4 0.5)))) + + +(ert-deftest no-sides-are-equal-3 () + (should (scalenep '(5 4 6)))) + + +(ert-deftest all-sides-are-equal-2 () + (should-not (scalenep '(4 4 4)))) + + +(ert-deftest first-and-second-sides-are-equal () + (should-not (scalenep '(4 4 3)))) + + +(ert-deftest first-and-third-sides-are-equal () + (should-not (scalenep '(3 4 3)))) + + +(ert-deftest second-and-third-sides-are-equal () + (should-not (scalenep '(4 3 3)))) + + +(ert-deftest may-not-violate-triangle-inequality () + (should-not (scalenep '(7 3 2)))) + + +(ert-deftest sides-may-be-floats-3 () + (should (scalenep '(0.5 0.4 0.6)))) + +(provide 'triangle-test) +;;; triangle-test.el ends here diff --git a/exercises/practice/triangle/triangle.el b/exercises/practice/triangle/triangle.el new file mode 100644 index 00000000..f07ec335 --- /dev/null +++ b/exercises/practice/triangle/triangle.el @@ -0,0 +1,18 @@ +;;; triangle.el --- Triangle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun equilateralp (sides) + (error "Delete this S-Expression and write your own implementation")) + +(defun isoscelesp (sides) + (error "Delete this S-Expression and write your own implementation")) + +(defun scalenep (sides) + (error "Delete this S-Expression and write your own implementation")) + +(provide 'triangle) +;;; triangle.el ends here diff --git a/exercises/practice/two-bucket/.docs/instructions.md b/exercises/practice/two-bucket/.docs/instructions.md new file mode 100644 index 00000000..30d779aa --- /dev/null +++ b/exercises/practice/two-bucket/.docs/instructions.md @@ -0,0 +1,46 @@ +# Instructions + +Given two buckets of different size and which bucket to fill first, determine how many actions are required to measure an exact number of liters by strategically transferring fluid between the buckets. + +There are some rules that your solution must follow: + +- You can only do one action at a time. +- There are only 3 possible actions: + 1. Pouring one bucket into the other bucket until either: + a) the first bucket is empty + b) the second bucket is full + 2. Emptying a bucket and doing nothing to the other. + 3. Filling a bucket and doing nothing to the other. +- After an action, you may not arrive at a state where the initial starting bucket is empty and the other bucket is full. + +Your program will take as input: + +- the size of bucket one +- the size of bucket two +- the desired number of liters to reach +- which bucket to fill first, either bucket one or bucket two + +Your program should determine: + +- the total number of actions it should take to reach the desired number of liters, including the first fill of the starting bucket +- which bucket should end up with the desired number of liters - either bucket one or bucket two +- how many liters are left in the other bucket + +Note: any time a change is made to either or both buckets counts as one (1) action. + +Example: +Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. +Let's say at a given step, bucket one is holding 7 liters and bucket two is holding 8 liters (7,8). +If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one action. +Instead, if you had poured from bucket one into bucket two until bucket two was full, resulting in 4 liters in bucket one and 11 liters in bucket two (4,11), that would also only count as one action. + +Another Example: +Bucket one can hold 3 liters, and bucket two can hold up to 5 liters. +You are told you must start with bucket one. +So your first action is to fill bucket one. +You choose to empty bucket one for your second action. +For your third action, you may not fill bucket two, because this violates the third rule -- you may not end up in a state after any action where the starting bucket is empty and the other bucket is full. + +Written with <3 at [Fullstack Academy][fullstack] by Lindsay Levine. + +[fullstack]: https://www.fullstackacademy.com/ diff --git a/exercises/practice/two-bucket/.meta/config.json b/exercises/practice/two-bucket/.meta/config.json new file mode 100644 index 00000000..c7adf309 --- /dev/null +++ b/exercises/practice/two-bucket/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "two-bucket.el" + ], + "test": [ + "two-bucket-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Given two buckets of different size, demonstrate how to measure an exact number of liters.", + "source": "Water Pouring Problem", + "source_url": "https://demonstrations.wolfram.com/WaterPouringProblem/" +} diff --git a/exercises/practice/two-bucket/.meta/example.el b/exercises/practice/two-bucket/.meta/example.el new file mode 100644 index 00000000..dd4f8d01 --- /dev/null +++ b/exercises/practice/two-bucket/.meta/example.el @@ -0,0 +1,68 @@ +;;; two-bucket.el --- Two Bucket (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(define-error 'goal-not-possible "impossible") + +(cl-defstruct (bucket (:constructor make-bucket (capacity name)) + (:copier nil)) + capacity + (amount 0) + name) + +(defun empty (bucket) + (setf (bucket-amount bucket) 0)) + +(defun fill (bucket) + (setf (bucket-amount bucket) (bucket-capacity bucket))) + +(defun fullp (bucket) + (= (bucket-amount bucket) (bucket-capacity bucket))) + +(defun overflowingp (bucket) + (when (> (bucket-amount bucket) (bucket-capacity bucket)) + (- (bucket-amount bucket) (bucket-capacity bucket)))) + +(defun transfer (from-bucket to-bucket) + (cl-incf (bucket-amount to-bucket) (bucket-amount from-bucket)) + (empty from-bucket) + (when-let ((excess (overflowingp to-bucket))) + (setf (bucket-amount from-bucket) excess) + (fill to-bucket))) + +(defun unsolvablep (bucket-one bucket-two goal) + (or (cl-plusp (% goal (cl-gcd bucket-one bucket-two))) + (and (> goal bucket-one) (> goal bucket-two)))) + +(defun retrieve-results (bucket-one bucket-two goal) + (if (= goal (bucket-amount bucket-one)) + (list (bucket-name bucket-one) (bucket-amount bucket-two)) + (list (bucket-name bucket-two) (bucket-amount bucket-one)))) + +(defun iterate-through-puzzle (start-bucket other-bucket goal) + (fill start-bucket) + (cl-loop for moves from 1 + until (or (= goal (bucket-amount start-bucket)) + (= goal (bucket-amount other-bucket))) + do (cond + ((= goal (bucket-capacity other-bucket)) (fill other-bucket)) + ((fullp other-bucket) (empty other-bucket)) + ((zerop (bucket-amount start-bucket)) (fill start-bucket)) + (t (transfer start-bucket other-bucket))) + finally (return (cons moves (retrieve-results start-bucket other-bucket goal))))) + +(defun measure (bucket-one bucket-two goal start-bucket) + (if (unsolvablep bucket-one bucket-two goal) + (signal 'goal-not-possible nil) + (let* ((first-bucket (make-bucket bucket-one 'one)) + (second-bucket (make-bucket bucket-two 'two)) + (results (if (eq start-bucket 'one) + (iterate-through-puzzle first-bucket second-bucket goal) + (iterate-through-puzzle second-bucket first-bucket goal)))) + (cons (car results) (cdr results))))) + + +(provide 'two-bucket) +;;; two-bucket.el ends here diff --git a/exercises/practice/two-bucket/.meta/tests.toml b/exercises/practice/two-bucket/.meta/tests.toml new file mode 100644 index 00000000..d6ff02f5 --- /dev/null +++ b/exercises/practice/two-bucket/.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. + +[a6f2b4ba-065f-4dca-b6f0-e3eee51cb661] +description = "Measure using bucket one of size 3 and bucket two of size 5 - start with bucket one" + +[6c4ea451-9678-4926-b9b3-68364e066d40] +description = "Measure using bucket one of size 3 and bucket two of size 5 - start with bucket two" + +[3389f45e-6a56-46d5-9607-75aa930502ff] +description = "Measure using bucket one of size 7 and bucket two of size 11 - start with bucket one" + +[fe0ff9a0-3ea5-4bf7-b17d-6d4243961aa1] +description = "Measure using bucket one of size 7 and bucket two of size 11 - start with bucket two" + +[0ee1f57e-da84-44f7-ac91-38b878691602] +description = "Measure one step using bucket one of size 1 and bucket two of size 3 - start with bucket two" + +[eb329c63-5540-4735-b30b-97f7f4df0f84] +description = "Measure using bucket one of size 2 and bucket two of size 3 - start with bucket one and end with bucket two" + +[449be72d-b10a-4f4b-a959-ca741e333b72] +description = "Not possible to reach the goal" + +[aac38b7a-77f4-4d62-9b91-8846d533b054] +description = "With the same buckets but a different goal, then it is possible" + +[74633132-0ccf-49de-8450-af4ab2e3b299] +description = "Goal larger than both buckets is impossible" diff --git a/exercises/practice/two-bucket/two-bucket-test.el b/exercises/practice/two-bucket/two-bucket-test.el new file mode 100644 index 00000000..8fda0003 --- /dev/null +++ b/exercises/practice/two-bucket/two-bucket-test.el @@ -0,0 +1,77 @@ +;;; two-bucket-test.el --- Two Bucket (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "two-bucket.el") +(declare-function measure "two-bucket.el" (bucket-one bucket-two goal start-bucket)) + + +(ert-deftest measure-using-bucket-one-of-size-3-and-bucket-two-of-size-5-start-with-bucket-one () + ;; Function under test: measure + ;; Input: {"bucketOne":3,"bucketTwo":5,"goal":1,"startBucket":"one"} + ;; Expected: {"moves":4,"goalBucket":"one","otherBucket":5} + (should (equal (measure 3 5 1 'one) '(4 one 5)))) + + +(ert-deftest measure-using-bucket-one-of-size-3-and-bucket-two-of-size-5-start-with-bucket-two () + ;; Function under test: measure + ;; Input: {"bucketOne":3,"bucketTwo":5,"goal":1,"startBucket":"two"} + ;; Expected: {"moves":8,"goalBucket":"two","otherBucket":3} + (should (equal (measure 3 5 1 'two) '(8 two 3)))) + + +(ert-deftest measure-using-bucket-one-of-size-7-and-bucket-two-of-size-11-start-with-bucket-one () + ;; Function under test: measure + ;; Input: {"bucketOne":7,"bucketTwo":11,"goal":2,"startBucket":"one"} + ;; Expected: {"moves":14,"goalBucket":"one","otherBucket":11} + (should (equal (measure 7 11 2 'one) '(14 one 11)))) + + +(ert-deftest measure-using-bucket-one-of-size-7-and-bucket-two-of-size-11-start-with-bucket-two () + ;; Function under test: measure + ;; Input: {"bucketOne":7,"bucketTwo":11,"goal":2,"startBucket":"two"} + ;; Expected: {"moves":18,"goalBucket":"two","otherBucket":7} + (should (equal (measure 7 11 2 'two) '(18 two 7)))) + + +(ert-deftest measure-one-step-using-bucket-one-of-size-1-and-bucket-two-of-size-3-start-with-bucket-two () + ;; Function under test: measure + ;; Input: {"bucketOne":1,"bucketTwo":3,"goal":3,"startBucket":"two"} + ;; Expected: {"moves":1,"goalBucket":"two","otherBucket":0} + (should (equal (measure 1 3 3 'two) '(1 two 0)))) + + +(ert-deftest measure-using-bucket-one-of-size-2-and-bucket-two-of-size-3-start-with-bucket-one-and-end-with-bucket-two () + ;; Function under test: measure + ;; Input: {"bucketOne":2,"bucketTwo":3,"goal":3,"startBucket":"one"} + ;; Expected: {"moves":2,"goalBucket":"two","otherBucket":2} + (should (equal (measure 2 3 3 'one) '(2 two 2)))) + + +(ert-deftest not-possible-to-reach-the-goal () + ;; Function under test: measure + ;; Input: {"bucketOne":6,"bucketTwo":15,"goal":5,"startBucket":"one"} + ;; Expected: {"error":"impossible"} + (should-error (measure 6 15 5 'one) :type 'goal-not-possible)) + + +(ert-deftest with-the-same-buckets-but-a-different-goal-then-it-is-possible () + ;; Function under test: measure + ;; Input: {"bucketOne":6,"bucketTwo":15,"goal":9,"startBucket":"one"} + ;; Expected: {"moves":10,"goalBucket":"two","otherBucket":0} + (should (equal (measure 6 15 9 'one) '(10 two 0)))) + + +(ert-deftest goal-larger-than-both-buckets-is-impossible () + ;; Function under test: measure + ;; Input: {"bucketOne":5,"bucketTwo":7,"goal":8,"startBucket":"one"} + ;; Expected: {"error":"impossible"} + (should-error (measure 5 7 8 'one) :type 'goal-not-possible)) + + +(provide 'two-bucket-test) +;;; two-bucket-test.el ends here + diff --git a/exercises/practice/two-bucket/two-bucket.el b/exercises/practice/two-bucket/two-bucket.el new file mode 100644 index 00000000..58a1fadd --- /dev/null +++ b/exercises/practice/two-bucket/two-bucket.el @@ -0,0 +1,17 @@ +;;; two-bucket.el --- Two Bucket (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(define-error 'goal-not-possible + (error "Delete this S-Expression and write your own implementation")) + +(defun measure (bucket-one bucket-two goal start-bucket) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'two-bucket) +;;; two-bucket.el ends here + diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md index bdd72bde..adc53487 100644 --- a/exercises/practice/two-fer/.docs/instructions.md +++ b/exercises/practice/two-fer/.docs/instructions.md @@ -1,17 +1,14 @@ # Instructions -`Two-fer` or `2-fer` is short for two for one. -One for you and one for me. +Your task is to determine what you will say as you give away the extra cookie. -Given a name, return a string with the message: +If you know the person's name (e.g. if they're named Do-yun), then you will say: ```text -One for name, one for me. +One for Do-yun, one for me. ``` -Where "name" is the given name. - -However, if the name is missing, return the string: +If you don't know the person's name, you will say _you_ instead. ```text One for you, one for me. @@ -19,9 +16,9 @@ One for you, one for me. Here are some examples: -|Name |String to return -|:-------|:------------------ -|Alice |One for Alice, one for me. -|Bob |One for Bob, one for me. -| |One for you, one for me. -|Zaphod |One for Zaphod, one for me. +| 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/variable-length-quantity/.docs/instructions.md b/exercises/practice/variable-length-quantity/.docs/instructions.md new file mode 100644 index 00000000..50125482 --- /dev/null +++ b/exercises/practice/variable-length-quantity/.docs/instructions.md @@ -0,0 +1,34 @@ +# Instructions + +Implement variable length quantity encoding and decoding. + +The goal of this exercise is to implement [VLQ][vlq] encoding/decoding. + +In short, the goal of this encoding is to encode integer values in a way that would save bytes. +Only the first 7 bits of each byte are significant (right-justified; sort of like an ASCII byte). +So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes. +Of course, you will have a variable number of bytes depending upon your integer. +To indicate which is the last byte of the series, you leave bit #7 clear. +In all of the preceding bytes, you set bit #7. + +So, if an integer is between `0-127`, it can be represented as one byte. +Although VLQ can deal with numbers of arbitrary sizes, for this exercise we will restrict ourselves to only numbers that fit in a 32-bit unsigned integer. +Here are examples of integers as 32-bit values, and the variable length quantities that they translate to: + +```text + NUMBER VARIABLE QUANTITY +00000000 00 +00000040 40 +0000007F 7F +00000080 81 00 +00002000 C0 00 +00003FFF FF 7F +00004000 81 80 00 +00100000 C0 80 00 +001FFFFF FF FF 7F +00200000 81 80 80 00 +08000000 C0 80 80 00 +0FFFFFFF FF FF FF 7F +``` + +[vlq]: https://en.wikipedia.org/wiki/Variable-length_quantity diff --git a/exercises/practice/variable-length-quantity/.meta/config.json b/exercises/practice/variable-length-quantity/.meta/config.json new file mode 100644 index 00000000..3fe14e33 --- /dev/null +++ b/exercises/practice/variable-length-quantity/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "variable-length-quantity.el" + ], + "test": [ + "variable-length-quantity-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Implement variable length quantity encoding and decoding.", + "source": "A poor Splice developer having to implement MIDI encoding/decoding.", + "source_url": "https://splice.com/" +} diff --git a/exercises/practice/variable-length-quantity/.meta/example.el b/exercises/practice/variable-length-quantity/.meta/example.el new file mode 100644 index 00000000..d4f181a5 --- /dev/null +++ b/exercises/practice/variable-length-quantity/.meta/example.el @@ -0,0 +1,40 @@ +;;; variable-length-quantity.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun encode (hex-numbers) + "Encode a list of hexadecimal numbers into VLQ format, returning a list of hexadecimal values." + (let ((encode-single (lambda (number) + (let ((byte-string (list (logand number #x7F)))) ; Initialize with the least significant byte + (setq number (lsh number -7)) ; Shift right before the loop + (while (> number 0) + (push (logior (logand number #x7F) #x80) byte-string) + (setq number (lsh number -7))) + byte-string)))) + (apply #'append (mapcar encode-single hex-numbers)))) + + +(defun decode (hex-values) + "Decode a list of hexadecimal values from VLQ format into hexadecimal numbers." + (let ((values '()) + (number 0) + (incomplete t)) + (dolist (byte hex-values) + (setq number (lsh number 7)) + (setq number (logior number (logand byte #x7F))) + (if (= (logand byte #x80) 0) + (progn + (setq values (cons number values)) + (setq number 0) + (setq incomplete nil)) + (setq incomplete t))) + (if incomplete + (error "imcomplete sequence") + (reverse values)))) + + +(provide 'variable-length-quantity) +;;; variable-length-quantity.el ends here diff --git a/exercises/practice/variable-length-quantity/.meta/tests.toml b/exercises/practice/variable-length-quantity/.meta/tests.toml new file mode 100644 index 00000000..c9af549f --- /dev/null +++ b/exercises/practice/variable-length-quantity/.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. + +[35c9db2e-f781-4c52-b73b-8e76427defd0] +description = "Encode a series of integers, producing a series of bytes. -> zero" + +[be44d299-a151-4604-a10e-d4b867f41540] +description = "Encode a series of integers, producing a series of bytes. -> arbitrary single byte" + +[ea399615-d274-4af6-bbef-a1c23c9e1346] +description = "Encode a series of integers, producing a series of bytes. -> largest single byte" + +[77b07086-bd3f-4882-8476-8dcafee79b1c] +description = "Encode a series of integers, producing a series of bytes. -> smallest double byte" + +[63955a49-2690-4e22-a556-0040648d6b2d] +description = "Encode a series of integers, producing a series of bytes. -> arbitrary double byte" + +[29da7031-0067-43d3-83a7-4f14b29ed97a] +description = "Encode a series of integers, producing a series of bytes. -> largest double byte" + +[3345d2e3-79a9-4999-869e-d4856e3a8e01] +description = "Encode a series of integers, producing a series of bytes. -> smallest triple byte" + +[5df0bc2d-2a57-4300-a653-a75ee4bd0bee] +description = "Encode a series of integers, producing a series of bytes. -> arbitrary triple byte" + +[f51d8539-312d-4db1-945c-250222c6aa22] +description = "Encode a series of integers, producing a series of bytes. -> largest triple byte" + +[da78228b-544f-47b7-8bfe-d16b35bbe570] +description = "Encode a series of integers, producing a series of bytes. -> smallest quadruple byte" + +[11ed3469-a933-46f1-996f-2231e05d7bb6] +description = "Encode a series of integers, producing a series of bytes. -> arbitrary quadruple byte" + +[d5f3f3c3-e0f1-4e7f-aad0-18a44f223d1c] +description = "Encode a series of integers, producing a series of bytes. -> largest quadruple byte" + +[91a18b33-24e7-4bfb-bbca-eca78ff4fc47] +description = "Encode a series of integers, producing a series of bytes. -> smallest quintuple byte" + +[5f34ff12-2952-4669-95fe-2d11b693d331] +description = "Encode a series of integers, producing a series of bytes. -> arbitrary quintuple byte" + +[7489694b-88c3-4078-9864-6fe802411009] +description = "Encode a series of integers, producing a series of bytes. -> maximum 32-bit integer input" + +[f9b91821-cada-4a73-9421-3c81d6ff3661] +description = "Encode a series of integers, producing a series of bytes. -> two single-byte values" + +[68694449-25d2-4974-ba75-fa7bb36db212] +description = "Encode a series of integers, producing a series of bytes. -> two multi-byte values" + +[51a06b5c-de1b-4487-9a50-9db1b8930d85] +description = "Encode a series of integers, producing a series of bytes. -> many multi-byte values" + +[baa73993-4514-4915-bac0-f7f585e0e59a] +description = "Decode a series of bytes, producing a series of integers. -> one byte" + +[72e94369-29f9-46f2-8c95-6c5b7a595aee] +description = "Decode a series of bytes, producing a series of integers. -> two bytes" + +[df5a44c4-56f7-464e-a997-1db5f63ce691] +description = "Decode a series of bytes, producing a series of integers. -> three bytes" + +[1bb58684-f2dc-450a-8406-1f3452aa1947] +description = "Decode a series of bytes, producing a series of integers. -> four bytes" + +[cecd5233-49f1-4dd1-a41a-9840a40f09cd] +description = "Decode a series of bytes, producing a series of integers. -> maximum 32-bit integer" + +[e7d74ba3-8b8e-4bcb-858d-d08302e15695] +description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error" + +[aa378291-9043-4724-bc53-aca1b4a3fcb6] +description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error, even if value is zero" + +[a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee] +description = "Decode a series of bytes, producing a series of integers. -> multiple values" diff --git a/exercises/practice/variable-length-quantity/variable-length-quantity-test.el b/exercises/practice/variable-length-quantity/variable-length-quantity-test.el new file mode 100644 index 00000000..de661317 --- /dev/null +++ b/exercises/practice/variable-length-quantity/variable-length-quantity-test.el @@ -0,0 +1,118 @@ +;;; variable-length-quantity-test.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "variable-length-quantity.el") +(declare-function encode "variable-length-quantity.el" (integers)) +(declare-function decode "variable-length-quantity.el" (integers)) + + +(ert-deftest zero () + (should (equal (encode '(#x0)) '(#x0)))) + + +(ert-deftest arbitrary-single-byte () + (should (equal (encode '(#x40)) '(#x40)))) + + +(ert-deftest largest-single-byte () + (should (equal (encode '(#x7F)) '(#x7F)))) + + +(ert-deftest smallest-double-byte () + (should (equal (encode '(#x80)) '(#x81 #x0)))) + + +(ert-deftest arbitrary-double-byte () + (should (equal (encode '(#x2000)) '(#xC0 #x0)))) + + +(ert-deftest largest-double-byte () + (should (equal (encode '(#x3FFF)) '(#xFF #x7F)))) + + +(ert-deftest smallest-triple-byte () + (should (equal (encode '(#x4000)) '(#x81 #x80 #x0)))) + + +(ert-deftest arbitrary-triple-byte () + (should (equal (encode '(#x100000)) '(#xC0 #x80 #x0)))) + + +(ert-deftest largest-triple-byte () + (should (equal (encode '(#x1FFFFF)) '(#xFF #xFF #x7F)))) + + +(ert-deftest smallest-quadruple-byte () + (should (equal (encode '(#x200000)) '(#x81 #x80 #x80 #x0)))) + + +(ert-deftest arbitrary-quadruple-byte () + (should (equal (encode '(#x8000000)) '(#xC0 #x80 #x80 #x0)))) + + +(ert-deftest largest-quadruple-byte () + (should (equal (encode '(#xFFFFFFF)) '(#xFF #xFF #xFF #x7F)))) + + +(ert-deftest smallest-quintuple-byte () + (should (equal (encode '(#x10000000)) '(#x81 #x80 #x80 #x80 #x0)))) + + +(ert-deftest arbitrary-quintuple-byte () + (should (equal (encode '(#xFF000000)) '(#x8F #xF8 #x80 #x80 #x0)))) + + +(ert-deftest maximum-32-bit-integer-input () + (should (equal (encode '(#xFFFFFFFF)) '(#x8F #xFF #xFF #xFF #x7F)))) + + +(ert-deftest two-single-byte-values () + (should (equal (encode '(#x40 #x7F)) '(#x40 #x7F)))) + + +(ert-deftest two-multi-byte-values () + (should (equal (encode '(#x4000 #x123456)) '(#x81 #x80 #x0 #xC8 #xE8 #x56)))) + + +(ert-deftest many-multi-byte-values () + (should (equal (encode '(#x2000 #x123456 #xFFFFFFF #x0 #x3FFF #x4000)) '(#xC0 #x0 #xC8 #xE8 #x56 #xFF #xFF #xFF #x7F #x0 #xFF #x7F #x81 #x80 #x0)))) + + +(ert-deftest one-byte () + (should (equal (decode '(#x7F)) '(#x7F)))) + + +(ert-deftest two-bytes () + (should (equal (decode '(#xC0 #x0)) '(#x2000)))) + + +(ert-deftest three-bytes () + (should (equal (decode '(#xFF #xFF #x7F)) '(#x1FFFFF)))) + + +(ert-deftest four-bytes () + (should (equal (decode '(#x81 #x80 #x80 #x0)) '(#x200000)))) + + +(ert-deftest maximum-32-bit-integer () + (should (equal (decode '(#x8F #xFF #xFF #xFF #x7F)) '(#xFFFFFFFF)))) + + +(ert-deftest incomplete-sequence-causes-error () + (should-error (decode '(#xFF)))) + + +(ert-deftest incomplete-sequence-causes-error-even-if-value-is-zero () + (should-error (decode '(#x80)))) + + +(ert-deftest multiple-values () + (should (equal (decode '(#xC0 #x0 #xC8 #xE8 #x56 #xFF #xFF #xFF #x7F #x0 #xFF #x7F #x81 #x80 #x0)) '(#x2000 #x123456 #xFFFFFFF #x0 #x3FFF #x4000)))) + + +(provide 'variable-length-quantity-test) +;;; variable-length-quantity-test.el ends here diff --git a/exercises/practice/variable-length-quantity/variable-length-quantity.el b/exercises/practice/variable-length-quantity/variable-length-quantity.el new file mode 100644 index 00000000..8ff73b5d --- /dev/null +++ b/exercises/practice/variable-length-quantity/variable-length-quantity.el @@ -0,0 +1,18 @@ +;;; variable-length-quantity.el --- Variable Length Quantity (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun encode (integers) + (error "Delete this S-Expression and write your own implementation")) + + +(defun decode (integers) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'variable-length-quantity) +;;; variable-length-quantity.el ends here + diff --git a/exercises/practice/word-count/.docs/instructions.md b/exercises/practice/word-count/.docs/instructions.md index 8b7f03ed..064393c8 100644 --- a/exercises/practice/word-count/.docs/instructions.md +++ b/exercises/practice/word-count/.docs/instructions.md @@ -1,31 +1,47 @@ # Instructions -Given a phrase, count the occurrences of each _word_ in that phrase. +Your task is to count how many times each word occurs in a subtitle of a drama. -For the purposes of this exercise you can expect that a _word_ will always be one of: +The subtitles from these dramas use only ASCII characters. -1. A _number_ composed of one or more ASCII digits (ie "0" or "1234") OR -2. A _simple word_ composed of one or more ASCII letters (ie "a" or "they") OR -3. A _contraction_ of two _simple words_ joined by a single apostrophe (ie "it's" or "they're") +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. -When counting words you can assume the following rules: +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. -1. The count is _case insensitive_ (ie "You", "you", and "YOU" are 3 uses of the same word) -2. The count is _unordered_; the tests will ignore how words and counts are ordered -3. Other than the apostrophe in a _contraction_ all forms of _punctuation_ are regarded as spaces -4. The words can be separated by _any_ form of whitespace (ie "\t", "\n", " ") +Numbers are considered words. +If the subtitles say _It costs 100 dollars._ then _100_ will be its own word. -For example, for the phrase `"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` the count would be: +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 -that's: 1 -the: 2 -password: 2 123: 1 -cried: 1 -special: 1 agent: 1 -so: 1 -i: 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 index d15a2f59..30ec95ec 100644 --- a/exercises/practice/word-count/.meta/config.json +++ b/exercises/practice/word-count/.meta/config.json @@ -1,12 +1,14 @@ { - "authors": [], + "authors": [ + "cpaulbond" + ], "contributors": [ "canweriotnow", "npostavs", "vermiculus", "wasamasa", "yurrriq", - "fap" + "fapdash" ], "files": { "solution": [ diff --git a/exercises/practice/yacht/.docs/instructions.md b/exercises/practice/yacht/.docs/instructions.md new file mode 100644 index 00000000..519b7a68 --- /dev/null +++ b/exercises/practice/yacht/.docs/instructions.md @@ -0,0 +1,30 @@ +# Instructions + +Given five dice and a category, calculate the score of the dice for that category. + +~~~~exercism/note +You'll always be presented with five dice. +Each dice's value will be between one and six inclusively. +The dice may be unordered. +~~~~ + +## Scores in Yacht + +| Category | Score | Description | Example | +| --------------- | ---------------------- | ---------------------------------------- | ------------------- | +| Ones | 1 × number of ones | Any combination | 1 1 1 4 5 scores 3 | +| Twos | 2 × number of twos | Any combination | 2 2 3 4 5 scores 4 | +| Threes | 3 × number of threes | Any combination | 3 3 3 3 3 scores 15 | +| Fours | 4 × number of fours | Any combination | 1 2 3 3 5 scores 0 | +| Fives | 5 × number of fives | Any combination | 5 1 5 2 5 scores 15 | +| Sixes | 6 × number of sixes | Any combination | 2 3 4 5 6 scores 6 | +| Full House | Total of the dice | Three of one number and two of another | 3 3 3 5 5 scores 19 | +| Four of a Kind | Total of the four dice | At least four dice showing the same face | 4 4 4 4 6 scores 16 | +| Little Straight | 30 points | 1-2-3-4-5 | 1 2 3 4 5 scores 30 | +| Big Straight | 30 points | 2-3-4-5-6 | 2 3 4 5 6 scores 30 | +| Choice | Sum of the dice | Any combination | 2 3 3 4 6 scores 18 | +| Yacht | 50 points | All five dice showing the same face | 4 4 4 4 4 scores 50 | + +If the dice do **not** satisfy the requirements of a category, the score is zero. +If, for example, _Four Of A Kind_ is entered in the _Yacht_ category, zero points are scored. +A _Yacht_ scores zero if entered in the _Full House_ category. diff --git a/exercises/practice/yacht/.docs/introduction.md b/exercises/practice/yacht/.docs/introduction.md new file mode 100644 index 00000000..5b541f56 --- /dev/null +++ b/exercises/practice/yacht/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +Each year, something new is "all the rage" in your high school. +This year it is a dice game: [Yacht][yacht]. + +The game of Yacht is from the same family as Poker Dice, Generala and particularly Yahtzee, of which it is a precursor. +The game consists of twelve rounds. +In each, five dice are rolled and the player chooses one of twelve categories. +The chosen category is then used to score the throw of the dice. + +[yacht]: https://en.wikipedia.org/wiki/Yacht_(dice_game) diff --git a/exercises/practice/yacht/.meta/config.json b/exercises/practice/yacht/.meta/config.json new file mode 100644 index 00000000..16bd1671 --- /dev/null +++ b/exercises/practice/yacht/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "yacht.el" + ], + "test": [ + "yacht-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Score a single throw of dice in the game Yacht.", + "source": "James Kilfiger, using Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)" +} diff --git a/exercises/practice/yacht/.meta/example.el b/exercises/practice/yacht/.meta/example.el new file mode 100644 index 00000000..b939ed4e --- /dev/null +++ b/exercises/practice/yacht/.meta/example.el @@ -0,0 +1,58 @@ +;;; yacht.el --- Yacht (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun hash-values (ht) + (let (vals) + (maphash (lambda (_ v) (push v vals)) ht) + (reverse vals))) + +(defun frequencies (sequence) + (let ((occurrences (make-hash-table :test 'equal))) + (dolist (n sequence) + (puthash n (1+ (gethash n occurrences 0)) occurrences)) + occurrences)) + +(defun full-house (occurrences) + (let ((vals (sort (hash-values occurrences) #'<))) + (equal vals '(2 3)))) + +(defun four-of-a-kind-dice (occurrences) + (let (pair) + (maphash (lambda (key val) + (when (>= val 4) + (setq pair key))) + occurrences) + pair)) + +(defun score (scores category) + (pcase category + (:ones (cl-count 1 scores)) + (:twos (* 2 (cl-count 2 scores))) + (:threes (* 3 (cl-count 3 scores))) + (:fours (* 4 (cl-count 4 scores))) + (:fives (* 5 (cl-count 5 scores))) + (:sixes (* 6 (cl-count 6 scores))) + (:full-house (if (full-house (frequencies scores)) + (cl-reduce '+ scores) + 0)) + (:four-of-a-kind (let ((dice (four-of-a-kind-dice (frequencies scores)))) + (if dice (* 4 dice) 0))) + (:little-straight (if (equal '(1 2 3 4 5) (sort scores #'<)) + 30 + 0)) + (:big-straight (if (equal '(2 3 4 5 6) (sort scores #'<)) + 30 + 0)) + (:choice (cl-reduce '+ scores)) + (:yacht (if (= 1 (hash-table-count (frequencies scores))) + 50 + 0)) + (_ 0))) + + +(provide 'yacht) +;;; yacht.el ends here diff --git a/exercises/practice/yacht/.meta/tests.toml b/exercises/practice/yacht/.meta/tests.toml new file mode 100644 index 00000000..b9d92037 --- /dev/null +++ b/exercises/practice/yacht/.meta/tests.toml @@ -0,0 +1,97 @@ +# 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. + +[3060e4a5-4063-4deb-a380-a630b43a84b6] +description = "Yacht" + +[15026df2-f567-482f-b4d5-5297d57769d9] +description = "Not Yacht" + +[36b6af0c-ca06-4666-97de-5d31213957a4] +description = "Ones" + +[023a07c8-6c6e-44d0-bc17-efc5e1b8205a] +description = "Ones, out of order" + +[7189afac-cccd-4a74-8182-1cb1f374e496] +description = "No ones" + +[793c4292-dd14-49c4-9707-6d9c56cee725] +description = "Twos" + +[dc41bceb-d0c5-4634-a734-c01b4233a0c6] +description = "Fours" + +[f6125417-5c8a-4bca-bc5b-b4b76d0d28c8] +description = "Yacht counted as threes" + +[464fc809-96ed-46e4-acb8-d44e302e9726] +description = "Yacht of 3s counted as fives" + +[d054227f-3a71-4565-a684-5c7e621ec1e9] +description = "Fives" + +[e8a036e0-9d21-443a-8b5f-e15a9e19a761] +description = "Sixes" + +[51cb26db-6b24-49af-a9ff-12f53b252eea] +description = "Full house two small, three big" + +[1822ca9d-f235-4447-b430-2e8cfc448f0c] +description = "Full house three small, two big" + +[b208a3fc-db2e-4363-a936-9e9a71e69c07] +description = "Two pair is not a full house" + +[b90209c3-5956-445b-8a0b-0ac8b906b1c2] +description = "Four of a kind is not a full house" + +[32a3f4ee-9142-4edf-ba70-6c0f96eb4b0c] +description = "Yacht is not a full house" + +[b286084d-0568-4460-844a-ba79d71d79c6] +description = "Four of a Kind" + +[f25c0c90-5397-4732-9779-b1e9b5f612ca] +description = "Yacht can be scored as Four of a Kind" + +[9f8ef4f0-72bb-401a-a871-cbad39c9cb08] +description = "Full house is not Four of a Kind" + +[b4743c82-1eb8-4a65-98f7-33ad126905cd] +description = "Little Straight" + +[7ac08422-41bf-459c-8187-a38a12d080bc] +description = "Little Straight as Big Straight" + +[97bde8f7-9058-43ea-9de7-0bc3ed6d3002] +description = "Four in order but not a little straight" + +[cef35ff9-9c5e-4fd2-ae95-6e4af5e95a99] +description = "No pairs but not a little straight" + +[fd785ad2-c060-4e45-81c6-ea2bbb781b9d] +description = "Minimum is 1, maximum is 5, but not a little straight" + +[35bd74a6-5cf6-431a-97a3-4f713663f467] +description = "Big Straight" + +[87c67e1e-3e87-4f3a-a9b1-62927822b250] +description = "Big Straight as little straight" + +[c1fa0a3a-40ba-4153-a42d-32bc34d2521e] +description = "No pairs but not a big straight" + +[207e7300-5d10-43e5-afdd-213e3ac8827d] +description = "Choice" + +[b524c0cf-32d2-4b40-8fb3-be3500f3f135] +description = "Yacht as choice" diff --git a/exercises/practice/yacht/yacht-test.el b/exercises/practice/yacht/yacht-test.el new file mode 100644 index 00000000..f0cd72bc --- /dev/null +++ b/exercises/practice/yacht/yacht-test.el @@ -0,0 +1,129 @@ +;;; yacht-test.el --- Yacht (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "yacht.el") +(declare-function score "yacht.el" (scores category)) + + +(ert-deftest yacht () + (should (equal (score '(5 5 5 5 5) :yacht) 50))) + + +(ert-deftest not-yacht () + (should (equal (score '(1 3 3 2 5) :yacht) 0))) + + +(ert-deftest ones () + (should (equal (score '(1 1 1 3 5) :ones) 3))) + + +(ert-deftest ones-out-of-order () + (should (equal (score '(3 1 1 5 1) :ones) 3))) + + +(ert-deftest no-ones () + (should (equal (score '(4 3 6 5 5) :ones) 0))) + + +(ert-deftest twos () + (should (equal (score '(2 3 4 5 6) :twos) 2))) + + +(ert-deftest fours () + (should (equal (score '(1 4 1 4 1) :fours) 8))) + + +(ert-deftest yacht-counted-as-threes () + (should (equal (score '(3 3 3 3 3) :threes) 15))) + + +(ert-deftest yacht-of-3s-counted-as-fives () + (should (equal (score '(3 3 3 3 3) :fives) 0))) + + +(ert-deftest fives () + (should (equal (score '(1 5 3 5 3) :fives) 10))) + + +(ert-deftest sixes () + (should (equal (score '(2 3 4 5 6) :sixes) 6))) + + +(ert-deftest full-house-two-small-three-big () + (should (equal (score '(2 2 4 4 4) :full-house) 16))) + + +(ert-deftest full-house-three-small-two-big () + (should (equal (score '(5 3 3 5 3) :full-house) 19))) + + +(ert-deftest two-pair-is-not-a-full-house () + (should (equal (score '(2 2 4 4 5) :full-house) 0))) + + +(ert-deftest four-of-a-kind-is-not-a-full-house () + (should (equal (score '(1 4 4 4 4) :full-house) 0))) + + +(ert-deftest yacht-is-not-a-full-house () + (should (equal (score '(2 2 2 2 2) :full-house) 0))) + + +(ert-deftest four-of-a-kind () + (should (equal (score '(6 6 4 6 6) :four-of-a-kind) 24))) + + +(ert-deftest yacht-can-be-scored-as-four-of-a-kind () + (should (equal (score '(3 3 3 3 3 ) :four-of-a-kind) 12))) + + +(ert-deftest full-house-is-not-four-of-a-kind () + (should (equal (score '(3 3 3 5 5) :four-of-a-kind) 0))) + + +(ert-deftest little-straight () + (should (equal (score '(3 5 4 1 2) :little-straight) 30))) + + +(ert-deftest little-straight-as-big-straight () + (should (equal (score '(1 2 3 4 5) :big-straight) 0))) + + +(ert-deftest four-in-order-but-not-a-little-straight () + (should (equal (score '(1 1 2 3 4) :little-straight) 0))) + + +(ert-deftest no-pairs-but-not-a-little-straight () + (should (equal (score '(1 2 3 4 6) :little-straight) 0))) + + +(ert-deftest minimum-is-1-maximum-is-5-but-not-a-little-straight () + (should (equal (score '(1 1 3 4 5) :little-straight) 0))) + + +(ert-deftest big-straight () + (should (equal (score '(4 6 2 5 3) :big-straight) 30))) + + +(ert-deftest big-straight-as-little-straight () + (should (equal (score '(6 5 4 3 2) :little-straight) 0))) + + +(ert-deftest no-pairs-but-not-a-big-straight () + (should (equal (score '(6 5 4 3 1) :big-straight) 0))) + + +(ert-deftest choice () + (should (equal (score '(3 3 5 6 6) :choice) 23))) + + +(ert-deftest yacht-as-choice () + (should (equal (score '(2 2 2 2 2) :choice) 10))) + + +(provide 'yacht-test) +;;; yacht-test.el ends here diff --git a/exercises/practice/yacht/yacht.el b/exercises/practice/yacht/yacht.el new file mode 100644 index 00000000..09a5dc92 --- /dev/null +++ b/exercises/practice/yacht/yacht.el @@ -0,0 +1,14 @@ +;;; yacht.el --- Yacht (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun score (scores category) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'yacht) +;;; yacht.el ends here + diff --git a/exercises/practice/zebra-puzzle/.docs/instructions.md b/exercises/practice/zebra-puzzle/.docs/instructions.md new file mode 100644 index 00000000..aedce9b2 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.docs/instructions.md @@ -0,0 +1,32 @@ +# Instructions + +Your task is to solve the Zebra Puzzle to find the answer to these two questions: + +- Which of the residents drinks water? +- Who owns the zebra? + +## Puzzle + +The following 15 statements are all known to be true: + +1. There are five houses. +2. The Englishman lives in the red house. +3. The Spaniard owns the dog. +4. The person in the green house drinks coffee. +5. The Ukrainian drinks tea. +6. The green house is immediately to the right of the ivory house. +7. The snail owner likes to go dancing. +8. The person in the yellow house is a painter. +9. The person in the middle house drinks milk. +10. The Norwegian lives in the first house. +11. The person who enjoys reading lives in the house next to the person with the fox. +12. The painter's house is next to the house with the horse. +13. The person who plays football drinks orange juice. +14. The Japanese person plays chess. +15. The Norwegian lives next to the blue house. + +Additionally, each of the five houses is painted a different color, and their inhabitants are of different national extractions, own different pets, drink different beverages and engage in different hobbies. + +~~~~exercism/note +There are 24 billion (5!⁵ = 24,883,200,000) possible solutions, so try ruling out as many solutions as possible. +~~~~ diff --git a/exercises/practice/zebra-puzzle/.docs/introduction.md b/exercises/practice/zebra-puzzle/.docs/introduction.md new file mode 100644 index 00000000..bbcaa6fd --- /dev/null +++ b/exercises/practice/zebra-puzzle/.docs/introduction.md @@ -0,0 +1,15 @@ +# Introduction + +The Zebra Puzzle is a famous logic puzzle in which there are five houses, each painted a different color. +The houses have different inhabitants, who have different nationalities, own different pets, drink different beverages and enjoy different hobbies. + +To help you solve the puzzle, you're given 15 statements describing the solution. +However, only by combining the information in _all_ statements will you be able to find the solution to the puzzle. + +~~~~exercism/note +The Zebra Puzzle is a [Constraint satisfaction problem (CSP)][constraint-satisfaction-problem]. +In such a problem, you have a set of possible values and a set of constraints that limit which values are valid. +Another well-known CSP is Sudoku. + +[constraint-satisfaction-problem]: https://en.wikipedia.org/wiki/Constraint_satisfaction_problem +~~~~ diff --git a/exercises/practice/zebra-puzzle/.meta/config.json b/exercises/practice/zebra-puzzle/.meta/config.json new file mode 100644 index 00000000..37ac3db7 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "kmarker1101" + ], + "files": { + "solution": [ + "zebra-puzzle.el" + ], + "test": [ + "zebra-puzzle-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "blurb": "Solve the zebra puzzle.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle" +} diff --git a/exercises/practice/zebra-puzzle/.meta/example.el b/exercises/practice/zebra-puzzle/.meta/example.el new file mode 100644 index 00000000..7cd80917 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/example.el @@ -0,0 +1,157 @@ +;;; zebra-puzzle.el --- Zebra Puzzle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defvar all-houses nil) + +(defvar house-constraints nil) + +(defvar feasible-houses nil) + +(defvar street-constraints nil) + +(defvar two-house-constraints nil) + +(defvar feasible-streets nil) + +(defvar all-streets nil) + +(defvar houses-by-number nil) + +(cl-defstruct house + number color owner pet drink hobby) + +(setq all-houses + (cl-loop for n in '(1 2 3 4 5) + append (cl-loop for c in '(red green ivory yellow blue) + append (cl-loop for o in '(Englishman Spaniard Ukrainian Norwegian Japanese) + append (cl-loop for p in '(dog snails fox horse zebra) + append (cl-loop for d in '(coffee tea milk orange-juice water) + append (cl-loop for e in '(dancing painting reading football chess) + collect (make-house :number n :color c :owner o :pet p :drink d :hobby e)))))))) + +(setq house-constraints + (list + (lambda (h) (equal (equal (house-owner h) 'Englishman) + (equal (house-color h) 'red))) + (lambda (h) (equal (equal (house-owner h) 'Spaniard) + (equal (house-pet h) 'dog))) + (lambda (h) (equal (equal (house-drink h) 'coffee) + (equal (house-color h) 'green))) + (lambda (h) (equal (equal (house-owner h) 'Ukrainian) + (equal (house-drink h) 'tea))) + (lambda (h) (equal (equal (house-hobby h) 'dancing) + (equal (house-pet h) 'snails))) + (lambda (h) (equal (equal (house-hobby h) 'painting) + (equal (house-color h) 'yellow))) + (lambda (h) (equal (equal (house-drink h) 'milk) + (= (house-number h) 3))) + (lambda (h) (equal (equal (house-owner h) 'Norwegian) + (= (house-number h) 1))) + (lambda (h) (equal (equal (house-hobby h) 'football) + (equal (house-drink h) 'orange-juice))) + (lambda (h) (equal (equal (house-owner h) 'Japanese) + (equal (house-hobby h) 'chess))) + (lambda (h) (equal (= (house-number h) 2) + (equal (house-color h) 'blue))) + (lambda (h) (if (equal (house-color h) 'green) + (not (= (house-number h) 1)) + t)) + (lambda (h) (if (equal (house-color h) 'ivory) + (not (= (house-number h) 5)) + t)))) + +(defun apply-constraints (constraints items) + "Apply a list of CONSTRAINTS to a list of ITEMS." + (cl-loop for constraint in constraints + do (setq items (cl-remove-if-not constraint items)) + finally return items)) + +(setq feasible-houses + (apply-constraints house-constraints all-houses)) + +(defun next-door? (h1 h2) + "Check if two houses, H1 and H2, are next to each other." + (= (abs (- (house-number h1) (house-number h2))) 1)) + +(setq two-house-constraints + (list + (lambda (h1 h2) + (if (and (equal (house-color h1) 'green) + (equal (house-color h2) 'ivory)) + (= (house-number h1) (1+ (house-number h2))) + t)) + (lambda (h1 h2) + (if (and (equal (house-hobby h1) 'reading) + (equal (house-pet h2) 'fox)) + (next-door? h1 h2) + t)) + (lambda (h1 h2) + (if (and (equal (house-hobby h1) 'painting) + (equal (house-pet h2) 'horse)) + (next-door? h1 h2) + t)))) + +(defun check-street (constraint street) + "Check if all pairs of houses in STREET satisfy CONSTRAINT." + (cl-loop for h1 in street + always (cl-loop for h2 in street + always (funcall constraint h1 h2)))) + +(setq street-constraints + (mapcar (lambda (c) + (lambda (s) (check-street c s))) + two-house-constraints)) + +(setq houses-by-number + (cl-loop for n in '(1 2 3 4 5) + collect (cl-remove-if-not (lambda (h) (= n (house-number h))) feasible-houses))) + +(defun disjoint? (h1 h2) + "Check if two houses, H1 and H2, are disjoint in terms of their attributes." + (not (or (equal (house-color h1) (house-color h2)) + (equal (house-owner h1) (house-owner h2)) + (equal (house-pet h1) (house-pet h2)) + (equal (house-drink h1) (house-drink h2)) + (equal (house-hobby h1) (house-hobby h2))))) + +(defun extends? (street h) + "Check if a house, H, can be added to a STREET." + (cl-every (lambda (h1) (disjoint? h h1)) street)) + +(defun build-streets (houses street) + "Recursively build all possible STREET with HOUSES." + (if (null houses) + (list street) + (cl-loop for h in (car houses) + when (extends? street h) + append (build-streets (cdr houses) (cons h street))))) + +(setq all-streets + (build-streets houses-by-number '())) + +(setq feasible-streets + (apply-constraints street-constraints all-streets)) + +(defun find-owner (condition) + "Find the owner of a house based on a CONDITION." + (if (= 1 (length feasible-streets)) + (cl-loop for h in (car feasible-streets) + if (funcall condition h) + return (house-owner h)) + nil)) + +(defun owns-zebra () + "Find the owner of the zebra." + (find-owner (lambda (h) (equal (house-pet h) 'zebra)))) + +(defun drinks-water () + "Find the owner of the house where water is drunk." + (find-owner (lambda (h) (equal (house-drink h) 'water)))) + +(provide 'zebra-puzzle) +;;; zebra-puzzle.el ends here diff --git a/exercises/practice/zebra-puzzle/.meta/tests.toml b/exercises/practice/zebra-puzzle/.meta/tests.toml new file mode 100644 index 00000000..56c21c7a --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/tests.toml @@ -0,0 +1,16 @@ +# 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. + +[16efb4e4-8ad7-4d5e-ba96-e5537b66fd42] +description = "resident who drinks water" + +[084d5b8b-24e2-40e6-b008-c800da8cd257] +description = "resident who owns zebra" diff --git a/exercises/practice/zebra-puzzle/zebra-puzzle-test.el b/exercises/practice/zebra-puzzle/zebra-puzzle-test.el new file mode 100644 index 00000000..249e9e92 --- /dev/null +++ b/exercises/practice/zebra-puzzle/zebra-puzzle-test.el @@ -0,0 +1,22 @@ +;;; zebra-puzzle-test.el --- Zebra Puzzle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "zebra-puzzle.el") +(declare-function drinks-water "zebra-puzzle.el" ()) +(declare-function owns-zebra "zebra-puzzle.el" ()) + + +(ert-deftest resident-who-drinks-water () + (should (string= (drinks-water) "Norwegian"))) + + +(ert-deftest resident-who-owns-zebra () + (should (string= (owns-zebra) "Japanese"))) + + +(provide 'zebra-puzzle-test) +;;; zebra-puzzle-test.el ends here diff --git a/exercises/practice/zebra-puzzle/zebra-puzzle.el b/exercises/practice/zebra-puzzle/zebra-puzzle.el new file mode 100644 index 00000000..4bd0ce93 --- /dev/null +++ b/exercises/practice/zebra-puzzle/zebra-puzzle.el @@ -0,0 +1,18 @@ +;;; zebra-puzzle.el --- Zebra Puzzle (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun drinks-water () + (error "Delete this S-Expression and write your own implementation")) + + +(defun owns-zebra () + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'zebra-puzzle) +;;; zebra-puzzle.el ends here + diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000..7b5b7170 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,6 @@ +# Exercism Emacs Lisp track tools + +## practice-exercise-generator.el + +The exercise generator creates solution stub files and test stub files from the canonical data json. +Don't call it directly, instead call via /bin/generate_practice_exercise . diff --git a/tools/install-packages.el b/tools/install-packages.el new file mode 100644 index 00000000..a27dd593 --- /dev/null +++ b/tools/install-packages.el @@ -0,0 +1,15 @@ +(require 'cl-extra) +(defun exercism//install-required-packages () + (require 'package) + (package-initialize) + (let ((required-packages '(mustache ht string-inflection))) + (when (not (cl-every #'package-installed-p required-packages)) + (add-to-list + 'package-archives '("gnu" . "https://elpa.gnu.org/packages/") + t) + (add-to-list + 'package-archives '("melpa" . "https://melpa.org/packages/") + t) + (package-refresh-contents) + (dolist (pkg required-packages) + (package-install pkg))))) diff --git a/tools/practice-exercise-generator.el b/tools/practice-exercise-generator.el new file mode 100644 index 00000000..34b3f93d --- /dev/null +++ b/tools/practice-exercise-generator.el @@ -0,0 +1,199 @@ +;;; practice-exercise-generator.el --- Practice Exercise Generator (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Used to generate solution and test stub files for practice exercises. +;;; Intended to be called by /bin/generate_practice_exercise. + +;;; Known issues: +;;; Generates two newlines at the end of the solution and test file. + +;;; Code: + +(exercism//install-required-packages) + +(require 'mustache) +(require 'ht) +(require 'string-inflection) +(require 'json) +(require 'subr-x) +(require 'seq) + +(defun exercism/generate-practice-exercise (exercise-slug) + "Generate scaffolding for practice exercise EXERCISE-SLUG from the canonical data found in the problem-specifications." + (with-temp-buffer + (url-insert-file-contents + (concat + "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/" + exercise-slug + "/canonical-data.json")) + (goto-char 1) + + (let* ((json-object-type 'hash-table) + (json-key-type 'string) + (canonical-data (json-read)) + (functions (exercism//retrieve-functions canonical-data))) + (exercism//generate-solution-stubs + exercise-slug canonical-data functions) + (exercism//generate-test-stubs + exercise-slug canonical-data functions)))) + +(defun exercism//generate-solution-stubs + (exercise-slug canonical-data functions) + "Generate solution file with function stubs. +File location is exercises/practice/EXERCISE-SLUG/EXERCISE-SLUG.el." + (let* ((mustache-partial-paths (list "templates/partials")) + (package-title (exercism//retrieve-title exercise-slug)) + (generated-stubs + (mustache-render + (exercism//file-to-string + "templates/exercises/practice/solution.mustache") + (ht + ("filename" exercise-slug) + ("package-title" package-title) + ("functions" functions))))) + (exercism//write-to-file + (concat + "../exercises/practice/" exercise-slug "/" exercise-slug ".el") + generated-stubs) + (exercism//write-to-file + (concat + "../exercises/practice/" exercise-slug "/.meta/example.el") + generated-stubs))) + +(defun exercism//retrieve-functions (canonical-data) + "For each function to implement, retrieve function-name and function-paramater from CANONICAL-DATA. + +Returns a list of hash-tables. +Each hash-table has the keys: + function-name: The name of the function in kebab-case + function-parameters: The parameters of the function in kebab-case, separated by spaces" + (seq-uniq (flatten-tree + (append + (named-let retrieve ((cases canonical-data)) + (if (hash-table-p cases) + (retrieve (gethash "cases" cases)) + (mapcar + (lambda (elem) + (if (gethash "cases" elem) + (retrieve (gethash "cases" elem)) + (ht + ("function-name" + (string-inflection-kebab-case-function + (gethash "property" elem))) + ("function-parameters" (string-join + (seq-map + 'string-inflection-kebab-case-function + (hash-table-keys (gethash "input" elem))) + " "))))) + cases))))) + (lambda (elem1 elem2) + (equal + (gethash "function-name" elem1) + (gethash "function-name" elem2))))) + +(defun exercism//retrieve-title (exercise-slug) + "Retrieve canonical title of exercise EXERCISE-SLUG from metadata.toml." + (with-temp-buffer + (url-insert-file-contents + (concat + "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/" + exercise-slug + "/metadata.toml")) + (beginning-of-buffer) + (save-match-data + (re-search-forward "^title *= *\"\\(.*\\)\"$") + (match-string 1)))) + +(defun exercism//write-to-file (file string) + "Writes STRING into FILE." + (with-temp-file file + (insert string))) + +(defun exercism//file-to-string (file) + "Convert FILE to string." + (with-temp-buffer + (insert-file-contents file) + (buffer-string))) + +(defun exercism//generate-test-stubs + (exercise-slug canonical-data functions) + "Generate test file with test function stubs. +File location is exercises/practice/EXERCISE-SLUG/EXERCISE-SLUG-test.el. + +Includes the expected input and output as a comment, so contributors +don't have to look up that information separately." + (let* ((tests (exercism//retrieve-tests canonical-data)) + (package-title (exercism//retrieve-title exercise-slug)) + (mustache-partial-paths (list "templates/partials")) + (generated-test-stubs + (mustache-render + (exercism//file-to-string + "templates/exercises/practice/test.mustache") + (ht + ("solution-filename" exercise-slug) + ("filename" (concat exercise-slug "-test")) + ("package-title" package-title) + ("functions" functions) + ("tests" tests))))) + (exercism//write-to-file + (concat + "../exercises/practice/" + exercise-slug + "/" + exercise-slug + "-test.el") + generated-test-stubs))) + +(defun exercism//retrieve-tests (canonical-data) + "Retrieve test data from CANONICAL-DATA. +Deprecated tests (tests that get reimplemented) are discarded. + +Returns a list of hash-tables. +Each hash-table has the keys: + test-name: name of test function in kebab-case + uuid: uuid that identifies the test + reimplements: either nil or uuid of test that gets reimplemented by that test + function-under-test: name of the function under test in kebab-case + input: text representation of the input to the function under test + expected: text representation of the expected result of the function under test" + (let* ((tests + (flatten-tree + (append + (named-let retrieve ((cases canonical-data)) + (if (hash-table-p cases) + (retrieve (gethash "cases" cases)) + (mapcar + (lambda (elem) + (if (gethash "cases" elem) + (retrieve (gethash "cases" elem)) + (ht + ("test-name" + (string-inflection-kebab-case-function + (replace-regexp-in-string + "[^[:alpha:]-]" "" + (replace-regexp-in-string + "[ |_]" "-" (gethash "description" elem))))) + ("uuid" (gethash "uuid" elem)) + ("reimplements" (gethash "reimplements" elem)) + ("function-under-test" + (string-inflection-kebab-case-function + (gethash "property" elem))) + ("input" (json-encode (gethash "input" elem))) + ("expected" + (json-encode (gethash "expected" elem)))))) + cases)))))) + (reimplemented-uuids + (seq-map + (lambda (hash) (gethash "reimplements" hash)) tests)) + (tests-without-reimplemented + (seq-filter + (lambda (elem) + (not + (seq-contains + reimplemented-uuids (gethash "uuid" elem)))) + tests))) + tests-without-reimplemented)) + +(provide 'practice-exercise-generator) +;;; practice-exercise-generator.el ends here diff --git a/tools/templates/exercises/practice/solution.mustache b/tools/templates/exercises/practice/solution.mustache new file mode 100644 index 00000000..521b637d --- /dev/null +++ b/tools/templates/exercises/practice/solution.mustache @@ -0,0 +1,10 @@ +{{> header}} + +{{#functions}} + +(defun {{function-name}} ({{function-parameters}}) + (error "Delete this S-Expression and write your own implementation")) +{{/functions}} + + +{{> footer}} diff --git a/tools/templates/exercises/practice/test.mustache b/tools/templates/exercises/practice/test.mustache new file mode 100644 index 00000000..77a589e8 --- /dev/null +++ b/tools/templates/exercises/practice/test.mustache @@ -0,0 +1,20 @@ +{{> header}} + +(load-file "{{solution-filename}}.el") +{{#functions}} +(declare-function {{function-name}} "{{solution-filename}}.el" ({{function-parameters}})) +{{/functions}} + + +{{#tests}} + + +(ert-deftest {{test-name}} () + ;; Function under test: {{function-under-test}} + ;; Input: {{{input}}} + ;; Expected: {{{expected}}} + (error "Delete this S-Expression and implement the test")) +{{/tests}} + + +{{> footer}} diff --git a/tools/templates/partials/footer.mustache b/tools/templates/partials/footer.mustache new file mode 100644 index 00000000..f68600f0 --- /dev/null +++ b/tools/templates/partials/footer.mustache @@ -0,0 +1,2 @@ +(provide '{{filename}}) +;;; {{filename}}.el ends here diff --git a/tools/templates/partials/header.mustache b/tools/templates/partials/header.mustache new file mode 100644 index 00000000..cfe2f11a --- /dev/null +++ b/tools/templates/partials/header.mustache @@ -0,0 +1,5 @@ +;;; {{filename}}.el --- {{package-title}} (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: