Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tooling and CI set up + 1st four exercises #25

Merged
merged 24 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
41c644f
hello-word, leap, configuration updates
gvrooyen Aug 23, 2024
4f70d44
Add workflow in test.yml
gvrooyen Aug 23, 2024
27d56ac
Modified CI workflow to build Odin from source
gvrooyen Aug 23, 2024
c7fb86e
Completed difference-of-squares, added format-all
gvrooyen Aug 24, 2024
6d31497
Formatted source, added formatting to webhook
gvrooyen Aug 24, 2024
aa92d6e
Added step to push autoformatting changes
gvrooyen Aug 24, 2024
92817bc
Modified test.yml to not fail on no formatting
gvrooyen Aug 24, 2024
17ef99f
Added missing dashes in argument
gvrooyen Aug 24, 2024
5cf8060
Fixed bin/verify-exercises
gvrooyen Aug 24, 2024
0024848
Added the "grains" exercise
gvrooyen Aug 25, 2024
48c2bbe
Updated README; fixed test verification
gvrooyen Aug 25, 2024
11a21ff
Formatted config files with configlet
gvrooyen Aug 26, 2024
3ffcf58
Fixed testing bug that clobbered stub solutions
gvrooyen Aug 26, 2024
97b7017
Small code fixes. Updated README.
gvrooyen Aug 26, 2024
fd4c5d1
Added resistor-color exercise. Updated README.
gvrooyen Aug 26, 2024
cfbb1b8
Fixed and tested the test.yml action
gvrooyen Aug 27, 2024
deb0858
Implemented the resistor-color exercise
gvrooyen Aug 27, 2024
3ea8588
Removed configlet dependency in test.yml
gvrooyen Aug 27, 2024
9cd2286
Moved the exercise checklist to a linked GH issue
gvrooyen Aug 28, 2024
2a8d7cc
Test runner now checks that stub solution fails
gvrooyen Aug 28, 2024
0d5ecdd
Fixed incorrect stubs, added panics for stub procs
gvrooyen Aug 28, 2024
585abb5
Removed fancy terminal color commands
gvrooyen Aug 28, 2024
5f81ce0
Added support for skipping and unskipping tests
gvrooyen Aug 29, 2024
1bf9d9c
Removed mention of NotImplemented in the README
gvrooyen Aug 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# This workflow will do a clean install of the dependencies and run tests across different versions
#
# Replace <track> with the track name
# Replace <image-name> with an image to run the jobs on
# Replace <action to setup tooling> with a github action to setup tooling on the image
# Replace <install dependencies> 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=
Expand All @@ -13,7 +9,7 @@
# Requires scripts:
# - bin/test

name: <track> / Test
name: odin / Test

on:
push:
Expand All @@ -23,17 +19,39 @@ on:

jobs:
ci:
runs-on: <image-name>
runs-on: ubuntu-20.04

steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

- name: Use <setup tooling>
uses: <action to setup tooling>
- name: Setup Odin
uses: laytan/setup-odin@v2
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
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
run: <install dependencies>
run: bin/fetch-configlet
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved

- 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
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
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 '[email protected]'
git diff --quiet && git diff --staged --quiet || git commit -am "Automated formatting" && git push
38 changes: 36 additions & 2 deletions README.md
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,46 @@

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:
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
- 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
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
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
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
(`uuid`) which is best lifted from another track. This can also be used to populate the slug,
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
name, difficulty, and other fields that should typically be similar between tracks.
2. Run `bin/gen-exercise.sh <slug>` to automatically generate the exercise skeleton in the
`exercises/practice/<slug>/` directory.
3. Edit `exercises/practice/<slug>/.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/<slug>/<slug>_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.
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved

## TODO
- Let `bin/gen-exercise.sh` automatically configure `.meta/config.json` when a new exercise skeleton
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
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)

Expand Down
6 changes: 6 additions & 0 deletions bin/format-all.sh
Original file line number Diff line number Diff line change
@@ -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."
6 changes: 3 additions & 3 deletions bin/gen-exercise.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved

echo "Be sure to implement the following files:"
echo -e "\t${solution_file}"
Expand All @@ -110,4 +110,4 @@ EOL

echo "Running configlet lint:"
bin/configlet lint
fi
fi
4 changes: 3 additions & 1 deletion bin/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ function run_test() {
meta=".meta"
exercise_name="${1}"
exercise_path="${exercises_path}/${exercise_name}"

echo "$exercise_name / $exercise_path"
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved

if [ -n "${exercise_name}" ] && [ -d "${exercise_path}" ]; then
echo "Running test for exercise: ${exercise_name}"
Expand Down Expand Up @@ -54,4 +56,4 @@ function run_test() {
fi
}

run_test $@
run_test $@
6 changes: 3 additions & 3 deletions bin/verify-exercises
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand All @@ -22,14 +22,14 @@
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 "$(basename "${concept_exercise_dir}")"
fi
done

# Verify the Practice Exercises
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 "$(basename "${practice_exercise_dir}")"
fi
done
26 changes: 26 additions & 0 deletions config.json
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,23 @@
"name": "Leap",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"uuid": "ce45a52e-0541-4384-8abf-b787bd49cbf7",
"slug": "difference-of-squares",
"name": "Difference Of Squares",
"practices": [],
"prerequisites": [],
"difficulty": 1
},
{
"uuid": "b5b9be18-9141-4176-8f8c-3dd14d14bed5",
"slug": "grains",
"name": "Grains",
"practices": [],
"prerequisites": [],
"difficulty": 2
}
]
},
Expand All @@ -67,6 +83,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": [
Expand Down
14 changes: 14 additions & 0 deletions exercises/practice/difference-of-squares/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions exercises/practice/difference-of-squares/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"authors": ["gvrooyen"],
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
"files": {
"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",
"source_url": "https://projecteuler.net/problem=6"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package difference_of_squares

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)
}
37 changes: 37 additions & 0 deletions exercises/practice/difference-of-squares/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package difference_of_squares

square_of_sum :: proc(n: int) -> int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be the student stub. It looks like this is the example solution.

Copy link
Contributor Author

@gvrooyen gvrooyen Aug 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😳 Oops, yes I think this got overwritten by the test script and I missed it – that needs a fix too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test script had a bug where it clobbered student stubs if the test failed. This has been fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file still needs to revert to an actual stub :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh man, this has tripped me up too many times now 🙈. I'll fix this, and add a check to bin/run-test.sh that fails the test if the stub solution passes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test that fails if the stub turns out to be a valid solution

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)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package difference_of_squares

import "core:testing"

@(test)
test_square_of_sum_1 :: proc(t: ^testing.T) {
testing.expect_value(t, square_of_sum(1), 1)
}

@(test)
gvrooyen marked this conversation as resolved.
Show resolved Hide resolved
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)
}

@(test)
sum_of_squares_1_test :: proc(t: ^testing.T) {
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)
}

@(test)
sum_of_squares_100_test :: proc(t: ^testing.T) {
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)
}

@(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)
}
15 changes: 15 additions & 0 deletions exercises/practice/grains/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions exercises/practice/grains/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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"
}
Loading