Skip to content

Commit ae3cb9b

Browse files
jonathanpallantlistochkin
authored andcommitted
Add Green and Yellow.
Copied over from the old repo, but revised to make it a bit simpler.
1 parent 524a2f7 commit ae3cb9b

File tree

10 files changed

+778
-0
lines changed

10 files changed

+778
-0
lines changed

exercise-book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [SimpleDB](./simple-db.md)
1313
- [Knowledge](./simple-db-knowledge.md)
1414
- [Step-by-Step Solution](./simple-db-solution.md)
15+
- [Green and Yellow game](./green-yellow-game.md)
1516

1617
# Applied Rust
1718

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Green and Yellow Game
2+
3+
In this assignment we will implement the game "Green and Yellow". It’s like Wordle, but with numerical digits instead of letters. But for legal reasons it’s also entirely unlike Wordle, nor remotely similar to the 1970’s board-game "Mastermind".
4+
5+
## After completing this exercise you are able to
6+
7+
- Work with rust slices and vectors
8+
- Accept input from stdin
9+
- Iterate through arrays and slices
10+
- Generate random numbers
11+
12+
## Prerequisites
13+
14+
For completing this exercise you need to have:
15+
16+
- basic Rust programming skills
17+
- the Rust Syntax Cheat Sheet
18+
19+
## Task
20+
21+
1. Create a new binary crate called `green-yellow`
22+
1. Copy all the test cases into into your `main.rs`
23+
1. Define a function `fn calc_green_and_yellow(guess: &[u8; 4], secret: &[u8; 4]) -> String` that implements the following rules:
24+
- Return a string containing four Unicode characters
25+
- For every item in guess, if `guess[i] == secret[i]`, then position `i` in the output String should be a green block (`🟩`)
26+
- Then, for every item in guess, if `guess[i]` is in `secret` somewhere, and hasn't already been matched, then position `i` in the output String should be a yellow block (`🟨`)
27+
- If any of the guesses do not appear in the secret, then that position in the output String should be a grey block (``)
28+
2. Ensure all the test cases pass!
29+
3. Write a main function that implements the following:
30+
- Generate 4 random digits - our 'secret'
31+
- Go into a loop
32+
- Read a string from *Standard In* and trim the whitespace off it
33+
- Parse that string into a guess, containing four digits (give an error if the user makes a mistake)
34+
- Run the calculation routine above and print the coloured blocks
35+
- Exit if all the blocks are green
36+
4. Play the game
37+
38+
If you need it, we have provided a [complete solution](../../exercise-solutions/green-yellow/src/bin/complete.rs) for this exercise.
39+
40+
Your test cases are:
41+
42+
```rust
43+
#[test]
44+
fn all_wrong() {
45+
assert_eq!(
46+
&calc_green_and_yellow(&[5, 6, 7, 8], &[1, 2, 3, 4]),
47+
"⬜⬜⬜⬜"
48+
);
49+
}
50+
51+
#[test]
52+
fn all_green() {
53+
assert_eq!(
54+
&calc_green_and_yellow(&[1, 2, 3, 4], &[1, 2, 3, 4]),
55+
"🟩🟩🟩🟩"
56+
);
57+
}
58+
59+
#[test]
60+
fn one_wrong() {
61+
assert_eq!(
62+
&calc_green_and_yellow(&[1, 2, 3, 5], &[1, 2, 3, 4]),
63+
"🟩🟩🟩⬜"
64+
);
65+
}
66+
67+
#[test]
68+
fn all_yellow() {
69+
assert_eq!(
70+
&calc_green_and_yellow(&[4, 3, 2, 1], &[1, 2, 3, 4]),
71+
"🟨🟨🟨🟨"
72+
);
73+
}
74+
75+
#[test]
76+
fn one_wrong_but_duplicate() {
77+
assert_eq!(
78+
&calc_green_and_yellow(&[1, 2, 3, 1], &[1, 2, 3, 4]),
79+
"🟩🟩🟩⬜"
80+
);
81+
}
82+
83+
#[test]
84+
fn one_right_others_duplicate() {
85+
assert_eq!(
86+
&calc_green_and_yellow(&[1, 1, 1, 1], &[1, 2, 3, 4]),
87+
"🟩⬜⬜⬜"
88+
);
89+
}
90+
91+
#[test]
92+
fn two_right_two_swapped() {
93+
assert_eq!(
94+
&calc_green_and_yellow(&[1, 2, 2, 2], &[2, 2, 2, 1]),
95+
"🟨🟩🟩🟨"
96+
);
97+
}
98+
99+
#[test]
100+
fn two_wrong_two_swapped() {
101+
assert_eq!(
102+
&calc_green_and_yellow(&[1, 3, 3, 2], &[2, 2, 2, 1]),
103+
"🟨⬜⬜🟨"
104+
);
105+
}
106+
107+
#[test]
108+
fn a_bit_of_everything() {
109+
assert_eq!(
110+
&calc_green_and_yellow(&[1, 9, 4, 3], &[1, 2, 3, 4]),
111+
"🟩⬜🟨🟨"
112+
);
113+
}
114+
```
115+
116+
## Knowledge
117+
118+
### Generating Random Numbers
119+
120+
There are no random number generators in the standard library - you have to use the `rand` crate.
121+
122+
You will need to change `Cargo.toml` to depend on the `rand` crate - we suggest version `0.8`.
123+
124+
You need a random number generator (call `rand::thread_rng()`), and using that you can generate a number out of a given range with `gen_range`. See <https://docs.rs/rand> for more details.
125+
126+
### Reading from the Console
127+
128+
You need to grab a standard input handle with `std::io::stdin()`. This implments the `std::io::Write` trait, so you can call `read_to_string(&mut some_string)` and get a line of text into your `some_string: String` variable.
129+
130+
### Parsing Strings into Integers
131+
132+
Strings have a `parse()` method, which returns a `Result`, because of course the user may not have typed in a proper digit. The `parse()` function works out what you are trying to create based on context - so if you want a `u8`, try `let x: u8 = my_str.parse().unwrap()`. Or you can say `let x = my_str.parse::<u8>().unwrap()`. Of course, try and do something better than unwrap!
133+
134+
## Step-by-Step-Solution
135+
136+
In general, we also recommend to use the Rust documentation to figure out things you are missing to familiarize yourself with it. If you ever feel completely stuck or that you haven’t understood something, please hail the trainers quickly.
137+
138+
### Step 1: New Project
139+
140+
Create a new binary Cargo project, check the build and see if it runs.
141+
142+
<details>
143+
<summary>Solution</summary>
144+
145+
```shell
146+
cargo new green-yellow
147+
cd fizzbuzz
148+
cargo run
149+
```
150+
151+
</details>
152+
153+
### Step 2: Generate some squares
154+
155+
Get `calc_green_and_yellow` to just generate grey blocks. We put them in an `Vec` first, as that's easier to index than a string.
156+
157+
Call the function from `main()` to avoid the warning about it being unused.
158+
159+
<details>
160+
<summary>Solution</summary>
161+
162+
```rust ignore
163+
{{#include ../../exercise-solutions/green-yellow/src/bin/step2.rs:3:7}}
164+
```
165+
166+
</details>
167+
168+
### Step 3: Check for green squares
169+
170+
You need to go through every pair of items in the input arrays and check if they are the same. If so, set the output square to be green.
171+
172+
<details>
173+
<summary>Solution</summary>
174+
175+
```rust ignore
176+
{{#include ../../exercise-solutions/green-yellow/src/bin/step3.rs:3:13}}
177+
```
178+
179+
</details>
180+
181+
### Step 4: Check for yellow squares
182+
183+
This gets a little more tricky.
184+
185+
We need to loop through every item in the guess array and compare it to every item in the secret array. But! We must make sure we ignore any values we already 'used up' when we produced the green squares.
186+
187+
Let's do this by copying the input, so we can make it mutable, and mark off any values used in the green-square-loop by setting them to zero.
188+
189+
<details>
190+
<summary>Solution</summary>
191+
192+
```rust ignore
193+
{{#include ../../exercise-solutions/green-yellow/src/bin/step4.rs:3:25}}
194+
```
195+
196+
</details>
197+
198+
### Step 5: Get some random numbers
199+
200+
Add `rand = "0.8"` to your Cargo.toml, and make a random number generator with `rand::thread_rng()` (Random Number Generator). You will also have to `use rand::Rng;` to bring the trait into scope.
201+
202+
Call `your_rng.gen_range()` in a loop.
203+
204+
<details>
205+
<summary>Solution</summary>
206+
207+
```rust ignore
208+
{{#include ../../exercise-solutions/green-yellow/src/bin/step5.rs:29:38}}
209+
```
210+
211+
</details>
212+
213+
### Step 6: Make the game loop
214+
215+
We a loop to handle each guess the user makes.
216+
217+
For each guess we need to read from Standard Input (using `std::io::stdin()` and its `read_line()`) method.
218+
219+
You will need to `trim` and then `split` the input, then `parse` each piece into a digit.
220+
221+
* If the digit doesn't parse, `continue` the loop.
222+
* If the digit parses but it out of range, `continue` the loop.
223+
* If you get the wrong number of digits, `continue` the loop.
224+
* If the guess matches the secret, then break out of the loop and congratulate the winner.
225+
* Otherwise run the guess through our calculation function and print the squares.
226+
227+
<details>
228+
<summary>Solution</summary>
229+
230+
```rust ignore
231+
{{#include ../../exercise-solutions/green-yellow/src/bin/step6.rs:41:72}}
232+
```
233+
234+
</details>

exercise-solutions/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"fizzbuzz",
55
"rustlatin/*",
66
"simple-db/*",
7+
"green-yellow",
78
"urls-match-result",
89
"shapes-part-1",
910
"shapes-part-2",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "green-yellow"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
rand = "0.8.5"

0 commit comments

Comments
 (0)