Skip to content

Commit

Permalink
✨ (concept) Add new concept exercise cars-assemble
Browse files Browse the repository at this point in the history
This is inspired by the same in csharp track. Provides a gentle introduction to variable assignment, if-statements and numbers.
  • Loading branch information
devkabiir committed May 1, 2023
1 parent 796f2ca commit ed689eb
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 0 deletions.
15 changes: 15 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@
},
"exercises": {
"concept": [
{
"slug": "cars-assemble",
"uuid": "1d0e6e2c-c319-48bc-8626-7b3c98473f42",
"name": "Cars Assemble",
"difficulty": 1,
"concepts": [
"if-statements",
"assignment",
"numbers"
],
"prerequisites": [
"booleans"
],
"status": "wip"
},
{
"slug": "lucians-luscious-lasagna",
"uuid": "29a2d3bd-eec8-454d-9dba-4b2d7d071925",
Expand Down
34 changes: 34 additions & 0 deletions exercises/concept/cars-assemble/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Hints

## General

- [Rust book][rust-book-data-types] on Integers and Floating point numbers.

## 1. Calculate the success rate

- Determining the success rate can be done through a [conditional statement][if-statement].

## 2. Calculate the production rate per second

- Use `.into()` to convert `i32` to `f64`.
- Use the `success_rate()` method you wrote earlier to determine the success rate.
- Rust does not allow for multiplication to be applied to two different number
types (such as an `i32` and a `f64`). Both must be of the same type.
- Numbers can be compared using the built-in comparision and equality operators.
Checkout [full list of operators][operators-list] supported by Rust.
## 3. Calculate the number of working items produced per second

- Use `.into()` to convert `i32` to `f64`.
- Rounding a float to a certain precision (here 1 decimal places) can be done using:
```rust
let my_float = 3.166;
let rounded = (my_float * 10.0).round() / 10.0;
// => 3.2
```
Read more on this [StackOverflow answer][stackoverflow-rounding].

[if-statement]: https://doc.rust-lang.org/rust-by-example/flow_control/if_else.html#ifelse
[stackoverflow-rounding]: https://stackoverflow.com/a/28656825
[operators-list]:https://doc.rust-lang.org/book/appendix-02-operators.html#operators
[rust-book-data-types]:
https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-types
51 changes: 51 additions & 0 deletions exercises/concept/cars-assemble/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Instructions

In this exercise you'll be writing code to analyze the production of an assembly line in a car factory.
The assembly line's speed can range from `0` (off) to `10` (maximum).

At its lowest speed (`1`), `221` cars are produced each hour. The production increases linearly with the speed.
So with the speed set to `4`, it should produce `4 * 221 = 884` cars per hour.
However, higher speeds increase the likelihood that faulty cars are produced, which then have to be discarded.

You have three tasks.

## 1. Calculate the success rate

Implement the `success_rate()` function to calculate the ratio of an item being
created without error for a given speed.
The following table shows how speed influences the success rate:

- `0`: 0% success rate.
- `1` to `4`: 100% success rate.
- `5` to `8`: 90% success rate.
- `9`: 80% success rate.
- `10`: 77% success rate.

```rust
success_rate(10)
// => 0.77
```

Note that the value returned is a `f64`.

## 2. Calculate the production rate per hour

Implement the `production_rate_per_hour()` function to calculate the assembly line's production rate per hour, taking into account its success rate:

```rust
production_rate_per_hour(6)
// => 1193.4
```

Note that the value returned is a `f64`.

## 3. Calculate the number of working items produced per minute

Implement the `working_items_per_minute()` function to calculate how many working cars are produced per minute:

```rust
working_items_per_minute(6)
// => 19
```

Note that the value returned is an `i32`.
180 changes: 180 additions & 0 deletions exercises/concept/cars-assemble/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Introduction

## Numbers

There are two different types of numbers in Rust:

- Integers: numbers with no digits behind the decimal separator (whole numbers). Examples are `-6`, `0`, `1`, `25`, `976` and `500000`.
- Floating-point numbers: numbers with zero or more digits behind the decimal separator. Examples are `-2.4`, `0.1`, `3.14`, `16.984025` and `1024.0`.

The two default numeric types in Rust are `i32` and `f64`. An `i32` is a 32-bit integer and a `f64` is a 64-bit floating-point number.

Arithmetic is done using the standard arithmetic operators. Numbers can be
compared using the standard numeric comparison operators and the equality (`==`)
and inequality (`!=`) operators.

## Assignment

The following syntax can be used to define a variable and assign a value to it.

```rust
let my_variable = 5;
```

The above defines a variable with value 5 which automatically makes its type as
`i32`. The value of this variable cannot be changed.
In Rust, `snake_case` is used to define the name of a variable.

To create a variable that can be assigned a different value one can use `mut`
keyword:

```rust
let mut my_variable = 5;
my_variable = 10;
```

In Rust, integers and floats are different types hence you cannot assign a
float to an integer variable and vice-versa. Not even if the values seem equal:

```rust
let my_int = 5;
let my_float = my_int; // => panic

let my_float2 = 5.0;
let my_int2 = my_float2; // => panic
```

### Constants
Like immutable variables, *constants* are values that are bound to a name and
are not allowed to change, but there are a few differences between constants
and variables.

- First, you aren’t allowed to use `mut` with constants.
- Constants aren’t just immutable by default—they’re always immutable.
- You declare constants using the `const` keyword instead of the `let` keyword, and the type of the value *must* be annotated.
- Constants can be declared in any scope, including the global scope, which makes them useful for values that many parts of code need to know about.
- The last difference is that constants may be set only to a constant expression, not the result of a value that could only be computed at runtime.

Here’s an example of a constant declaration:

```rust
const SECONDS_IN_A_MINUTE: u32 = 60;
```

In Rust, constants are defined using `CAPITAL_SNAKE_CASE`.

### Rust requires explicit conversion between types

Using the `.into()` method:

```rust
let my_int = 50;
let my_float: f64 = my_int.into(); // works as expected
```

Note that this requires specifying the variable type explicitly.

As an `i32` has less precision than a `f64`, converting from an `i32` to a `f64` is safe and lossless. However, converting from a `f64` to an `i32` could mean losing data.

## Numeric Operators

Rust supports various operators on integers and floats.

Take a look at the following table that illustrates how each operator behaves with integers and floats.

| Symbol | Integer | Floating Point |
|--------|-------------------------|----------------|
| `+` | Addition | Addition |
| `-` | Subtraction | Subtraction |
| `*` | Multiplication | Multiplication |
| `/` | Division* | Division |
| `%` | Remainder** | Remainder |

\* Integer division rounds towards zero.

\*\* Rust uses a remainder defined with [truncating division].
Given `remainder = dividend % divisor`, the remainder will have the same sign as the dividend.

Note that both operands of an operator must be of same type.
e.g. For `+` operator, an integer cannot be added to a float and vice-versa.

Following example demonstrates usage for these operators.

```rust
let result_1 = 3 + 6;
// => 9
let result_2 = 5.5 - 1.25;
// => 4.25
let result_3 = -5 * 14;
// => -70
let result_4 = 14 / 3;
// => 4
let result_5 = 100 % 7;
// => 2
```

## If Statements

In this exercise you must conditionally execute logic. The most common way to do this in Rust is by using an `if/else` statement:

`if` expressions start with the keyword `if`, followed by a condition. In this
case, the condition checks whether or not the variable `number` has a value less
than `5`.


```rust
let number = 3;

if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
```

Optionally, we can also include an `else` expression, which we chose to do here,
to give the program an alternative block of code to execute should the condition
evaluate to `false`.

If you don’t provide an `else` expression and the condition is `false`, the program will just skip the `if` block and move on to the next bit of code.

The condition of an `if` statement must be of type `bool`. Rust has no concept of
_truthy_ values.

```rust
let number = 3;

if number {
println!("number was three");
}
```

The `if` condition evaluates to a value of `3` this time, and Rust throws an
error:

```txt
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error
```

You can use multiple conditions by combining `if` and `else` in an `else if` expression. For example:

```rust
let x = 6;

if x == 5 {
// Execute logic if x equals 5
} else if x > 7 {
// Execute logic if x greater than 7
} else {
// Execute logic in all other cases
}
```


[truncating division]:
https://en.wikipedia.org/wiki/Modulo_operation#Variants_of_the_definition
8 changes: 8 additions & 0 deletions exercises/concept/cars-assemble/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
**/*.rs.bk

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
19 changes: 19 additions & 0 deletions exercises/concept/cars-assemble/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"blurb": "Learn about numbers by analyzing the production of an assembly line.",
"authors": [
"devkabiir"
],
"contributors": [],
"files": {
"solution": [
"src/lib.rs",
"Cargo.toml"
],
"test": [
"tests/cars-assemble.rs"
],
"exemplar": [
".meta/exemplar.rs"
]
}
}
26 changes: 26 additions & 0 deletions exercises/concept/cars-assemble/.meta/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Design

## Learning objectives

- Know of the existence of the two default number types, `i32` and `f64`.
- Understand that an `i32` represents whole numbers, and a `f64` represents floating-point numbers.
- Know of basic operators such as multiplication, comparison and equality.
- Know how to convert from one numeric type to another.
- Know how to conditionally execute code using an `if` statement.

## Out of scope

- Any other numeric types besides `i32` and `f64` (so no `float`, `byte`, etc.).
- Parsing a `string` to an `i32` or `f64`.
- Converting an `i32` or `f64` to a `string`.

## Concepts

- `integers`: know about `i32` and `f64` types
- `if-statements`: know how to conditionally execute code using an `if` statement.
- `math-operators`: know the math operators; know about precedence; know the role of parentheses
- `assignment`: know the syntax and behavior of assignment in OO languages

## Prerequisites

- `booleans`
33 changes: 33 additions & 0 deletions exercises/concept/cars-assemble/.meta/exemplar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const PRODUCTION_RATE_PER_HOUR_FOR_DEFAULT_SPEED: i32 = 221;

pub fn production_rate_per_hour(speed: i32) -> f64 {
let result = production_rate_per_hour_for_speed(speed) * success_rate(speed);

round_to_1_decimal(result)
}

fn round_to_1_decimal(input: f64) -> f64 {
(input * 10.0).round() / 10.0
}

fn production_rate_per_hour_for_speed(speed: i32) -> f64 {
(PRODUCTION_RATE_PER_HOUR_FOR_DEFAULT_SPEED * speed).into()
}

pub fn working_items_per_minute(speed: i32) -> i32 {
(production_rate_per_hour(speed) / 60.0).into()
}

pub fn success_rate(speed: i32) -> f64 {
if speed == 10 {
0.77
} else if speed == 9 {
0.8
} else if speed >= 5 {
0.9
} else if speed <= 0 {
0.0
} else {
1.0
}
}
4 changes: 4 additions & 0 deletions exercises/concept/cars-assemble/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
edition = "2021"
name = "cars_assemble"
version = "1.0.0"
11 changes: 11 additions & 0 deletions exercises/concept/cars-assemble/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub fn production_rate_per_hour(_speed: i32) -> f32 {
unimplemented!("Implement production_rate_per_hour")
}

pub fn working_items_per_minute(_speed: i32) -> i32 {
unimplemented!("Implement working_items_per_minute")
}

pub fn success_rate(_speed: i32) -> f32 {
unimplemented!("Implement success_rate")
}
Loading

0 comments on commit ed689eb

Please sign in to comment.