From c157767999050b80d578498e0bcda604fba044c6 Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Fri, 24 Sep 2021 14:08:52 -0300 Subject: [PATCH] Exercism: Low power --- .../.exercism/config.json | 17 ++ .../.exercism/metadata.json | 1 + rust/low-power-embedded-game/Cargo.lock | 7 + rust/low-power-embedded-game/Cargo.toml | 4 + rust/low-power-embedded-game/HELP.md | 85 +++++++++ rust/low-power-embedded-game/HINTS.md | 23 +++ rust/low-power-embedded-game/README.md | 162 ++++++++++++++++++ rust/low-power-embedded-game/src/lib.rs | 22 +++ .../tests/low-power-embedded-game.rs | 96 +++++++++++ 9 files changed, 417 insertions(+) create mode 100644 rust/low-power-embedded-game/.exercism/config.json create mode 100644 rust/low-power-embedded-game/.exercism/metadata.json create mode 100644 rust/low-power-embedded-game/Cargo.lock create mode 100644 rust/low-power-embedded-game/Cargo.toml create mode 100644 rust/low-power-embedded-game/HELP.md create mode 100644 rust/low-power-embedded-game/HINTS.md create mode 100644 rust/low-power-embedded-game/README.md create mode 100644 rust/low-power-embedded-game/src/lib.rs create mode 100644 rust/low-power-embedded-game/tests/low-power-embedded-game.rs diff --git a/rust/low-power-embedded-game/.exercism/config.json b/rust/low-power-embedded-game/.exercism/config.json new file mode 100644 index 0000000..178e3b3 --- /dev/null +++ b/rust/low-power-embedded-game/.exercism/config.json @@ -0,0 +1,17 @@ +{ + "blurb": "Learn tuples while writing convenience functions for a low-power embedded game", + "authors": [ + "coriolinus" + ], + "files": { + "solution": [ + "src/lib.rs" + ], + "test": [ + "tests/low-power-embedded-game.rs" + ], + "exemplar": [ + ".meta/exemplar.rs" + ] + } +} diff --git a/rust/low-power-embedded-game/.exercism/metadata.json b/rust/low-power-embedded-game/.exercism/metadata.json new file mode 100644 index 0000000..223482a --- /dev/null +++ b/rust/low-power-embedded-game/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"rust","exercise":"low-power-embedded-game","id":"412e53bdec5d453abfee32ee4b1156c2","url":"https://exercism.org/tracks/rust/exercises/low-power-embedded-game","handle":"JBiason","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/rust/low-power-embedded-game/Cargo.lock b/rust/low-power-embedded-game/Cargo.lock new file mode 100644 index 0000000..f10136f --- /dev/null +++ b/rust/low-power-embedded-game/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "low_power_embedded_game" +version = "0.1.0" diff --git a/rust/low-power-embedded-game/Cargo.toml b/rust/low-power-embedded-game/Cargo.toml new file mode 100644 index 0000000..8107652 --- /dev/null +++ b/rust/low-power-embedded-game/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "low_power_embedded_game" +version = "0.1.0" +edition = "2018" diff --git a/rust/low-power-embedded-game/HELP.md b/rust/low-power-embedded-game/HELP.md new file mode 100644 index 0000000..b4252f8 --- /dev/null +++ b/rust/low-power-embedded-game/HELP.md @@ -0,0 +1,85 @@ +# Help + +## Running the tests + +Execute the tests with: + +```bash +$ cargo test +``` + +All but the first test have been ignored. After you get the first test to +pass, open the tests source file which is located in the `tests` directory +and remove the `#[ignore]` flag from the next test and get the tests to pass +again. Each separate test is a function with `#[test]` flag above it. +Continue, until you pass every test. + +If you wish to run _only ignored_ tests without editing the tests source file, use: + +```bash +$ cargo test -- --ignored +``` + +If you are using Rust 1.51 or later, you can run _all_ tests with + +```bash +$ cargo test -- --include-ignored +``` + +To run a specific test, for example `some_test`, you can use: + +```bash +$ cargo test some_test +``` + +If the specific test is ignored, use: + +```bash +$ cargo test some_test -- --ignored +``` + +To learn more about Rust tests refer to the online [test documentation][rust-tests]. + +[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html + +## Submitting your solution + +You can submit your solution using the `exercism submit src/lib.rs` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Rust track's documentation](https://exercism.org/docs/tracks/rust) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +## Rust Installation + +Refer to the [exercism help page][help-page] for Rust installation and learning +resources. + +## Submitting the solution + +Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer. + +## Feedback, Issues, Pull Requests + +The GitHub [track repository][github] is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help! + +If you want to know more about Exercism, take a look at the [contribution guide]. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[help-page]: https://exercism.io/tracks/rust/learning +[github]: https://github.com/exercism/rust +[contribution guide]: https://exercism.io/docs/community/contributors \ No newline at end of file diff --git a/rust/low-power-embedded-game/HINTS.md b/rust/low-power-embedded-game/HINTS.md new file mode 100644 index 0000000..a056a5a --- /dev/null +++ b/rust/low-power-embedded-game/HINTS.md @@ -0,0 +1,23 @@ +# Hints + +## General + +- [Rust Book: The Tuple Type](https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type) +- [Rust By Example: Tuples](https://doc.rust-lang.org/stable/rust-by-example/primitives/tuples.html) +- [Rust By Example: Destructuring](https://doc.rust-lang.org/stable/rust-by-example/flow_control/match/destructuring.html) + +## 1. Write a function `divmod` which returns both the quotient and remainder of a division + +- Don't worry about optimizing for efficiency; the naive implementation is fine + +## 2. Write an iterator adaptor `evens` which returns the even items from an arbitrary iterator + +- Just chain together the suggested methods and everything will work out +- A number `n` is even if `n % 2 == 0` +- A closure is a function with abbreviated syntax: the argument name(s) go within a pair of `|` symbols, and the expression follows. Unlike normal functions, it is not always necessary to explicitly state the type of each argument, just the name. For example, here is how you can construct an iterator of odd squares: `(0..).map(|n| 2 * n + 1).map(|n| n * n)`. + +## 3. Implement a `manhattan` method on a `Position` tuple struct + +- Don't worry about method syntax; just replacing the `unimplemented` portion within the `impl Position` block will do the right thing. +- Consider that some values within a `Position` may be negative, but a distance is never negative. +- Calculating the absolute value is [built-in](https://doc.rust-lang.org/std/primitive.i16.html#method.abs) \ No newline at end of file diff --git a/rust/low-power-embedded-game/README.md b/rust/low-power-embedded-game/README.md new file mode 100644 index 0000000..9027657 --- /dev/null +++ b/rust/low-power-embedded-game/README.md @@ -0,0 +1,162 @@ +# Low-Power Embedded Game + +Welcome to Low-Power Embedded Game on Exercism's Rust Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +Tuples are a lightweight way to group a fixed set of arbitrary types of data together. A tuple doesn't have +a particular name; naming a data structure turns it into a `struct`. A tuple's fields don't have names; +they are accessed by means of destructuring or by position. + +## Syntax + +### Creation + +Tuples are always created with a tuple expression: + +```rust +// pointless but legal +let unit = (); +// single element +let single_element = ("note the comma",); +// two element +let two_element = (123, "elements can be of differing types"); +``` + +Tuples can have an arbitrary number of elements. + +### Access by destructuring + +It is possible to access the elements of a tuple by destructuring. This just means assigning variable +names to the individual elements of the tuple, consuming it. + +```rust +let (elem1, _elem2) = two_element; +assert_eq!(elem1, 123); +``` + +### Access by position + +It is also possible to access the elements of a tuple by numeric positional index. Indexing, as always, +begins at 0. + +```rust +let notation = single_element.0; +assert_eq!(notation, "note the comma"); +``` + +## Tuple Structs + +You will also be asked to work with tuple structs. Like normal structs, these are named types; unlike +normal structs, they have anonymous fields. Their syntax is very similar to normal tuple syntax. It is +legal to use both destructuring and positional access. + +```rust +struct TupleStruct(u8, i32); +let my_tuple_struct = TupleStruct(123, -321); +let neg = my_tuple_struct.1; +let TupleStruct(byte, _) = my_tuple_struct; +assert_eq!(neg, -321); +assert_eq!(byte, 123); +``` + +### Field Visibility + +All fields of anonymous tuples are always public. However, fields of tuple structs have individual +visibility which defaults to private, just like fields of standard structs. You can make the fields +public with the `pub` modifier, just as in a standard struct. + +```rust +// fails due to private fields +mod tuple { pub struct TupleStruct(u8, i32); } +fn main() { let _my_tuple_struct = tuple::TupleStruct(123, -321); } +``` + +```rust +// succeeds: fields are public +mod tuple { pub struct TupleStruct(pub u8, pub i32); } +fn main() { let _my_tuple_struct = tuple::TupleStruct(123, -321); } +``` + +## Instructions + +You are working on a game targeting a low-power embedded system and need to write several convenience functions which will be used by other parts of the game. + +## 1. Calculate the quotient and remainder of a division + +A quotient is the output of a division. + +```rust +fn divmod(dividend: i16, divisor: i16) -> (i16, i16) +``` + +Example: + +```rust +assert_eq!(divmod(10, 3), (3, 1)); +``` + +## 2. Choose even-positioned items from an iterator + +This will be helpful to enable a screen-buffer optimization, your boss promises. + +Iterators are items which expose the methods defined by the [`Iterator` trait](https://doc.rust-lang.org/std/iter/trait.Iterator.html). That documentation is fairly extensive, because they offer many methods; here are the most relevant properties: + +- An iterator is an arbitrary-length stream of items +- They have an [`enumerate` method](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.enumerate) which returns a tuple `(i, val)` for each value +- They have a [`filter` method](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter) which uses a closure to determine whether to yield an element of the iterator +- They have a [`map` method](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) which uses a closure to modify elements of the iterator + +Because your function can run on any kind of iterator, it uses `impl` to signify that this is a trait instance instead of a simple item. Likewise, the `` syntax just means that it doesn't matter what kind of item the iterator produces; your function can produce the even elements of any iterator. + +```rust +fn evens(iter: impl Iterator) -> impl Iterator +``` + +Examples: + +```rust +let mut even_ints = evens(0_u8..); +assert_eq!(even_ints.next(), Some(0)); +assert_eq!(even_ints.next(), Some(2)); +assert_eq!(even_ints.next(), Some(4)); +assert_eq!(even_ints.next(), Some(6)); +``` + +```rust +let mut evens_from_odds = evens(1_i16..); +assert_eq!(evens_from_odds.next(), Some(1)); +assert_eq!(evens_from_odds.next(), Some(3)); +assert_eq!(evens_from_odds.next(), Some(5)); +assert_eq!(evens_from_odds.next(), Some(7)); +``` + +## 3. Calculate the manhattan distance of a position from the origin + +For mapping convenience, you have a tuple struct `Position`: + +```rust +struct Position(i16, i16); +``` + +You need to implement a method `manhattan` on `Position` which returns the [manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) of that position from the origin (`Position(0, 0)`). + +```rust +impl Position { + fn manhattan(&self) -> i16 +} +``` + +Example: + +```rust +assert_eq!(Position(3, 4).manhattan(), 7); +``` + +## Source + +### Created by + +- @coriolinus \ No newline at end of file diff --git a/rust/low-power-embedded-game/src/lib.rs b/rust/low-power-embedded-game/src/lib.rs new file mode 100644 index 0000000..d986aa0 --- /dev/null +++ b/rust/low-power-embedded-game/src/lib.rs @@ -0,0 +1,22 @@ +// This stub file contains items which aren't used yet; feel free to remove this module attribute +// to enable stricter warnings. +#![allow(unused)] + +pub fn divmod(dividend: i16, divisor: i16) -> (i16, i16) { + let remaining = dividend % divisor; + let divisions = (dividend - remaining) / divisor; + (divisions, remaining) +} + +pub fn evens(iter: impl Iterator) -> impl Iterator { + iter.enumerate() + .filter(|(pos, val)| pos % 2 == 0) + .map(|(_, val)| val) +} + +pub struct Position(pub i16, pub i16); +impl Position { + pub fn manhattan(&self) -> i16 { + self.0.abs() + self.1.abs() + } +} diff --git a/rust/low-power-embedded-game/tests/low-power-embedded-game.rs b/rust/low-power-embedded-game/tests/low-power-embedded-game.rs new file mode 100644 index 0000000..c99390c --- /dev/null +++ b/rust/low-power-embedded-game/tests/low-power-embedded-game.rs @@ -0,0 +1,96 @@ +mod divmod { + //! tests of divmod + //! + //! note that we're only testing positive quantities; no need to get into the mod/rem distinction + + use low_power_embedded_game::divmod; + + #[test] + fn example() { + assert_eq!(divmod(10, 3), (3, 1)); + } + + #[test] + fn powerup() { + assert_eq!(divmod(100, 3), (33, 1)); + } + + #[test] + fn less() { + assert_eq!(divmod(3, 10), (0, 3)); + } + + #[test] + fn eq() { + assert_eq!(divmod(3, 3), (1, 0)); + } + + #[test] + fn multiple() { + assert_eq!(divmod(9, 3), (3, 0)); + } +} + +mod evens { + use low_power_embedded_game::evens; + + #[test] + fn simple_i32() { + let out: Vec = evens(0..).take(5).collect(); + assert_eq!(out, &[0, 2, 4, 6, 8]); + } + + #[test] + fn reverse_i32() { + let out: Vec = evens((0..=10).rev()).collect(); + assert_eq!(out, &[10, 8, 6, 4, 2, 0]); + } + + #[test] + fn offset_i32() { + let out: Vec = evens(1..).take(5).collect(); + assert_eq!(out, &[1, 3, 5, 7, 9]); + } + + #[test] + fn strs() { + let input = "You really must never be above joking.".split_whitespace(); + let expected: Vec<_> = "You must be joking.".split_whitespace().collect(); + let out: Vec<_> = evens(input).collect(); + assert_eq!(out, expected); + } +} + +mod manhattan { + use low_power_embedded_game::Position; + + #[test] + fn origin() { + assert_eq!(Position(0, 0).manhattan(), 0); + } + + #[test] + fn q1_unit() { + assert_eq!(Position(1, 1).manhattan(), 2); + } + + #[test] + fn q2_unit() { + assert_eq!(Position(1, -1).manhattan(), 2); + } + + #[test] + fn q3_unit() { + assert_eq!(Position(-1, -1).manhattan(), 2); + } + + #[test] + fn q4_unit() { + assert_eq!(Position(-1, 1).manhattan(), 2); + } + + #[test] + fn relative_prime() { + assert_eq!(Position(30, 70).manhattan(), 100); + } +}