Skip to content

Commit

Permalink
Add Low power embedded game exercise (#17)
Browse files Browse the repository at this point in the history
* remove lint markdown

* add low-power-embedded-game

* Add back lint markdown, just commented out
  • Loading branch information
Nenad Misić authored Jun 21, 2024
1 parent 7ed5db8 commit 0d6af91
Show file tree
Hide file tree
Showing 20 changed files with 474 additions and 8 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ on:
- cron: "0 0 * * 0"

jobs:
markdownlint:
name: markdown lint
runs-on: ubuntu-22.04
# markdownlint:
# name: markdown lint
# runs-on: ubuntu-22.04

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

- name: Run markdown lint
run: ./bin/lint_markdown.sh
# - name: Run markdown lint
# run: ./bin/lint_markdown.sh

# stolen from https://raw.githubusercontent.com/exercism/github-actions/main/.github/workflows/shellcheck.yml
shellcheck:
Expand Down
Empty file modified bin/lint_markdown.sh
100755 → 100644
Empty file.
7 changes: 7 additions & 0 deletions concepts/traits/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"blurb": "<todo>",
"authors": [
"misicnenad"
],
"contributors": []
}
1 change: 1 addition & 0 deletions concepts/traits/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Traits
1 change: 1 addition & 0 deletions concepts/traits/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Introduction
1 change: 1 addition & 0 deletions concepts/traits/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
7 changes: 7 additions & 0 deletions concepts/tuples/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"blurb": "<todo>",
"authors": [
"misicnenad"
],
"contributors": []
}
1 change: 1 addition & 0 deletions concepts/tuples/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# The Tuple Type
1 change: 1 addition & 0 deletions concepts/tuples/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Introduction
1 change: 1 addition & 0 deletions concepts/tuples/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
18 changes: 18 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@
],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "low-power-embedded-game",
"name": "low-power-embedded-game",
"uuid": "f3b7ce44-1667-42b4-b792-401d36aee2f1",
"practices": ["tuples", "traits", "control-flow"],
"prerequisites": [],
"difficulty": 2
}
]
},
Expand Down Expand Up @@ -146,6 +154,16 @@
"uuid": "887864e6-8d80-433c-8a1e-b11a5f79ae35",
"slug": "control-flow",
"name": "Control Flow"
},
{
"uuid": "4665675b-1abe-47ea-9701-32fb0220c159",
"slug": "tuples",
"name": "The Tuple Type"
},
{
"uuid": "fe83c9a6-cf50-47b1-9993-21ddd1e895ee",
"slug": "traits",
"name": "Traits"
}
],
"key_features": [
Expand Down
29 changes: 29 additions & 0 deletions exercises/practice/low-power-embedded-game/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Hints

## General

- [The Cairo Book: The Tuple Type](https://book.cairo-lang.org/ch02-02-data-types.html?highlight=tuple#the-tuple-type)
- [Starknet By Example: Tuples](https://starknet-by-example.voyager.online/getting-started/cairo_cheatsheet/tuples.html)

## 1. Write a function `divmod` which returns 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 `todo` 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 will be supported natively in Cairo at some
point, we provided a helper trait you can use in the meantime
66 changes: 66 additions & 0 deletions exercises/practice/low-power-embedded-game/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Instructions

You are working on a game targeting a low-power embedded system and need to
write several convenience traits 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(self: @u16, divisor: u16) -> (u16, u16)
```

Example:

```rust
let dividend: u16 = 10;
assert_eq!(dividend.divmod(3), (3, 1));
```

## 2. Choose even-positioned items from an array

This will be helpful to enable a screen-buffer optimization, your boss
promises.

```rust
trait EvensTrait<T> {
fn evens(self: @Array<T>) -> Array<T>;
}
```

Examples:

```rust
let arr: Array<u16> = array![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(arr.evens(), array![0, 2, 4, 6, 8]);
```

```rust
let arr: Array<u16> = array![1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(arr.evens(), array![1, 3, 5, 7, 9]);
```

## 3. Calculate the manhattan distance of a position from the origin

For mapping convenience, you have a tuple type `Position`:

```rust
type 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 (`let p: Position = (0, 0)`).

```rust
trait PositionTrait {
fn manhattan(self: @Position) -> i16
}
```

Example:

```rust
let p: Position = (3, 4);
assert_eq!(p.manhattan(), 7);
```
75 changes: 75 additions & 0 deletions exercises/practice/low-power-embedded-game/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 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); }
```
18 changes: 18 additions & 0 deletions exercises/practice/low-power-embedded-game/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"authors": [
"misicnenad"
],
"files": {
"solution": [
"src/lib.cairo",
"Scarb.toml"
],
"test": [
"src/tests.cairo"
],
"example": [
".meta/example.cairo"
]
},
"blurb": "Learn tuples while writing convenience functions for a low-power embedded game"
}
32 changes: 32 additions & 0 deletions exercises/practice/low-power-embedded-game/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Design

## Goal

Introduce the student to tuples and how to work with them. Understand that traits can be automatically generated from an implementation.

## Learning objectives

- know how to create tuples and tuple structs
- know how to destructure tuples into new variables, e.g. `let (x, y) = a_point;`;
- see that traits can be automatically generated by the compiler if the `impl` is annotated with the `#[generate_trait]` attribute.

## Out of scope

- enum variants with tuple structs

## Concepts

- tuples
- destructuring
- traits

## Prerequisites

- traits

## Resources to refer to

### Hints

- <https://book.cairo-lang.org/ch02-02-data-types.html?highlight=tuples#the-tuple-type>
- <https://book.cairo-lang.org/ch05-03-method-syntax.html?highlight=generate_tra#the-generate_trait-attribute>
45 changes: 45 additions & 0 deletions exercises/practice/low-power-embedded-game/.meta/example.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#[generate_trait]
pub impl DivmodImpl of DivmodTrait {
fn divmod(self: @u16, divisor: u16) -> (u16, u16) {
(*self / divisor, *self % divisor)
}
}

#[generate_trait]
pub impl EvensImpl<T, +Drop<T>, +Copy<T>> of EvensTrait<T> {
fn evens(self: @Array<T>) -> Array<T> {
let mut result: Array<T> = array![];
let mut i = 0;
while i < self.len() {
if i % 2 == 0 {
result.append(*self.at(i));
}
i += 1;
};
result
}
}

pub type Position = (i16, i16);

#[generate_trait]
pub impl PositionImpl of PositionTrait {
fn manhattan(self: @Position) -> i16 {
let (x, y) = *self;
x.abs() + y.abs()
}
}

#[generate_trait]
pub impl AbsImpl of AbsTrait {
fn abs(self: @i16) -> i16 {
if *self < 0 {
*self * -1
} else {
*self
}
}
}

#[cfg(test)]
mod tests;
4 changes: 4 additions & 0 deletions exercises/practice/low-power-embedded-game/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
name = "low_power_embedded_game"
version = "0.1.0"
edition = "2023_11"
36 changes: 36 additions & 0 deletions exercises/practice/low-power-embedded-game/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#[generate_trait]
pub impl DivmodImpl of DivmodTrait {
fn divmod(self: @u16, divisor: u16) -> (u16, u16) {
panic!("implement `fn divmod`")
}
}

#[generate_trait]
pub impl EvensImpl<T, +Drop<T>, +Copy<T>> of EvensTrait<T> {
fn evens(self: @Array<T>) -> Array<T> {
panic!("implement `fn evens`")
}
}

pub type Position = (i16, i16);

#[generate_trait]
pub impl PositionImpl of PositionTrait {
fn manhattan(self: @Position) -> i16 {
panic!("implement `fn manhattan`")
}
}

#[generate_trait]
pub impl AbsImpl of AbsTrait {
fn abs(self: @i16) -> i16 {
if *self < 0 {
*self * -1
} else {
*self
}
}
}

#[cfg(test)]
mod tests;
Loading

0 comments on commit 0d6af91

Please sign in to comment.