From 41c644fa40d53a95ed3727965f8f0716cca74ce6 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Fri, 23 Aug 2024 14:30:34 +0200 Subject: [PATCH 01/24] hello-word, leap, configuration updates Completed the hello-world and leap exercises. Updated configuration. --- .../.docs/instructions.md | 14 ++++++ .../difference-of-squares/.meta/config.json | 11 +++++ .../.meta/difference_of_squares_example.odin | 6 +++ .../difference-of-squares/.meta/tests.toml | 37 ++++++++++++++ .../difference_of_squares.odin | 6 +++ .../difference_of_squares_test.odin | 31 ++++++++++++ .../hello-world/.docs/instructions.md | 16 +++++++ .../practice/hello-world/.meta/config.json | 11 +++++ .../.meta/hello_world_example.odin | 5 ++ .../practice/hello-world/.meta/tests.toml | 13 +++++ .../practice/hello-world/hello_world.odin | 5 ++ .../hello-world/hello_world_test.odin | 10 ++++ .../practice/leap/.meta/leap_example.odin | 5 ++ exercises/practice/leap/leap.odin | 5 ++ exercises/practice/leap/leap_test.odin | 48 +++++++++++++++++++ 15 files changed, 223 insertions(+) create mode 100644 exercises/practice/difference-of-squares/.docs/instructions.md create mode 100644 exercises/practice/difference-of-squares/.meta/config.json create mode 100644 exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin create mode 100644 exercises/practice/difference-of-squares/.meta/tests.toml create mode 100644 exercises/practice/difference-of-squares/difference_of_squares.odin create mode 100644 exercises/practice/difference-of-squares/difference_of_squares_test.odin create mode 100644 exercises/practice/hello-world/.docs/instructions.md create mode 100644 exercises/practice/hello-world/.meta/config.json create mode 100644 exercises/practice/hello-world/.meta/hello_world_example.odin create mode 100644 exercises/practice/hello-world/.meta/tests.toml create mode 100644 exercises/practice/hello-world/hello_world.odin create mode 100644 exercises/practice/hello-world/hello_world_test.odin create mode 100644 exercises/practice/leap/.meta/leap_example.odin create mode 100644 exercises/practice/leap/leap.odin create mode 100644 exercises/practice/leap/leap_test.odin diff --git a/exercises/practice/difference-of-squares/.docs/instructions.md b/exercises/practice/difference-of-squares/.docs/instructions.md new file mode 100644 index 0000000..39c38b5 --- /dev/null +++ b/exercises/practice/difference-of-squares/.docs/instructions.md @@ -0,0 +1,14 @@ +# Instructions + +Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. + +The square of the sum of the first ten natural numbers is +(1 + 2 + ... + 10)² = 55² = 3025. + +The sum of the squares of the first ten natural numbers is +1² + 2² + ... + 10² = 385. + +Hence the difference between the square of the sum of the first ten natural numbers and the sum of the squares of the first ten natural numbers is 3025 - 385 = 2640. + +You are not expected to discover an efficient solution to this yourself from first principles; research is allowed, indeed, encouraged. +Finding the best algorithm for the problem is a key skill in software engineering. diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json new file mode 100644 index 0000000..9ef180c --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/config.json @@ -0,0 +1,11 @@ +{ + "authors": [], + "files": { + "solution": [], + "test": [], + "example": [] + }, + "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.", + "source": "Problem 6 at Project Euler", + "source_url": "https://projecteuler.net/problem=6" +} diff --git a/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin b/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin new file mode 100644 index 0000000..9fa06f6 --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin @@ -0,0 +1,6 @@ +package difference_of_squares + +null :: proc() -> string { + return "TODO: Implement me!" +} + diff --git a/exercises/practice/difference-of-squares/.meta/tests.toml b/exercises/practice/difference-of-squares/.meta/tests.toml new file mode 100644 index 0000000..e54414c --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/tests.toml @@ -0,0 +1,37 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[e46c542b-31fc-4506-bcae-6b62b3268537] +description = "Square the sum of the numbers up to the given number -> square of sum 1" + +[9b3f96cb-638d-41ee-99b7-b4f9c0622948] +description = "Square the sum of the numbers up to the given number -> square of sum 5" + +[54ba043f-3c35-4d43-86ff-3a41625d5e86] +description = "Square the sum of the numbers up to the given number -> square of sum 100" + +[01d84507-b03e-4238-9395-dd61d03074b5] +description = "Sum the squares of the numbers up to the given number -> sum of squares 1" + +[c93900cd-8cc2-4ca4-917b-dd3027023499] +description = "Sum the squares of the numbers up to the given number -> sum of squares 5" + +[94807386-73e4-4d9e-8dec-69eb135b19e4] +description = "Sum the squares of the numbers up to the given number -> sum of squares 100" + +[44f72ae6-31a7-437f-858d-2c0837adabb6] +description = "Subtract sum of squares from square of sums -> difference of squares 1" + +[005cb2bf-a0c8-46f3-ae25-924029f8b00b] +description = "Subtract sum of squares from square of sums -> difference of squares 5" + +[b1bf19de-9a16-41c0-a62b-1f02ecc0b036] +description = "Subtract sum of squares from square of sums -> difference of squares 100" diff --git a/exercises/practice/difference-of-squares/difference_of_squares.odin b/exercises/practice/difference-of-squares/difference_of_squares.odin new file mode 100644 index 0000000..9fa06f6 --- /dev/null +++ b/exercises/practice/difference-of-squares/difference_of_squares.odin @@ -0,0 +1,6 @@ +package difference_of_squares + +null :: proc() -> string { + return "TODO: Implement me!" +} + diff --git a/exercises/practice/difference-of-squares/difference_of_squares_test.odin b/exercises/practice/difference-of-squares/difference_of_squares_test.odin new file mode 100644 index 0000000..9e94ef2 --- /dev/null +++ b/exercises/practice/difference-of-squares/difference_of_squares_test.odin @@ -0,0 +1,31 @@ +package difference_of_squares + +import "core:testing" + +@(test) +test_square_the_sum_of_the_numbers_up_to_the_given_number :: proc(t: ^testing.T) { + expected := null + input := `null` + result := null(input) + + testing.expect_value(t, result, expected) +} + +@(test) +test_sum_the_squares_of_the_numbers_up_to_the_given_number :: proc(t: ^testing.T) { + expected := null + input := `null` + result := null(input) + + testing.expect_value(t, result, expected) +} + +@(test) +test_subtract_sum_of_squares_from_square_of_sums :: proc(t: ^testing.T) { + expected := null + input := `null` + result := null(input) + + testing.expect_value(t, result, expected) +} + diff --git a/exercises/practice/hello-world/.docs/instructions.md b/exercises/practice/hello-world/.docs/instructions.md new file mode 100644 index 0000000..c9570e4 --- /dev/null +++ b/exercises/practice/hello-world/.docs/instructions.md @@ -0,0 +1,16 @@ +# Instructions + +The classical introductory exercise. +Just say "Hello, World!". + +["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment. + +The objectives are simple: + +- Modify the provided code so that it produces the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json new file mode 100644 index 0000000..566a7c7 --- /dev/null +++ b/exercises/practice/hello-world/.meta/config.json @@ -0,0 +1,11 @@ +{ + "authors": ["gvrooyen"], + "files": { + "solution": ["hello_world.odin"], + "test": ["hello_world_test.odin"], + "example": [".meta/hello_world_example.odin"] + }, + "blurb": "Exercism's classic introductory exercise. Just say \"Hello, World!\".", + "source": "This is an exercise to introduce users to using Exercism", + "source_url": "https://en.wikipedia.org/wiki/%22Hello,_world!%22_program" +} diff --git a/exercises/practice/hello-world/.meta/hello_world_example.odin b/exercises/practice/hello-world/.meta/hello_world_example.odin new file mode 100644 index 0000000..72190c1 --- /dev/null +++ b/exercises/practice/hello-world/.meta/hello_world_example.odin @@ -0,0 +1,5 @@ +package hello_world + +hello_world :: proc() -> string { + return "Hello, World!" +} diff --git a/exercises/practice/hello-world/.meta/tests.toml b/exercises/practice/hello-world/.meta/tests.toml new file mode 100644 index 0000000..73466d6 --- /dev/null +++ b/exercises/practice/hello-world/.meta/tests.toml @@ -0,0 +1,13 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[af9ffe10-dc13-42d8-a742-e7bdafac449d] +description = "Say Hi!" diff --git a/exercises/practice/hello-world/hello_world.odin b/exercises/practice/hello-world/hello_world.odin new file mode 100644 index 0000000..72190c1 --- /dev/null +++ b/exercises/practice/hello-world/hello_world.odin @@ -0,0 +1,5 @@ +package hello_world + +hello_world :: proc() -> string { + return "Hello, World!" +} diff --git a/exercises/practice/hello-world/hello_world_test.odin b/exercises/practice/hello-world/hello_world_test.odin new file mode 100644 index 0000000..c2c10fe --- /dev/null +++ b/exercises/practice/hello-world/hello_world_test.odin @@ -0,0 +1,10 @@ +package hello_world + +import "core:testing" + +@(test) +test :: proc(t: ^testing.T) { + expected := "Hello, World!" + + testing.expect_value(t, hello_world(), expected) +} diff --git a/exercises/practice/leap/.meta/leap_example.odin b/exercises/practice/leap/.meta/leap_example.odin new file mode 100644 index 0000000..f8a08ee --- /dev/null +++ b/exercises/practice/leap/.meta/leap_example.odin @@ -0,0 +1,5 @@ +package leap + +is_leap_year :: proc(year: int) -> bool { + return year % 400 == 0 || year % 4 == 0 && year % 100 != 0 +} diff --git a/exercises/practice/leap/leap.odin b/exercises/practice/leap/leap.odin new file mode 100644 index 0000000..02aea4d --- /dev/null +++ b/exercises/practice/leap/leap.odin @@ -0,0 +1,5 @@ +package leap + +is_leap_year :: proc(year: int) -> bool { + return false +} diff --git a/exercises/practice/leap/leap_test.odin b/exercises/practice/leap/leap_test.odin new file mode 100644 index 0000000..cf80139 --- /dev/null +++ b/exercises/practice/leap/leap_test.odin @@ -0,0 +1,48 @@ +package leap + +import "core:testing" + +@(test) +test_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { + testing.expect(t, !is_leap_year(2015)) +} + +@(test) +test_divisible_by_2_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { + testing.expect(t, !is_leap_year(1970)) +} + +@(test) +test_divisible_by_4_not_divisible_by_100_in_leap_year :: proc(t: ^testing.T) { + testing.expect(t, is_leap_year(1996)) +} + +@(test) +test_divisible_by_4_and_5_is_still_a_leap_year :: proc(t: ^testing.T) { + testing.expect(t, is_leap_year(1960)) +} + +@(test) +test_divisible_by_100_not_divisible_by_400_in_common_year :: proc(t: ^testing.T) { + testing.expect(t, !is_leap_year(2100)) +} + +@(test) +test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc(t: ^testing.T) { + testing.expect(t, !is_leap_year(1900)) +} + +@(test) +test_divisible_by_400 :: proc(t: ^testing.T) { + testing.expect(t, is_leap_year(2000)) +} + +@(test) +test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc(t: ^testing.T) { + testing.expect(t, is_leap_year(2400)) +} + +@(test) +test_divisible_by_200_not_divisible_by_400_in_common_year :: proc(t: ^testing.T) { + testing.expect(t, !is_leap_year(1800)) +} From 4f70d44ab80f1bf727348092b248595d7a5ed852 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Fri, 23 Aug 2024 17:54:02 +0200 Subject: [PATCH 02/24] Add workflow in test.yml --- .github/workflows/test.yml | 21 +++++++++++++------ README.md | 2 +- bin/gen-exercise.sh | 6 +++--- config.json | 18 ++++++++++++++++ .../difference-of-squares/.meta/config.json | 8 +++---- exercises/practice/leap/.meta/config.json | 8 +++---- 6 files changed, 45 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index df75aba..0da2ca8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ # Requires scripts: # - bin/test -name: / Test +name: odin / Test on: push: @@ -23,17 +23,26 @@ on: jobs: ci: - runs-on: + runs-on: ubuntu-20.04 steps: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - name: Use - uses: + - name: Setup Odin + uses: laytan/setup-odin@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + release: dev-2024-08 + llvm-version: 14 - name: Install project dependencies - run: + run: bin/fetch-configlet + + - name: Run the project linter + run: bin/configlet lint - name: Verify all exercises - run: bin/verify-exercises + run: bin/run-test.sh + + diff --git a/README.md b/README.md index 63ffcdb..c86a81d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Official Exercism forum thread about this track: https://forum.exercism.org/t/new-track-odin-programming-language/7379 -Borring concepts from other C-based/C-adjacent language tracks: +Borrowing concepts from other C-based/C-adjacent language tracks: - https://github.com/exercism/c - https://github.com/exercism/zig diff --git a/bin/gen-exercise.sh b/bin/gen-exercise.sh index e7c8fac..9b84a72 100755 --- a/bin/gen-exercise.sh +++ b/bin/gen-exercise.sh @@ -99,8 +99,8 @@ EOL # Make the example file a simple copy of the solution file cp ${solution_file} ${example_file} - echo "Formatting new Odin files:" - bin/odinfmt -w ${exercises_path} + # echo "Formatting new Odin files:" + # bin/odinfmt -w ${exercises_path} echo "Be sure to implement the following files:" echo -e "\t${solution_file}" @@ -110,4 +110,4 @@ EOL echo "Running configlet lint:" bin/configlet lint -fi \ No newline at end of file +fi diff --git a/config.json b/config.json index cb6448d..1ab3079 100644 --- a/config.json +++ b/config.json @@ -42,6 +42,14 @@ "name": "Leap", "practices": [], "prerequisites": [], + "difficulty": 2 + }, + { + "uuid": "ce45a52e-0541-4384-8abf-b787bd49cbf7", + "slug": "difference-of-squares", + "name": "Difference Of Squares", + "practices": [], + "prerequisites": [], "difficulty": 1 } ] @@ -67,6 +75,16 @@ "title": "Cross-compile", "content": "Odin can build for a plethora of targets and cross-compiling is a first class use case.", "icon": "cross-platform" + }, + { + "title": "Modern", + "content": "Odin is designed from the bottom up for the modern computer.", + "icon": "powerful" + }, + { + "title": "Fun", + "content": "Odin is the C alternative for the Joy of Programming.", + "icon": "fun" } ], "tags": [ diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json index 9ef180c..4bc9f7a 100644 --- a/exercises/practice/difference-of-squares/.meta/config.json +++ b/exercises/practice/difference-of-squares/.meta/config.json @@ -1,9 +1,9 @@ { - "authors": [], + "authors": ["gvrooyen"], "files": { - "solution": [], - "test": [], - "example": [] + "solution": ["difference_of_squares.odin"], + "test": ["difference_of_squares_test.odin"], + "example": [".meta/difference_of_squares_example.odin"] }, "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.", "source": "Problem 6 at Project Euler", diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json index dcbf411..a7e2d87 100644 --- a/exercises/practice/leap/.meta/config.json +++ b/exercises/practice/leap/.meta/config.json @@ -1,9 +1,9 @@ { - "authors": [], + "authors": ["gvrooyen"], "files": { - "solution": [], - "test": [], - "example": [] + "solution": ["leap.odin"], + "test": ["leap_test.odin"], + "example": [".meta/leap_example.odin"] }, "blurb": "Determine whether a given year is a leap year.", "source": "CodeRanch Cattle Drive, Assignment 3", From 27d56acaf0e2eccfc531930b61fb7904fb02641c Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Fri, 23 Aug 2024 18:17:23 +0200 Subject: [PATCH 03/24] Modified CI workflow to build Odin from source --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0da2ca8..88678df 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: uses: laytan/setup-odin@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - release: dev-2024-08 + release: false llvm-version: 14 - name: Install project dependencies From c7fb86e439a15e6937987b04b877fea5eea91637 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sat, 24 Aug 2024 12:52:15 +0200 Subject: [PATCH 04/24] Completed difference-of-squares, added format-all Completed the difference-of-squares practice exercise, and added the `bin/format-all.sh` script that recursively searches for .odin files and runs `odinfmt` on them. --- .github/workflows/test.yml | 6 +++ bin/format-all.sh | 6 +++ .../.meta/difference_of_squares_example.odin | 12 ++++- .../difference_of_squares.odin | 12 ++++- .../difference_of_squares_test.odin | 47 +++++++++++++------ ols.json | 3 +- 6 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 bin/format-all.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88678df..863344e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,13 @@ jobs: uses: laytan/setup-odin@v2 with: token: ${{ secrets.GITHUB_TOKEN }} + + # TODO: Pull a specific version of Odin compatible with the OS. + # The current setting pulls the latest version of Odin from the repo, and rebuilds it from + # scratch. This is slow, and clutters the action's log. Rather ensure that the OS has the + # right dependencies for a recent versioned release of the compiler. release: false + llvm-version: 14 - name: Install project dependencies diff --git a/bin/format-all.sh b/bin/format-all.sh new file mode 100644 index 0000000..cc498e0 --- /dev/null +++ b/bin/format-all.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Runs `odinfmt` on all .odin files in the current tree. + +find . -type f -name "*.odin" -exec odinfmt -w {} \; +echo "All Odin files have been formatted." diff --git a/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin b/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin index 9fa06f6..e957639 100644 --- a/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin +++ b/exercises/practice/difference-of-squares/.meta/difference_of_squares_example.odin @@ -1,6 +1,14 @@ package difference_of_squares -null :: proc() -> string { - return "TODO: Implement me!" +square_of_sum :: proc(n: int) -> int { + sum := n * (n + 1) / 2 + return sum * sum } +sum_of_squares :: proc(n: int) -> int { + return n * (n + 1) * (2 * n + 1) / 6 +} + +difference :: proc(n: int) -> int { + return square_of_sum(n) - sum_of_squares(n) +} diff --git a/exercises/practice/difference-of-squares/difference_of_squares.odin b/exercises/practice/difference-of-squares/difference_of_squares.odin index 9fa06f6..e957639 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares.odin +++ b/exercises/practice/difference-of-squares/difference_of_squares.odin @@ -1,6 +1,14 @@ package difference_of_squares -null :: proc() -> string { - return "TODO: Implement me!" +square_of_sum :: proc(n: int) -> int { + sum := n * (n + 1) / 2 + return sum * sum } +sum_of_squares :: proc(n: int) -> int { + return n * (n + 1) * (2 * n + 1) / 6 +} + +difference :: proc(n: int) -> int { + return square_of_sum(n) - sum_of_squares(n) +} diff --git a/exercises/practice/difference-of-squares/difference_of_squares_test.odin b/exercises/practice/difference-of-squares/difference_of_squares_test.odin index 9e94ef2..5de37d1 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares_test.odin +++ b/exercises/practice/difference-of-squares/difference_of_squares_test.odin @@ -3,29 +3,46 @@ package difference_of_squares import "core:testing" @(test) -test_square_the_sum_of_the_numbers_up_to_the_given_number :: proc(t: ^testing.T) { - expected := null - input := `null` - result := null(input) +test_square_of_sum_1 :: proc(t: ^testing.T) { + testing.expect_value(t, square_of_sum(1), 1) +} + +@(test) +test_square_of_sum_5 :: proc(t: ^testing.T) { + testing.expect_value(t, square_of_sum(5), 225) +} + +@(test) +test_square_of_sum_100 :: proc(t: ^testing.T) { + testing.expect_value(t, square_of_sum(100), 25_502_500) +} - testing.expect_value(t, result, expected) +@(test) +sum_of_squares_1_test :: proc(t: ^testing.T) { + testing.expect_value(t, sum_of_squares(1), 1) } @(test) -test_sum_the_squares_of_the_numbers_up_to_the_given_number :: proc(t: ^testing.T) { - expected := null - input := `null` - result := null(input) +sum_of_squares_5_test :: proc(t: ^testing.T) { + testing.expect_value(t, sum_of_squares(5), 55) +} - testing.expect_value(t, result, expected) +@(test) +sum_of_squares_100_test :: proc(t: ^testing.T) { + testing.expect_value(t, sum_of_squares(100), 338_350) } @(test) -test_subtract_sum_of_squares_from_square_of_sums :: proc(t: ^testing.T) { - expected := null - input := `null` - result := null(input) +difference_of_squares_1_test :: proc(t: ^testing.T) { + testing.expect_value(t, difference(1), 0) +} - testing.expect_value(t, result, expected) +@(test) +difference_of_squares_5_test :: proc(t: ^testing.T) { + testing.expect_value(t, difference(5), 170) } +@(test) +difference_of_squares_100_test :: proc(t: ^testing.T) { + testing.expect_value(t, difference(100), 25_164_150) +} diff --git a/ols.json b/ols.json index bf28ead..8a9df08 100644 --- a/ols.json +++ b/ols.json @@ -6,5 +6,6 @@ "enable_semantic_tokens": false, "enable_document_symbols": true, "enable_hover": true, + "enable_inlay_hints": true, "enable_snippets": true -} \ No newline at end of file +} From 6d314975a14f410a87dab90eec90b675050d89bc Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sat, 24 Aug 2024 13:23:26 +0200 Subject: [PATCH 05/24] Formatted source, added formatting to webhook All source files have been reformatted with the `odinfmt.json` spec. `bin/format-all.sh` is now run automatically on commit. --- .github/workflows/test.yml | 10 +- README.md | 1 + bin/format-all.sh | 0 .../difference_of_squares_test.odin | 18 +- .../hello-world/hello_world_test.odin | 4 +- .../practice/leap/.meta/leap_example.odin | 2 +- exercises/practice/leap/leap.odin | 2 +- exercises/practice/leap/leap_test.odin | 34 ++-- src/odin_code_generator.odin | 185 ++++++++++-------- 9 files changed, 143 insertions(+), 113 deletions(-) mode change 100644 => 100755 bin/format-all.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 863344e..41f26dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,5 @@ -# This workflow will do a clean install of the dependencies and run tests across different versions -# -# Replace with the track name -# Replace with an image to run the jobs on -# Replace with a github action to setup tooling on the image -# Replace with a cli command to install the dependencies +# This workflow will do a clean install of the dependencies and run tests across different versions. +# It will also apply automatic code formatting. # # Find Github Actions to setup tooling here: # - https://github.com/actions/?q=setup&type=&language= @@ -51,4 +47,6 @@ jobs: - name: Verify all exercises run: bin/run-test.sh + - name: Apply code formatting to all .odin files + run: bin/format-all.sh diff --git a/README.md b/README.md index c86a81d..d0c5418 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Borrowing concepts from other C-based/C-adjacent language tracks: ## TODO +- Add actual verification steps to `bin/ - Figure out how to build an Odin test runner (currently using bash script for this) - [Highlight.js support for Odin](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md) diff --git a/bin/format-all.sh b/bin/format-all.sh old mode 100644 new mode 100755 diff --git a/exercises/practice/difference-of-squares/difference_of_squares_test.odin b/exercises/practice/difference-of-squares/difference_of_squares_test.odin index 5de37d1..1c91db7 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares_test.odin +++ b/exercises/practice/difference-of-squares/difference_of_squares_test.odin @@ -4,45 +4,45 @@ import "core:testing" @(test) test_square_of_sum_1 :: proc(t: ^testing.T) { - testing.expect_value(t, square_of_sum(1), 1) + testing.expect_value(t, square_of_sum(1), 1) } @(test) test_square_of_sum_5 :: proc(t: ^testing.T) { - testing.expect_value(t, square_of_sum(5), 225) + testing.expect_value(t, square_of_sum(5), 225) } @(test) test_square_of_sum_100 :: proc(t: ^testing.T) { - testing.expect_value(t, square_of_sum(100), 25_502_500) + testing.expect_value(t, square_of_sum(100), 25_502_500) } @(test) sum_of_squares_1_test :: proc(t: ^testing.T) { - testing.expect_value(t, sum_of_squares(1), 1) + testing.expect_value(t, sum_of_squares(1), 1) } @(test) sum_of_squares_5_test :: proc(t: ^testing.T) { - testing.expect_value(t, sum_of_squares(5), 55) + testing.expect_value(t, sum_of_squares(5), 55) } @(test) sum_of_squares_100_test :: proc(t: ^testing.T) { - testing.expect_value(t, sum_of_squares(100), 338_350) + testing.expect_value(t, sum_of_squares(100), 338_350) } @(test) difference_of_squares_1_test :: proc(t: ^testing.T) { - testing.expect_value(t, difference(1), 0) + testing.expect_value(t, difference(1), 0) } @(test) difference_of_squares_5_test :: proc(t: ^testing.T) { - testing.expect_value(t, difference(5), 170) + testing.expect_value(t, difference(5), 170) } @(test) difference_of_squares_100_test :: proc(t: ^testing.T) { - testing.expect_value(t, difference(100), 25_164_150) + testing.expect_value(t, difference(100), 25_164_150) } diff --git a/exercises/practice/hello-world/hello_world_test.odin b/exercises/practice/hello-world/hello_world_test.odin index c2c10fe..141b7f9 100644 --- a/exercises/practice/hello-world/hello_world_test.odin +++ b/exercises/practice/hello-world/hello_world_test.odin @@ -4,7 +4,7 @@ import "core:testing" @(test) test :: proc(t: ^testing.T) { - expected := "Hello, World!" + expected := "Hello, World!" - testing.expect_value(t, hello_world(), expected) + testing.expect_value(t, hello_world(), expected) } diff --git a/exercises/practice/leap/.meta/leap_example.odin b/exercises/practice/leap/.meta/leap_example.odin index f8a08ee..f8501d0 100644 --- a/exercises/practice/leap/.meta/leap_example.odin +++ b/exercises/practice/leap/.meta/leap_example.odin @@ -1,5 +1,5 @@ package leap is_leap_year :: proc(year: int) -> bool { - return year % 400 == 0 || year % 4 == 0 && year % 100 != 0 + return year % 400 == 0 || year % 4 == 0 && year % 100 != 0 } diff --git a/exercises/practice/leap/leap.odin b/exercises/practice/leap/leap.odin index 02aea4d..1fe61d4 100644 --- a/exercises/practice/leap/leap.odin +++ b/exercises/practice/leap/leap.odin @@ -1,5 +1,5 @@ package leap is_leap_year :: proc(year: int) -> bool { - return false + return false } diff --git a/exercises/practice/leap/leap_test.odin b/exercises/practice/leap/leap_test.odin index cf80139..f401bc2 100644 --- a/exercises/practice/leap/leap_test.odin +++ b/exercises/practice/leap/leap_test.odin @@ -4,45 +4,53 @@ import "core:testing" @(test) test_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(2015)) + testing.expect(t, !is_leap_year(2015)) } @(test) test_divisible_by_2_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(1970)) + testing.expect(t, !is_leap_year(1970)) } @(test) test_divisible_by_4_not_divisible_by_100_in_leap_year :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(1996)) + testing.expect(t, is_leap_year(1996)) } @(test) test_divisible_by_4_and_5_is_still_a_leap_year :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(1960)) + testing.expect(t, is_leap_year(1960)) } @(test) -test_divisible_by_100_not_divisible_by_400_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(2100)) +test_divisible_by_100_not_divisible_by_400_in_common_year :: proc( + t: ^testing.T, +) { + testing.expect(t, !is_leap_year(2100)) } @(test) -test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(1900)) +test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc( + t: ^testing.T, +) { + testing.expect(t, !is_leap_year(1900)) } @(test) test_divisible_by_400 :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(2000)) + testing.expect(t, is_leap_year(2000)) } @(test) -test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(2400)) +test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc( + t: ^testing.T, +) { + testing.expect(t, is_leap_year(2400)) } @(test) -test_divisible_by_200_not_divisible_by_400_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(1800)) +test_divisible_by_200_not_divisible_by_400_in_common_year :: proc( + t: ^testing.T, +) { + testing.expect(t, !is_leap_year(1800)) } diff --git a/src/odin_code_generator.odin b/src/odin_code_generator.odin index 3b515bf..66cf109 100644 --- a/src/odin_code_generator.odin +++ b/src/odin_code_generator.odin @@ -4,63 +4,51 @@ import "core:fmt" import "core:strings" Argument :: struct { - name: string, - type: string, + name: string, + type: string, value: any, } Function :: struct { - name: string, - args: []Argument, - ret: Argument + name: string, + args: []Argument, + ret: Argument, } Test :: struct { - name: string, - function: Function, - expected: any, - actual: any + name: string, + function: Function, + expected: any, + actual: any, } main :: proc() { - args := []Argument{ - Argument { - name = "abc", - type = "int", - value = 10 - }, - Argument { - name = "xyz", - type = "string", - value = "foo" - } - } - - function := Function { - name = "bob", - args = args, - ret = Argument { - name = "www", - type = "string", - value = "123" - } - } - - test := Test { - name = "should_do_the_thing", - function = function, - expected = "123" - } - - indent_level := 0 + args := []Argument { + Argument{name = "abc", type = "int", value = 10}, + Argument{name = "xyz", type = "string", value = "foo"}, + } + + function := Function { + name = "bob", + args = args, + ret = Argument{name = "www", type = "string", value = "123"}, + } + + test := Test { + name = "should_do_the_thing", + function = function, + expected = "123", + } + + indent_level := 0 b := strings.builder_make() - w(&b, indent_level, true, `// main.odin`) + w(&b, indent_level, true, `// main.odin`) generate_function(&b, indent_level, function) - w(&b, indent_level, true, ``) - w(&b, indent_level, true, `// test.odin`) + w(&b, indent_level, true, ``) + w(&b, indent_level, true, `// test.odin`) generate_test(&b, indent_level, test) - w(&b, indent_level, true, ``) + w(&b, indent_level, true, ``) fmt.printf("{}", strings.to_string(b)) } @@ -70,27 +58,56 @@ generate_function :: proc(b: ^strings.Builder, i: int, function: Function) { w(b, i, true, ``) w(b, i, false, `{} :: proc(`, function.name) - for arg, index in function.args { - generate_argument(b, 0, false, arg) + for arg, index in function.args { + generate_argument(b, 0, false, arg) - last_index := len(function.args) - 1 - if index != last_index { - w(b, 0, false, `, `) - } - } + last_index := len(function.args) - 1 + if index != last_index { + w(b, 0, false, `, `) + } + } w(b, i, true, `) -> {{`) - w(b, i+1, true, `{}: {} = {}`, function.ret.name, function.ret.type, function.ret.value) - w(b, i+1, true, `return {}`, function.ret.name) + w( + b, + i + 1, + true, + `{}: {} = {}`, + function.ret.name, + function.ret.type, + function.ret.value, + ) + w(b, i + 1, true, `return {}`, function.ret.name) w(b, i, true, `}}`) } -generate_argument :: proc(b: ^strings.Builder, i: int, newline: bool, argument: Argument) { - if argument.type == "string" { - w(b, i, newline, `{}: {} = "{}"`, argument.name, argument.type, argument.value) - } else { - w(b, i, newline, `{}: {} = {}`, argument.name, argument.type, argument.value) - } +generate_argument :: proc( + b: ^strings.Builder, + i: int, + newline: bool, + argument: Argument, +) { + if argument.type == "string" { + w( + b, + i, + newline, + `{}: {} = "{}"`, + argument.name, + argument.type, + argument.value, + ) + } else { + w( + b, + i, + newline, + `{}: {} = {}`, + argument.name, + argument.type, + argument.value, + ) + } } generate_test :: proc(b: ^strings.Builder, i: int, test: Test) { @@ -100,40 +117,46 @@ generate_test :: proc(b: ^strings.Builder, i: int, test: Test) { w(b, i, true, ``) w(b, i, true, `@(test)`) w(b, i, true, `test_{} :: proc(t: ^testing.T) {{`, test.name) - w(b, i+1, true, `expected := {}`, test.expected) + w(b, i + 1, true, `expected := {}`, test.expected) - for arg, index in test.function.args { - generate_argument(b, i+1, true, arg) - } + for arg, index in test.function.args { + generate_argument(b, i + 1, true, arg) + } - w(b, i+1, false, `result := {}(`, test.function.name) + w(b, i + 1, false, `result := {}(`, test.function.name) - for arg, index in test.function.args { - w(b, i, false, `{}`, arg.name) + for arg, index in test.function.args { + w(b, i, false, `{}`, arg.name) - last_index := len(test.function.args) - 1 - if index != last_index { - w(b, 0, false, `, `) - } - } + last_index := len(test.function.args) - 1 + if index != last_index { + w(b, 0, false, `, `) + } + } w(b, 0, true, `)`) - w(b, i+1, true, `testing.expect_value(t, result, expected)`) + w(b, i + 1, true, `testing.expect_value(t, result, expected)`) w(b, i, true, `)`) } // Write -w :: proc(b: ^strings.Builder, ind := 0, newline := true, format: string, args: ..any) { - indent(b, ind) - fmt.sbprintf(b, format, ..args) - - if newline { - fmt.sbprintf(b, "\n") - } +w :: proc( + b: ^strings.Builder, + ind := 0, + newline := true, + format: string, + args: ..any, +) { + indent(b, ind) + fmt.sbprintf(b, format, ..args) + + if newline { + fmt.sbprintf(b, "\n") + } } // Generates a number of tab/space characters indent :: proc(b: ^strings.Builder, ind: int) { - indent_rune := '\t' - for i in 0.. Date: Sat, 24 Aug 2024 13:36:00 +0200 Subject: [PATCH 06/24] Added step to push autoformatting changes The `test.yml` GitHub workflow now pushes any changes that it makes, so that they persist in the repo. --- .github/workflows/test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 41f26dc..4ceca82 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,3 +50,10 @@ jobs: - name: Apply code formatting to all .odin files run: bin/format-all.sh + - name: Commit and push changes + run: | + git config --global user.name 'G-J van Rooyen' + git config --global user.email 'gvrooyen@gmail.com' + git add . + git commit -m "Automated formatting" + git push From 92817bce6c068cc6c74729d943cd24190208a2c0 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sat, 24 Aug 2024 13:44:38 +0200 Subject: [PATCH 07/24] Modified test.yml to not fail on no formatting `test.yml` now only attempts a commit and push if the automated formatting actually changed any files. --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ceca82..cf73403 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,6 +54,4 @@ jobs: run: | git config --global user.name 'G-J van Rooyen' git config --global user.email 'gvrooyen@gmail.com' - git add . - git commit -m "Automated formatting" - git push + git diff --quiet && git diff --staged quiet || git commit -am "Automated formatting" && git push From 17ef99f9934b1f7769f4eaa8e3927a4c64eece62 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sat, 24 Aug 2024 13:53:48 +0200 Subject: [PATCH 08/24] Added missing dashes in argument --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cf73403..d949077 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,4 +54,4 @@ jobs: run: | git config --global user.name 'G-J van Rooyen' git config --global user.email 'gvrooyen@gmail.com' - git diff --quiet && git diff --staged quiet || git commit -am "Automated formatting" && git push + git diff --quiet && git diff --staged --quiet || git commit -am "Automated formatting" && git push From 5cf80604651202170fc898b7c9bbfc46f0020626 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sat, 24 Aug 2024 14:08:22 +0200 Subject: [PATCH 09/24] Fixed bin/verify-exercises The verify-exercises script now runs the individual test for both concept and practice exercises, and is the default in the GitHub workflow. --- .github/workflows/test.yml | 2 +- bin/verify-exercises | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d949077..653e687 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: run: bin/configlet lint - name: Verify all exercises - run: bin/run-test.sh + run: bin/verify-exercises - name: Apply code formatting to all .odin files run: bin/format-all.sh diff --git a/bin/verify-exercises b/bin/verify-exercises index 8d31328..ab766b7 100755 --- a/bin/verify-exercises +++ b/bin/verify-exercises @@ -2,7 +2,7 @@ # Synopsis: # Test the track's exercises. -# +# # At a minimum, this file must check if the example/exemplar solution of each # Practice/Concept Exercise passes the exercise's tests. # @@ -22,7 +22,7 @@ for concept_exercise_dir in ./exercises/concept/*/; do if [ -d $concept_exercise_dir ]; then echo "Checking $(basename "${concept_exercise_dir}") exercise..." - # TODO: run command to verify that the exemplar solution passes the tests + bin/run-test.sh "${concept_exercise_dir}" fi done @@ -30,6 +30,6 @@ done for practice_exercise_dir in ./exercises/practice/*/; do if [ -d $practice_exercise_dir ]; then echo "Checking $(basename "${practice_exercise_dir}") exercise..." - # TODO: run command to verify that the example solution passes the tests + bin/run-test.sh "${practice_exercise_dir}" fi done From 0024848c3b66b50ba748d971bcecc0c7a6232a2b Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sun, 25 Aug 2024 19:08:39 +0200 Subject: [PATCH 10/24] Added the "grains" exercise Tests were lifted from the Gleam track. --- config.json | 8 ++ .../practice/grains/.docs/instructions.md | 15 +++ exercises/practice/grains/.meta/config.json | 11 +++ .../practice/grains/.meta/grains_example.odin | 35 +++++++ exercises/practice/grains/.meta/tests.toml | 43 ++++++++ exercises/practice/grains/grains.odin | 18 ++++ exercises/practice/grains/grains_test.odin | 98 +++++++++++++++++++ 7 files changed, 228 insertions(+) create mode 100644 exercises/practice/grains/.docs/instructions.md create mode 100644 exercises/practice/grains/.meta/config.json create mode 100644 exercises/practice/grains/.meta/grains_example.odin create mode 100644 exercises/practice/grains/.meta/tests.toml create mode 100644 exercises/practice/grains/grains.odin create mode 100644 exercises/practice/grains/grains_test.odin diff --git a/config.json b/config.json index 1ab3079..9e86f97 100644 --- a/config.json +++ b/config.json @@ -51,6 +51,14 @@ "practices": [], "prerequisites": [], "difficulty": 1 + }, + { + "uuid": "b5b9be18-9141-4176-8f8c-3dd14d14bed5", + "slug": "grains", + "name": "Grains", + "practices": [], + "prerequisites": [], + "difficulty": 2 } ] }, diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md new file mode 100644 index 0000000..df479fc --- /dev/null +++ b/exercises/practice/grains/.docs/instructions.md @@ -0,0 +1,15 @@ +# Instructions + +Calculate the number of grains of wheat on a chessboard given that the number on each square doubles. + +There once was a wise servant who saved the life of a prince. +The king promised to pay whatever the servant could dream up. +Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. +One grain on the first square of a chess board, with the number of grains doubling on each successive square. + +There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). + +Write code that shows: + +- how many grains were on a given square, and +- the total number of grains on the chessboard diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json new file mode 100644 index 0000000..effaf1a --- /dev/null +++ b/exercises/practice/grains/.meta/config.json @@ -0,0 +1,11 @@ +{ + "authors": ["gvrooyen"], + "files": { + "solution": ["grains.odin"], + "test": ["grains_test.odin"], + "example": [".meta/grains_example.odin"] + }, + "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.", + "source": "The CodeRanch Cattle Drive, Assignment 6", + "source_url": "https://coderanch.com/wiki/718824/Grains" +} diff --git a/exercises/practice/grains/.meta/grains_example.odin b/exercises/practice/grains/.meta/grains_example.odin new file mode 100644 index 0000000..bf310d3 --- /dev/null +++ b/exercises/practice/grains/.meta/grains_example.odin @@ -0,0 +1,35 @@ +package grains + +Error :: enum { + None = 0, + InvalidSquare, + NotImplementedError, +} + +/* Calculate the number of grains on the specified square and return the resulting count, as well + as the sum of grains on this and all previous squares. +*/ +count :: proc(n: int) -> (u64, u64) { + acc: u64 = 1 + val: u64 = 1 + + for i := 2; i <= n; i += 1 { + val *= 2 + acc += val + } + + return val, acc +} + +// Returns the number of grains on the specified square. +square :: proc(n: int) -> (u64, Error) { + if n < 1 || n > 64 do return 0, .InvalidSquare + c, _ := count(n) + return c, .None +} + +// Returns the total number of squares on the board. +total :: proc() -> u64 { + _, t := count(64) + return t +} diff --git a/exercises/practice/grains/.meta/tests.toml b/exercises/practice/grains/.meta/tests.toml new file mode 100644 index 0000000..6ea68bc --- /dev/null +++ b/exercises/practice/grains/.meta/tests.toml @@ -0,0 +1,43 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[9fbde8de-36b2-49de-baf2-cd42d6f28405] +description = "returns the number of grains on the square -> grains on square 1" + +[ee1f30c2-01d8-4298-b25d-c677331b5e6d] +description = "returns the number of grains on the square -> grains on square 2" + +[10f45584-2fc3-4875-8ec6-666065d1163b] +description = "returns the number of grains on the square -> grains on square 3" + +[a7cbe01b-36f4-4601-b053-c5f6ae055170] +description = "returns the number of grains on the square -> grains on square 4" + +[c50acc89-8535-44e4-918f-b848ad2817d4] +description = "returns the number of grains on the square -> grains on square 16" + +[acd81b46-c2ad-4951-b848-80d15ed5a04f] +description = "returns the number of grains on the square -> grains on square 32" + +[c73b470a-5efb-4d53-9ac6-c5f6487f227b] +description = "returns the number of grains on the square -> grains on square 64" + +[1d47d832-3e85-4974-9466-5bd35af484e3] +description = "returns the number of grains on the square -> square 0 is invalid" + +[61974483-eeb2-465e-be54-ca5dde366453] +description = "returns the number of grains on the square -> negative square is invalid" + +[a95e4374-f32c-45a7-a10d-ffec475c012f] +description = "returns the number of grains on the square -> square greater than 64 is invalid" + +[6eb07385-3659-4b45-a6be-9dc474222750] +description = "returns the total number of grains on the board" diff --git a/exercises/practice/grains/grains.odin b/exercises/practice/grains/grains.odin new file mode 100644 index 0000000..0df0dc9 --- /dev/null +++ b/exercises/practice/grains/grains.odin @@ -0,0 +1,18 @@ +package grains + +Error :: enum { + None = 0 + // Implement me! +} + +// Returns the number of grains on the specified square. +square :: proc(n: int) -> (u64, Error) { + // Implement me! + return 0, .None +} + +// Returns the total number of squares on the board. +total :: proc() -> u64 { + // Implement me! + return 0 +} diff --git a/exercises/practice/grains/grains_test.odin b/exercises/practice/grains/grains_test.odin new file mode 100644 index 0000000..90606e3 --- /dev/null +++ b/exercises/practice/grains/grains_test.odin @@ -0,0 +1,98 @@ +package grains + +import "core:testing" + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_1 :: proc( + t: ^testing.T, +) { + c, e := square(1) + testing.expect_value(t, c, 1) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_2 :: proc( + t: ^testing.T, +) { + c, e := square(2) + testing.expect_value(t, c, 2) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_3 :: proc( + t: ^testing.T, +) { + c, e := square(3) + testing.expect_value(t, c, 4) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_4 :: proc( + t: ^testing.T, +) { + c, e := square(4) + testing.expect_value(t, c, 8) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_16 :: proc( + t: ^testing.T, +) { + c, e := square(16) + testing.expect_value(t, c, 32_768) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_32 :: proc( + t: ^testing.T, +) { + c, e := square(32) + testing.expect_value(t, c, 2_147_483_648) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_grains_on_square_64 :: proc( + t: ^testing.T, +) { + c, e := square(64) + testing.expect_value(t, c, 9_223_372_036_854_775_808) + testing.expect_value(t, e, Error.None) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_square_0_raises_an_exception :: proc( + t: ^testing.T, +) { + c, e := square(0) + testing.expect_value(t, c, 0) + testing.expect_value(t, e, Error.InvalidSquare) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_negative_square_raises_an_exception :: proc( + t: ^testing.T, +) { + c, e := square(-1) + testing.expect_value(t, c, 0) + testing.expect_value(t, e, Error.InvalidSquare) +} + +@(test) +test_returns_the_number_of_grains_on_the_square_square_greater_than_64_raises_an_exception :: proc( + t: ^testing.T, +) { + c, e := square(65) + testing.expect_value(t, c, 0) + testing.expect_value(t, e, Error.InvalidSquare) +} + +@(test) +test_returns_the_total_number_of_grains_on_the_board :: proc(t: ^testing.T) { + testing.expect_value(t, total(), 18_446_744_073_709_551_615) +} From 48c2bbea28e195d66038bea279ead6519c408ea2 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Sun, 25 Aug 2024 19:09:18 +0200 Subject: [PATCH 11/24] Updated README; fixed test verification - The README now contains more thorough instructions on how to add a new exercise. - Fixed a variable name in the test runner that caused the "test all solutions" loop to run multiple times when verifying solutions. --- README.md | 37 +++++++++++++++++++++++++++++++++++-- bin/run-test.sh | 4 +++- bin/verify-exercises | 4 ++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d0c5418..305f079 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,42 @@ Borrowing concepts from other C-based/C-adjacent language tracks: - https://github.com/exercism/c - https://github.com/exercism/zig -## TODO +## Contributing an Exercise +The `bin/` subdirectory contains several scripts to help you contribute exercises that will run +correctly on Exercism: + +- `configlet` is a tool to help track maintainers with the maintenance of their track. Fetch it by + running the `bin/fetch-configlet` script. Run `bin/configlet lint` to verify that the track is + properly structured. +- `bin/fetch-ols-odinfmt.sh` will fetch the Odin language server (`ols`) that can assist with + verifying Odin code directly in your IDE. `odinfmt` is a tool that can format Odin code according + to the specification in `odinfmt.json`. `odinfmt` is automatically invoked by the build system + whenever new code is pushed to the repository. +- `bin/format-all.sh` will run `odinfmt` on all `.odin` files in the repository. +- `bin/run-test.sh` runs the tests for a specific exercise, or for all exercises if no exercise name is + provided. +- `bin/verify-exercises` checks the integrity of all exercises, including tests. It is used by the + build system whenever new code is pushed to the repository. +- `bin/gen-exercise.sh` can be used to generate a new exercise. More details follow below. + +### Creating a New Exercise +1. Edit `config.json` to include the information about the new exercise. Add a new entry into the + `exercises` dictionary, either under `concept` or `practice`. You'll need the unique identifier + (`uuid`) which is best lifted from another track. This can also be used to populate the slug, + name, difficulty, and other fields that should typically be similar between tracks. +2. Run `bin/gen-exercise.sh ` to automatically generate the exercise skeleton in the + `exercises/practice//` directory. +3. Edit `exercises/practice//.meta/config.json` and populate the `files.solution`, + `files.test`, and `files.example` arrays to point to the generated files. Add your Exercism + username to the `authors` array. +4. `exercises/practice//_test.odin` will already contain stubs for a minimum number of + standard tests for the exercise, but will likely need editing to invoke the right function in the + solution, and to correctly test the output. It is strongly recommended that you look at the tests + from a reference track (e.g. C or Zig) and include a more thorough set of tests. -- Add actual verification steps to `bin/ +## TODO +- Let `bin/gen-exercise.sh` automatically configure `.meta/config.json` when a new exercise skeleton + is generated. - Figure out how to build an Odin test runner (currently using bash script for this) - [Highlight.js support for Odin](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md) diff --git a/bin/run-test.sh b/bin/run-test.sh index 5ec7cd5..5163006 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -15,6 +15,8 @@ function run_test() { meta=".meta" exercise_name="${1}" exercise_path="${exercises_path}/${exercise_name}" + + echo "$exercise_name / $exercise_path" if [ -n "${exercise_name}" ] && [ -d "${exercise_path}" ]; then echo "Running test for exercise: ${exercise_name}" @@ -54,4 +56,4 @@ function run_test() { fi } -run_test $@ \ No newline at end of file +run_test $@ diff --git a/bin/verify-exercises b/bin/verify-exercises index ab766b7..e6dc10e 100755 --- a/bin/verify-exercises +++ b/bin/verify-exercises @@ -22,7 +22,7 @@ for concept_exercise_dir in ./exercises/concept/*/; do if [ -d $concept_exercise_dir ]; then echo "Checking $(basename "${concept_exercise_dir}") exercise..." - bin/run-test.sh "${concept_exercise_dir}" + bin/run-test.sh "$(basename "${concept_exercise_dir}")" fi done @@ -30,6 +30,6 @@ done for practice_exercise_dir in ./exercises/practice/*/; do if [ -d $practice_exercise_dir ]; then echo "Checking $(basename "${practice_exercise_dir}") exercise..." - bin/run-test.sh "${practice_exercise_dir}" + bin/run-test.sh "$(basename "${practice_exercise_dir}")" fi done From 11a21ff8ab5e88beacf42c627321ecda80b6cf46 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Mon, 26 Aug 2024 08:51:24 +0200 Subject: [PATCH 12/24] Formatted config files with configlet --- config.json | 24 +++++++++---------- .../difference-of-squares/.meta/config.json | 16 +++++++++---- exercises/practice/grains/.meta/config.json | 16 +++++++++---- .../practice/hello-world/.meta/config.json | 16 +++++++++---- exercises/practice/leap/.meta/config.json | 16 +++++++++---- 5 files changed, 59 insertions(+), 29 deletions(-) diff --git a/config.json b/config.json index 9e86f97..6033bfa 100644 --- a/config.json +++ b/config.json @@ -26,43 +26,41 @@ ] }, "exercises": { - "concept": [], "practice": [ { - "uuid": "2cfe5afe-e94f-459c-aae5-d23d89931dda", "slug": "hello-world", "name": "Hello World", + "uuid": "2cfe5afe-e94f-459c-aae5-d23d89931dda", "practices": [], "prerequisites": [], "difficulty": 1 }, { - "uuid": "cd710981-b7de-4a80-beda-f6b95420a4d6", "slug": "leap", "name": "Leap", + "uuid": "cd710981-b7de-4a80-beda-f6b95420a4d6", "practices": [], "prerequisites": [], "difficulty": 2 }, { - "uuid": "ce45a52e-0541-4384-8abf-b787bd49cbf7", "slug": "difference-of-squares", "name": "Difference Of Squares", + "uuid": "ce45a52e-0541-4384-8abf-b787bd49cbf7", "practices": [], "prerequisites": [], "difficulty": 1 }, { - "uuid": "b5b9be18-9141-4176-8f8c-3dd14d14bed5", "slug": "grains", "name": "Grains", + "uuid": "b5b9be18-9141-4176-8f8c-3dd14d14bed5", "practices": [], "prerequisites": [], "difficulty": 2 } ] }, - "concepts": [], "key_features": [ { "title": "Simple", @@ -96,18 +94,18 @@ } ], "tags": [ + "execution_mode/compiled", "paradigm/imperative", "paradigm/procedural", - "typing/static", - "typing/strong", - "execution_mode/compiled", - "platform/windows", - "platform/mac", - "platform/linux", - "platform/ios", "platform/android", + "platform/ios", + "platform/linux", + "platform/mac", "platform/web", + "platform/windows", "runtime/standalone_executable", + "typing/static", + "typing/strong", "used_for/backends", "used_for/cross_platform_development", "used_for/embedded_systems", diff --git a/exercises/practice/difference-of-squares/.meta/config.json b/exercises/practice/difference-of-squares/.meta/config.json index 4bc9f7a..0ab7e56 100644 --- a/exercises/practice/difference-of-squares/.meta/config.json +++ b/exercises/practice/difference-of-squares/.meta/config.json @@ -1,9 +1,17 @@ { - "authors": ["gvrooyen"], + "authors": [ + "gvrooyen" + ], "files": { - "solution": ["difference_of_squares.odin"], - "test": ["difference_of_squares_test.odin"], - "example": [".meta/difference_of_squares_example.odin"] + "solution": [ + "difference_of_squares.odin" + ], + "test": [ + "difference_of_squares_test.odin" + ], + "example": [ + ".meta/difference_of_squares_example.odin" + ] }, "blurb": "Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.", "source": "Problem 6 at Project Euler", diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json index effaf1a..79969fe 100644 --- a/exercises/practice/grains/.meta/config.json +++ b/exercises/practice/grains/.meta/config.json @@ -1,9 +1,17 @@ { - "authors": ["gvrooyen"], + "authors": [ + "gvrooyen" + ], "files": { - "solution": ["grains.odin"], - "test": ["grains_test.odin"], - "example": [".meta/grains_example.odin"] + "solution": [ + "grains.odin" + ], + "test": [ + "grains_test.odin" + ], + "example": [ + ".meta/grains_example.odin" + ] }, "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", diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json index 566a7c7..07f0497 100644 --- a/exercises/practice/hello-world/.meta/config.json +++ b/exercises/practice/hello-world/.meta/config.json @@ -1,9 +1,17 @@ { - "authors": ["gvrooyen"], + "authors": [ + "gvrooyen" + ], "files": { - "solution": ["hello_world.odin"], - "test": ["hello_world_test.odin"], - "example": [".meta/hello_world_example.odin"] + "solution": [ + "hello_world.odin" + ], + "test": [ + "hello_world_test.odin" + ], + "example": [ + ".meta/hello_world_example.odin" + ] }, "blurb": "Exercism's classic introductory exercise. Just say \"Hello, World!\".", "source": "This is an exercise to introduce users to using Exercism", diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json index a7e2d87..6d5c31f 100644 --- a/exercises/practice/leap/.meta/config.json +++ b/exercises/practice/leap/.meta/config.json @@ -1,9 +1,17 @@ { - "authors": ["gvrooyen"], + "authors": [ + "gvrooyen" + ], "files": { - "solution": ["leap.odin"], - "test": ["leap_test.odin"], - "example": [".meta/leap_example.odin"] + "solution": [ + "leap.odin" + ], + "test": [ + "leap_test.odin" + ], + "example": [ + ".meta/leap_example.odin" + ] }, "blurb": "Determine whether a given year is a leap year.", "source": "CodeRanch Cattle Drive, Assignment 3", From 3ffcf5897362a1c10acf924762f1ed061a6f6249 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Mon, 26 Aug 2024 12:48:27 +0200 Subject: [PATCH 13/24] Fixed testing bug that clobbered stub solutions - Fixed bin/run-test.sh so that it doesn't delete the stub solution when a test fails. - Restored exercises, and added a NotImplemented error condition for the stubs. --- bin/run-test.sh | 5 ++-- .../practice/grains/.meta/grains_example.odin | 6 ++--- exercises/practice/grains/grains.odin | 8 +++--- exercises/practice/grains/grains_test.odin | 4 ++- .../practice/leap/.meta/leap_example.odin | 9 +++++-- exercises/practice/leap/leap.odin | 10 +++++-- exercises/practice/leap/leap_test.odin | 27 ++++++++++++------- 7 files changed, 46 insertions(+), 23 deletions(-) diff --git a/bin/run-test.sh b/bin/run-test.sh index 5163006..fbd88de 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -39,8 +39,9 @@ function run_test() { # Copy the example file into the main directory cp ${example_file} ${solution_file} - # Run the tests using the example file - odin test ${exercise_path} + # Run the tests using the example file. The `|| true` ensures that the script + # continues to the clean-up steps even if the tests fail. + odin test ${exercise_path} || true # Move the blank solution file back into the main directory mv "${exercise_path}/${meta}/${exercise_safe_name}.odin" ${solution_file} diff --git a/exercises/practice/grains/.meta/grains_example.odin b/exercises/practice/grains/.meta/grains_example.odin index bf310d3..be44b1c 100644 --- a/exercises/practice/grains/.meta/grains_example.odin +++ b/exercises/practice/grains/.meta/grains_example.odin @@ -3,7 +3,7 @@ package grains Error :: enum { None = 0, InvalidSquare, - NotImplementedError, + NotImplemented, } /* Calculate the number of grains on the specified square and return the resulting count, as well @@ -29,7 +29,7 @@ square :: proc(n: int) -> (u64, Error) { } // Returns the total number of squares on the board. -total :: proc() -> u64 { +total :: proc() -> (u64, Error) { _, t := count(64) - return t + return t, .None } diff --git a/exercises/practice/grains/grains.odin b/exercises/practice/grains/grains.odin index 0df0dc9..a5d75bc 100644 --- a/exercises/practice/grains/grains.odin +++ b/exercises/practice/grains/grains.odin @@ -1,18 +1,18 @@ package grains Error :: enum { - None = 0 // Implement me! + NotImplemented } // Returns the number of grains on the specified square. square :: proc(n: int) -> (u64, Error) { // Implement me! - return 0, .None + return 0, .NotImplemented } // Returns the total number of squares on the board. -total :: proc() -> u64 { +total :: proc() -> (u64, Error) { // Implement me! - return 0 + return 0, .NotImplemented } diff --git a/exercises/practice/grains/grains_test.odin b/exercises/practice/grains/grains_test.odin index 90606e3..b1e48cf 100644 --- a/exercises/practice/grains/grains_test.odin +++ b/exercises/practice/grains/grains_test.odin @@ -94,5 +94,7 @@ test_returns_the_number_of_grains_on_the_square_square_greater_than_64_raises_an @(test) test_returns_the_total_number_of_grains_on_the_board :: proc(t: ^testing.T) { - testing.expect_value(t, total(), 18_446_744_073_709_551_615) + c, e := total() + testing.expect_value(t, c, 18_446_744_073_709_551_615) + testing.expect_value(t, e, Error.None) } diff --git a/exercises/practice/leap/.meta/leap_example.odin b/exercises/practice/leap/.meta/leap_example.odin index f8501d0..ef60df0 100644 --- a/exercises/practice/leap/.meta/leap_example.odin +++ b/exercises/practice/leap/.meta/leap_example.odin @@ -1,5 +1,10 @@ package leap -is_leap_year :: proc(year: int) -> bool { - return year % 400 == 0 || year % 4 == 0 && year % 100 != 0 +Error :: enum { + None = 0, + NotImplemented +} + +is_leap_year :: proc(year: int) -> (bool, Error) { + return year % 400 == 0 || year % 4 == 0 && year % 100 != 0, .None } diff --git a/exercises/practice/leap/leap.odin b/exercises/practice/leap/leap.odin index 1fe61d4..feac812 100644 --- a/exercises/practice/leap/leap.odin +++ b/exercises/practice/leap/leap.odin @@ -1,5 +1,11 @@ package leap -is_leap_year :: proc(year: int) -> bool { - return false +Error :: enum { + // Implement me! + NotImplemented +} + +is_leap_year :: proc(year: int) -> (bool, Error) { + // Implement me! + return false, .NotImplemented } diff --git a/exercises/practice/leap/leap_test.odin b/exercises/practice/leap/leap_test.odin index f401bc2..147cc31 100644 --- a/exercises/practice/leap/leap_test.odin +++ b/exercises/practice/leap/leap_test.odin @@ -4,53 +4,62 @@ import "core:testing" @(test) test_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(2015)) + is_leap, _ := is_leap_year(2015) + testing.expect(t, !is_leap) } @(test) test_divisible_by_2_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - testing.expect(t, !is_leap_year(1970)) + is_leap, _ := is_leap_year(1970) + testing.expect(t, !is_leap) } @(test) test_divisible_by_4_not_divisible_by_100_in_leap_year :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(1996)) + is_leap, _ := is_leap_year(1996) + testing.expect(t, is_leap) } @(test) test_divisible_by_4_and_5_is_still_a_leap_year :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(1960)) + is_leap, _ := is_leap_year(1960) + testing.expect(t, is_leap) } @(test) test_divisible_by_100_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { - testing.expect(t, !is_leap_year(2100)) + is_leap, _ := is_leap_year(2100) + testing.expect(t, !is_leap) } @(test) test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc( t: ^testing.T, ) { - testing.expect(t, !is_leap_year(1900)) + is_leap, _ := is_leap_year(1900) + testing.expect(t, !is_leap) } @(test) test_divisible_by_400 :: proc(t: ^testing.T) { - testing.expect(t, is_leap_year(2000)) + is_leap, _ := is_leap_year(2000) + testing.expect(t, is_leap) } @(test) test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc( t: ^testing.T, ) { - testing.expect(t, is_leap_year(2400)) + is_leap, _ := is_leap_year(2400) + testing.expect(t, is_leap) } @(test) test_divisible_by_200_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { - testing.expect(t, !is_leap_year(1800)) + is_leap, _ := is_leap_year(1800) + testing.expect(t, !is_leap) } From 97b701784cfc0ff50faf28d78be91a6f90af0ff5 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Tue, 27 Aug 2024 00:28:40 +0200 Subject: [PATCH 14/24] Small code fixes. Updated README. - Ran the code formatter on source files. - Rehaul of the README to follow Python's example. --- .github/workflows/test.yml | 16 +-- README.md | 130 +++++++++++++++--- bin/fetch-ols-odinfmt.sh | 4 +- exercises/practice/grains/grains.odin | 2 +- .../practice/leap/.meta/leap_example.odin | 2 +- exercises/practice/leap/leap.odin | 2 +- 6 files changed, 120 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 653e687..a3abfaa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - name: Setup Odin - uses: laytan/setup-odin@v2 + uses: laytan/setup-odin with: token: ${{ secrets.GITHUB_TOKEN }} @@ -34,24 +34,12 @@ jobs: # The current setting pulls the latest version of Odin from the repo, and rebuilds it from # scratch. This is slow, and clutters the action's log. Rather ensure that the OS has the # right dependencies for a recent versioned release of the compiler. - release: false + release: dev-2024-08 llvm-version: 14 - name: Install project dependencies run: bin/fetch-configlet - - name: Run the project linter - run: bin/configlet lint - - name: Verify all exercises run: bin/verify-exercises - - - name: Apply code formatting to all .odin files - run: bin/format-all.sh - - - name: Commit and push changes - run: | - git config --global user.name 'G-J van Rooyen' - git config --global user.email 'gvrooyen@gmail.com' - git diff --quiet && git diff --staged --quiet || git commit -am "Automated formatting" && git push diff --git a/README.md b/README.md index 305f079..5d345ca 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,96 @@ # Odin -Official Exercism forum thread about this track: https://forum.exercism.org/t/new-track-odin-programming-language/7379 +
-Borrowing concepts from other C-based/C-adjacent language tracks: -- https://github.com/exercism/c -- https://github.com/exercism/zig +Hi.  👋🏽  👋  **We are happy you are here.**  🎉 🌟 + +
+ +**`exercism/odin`** is one of many programming language tracks on [Exercism](exercism-website). +This repo holds all the instructions, tests, code, and support files for Odin _exercises_ currently under development or implemented and available for students. + +🌟   Track exercises support the `dev-2024-08` release of Odin. + +Exercises are grouped into **concept** exercises which teach the Odin syllabus, which will eventually live [here][odin-syllabus], and **practice** exercises, which are unlocked by progressing in the syllabus tree  🌴 . +Concept exercises are constrained to a small set of language or syntax features. +Practice exercises are open-ended, and can be used to practice concepts learned, try out new techniques, and _play_. These two exercise groupings can be found in the track [config.json][config-json], and under the `odin/exercises` directory. + +

+ +
+ + + + + +🌟🌟  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]. + +                         Some defined roles in our community: [Contributors][exercism-contributors] **|** [Mentors][exercism-mentors] **|** [Maintainers][exercism-track-maintainers] **|** [Admins][exercism-admins] + +
+ +
+ + +Here to suggest a new feature or new exercise?? **Hooray!**  🎉   +We'd love if you did that via our [Exercism Community Forum][exercism-forum] where there is a [dedicated thread][odin-thread] for the new Odin track. +Please read [Suggesting Exercise Improvements][suggesting-improvements] & [Chesterton's Fence][chestertons-fence]. +_Thoughtful suggestions will likely result in faster & more enthusiastic responses from volunteers._ + +
+ + +✨ 🦄  _**Want to jump directly into Exercism specifications & detail?**_ +     [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] (_✨ version in [contributing][website-contributing-section] on exercism.org_) + +
+
+ +## Pre-launch Checklist +Here is a list of practice exercises that we aim to implement before the Odin track goes live on Exercism: + +- ~~Hello World~~ +- ~~Leap~~ +- ~~Difference of Squares~~ +- ~~Grains~~ +- Resistor Color +- Collatz Conjecture +- Queen Attack +- Darts +- Resistor Color Duo +- Hamming +- Space Age +- RNA Transcription +- Binary +- Eliud's Eggs +- Two Fer +- Raindrops +- D&D Character +- Perfect Numbers +- High Scores +- Pangram +- Resistor Color Trio +- Armstrong Numbers + +Other exercises are welcome too! +It is recommended that a track has 20 exercises or more (not counting "Hello World") to go live. ## Contributing an Exercise -The `bin/` subdirectory contains several scripts to help you contribute exercises that will run -correctly on Exercism: - -- `configlet` is a tool to help track maintainers with the maintenance of their track. Fetch it by - running the `bin/fetch-configlet` script. Run `bin/configlet lint` to verify that the track is - properly structured. -- `bin/fetch-ols-odinfmt.sh` will fetch the Odin language server (`ols`) that can assist with - verifying Odin code directly in your IDE. `odinfmt` is a tool that can format Odin code according - to the specification in `odinfmt.json`. `odinfmt` is automatically invoked by the build system +The `bin/` subdirectory contains several scripts to help you contribute exercises that will run correctly on Exercism: + +- `configlet` is a tool to help track maintainers with the maintenance of their track. + Fetch it by running the `bin/fetch-configlet` script. + Run `bin/configlet lint` to verify that the track is properly structured. +- `bin/fetch-ols-odinfmt.sh` will fetch the Odin language server (`ols`) that can assist with verifying Odin code directly in your IDE. + `odinfmt` is a tool that can format Odin code according to the specification in `odinfmt.json`. + Please run `odinfmt` before pushing your changes to the repository. whenever new code is pushed to the repository. - `bin/format-all.sh` will run `odinfmt` on all `.odin` files in the repository. -- `bin/run-test.sh` runs the tests for a specific exercise, or for all exercises if no exercise name is - provided. -- `bin/verify-exercises` checks the integrity of all exercises, including tests. It is used by the - build system whenever new code is pushed to the repository. +- `bin/run-test.sh` runs the tests for a specific exercise, or for all exercises if no exercise name is provided. +- `bin/verify-exercises` checks the integrity of all exercises, including tests. + It is used by the build system whenever new code is pushed to the repository. - `bin/gen-exercise.sh` can be used to generate a new exercise. More details follow below. ### Creating a New Exercise @@ -138,3 +207,30 @@ Basic linting finished successfully: - Required track docs are present - Required shared exercise docs are present ``` + +[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 +[config-json]: https://github.com/exercism/odin/blob/main/config.json +[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-forum]: https://forum.exercism.org/ +[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 +[freeing-maintainers]: https://exercism.org/blog/freeing-our-maintainers +[practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md +[prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md +[odin-release]: https://github.com/odin-lang/Odin/releases/tag/dev-2024-08 +[odin-syllabus]: https://exercism.org/tracks/odin/concepts +[odin-thread]: https://forum.exercism.org/t/new-track-odin-programming-language/7379 +[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 diff --git a/bin/fetch-ols-odinfmt.sh b/bin/fetch-ols-odinfmt.sh index d0bda42..4b20857 100755 --- a/bin/fetch-ols-odinfmt.sh +++ b/bin/fetch-ols-odinfmt.sh @@ -1,7 +1,7 @@ #!/bin/bash # version="refs/heads/master" -version="46892948312c14b44600ae9f557e86bd8c792343" +version="e2f4f96cd46b70360f3caa58acc4af14eb0e8688" bin_dir="bin" name="ols" @@ -35,4 +35,4 @@ mv odinfmt .. popd > /dev/null rm -rf $tarball_dir -rm -f $tarball_path \ No newline at end of file +rm -f $tarball_path diff --git a/exercises/practice/grains/grains.odin b/exercises/practice/grains/grains.odin index a5d75bc..7873e02 100644 --- a/exercises/practice/grains/grains.odin +++ b/exercises/practice/grains/grains.odin @@ -2,7 +2,7 @@ package grains Error :: enum { // Implement me! - NotImplemented + NotImplemented, } // Returns the number of grains on the specified square. diff --git a/exercises/practice/leap/.meta/leap_example.odin b/exercises/practice/leap/.meta/leap_example.odin index ef60df0..24ce39b 100644 --- a/exercises/practice/leap/.meta/leap_example.odin +++ b/exercises/practice/leap/.meta/leap_example.odin @@ -2,7 +2,7 @@ package leap Error :: enum { None = 0, - NotImplemented + NotImplemented, } is_leap_year :: proc(year: int) -> (bool, Error) { diff --git a/exercises/practice/leap/leap.odin b/exercises/practice/leap/leap.odin index feac812..f22d96c 100644 --- a/exercises/practice/leap/leap.odin +++ b/exercises/practice/leap/leap.odin @@ -2,7 +2,7 @@ package leap Error :: enum { // Implement me! - NotImplemented + NotImplemented, } is_leap_year :: proc(year: int) -> (bool, Error) { From fd4c5d1c02e377e40cbe20bd4e659b3529cd0cc4 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Tue, 27 Aug 2024 00:59:28 +0200 Subject: [PATCH 15/24] Added resistor-color exercise. Updated README. --- README.md | 124 ++---------------- config.json | 8 ++ .../resistor-color/.docs/instructions.md | 39 ++++++ .../practice/resistor-color/.meta/config.json | 19 +++ .../.meta/resistor_color_example.odin | 0 .../practice/resistor-color/.meta/tests.toml | 22 ++++ .../resistor-color/resistor_color.odin | 0 .../resistor-color/resistor_color_test.odin | 0 8 files changed, 98 insertions(+), 114 deletions(-) create mode 100644 exercises/practice/resistor-color/.docs/instructions.md create mode 100644 exercises/practice/resistor-color/.meta/config.json create mode 100644 exercises/practice/resistor-color/.meta/resistor_color_example.odin create mode 100644 exercises/practice/resistor-color/.meta/tests.toml create mode 100644 exercises/practice/resistor-color/resistor_color.odin create mode 100644 exercises/practice/resistor-color/resistor_color_test.odin diff --git a/README.md b/README.md index 5d345ca..489129d 100644 --- a/README.md +++ b/README.md @@ -91,122 +91,18 @@ The `bin/` subdirectory contains several scripts to help you contribute exercise - `bin/run-test.sh` runs the tests for a specific exercise, or for all exercises if no exercise name is provided. - `bin/verify-exercises` checks the integrity of all exercises, including tests. It is used by the build system whenever new code is pushed to the repository. -- `bin/gen-exercise.sh` can be used to generate a new exercise. More details follow below. +- `bin/configlet` can be used to generate a new exercise. More details follow below. ### Creating a New Exercise -1. Edit `config.json` to include the information about the new exercise. Add a new entry into the - `exercises` dictionary, either under `concept` or `practice`. You'll need the unique identifier - (`uuid`) which is best lifted from another track. This can also be used to populate the slug, - name, difficulty, and other fields that should typically be similar between tracks. -2. Run `bin/gen-exercise.sh ` to automatically generate the exercise skeleton in the - `exercises/practice//` directory. -3. Edit `exercises/practice//.meta/config.json` and populate the `files.solution`, - `files.test`, and `files.example` arrays to point to the generated files. Add your Exercism - username to the `authors` array. -4. `exercises/practice//_test.odin` will already contain stubs for a minimum number of - standard tests for the exercise, but will likely need editing to invoke the right function in the - solution, and to correctly test the output. It is strongly recommended that you look at the tests - from a reference track (e.g. C or Zig) and include a more thorough set of tests. - -## TODO -- Let `bin/gen-exercise.sh` automatically configure `.meta/config.json` when a new exercise skeleton - is generated. -- Figure out how to build an Odin test runner (currently using bash script for this) -- [Highlight.js support for Odin](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md) - -## Odin Docs - -- [Odin website](http://odin-lang.org) -- [Odin GitHub](https://github.com/odin-lang/Odin) -- [Odin examples](https://github.com/odin-lang/examples) -- [Odin language server](https://github.com/DanielGavin/ols) - -## Exercism Docs - -- https://exercism.org/docs/building/tracks/new/request-new -- https://exercism.org/docs/building/tracks/new/add-first-exercise -- https://exercism.org/docs/building/tracks/new/add-initial-exercises -- https://exercism.org/docs/building/tracks/new/setup-continuous-integration -- https://exercism.org/docs/building/tooling/test-runners -- https://github.com/exercism/generic-track -- https://github.com/exercism/problem-specifications - -## Example Nix Config - -```nix -{ pkgs }: -let - inherit (pkgs) lib; - - # TODO: Building odinfmt requires the nighly build of Odin itself - new_pkgs = import - (pkgs.fetchFromGitHub { - owner = "NixOS"; - repo = "nixpkgs"; - rev = "ef66aec42b5f9035a675496e9a7fe57b63505646"; - # sha256 = lib.fakeSha256; - sha256 = "1j1ywwk1wzcd60dbam3pif8z0v695ssmm8g4aw9j01wl36pds31a"; - }) - { }; - - odin = new_pkgs.odin; -in -{ - deps = [ - odin - pkgs.ruby - pkgs.gh - pkgs.just - pkgs.jq - ]; -} -``` - -*Below is the previous generic track readme; will modify later.* - ---- - -# Exercism Odin Track - -[![Configlet](https://github.com/exercism/odin/actions/workflows/configlet.yml/badge.svg)](https://github.com/exercism/odin/actions/workflows/configlet.yml) [![.github/workflows/test.yml](https://github.com/exercism/odin/actions/workflows/test.yml/badge.svg)](https://github.com/exercism/odin/actions/workflows/test.yml) - -Exercism exercises in Odin. - -## Testing - -To test the exercises, run `./bin/test`. -This command will iterate over all exercises and check to see if their exemplar/example implementation passes all the tests. - -### Track linting - -[`configlet`](https://exercism.org/docs/building/configlet) is an Exercism-wide tool for working with tracks. You can download it by running: - -```shell -./bin/fetch-configlet -``` - -Run its [`lint` command](https://exercism.org/docs/building/configlet/lint) to verify if all exercises have all the necessary files and if config files are correct: - -```shell -$ ./bin/configlet lint - -The lint command is under development. -Please re-run this command regularly to see if your track passes the latest linting rules. - -Basic linting finished successfully: -- config.json exists and is valid JSON -- config.json has these valid fields: - language, slug, active, blurb, version, status, online_editor, key_features, tags -- Every concept has the required .md files -- Every concept has a valid links.json file -- Every concept has a valid .meta/config.json file -- Every concept exercise has the required .md files -- Every concept exercise has a valid .meta/config.json file -- Every practice exercise has the required .md files -- Every practice exercise has a valid .meta/config.json file -- Required track docs are present -- Required shared exercise docs are present -``` +- Run `bin/configlet create --practice-exercise ` to automatically generate the exercise skeleton in the `exercises/practice//` directory and to update `config.json` to reference the new exercise. + You can add `--author ` as option to mark yourself as the creator of this exercise (or add it later in the exercise's `.meta/config.json` file.) +- Add a solution stub at the exercise's `.odin` file. + This is what users will start with when solving the exercise. +- Add tests to `_test.odin`. + Verify that the slug solution would fail _all_ tests. + Consider adding an error return enumeration such as `NotImplemented` to ensure that the stub solution's return values are invalid for all test cases. +- Implement a reference solution at `.meta/_example.odin`. +- Use `bin/run_test.sh ` to verify that your reference solution passes. [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 diff --git a/config.json b/config.json index 6033bfa..5303fca 100644 --- a/config.json +++ b/config.json @@ -58,6 +58,14 @@ "practices": [], "prerequisites": [], "difficulty": 2 + }, + { + "slug": "resistor-color", + "name": "Resistor Color", + "uuid": "ea5eb0a7-4a4a-4f08-b2e4-87a52bd64ce0", + "practices": [], + "prerequisites": [], + "difficulty": 1 } ] }, diff --git a/exercises/practice/resistor-color/.docs/instructions.md b/exercises/practice/resistor-color/.docs/instructions.md new file mode 100644 index 0000000..0125e71 --- /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 0000000..b07cbca --- /dev/null +++ b/exercises/practice/resistor-color/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "gvrooyen" + ], + "files": { + "solution": [ + "resistor_color.odin" + ], + "test": [ + "resistor_color_test.odin" + ], + "example": [ + ".meta/resistor_color_example.odin" + ] + }, + "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/resistor_color_example.odin b/exercises/practice/resistor-color/.meta/resistor_color_example.odin new file mode 100644 index 0000000..e69de29 diff --git a/exercises/practice/resistor-color/.meta/tests.toml b/exercises/practice/resistor-color/.meta/tests.toml new file mode 100644 index 0000000..9d4ee97 --- /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.odin b/exercises/practice/resistor-color/resistor_color.odin new file mode 100644 index 0000000..e69de29 diff --git a/exercises/practice/resistor-color/resistor_color_test.odin b/exercises/practice/resistor-color/resistor_color_test.odin new file mode 100644 index 0000000..e69de29 From cfbb1b8b1f0e49943bd51a44b952abd593fa22f9 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Tue, 27 Aug 2024 17:02:54 +0200 Subject: [PATCH 16/24] Fixed and tested the test.yml action The workflow now uses a fixed commit hash to pull a specific release of Odin. We now use Ubuntu 22.04 which has the libffi8 dependency, and add clang as the only other dependency. --- .github/workflows/test.yml | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a3abfaa..e5d4d19 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,13 +1,4 @@ -# This workflow will do a clean install of the dependencies and run tests across different versions. -# It will also apply automatic code formatting. -# -# Find Github Actions to setup tooling here: -# - https://github.com/actions/?q=setup&type=&language= -# - https://github.com/actions/starter-workflows/tree/main/ci -# - https://github.com/marketplace?type=actions&query=setup -# -# Requires scripts: -# - bin/test +# This workflow creates an Odin environment and runs all tests for all exercises. name: odin / Test @@ -19,23 +10,22 @@ on: jobs: ci: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: + - name: Update packages + run: sudo apt update + + - name: Install Clang + run: sudo apt -y install clang + - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - name: Setup Odin - uses: laytan/setup-odin + uses: laytan/setup-odin@41f9612bfec760bbb68b05b5747f319afe7c48d8 with: token: ${{ secrets.GITHUB_TOKEN }} - - # TODO: Pull a specific version of Odin compatible with the OS. - # The current setting pulls the latest version of Odin from the repo, and rebuilds it from - # scratch. This is slow, and clutters the action's log. Rather ensure that the OS has the - # right dependencies for a recent versioned release of the compiler. - release: dev-2024-08 - llvm-version: 14 - name: Install project dependencies From deb0858d0d0fd71a04765b7d0aaba5bf74891e9e Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Tue, 27 Aug 2024 21:43:00 +0200 Subject: [PATCH 17/24] Implemented the resistor-color exercise --- .github/workflows/test.yml | 2 +- .../hello-world/hello_world_test.odin | 2 +- exercises/practice/leap/.meta/tests.toml | 37 ++++++++++ .../.meta/resistor_color_example.odin | 33 +++++++++ .../resistor-color/resistor_color.odin | 14 ++++ .../resistor-color/resistor_color_test.odin | 73 +++++++++++++++++++ 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/leap/.meta/tests.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e5d4d19..9fef8c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ # This workflow creates an Odin environment and runs all tests for all exercises. -name: odin / Test +name: odin/Test on: push: diff --git a/exercises/practice/hello-world/hello_world_test.odin b/exercises/practice/hello-world/hello_world_test.odin index 141b7f9..3793e9b 100644 --- a/exercises/practice/hello-world/hello_world_test.odin +++ b/exercises/practice/hello-world/hello_world_test.odin @@ -3,7 +3,7 @@ package hello_world import "core:testing" @(test) -test :: proc(t: ^testing.T) { +say_hi :: proc(t: ^testing.T) { expected := "Hello, World!" testing.expect_value(t, hello_world(), expected) diff --git a/exercises/practice/leap/.meta/tests.toml b/exercises/practice/leap/.meta/tests.toml new file mode 100644 index 0000000..ce6ba32 --- /dev/null +++ b/exercises/practice/leap/.meta/tests.toml @@ -0,0 +1,37 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[6466b30d-519c-438e-935d-388224ab5223] +description = "year not divisible by 4 in common year" + +[ac227e82-ee82-4a09-9eb6-4f84331ffdb0] +description = "year divisible by 2, not divisible by 4 in common year" + +[4fe9b84c-8e65-489e-970b-856d60b8b78e] +description = "year divisible by 4, not divisible by 100 in leap year" + +[7fc6aed7-e63c-48f5-ae05-5fe182f60a5d] +description = "year divisible by 4 and 5 is still a leap year" + +[78a7848f-9667-4192-ae53-87b30c9a02dd] +description = "year divisible by 100, not divisible by 400 in common year" + +[9d70f938-537c-40a6-ba19-f50739ce8bac] +description = "year divisible by 100 but not by 3 is still not a leap year" + +[42ee56ad-d3e6-48f1-8e3f-c84078d916fc] +description = "year divisible by 400 is leap year" + +[57902c77-6fe9-40de-8302-587b5c27121e] +description = "year divisible by 400 but not by 125 is still a leap year" + +[c30331f6-f9f6-4881-ad38-8ca8c12520c1] +description = "year divisible by 200, not divisible by 400 in common year" diff --git a/exercises/practice/resistor-color/.meta/resistor_color_example.odin b/exercises/practice/resistor-color/.meta/resistor_color_example.odin index e69de29..3b26336 100644 --- a/exercises/practice/resistor-color/.meta/resistor_color_example.odin +++ b/exercises/practice/resistor-color/.meta/resistor_color_example.odin @@ -0,0 +1,33 @@ +package resistor_color + +Color :: enum { + Black, + Brown, + Red, + Orange, + Yellow, + Green, + Blue, + Violet, + Grey, + White, +} + +code :: proc(color: Color) -> int { + return int(color) +} + +colors :: proc() -> [10]Color { + return [10]Color { + .Black, + .Brown, + .Red, + .Orange, + .Yellow, + .Green, + .Blue, + .Violet, + .Grey, + .White, + } +} diff --git a/exercises/practice/resistor-color/resistor_color.odin b/exercises/practice/resistor-color/resistor_color.odin index e69de29..283070e 100644 --- a/exercises/practice/resistor-color/resistor_color.odin +++ b/exercises/practice/resistor-color/resistor_color.odin @@ -0,0 +1,14 @@ +package resistor_color + +Color :: enum { + // Implement me! +} + +code :: proc(color: Color) -> int { + // Implement me! + return -1 +} + +colors :: proc() { // -> ? + // Implement me! +} diff --git a/exercises/practice/resistor-color/resistor_color_test.odin b/exercises/practice/resistor-color/resistor_color_test.odin index e69de29..11a8187 100644 --- a/exercises/practice/resistor-color/resistor_color_test.odin +++ b/exercises/practice/resistor-color/resistor_color_test.odin @@ -0,0 +1,73 @@ +package resistor_color + +import "core:testing" + +@(test) +black :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Black), 0) +} + +@(test) +brown :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Brown), 1) +} + +@(test) +red :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Red), 2) +} + +@(test) +orange :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Orange), 3) +} + +@(test) +yellow :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Yellow), 4) +} + +@(test) +green :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Green), 5) +} + +@(test) +blue :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Blue), 6) +} + +@(test) +violet :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Violet), 7) +} + +@(test) +grey :: proc(t: ^testing.T) { + testing.expect_value(t, code(.Grey), 8) +} + +@(test) +white :: proc(t: ^testing.T) { + testing.expect_value(t, code(.White), 9) +} + +@(test) +all_colors :: proc(t: ^testing.T) { + testing.expect_value( + t, + colors(), + [10]Color { + .Black, + .Brown, + .Red, + .Orange, + .Yellow, + .Green, + .Blue, + .Violet, + .Grey, + .White, + }, + ) +} From 3ea858887fb7a45325a5d9ed504b818819ffa540 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Tue, 27 Aug 2024 22:24:19 +0200 Subject: [PATCH 18/24] Removed configlet dependency in test.yml --- .github/workflows/test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9fef8c0..b90c5c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,8 +28,5 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} llvm-version: 14 - - name: Install project dependencies - run: bin/fetch-configlet - - name: Verify all exercises run: bin/verify-exercises From 9cd22863fcf68b2c790887927f7efe386bfcf277 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Wed, 28 Aug 2024 10:34:26 +0200 Subject: [PATCH 19/24] Moved the exercise checklist to a linked GH issue --- README.md | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 489129d..35f11b1 100644 --- a/README.md +++ b/README.md @@ -48,36 +48,10 @@ _Thoughtful suggestions will likely result in faster & more enthusiastic respons

-## Pre-launch Checklist -Here is a list of practice exercises that we aim to implement before the Odin track goes live on Exercism: - -- ~~Hello World~~ -- ~~Leap~~ -- ~~Difference of Squares~~ -- ~~Grains~~ -- Resistor Color -- Collatz Conjecture -- Queen Attack -- Darts -- Resistor Color Duo -- Hamming -- Space Age -- RNA Transcription -- Binary -- Eliud's Eggs -- Two Fer -- Raindrops -- D&D Character -- Perfect Numbers -- High Scores -- Pangram -- Resistor Color Trio -- Armstrong Numbers - -Other exercises are welcome too! -It is recommended that a track has 20 exercises or more (not counting "Hello World") to go live. - ## Contributing an Exercise +If you are interested in contributing a new exercise, please have a look at [this issue][odin-backlog] to see which exercises are waiting on implementation. +Leave a comment in the issue to notify other contributors which exercise you plan to implement. + The `bin/` subdirectory contains several scripts to help you contribute exercises that will run correctly on Exercism: - `configlet` is a tool to help track maintainers with the maintenance of their track. @@ -124,6 +98,7 @@ The `bin/` subdirectory contains several scripts to help you contribute exercise [freeing-maintainers]: https://exercism.org/blog/freeing-our-maintainers [practice-exercises]: https://github.com/exercism/docs/blob/main/building/tracks/practice-exercises.md [prs]: https://github.com/exercism/docs/blob/main/community/good-member/pull-requests.md +[odin-backlog]: https://github.com/exercism/odin/issues/26 [odin-release]: https://github.com/odin-lang/Odin/releases/tag/dev-2024-08 [odin-syllabus]: https://exercism.org/tracks/odin/concepts [odin-thread]: https://forum.exercism.org/t/new-track-odin-programming-language/7379 From 2a8d7cc2cf631d092e4f2408a066a5e144d33a81 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Wed, 28 Aug 2024 18:27:46 +0200 Subject: [PATCH 20/24] Test runner now checks that stub solution fails Added a check to bin/run-test.sh so that verifies that the stub solution does *not* pass any tests. Fixed hello-world.odin to actually be a stub. --- bin/run-test.sh | 42 ++++++++++++------- .../practice/hello-world/hello_world.odin | 2 +- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/bin/run-test.sh b/bin/run-test.sh index fbd88de..b214b89 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -15,11 +15,12 @@ function run_test() { meta=".meta" exercise_name="${1}" exercise_path="${exercises_path}/${exercise_name}" + tmp_path=`mktemp -d` echo "$exercise_name / $exercise_path" if [ -n "${exercise_name}" ] && [ -d "${exercise_path}" ]; then - echo "Running test for exercise: ${exercise_name}" + echo -e "Running test for exercise: ${exercise_name}\n" # Turn something like "hello-world" into "hello_world" exercise_safe_name=$(echo $exercise_name | to_snake_case) @@ -33,21 +34,26 @@ function run_test() { # "exercises/practice/.meta/hello_world_example.odin" example_file="${exercise_path}/${meta}/${exercise_safe_name}_example.odin" - # Move the blank solution file into the meta directory for a bit - mv ${solution_file} ${exercise_path}/${meta} + # Copy the example and test files into the temporary directory + cp ${example_file} ${tmp_path}/${exercise_safe_name}.odin + cp ${test_file} ${tmp_path} - # Copy the example file into the main directory - cp ${example_file} ${solution_file} - - # Run the tests using the example file. The `|| true` ensures that the script - # continues to the clean-up steps even if the tests fail. - odin test ${exercise_path} || true - - # Move the blank solution file back into the main directory - mv "${exercise_path}/${meta}/${exercise_safe_name}.odin" ${solution_file} + # Run the tests using the example file to verify that it is a valid solution. + odin test ${tmp_path} + + echo -e "Checking that the stub solution *fails* the tests\n" + + # Copy the stub solution to the temporary directory + cp ${solution_file} ${tmp_path}/${exercise_safe_name}.odin + + # Run the test. If it passes, exit with a message and an error. + if odin test ${tmp_path} ; then + echo "ERROR: The stub solution must not pass the tests!" + exit 1 + else + echo "SUCCESS: The stub solution failed the tests above as expected." + fi - # Remove the built executable - rm -f ${exercise_name} else echo "Running all tests" for exercise in $(ls $exercises_path) @@ -57,4 +63,12 @@ function run_test() { fi } +# Delete the temp directory +function cleanup { + rm -rf ${tmp_path} +} + +# Register the cleanup function to be called on the EXIT signal +trap cleanup EXIT + run_test $@ diff --git a/exercises/practice/hello-world/hello_world.odin b/exercises/practice/hello-world/hello_world.odin index 72190c1..ab9d461 100644 --- a/exercises/practice/hello-world/hello_world.odin +++ b/exercises/practice/hello-world/hello_world.odin @@ -1,5 +1,5 @@ package hello_world hello_world :: proc() -> string { - return "Hello, World!" + return "Goodbye, Mars!" } From 0d5ecddec2c79dc0c314dff6b101437cb1ab4734 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Wed, 28 Aug 2024 20:34:30 +0200 Subject: [PATCH 21/24] Fixed incorrect stubs, added panics for stub procs - Improved bin/run-test.sh to add more visible color coding to the output. - Replaced NotImplemented error codes with a compile-time panic for procedures that have not yet been implemented. - Fixed stubs that were actually complete examples. --- bin/run-test.sh | 10 +++++-- .../difference_of_squares.odin | 7 +++-- exercises/practice/grains/grains.odin | 12 +++------ .../practice/leap/.meta/leap_example.odin | 9 ++----- exercises/practice/leap/leap.odin | 10 ++----- exercises/practice/leap/leap_test.odin | 27 +++++++------------ .../resistor-color/resistor_color.odin | 12 ++++----- 7 files changed, 33 insertions(+), 54 deletions(-) diff --git a/bin/run-test.sh b/bin/run-test.sh index b214b89..17f08a8 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -3,6 +3,11 @@ # Exit script if any subcommands fail set -e +# Terminal color definitions +COLOR_GREEN="$(tput setaf 2)$(tput bold)" +COLOR_RED="$(tput setaf 1)$(tput bold)" +COLOR_REST="$(tput sgr0)" + # TODO: Pull info from config, similar to how the Zig track does it: # https://github.com/exercism/zig/blob/main/bin/run-tests @@ -48,10 +53,10 @@ function run_test() { # Run the test. If it passes, exit with a message and an error. if odin test ${tmp_path} ; then - echo "ERROR: The stub solution must not pass the tests!" + printf '%s\nERROR: The stub solution must not pass the tests!\n\n' $COLOR_RED exit 1 else - echo "SUCCESS: The stub solution failed the tests above as expected." + printf '%s\nSUCCESS: The stub solution failed the tests above as expected.\n\n' $COLOR_GREEN fi else @@ -59,6 +64,7 @@ function run_test() { for exercise in $(ls $exercises_path) do run_test $exercise + printf "%s" $COLOR_REST done fi } diff --git a/exercises/practice/difference-of-squares/difference_of_squares.odin b/exercises/practice/difference-of-squares/difference_of_squares.odin index e957639..799b420 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares.odin +++ b/exercises/practice/difference-of-squares/difference_of_squares.odin @@ -1,14 +1,13 @@ package difference_of_squares square_of_sum :: proc(n: int) -> int { - sum := n * (n + 1) / 2 - return sum * sum + #panic("Please implement the `square_of_sum` procedure") } sum_of_squares :: proc(n: int) -> int { - return n * (n + 1) * (2 * n + 1) / 6 + #panic("Please implement the `sum_of_squares` procedure") } difference :: proc(n: int) -> int { - return square_of_sum(n) - sum_of_squares(n) + #panic("Please implement the `difference` procedure") } diff --git a/exercises/practice/grains/grains.odin b/exercises/practice/grains/grains.odin index 7873e02..9309232 100644 --- a/exercises/practice/grains/grains.odin +++ b/exercises/practice/grains/grains.odin @@ -1,18 +1,14 @@ package grains -Error :: enum { - // Implement me! - NotImplemented, -} +Error :: enum {}// Please inspect the tests to see which error states to enumerate here. + // Returns the number of grains on the specified square. square :: proc(n: int) -> (u64, Error) { - // Implement me! - return 0, .NotImplemented + #panic("Please implement the `square` procedure.") } // Returns the total number of squares on the board. total :: proc() -> (u64, Error) { - // Implement me! - return 0, .NotImplemented + #panic("Please implement the `total` procedure.") } diff --git a/exercises/practice/leap/.meta/leap_example.odin b/exercises/practice/leap/.meta/leap_example.odin index 24ce39b..f8501d0 100644 --- a/exercises/practice/leap/.meta/leap_example.odin +++ b/exercises/practice/leap/.meta/leap_example.odin @@ -1,10 +1,5 @@ package leap -Error :: enum { - None = 0, - NotImplemented, -} - -is_leap_year :: proc(year: int) -> (bool, Error) { - return year % 400 == 0 || year % 4 == 0 && year % 100 != 0, .None +is_leap_year :: proc(year: int) -> bool { + return year % 400 == 0 || year % 4 == 0 && year % 100 != 0 } diff --git a/exercises/practice/leap/leap.odin b/exercises/practice/leap/leap.odin index f22d96c..7056be7 100644 --- a/exercises/practice/leap/leap.odin +++ b/exercises/practice/leap/leap.odin @@ -1,11 +1,5 @@ package leap -Error :: enum { - // Implement me! - NotImplemented, -} - -is_leap_year :: proc(year: int) -> (bool, Error) { - // Implement me! - return false, .NotImplemented +is_leap_year :: proc(year: int) -> bool { + #panic("Please implement the `is_leap_year` procedure.") } diff --git a/exercises/practice/leap/leap_test.odin b/exercises/practice/leap/leap_test.odin index 147cc31..f401bc2 100644 --- a/exercises/practice/leap/leap_test.odin +++ b/exercises/practice/leap/leap_test.odin @@ -4,62 +4,53 @@ import "core:testing" @(test) test_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - is_leap, _ := is_leap_year(2015) - testing.expect(t, !is_leap) + testing.expect(t, !is_leap_year(2015)) } @(test) test_divisible_by_2_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { - is_leap, _ := is_leap_year(1970) - testing.expect(t, !is_leap) + testing.expect(t, !is_leap_year(1970)) } @(test) test_divisible_by_4_not_divisible_by_100_in_leap_year :: proc(t: ^testing.T) { - is_leap, _ := is_leap_year(1996) - testing.expect(t, is_leap) + testing.expect(t, is_leap_year(1996)) } @(test) test_divisible_by_4_and_5_is_still_a_leap_year :: proc(t: ^testing.T) { - is_leap, _ := is_leap_year(1960) - testing.expect(t, is_leap) + testing.expect(t, is_leap_year(1960)) } @(test) test_divisible_by_100_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { - is_leap, _ := is_leap_year(2100) - testing.expect(t, !is_leap) + testing.expect(t, !is_leap_year(2100)) } @(test) test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc( t: ^testing.T, ) { - is_leap, _ := is_leap_year(1900) - testing.expect(t, !is_leap) + testing.expect(t, !is_leap_year(1900)) } @(test) test_divisible_by_400 :: proc(t: ^testing.T) { - is_leap, _ := is_leap_year(2000) - testing.expect(t, is_leap) + testing.expect(t, is_leap_year(2000)) } @(test) test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc( t: ^testing.T, ) { - is_leap, _ := is_leap_year(2400) - testing.expect(t, is_leap) + testing.expect(t, is_leap_year(2400)) } @(test) test_divisible_by_200_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { - is_leap, _ := is_leap_year(1800) - testing.expect(t, !is_leap) + testing.expect(t, !is_leap_year(1800)) } diff --git a/exercises/practice/resistor-color/resistor_color.odin b/exercises/practice/resistor-color/resistor_color.odin index 283070e..70b2607 100644 --- a/exercises/practice/resistor-color/resistor_color.odin +++ b/exercises/practice/resistor-color/resistor_color.odin @@ -1,14 +1,12 @@ package resistor_color -Color :: enum { - // Implement me! -} +Color :: enum {}// Implement an enumeration of all the resistor colors. + code :: proc(color: Color) -> int { - // Implement me! - return -1 + #panic("Please implement the `code` procedure.") } -colors :: proc() { // -> ? - // Implement me! +colors :: proc() { // -> ? + #panic("Please implement the `colors` procedure.") } From 585abb56d42f32095a3b51fb43ef788b19d55f01 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Wed, 28 Aug 2024 22:10:20 +0200 Subject: [PATCH 22/24] Removed fancy terminal color commands --- bin/run-test.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/bin/run-test.sh b/bin/run-test.sh index 17f08a8..f20d0aa 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -3,11 +3,6 @@ # Exit script if any subcommands fail set -e -# Terminal color definitions -COLOR_GREEN="$(tput setaf 2)$(tput bold)" -COLOR_RED="$(tput setaf 1)$(tput bold)" -COLOR_REST="$(tput sgr0)" - # TODO: Pull info from config, similar to how the Zig track does it: # https://github.com/exercism/zig/blob/main/bin/run-tests @@ -53,10 +48,10 @@ function run_test() { # Run the test. If it passes, exit with a message and an error. if odin test ${tmp_path} ; then - printf '%s\nERROR: The stub solution must not pass the tests!\n\n' $COLOR_RED + echo -e '\nERROR: The stub solution must not pass the tests!\n' exit 1 else - printf '%s\nSUCCESS: The stub solution failed the tests above as expected.\n\n' $COLOR_GREEN + echo -e '\nSUCCESS: The stub solution failed the tests above as expected.\n' fi else @@ -64,7 +59,6 @@ function run_test() { for exercise in $(ls $exercises_path) do run_test $exercise - printf "%s" $COLOR_REST done fi } From 5f81ce0dd917ba9e8de62fdfe3b0c0d7686f7966 Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Thu, 29 Aug 2024 12:19:11 +0200 Subject: [PATCH 23/24] Added support for skipping and unskipping tests - Exercises' unit tests were modified so that only a minimal subset of tests are initially enabled; the rest are skipped. - The test runner was modified to automatically unskip all test cases before running the tests. --- bin/run-test.sh | 14 +++++++++-- .../difference_of_squares_test.odin | 21 ++++++++++------ exercises/practice/grains/grains_test.odin | 25 +++++++++++-------- exercises/practice/leap/leap_test.odin | 21 ++++++++++------ .../resistor-color/resistor_color_test.odin | 8 +++++- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/bin/run-test.sh b/bin/run-test.sh index f20d0aa..7c39d9b 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -34,9 +34,19 @@ function run_test() { # "exercises/practice/.meta/hello_world_example.odin" example_file="${exercise_path}/${meta}/${exercise_safe_name}_example.odin" - # Copy the example and test files into the temporary directory + # Copy the example file into the temporary directory cp ${example_file} ${tmp_path}/${exercise_safe_name}.odin - cp ${test_file} ${tmp_path} + + # Unskip all tests and write the processed test file to the temporary directory. + # The test file for the exercise often has several of the tests skippped initially, so that + # students can do test-driven development by enabling the next test, possibly see it fail, + # and then refining their solution. However, the test runner used by contributors and the CI + # pipeline always needs to run all tests. + # + # In Odin, a test can be skipped by commenting out the `@(test)` annotation preceding the + # test procedure. Here we unskip the test by searching for `\\ @(test)` lines and replacing + # them with `@test`. + sed s/"\/\/ @(test)"/"@(test)"/ ${test_file} > ${tmp_path}/${exercise_safe_name}_test.odin # Run the tests using the example file to verify that it is a valid solution. odin test ${tmp_path} diff --git a/exercises/practice/difference-of-squares/difference_of_squares_test.odin b/exercises/practice/difference-of-squares/difference_of_squares_test.odin index 1c91db7..0ffc78c 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares_test.odin +++ b/exercises/practice/difference-of-squares/difference_of_squares_test.odin @@ -1,3 +1,8 @@ +/* These are the unit tests for the exercise. Only the first one is enabled to start with. You can + * enable the other tests by uncommenting the `@(test)` attribute of the test procedure. Your + * solution should pass all tests before it is ready for submission. + */ + package difference_of_squares import "core:testing" @@ -7,42 +12,42 @@ test_square_of_sum_1 :: proc(t: ^testing.T) { testing.expect_value(t, square_of_sum(1), 1) } -@(test) +// @(test) test_square_of_sum_5 :: proc(t: ^testing.T) { testing.expect_value(t, square_of_sum(5), 225) } -@(test) +// @(test) test_square_of_sum_100 :: proc(t: ^testing.T) { testing.expect_value(t, square_of_sum(100), 25_502_500) } -@(test) +// @(test) sum_of_squares_1_test :: proc(t: ^testing.T) { testing.expect_value(t, sum_of_squares(1), 1) } -@(test) +// @(test) sum_of_squares_5_test :: proc(t: ^testing.T) { testing.expect_value(t, sum_of_squares(5), 55) } -@(test) +// @(test) sum_of_squares_100_test :: proc(t: ^testing.T) { testing.expect_value(t, sum_of_squares(100), 338_350) } -@(test) +// @(test) difference_of_squares_1_test :: proc(t: ^testing.T) { testing.expect_value(t, difference(1), 0) } -@(test) +// @(test) difference_of_squares_5_test :: proc(t: ^testing.T) { testing.expect_value(t, difference(5), 170) } -@(test) +// @(test) difference_of_squares_100_test :: proc(t: ^testing.T) { testing.expect_value(t, difference(100), 25_164_150) } diff --git a/exercises/practice/grains/grains_test.odin b/exercises/practice/grains/grains_test.odin index b1e48cf..710639f 100644 --- a/exercises/practice/grains/grains_test.odin +++ b/exercises/practice/grains/grains_test.odin @@ -1,3 +1,8 @@ +/* These are the unit tests for the exercise. Only the first one is enabled to start with. You can + * enable the other tests by uncommenting the `@(test)` attribute of the test procedure. Your + * solution should pass all tests before it is ready for submission. + */ + package grains import "core:testing" @@ -11,7 +16,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_1 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_2 :: proc( t: ^testing.T, ) { @@ -20,7 +25,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_2 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_3 :: proc( t: ^testing.T, ) { @@ -29,7 +34,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_3 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_4 :: proc( t: ^testing.T, ) { @@ -38,7 +43,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_4 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_16 :: proc( t: ^testing.T, ) { @@ -47,7 +52,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_16 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_32 :: proc( t: ^testing.T, ) { @@ -56,7 +61,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_32 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_grains_on_square_64 :: proc( t: ^testing.T, ) { @@ -65,7 +70,7 @@ test_returns_the_number_of_grains_on_the_square_grains_on_square_64 :: proc( testing.expect_value(t, e, Error.None) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_square_0_raises_an_exception :: proc( t: ^testing.T, ) { @@ -74,7 +79,7 @@ test_returns_the_number_of_grains_on_the_square_square_0_raises_an_exception :: testing.expect_value(t, e, Error.InvalidSquare) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_negative_square_raises_an_exception :: proc( t: ^testing.T, ) { @@ -83,7 +88,7 @@ test_returns_the_number_of_grains_on_the_square_negative_square_raises_an_except testing.expect_value(t, e, Error.InvalidSquare) } -@(test) +// @(test) test_returns_the_number_of_grains_on_the_square_square_greater_than_64_raises_an_exception :: proc( t: ^testing.T, ) { @@ -92,7 +97,7 @@ test_returns_the_number_of_grains_on_the_square_square_greater_than_64_raises_an testing.expect_value(t, e, Error.InvalidSquare) } -@(test) +// @(test) test_returns_the_total_number_of_grains_on_the_board :: proc(t: ^testing.T) { c, e := total() testing.expect_value(t, c, 18_446_744_073_709_551_615) diff --git a/exercises/practice/leap/leap_test.odin b/exercises/practice/leap/leap_test.odin index f401bc2..5f08b87 100644 --- a/exercises/practice/leap/leap_test.odin +++ b/exercises/practice/leap/leap_test.odin @@ -1,3 +1,8 @@ +/* These are the unit tests for the exercise. Only the first one is enabled to start with. You can + * enable the other tests by uncommenting the `@(test)` attribute of the test procedure. Your + * solution should pass all tests before it is ready for submission. + */ + package leap import "core:testing" @@ -7,48 +12,48 @@ test_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { testing.expect(t, !is_leap_year(2015)) } -@(test) +// @(test) test_divisible_by_2_not_divisible_by_4_in_common_year :: proc(t: ^testing.T) { testing.expect(t, !is_leap_year(1970)) } -@(test) +// @(test) test_divisible_by_4_not_divisible_by_100_in_leap_year :: proc(t: ^testing.T) { testing.expect(t, is_leap_year(1996)) } -@(test) +// @(test) test_divisible_by_4_and_5_is_still_a_leap_year :: proc(t: ^testing.T) { testing.expect(t, is_leap_year(1960)) } -@(test) +// @(test) test_divisible_by_100_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { testing.expect(t, !is_leap_year(2100)) } -@(test) +// @(test) test_divisible_by_100_but_not_by_3_is_still_not_a_leap_year :: proc( t: ^testing.T, ) { testing.expect(t, !is_leap_year(1900)) } -@(test) +// @(test) test_divisible_by_400 :: proc(t: ^testing.T) { testing.expect(t, is_leap_year(2000)) } -@(test) +// @(test) test_divisible_by_400_but_not_by_125_is_still_a_leap_year :: proc( t: ^testing.T, ) { testing.expect(t, is_leap_year(2400)) } -@(test) +// @(test) test_divisible_by_200_not_divisible_by_400_in_common_year :: proc( t: ^testing.T, ) { diff --git a/exercises/practice/resistor-color/resistor_color_test.odin b/exercises/practice/resistor-color/resistor_color_test.odin index 11a8187..e988003 100644 --- a/exercises/practice/resistor-color/resistor_color_test.odin +++ b/exercises/practice/resistor-color/resistor_color_test.odin @@ -1,3 +1,9 @@ +/* These are the unit tests for the exercise. Only the tests for the `code()` + * procedure are enabled to start with. You can enable the final test by + * uncommenting the`@(test)` attribute of the `all_colors` test procedure. Your + * solution should pass all tests before it is ready for submission. + */ + package resistor_color import "core:testing" @@ -52,7 +58,7 @@ white :: proc(t: ^testing.T) { testing.expect_value(t, code(.White), 9) } -@(test) +// @(test) all_colors :: proc(t: ^testing.T) { testing.expect_value( t, From 1bf9d9c30195b54e78fcb1dda4e132d0fb365fec Mon Sep 17 00:00:00 2001 From: G-J van Rooyen Date: Fri, 30 Aug 2024 17:21:19 +0200 Subject: [PATCH 24/24] Removed mention of NotImplemented in the README --- README.md | 5 +++-- bin/run-test.sh | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 35f11b1..99814fe 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,11 @@ The `bin/` subdirectory contains several scripts to help you contribute exercise - Run `bin/configlet create --practice-exercise ` to automatically generate the exercise skeleton in the `exercises/practice//` directory and to update `config.json` to reference the new exercise. You can add `--author ` as option to mark yourself as the creator of this exercise (or add it later in the exercise's `.meta/config.json` file.) - Add a solution stub at the exercise's `.odin` file. - This is what users will start with when solving the exercise. + This is what students will begin with when they start the exercise. + It should make it as easy as possible to understand what they need to solve, without revealing too much of the solution. + Stub functions should usually panic, e.g. `#panic("Please implement the function.")`. - Add tests to `_test.odin`. Verify that the slug solution would fail _all_ tests. - Consider adding an error return enumeration such as `NotImplemented` to ensure that the stub solution's return values are invalid for all test cases. - Implement a reference solution at `.meta/_example.odin`. - Use `bin/run_test.sh ` to verify that your reference solution passes. diff --git a/bin/run-test.sh b/bin/run-test.sh index 7c39d9b..53d94de 100755 --- a/bin/run-test.sh +++ b/bin/run-test.sh @@ -57,6 +57,12 @@ function run_test() { cp ${solution_file} ${tmp_path}/${exercise_safe_name}.odin # Run the test. If it passes, exit with a message and an error. + # TODO: Check that the stub fails _all_ the tests. + # We only check for a single failed test here -- the stub solution could solve all the cases + # but only fail on the most complicated one. Since the purpose of this test is mostly to + # double-check that the example didn't accidentally get duplicated as the stub, this isn't + # too critical for now. + if odin test ${tmp_path} ; then echo -e '\nERROR: The stub solution must not pass the tests!\n' exit 1