Created
May 26, 2026 10:14
-
-
Save jonathanpallant/1b7ef20d44f43f49e0a7d50bfe24f6fd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [package] | |
| name = "network-green-yellow" | |
| version = "0.1.0" | |
| edition = "2024" | |
| [dependencies] | |
| anyhow = "1.0.102" | |
| rand = "0.10.1" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! A library version of the Green and Yellow Game | |
| use rand::prelude::*; | |
| #[derive(Debug, Clone)] | |
| pub struct Game { | |
| secret: [u8; 4], | |
| } | |
| impl Game { | |
| /// Create a new game with a random number inside | |
| pub fn new() -> Game { | |
| let mut rng = rand::rng(); | |
| let possible_digits: Vec<u8> = (0..=9).collect(); | |
| let mut secret = [0u8; 4]; | |
| for digit in secret.iter_mut() { | |
| *digit = *possible_digits.choose(&mut rng).unwrap(); | |
| } | |
| Game { secret } | |
| } | |
| /// Make a guess and see how good it was | |
| pub fn calc_green_and_yellow(&self, guess: &[u8; 4]) -> String { | |
| let mut result = ["β¬"; 4]; | |
| let mut secret_handled = [false; 4]; | |
| for i in 0..guess.len() { | |
| if guess[i] == self.secret[i] { | |
| // that's a match | |
| result[i] = "π©"; | |
| // don't match this secret digit again | |
| secret_handled[i] = true; | |
| } | |
| } | |
| 'guess: for g_idx in 0..guess.len() { | |
| // only process guess digits we haven't already dealt with | |
| if result[g_idx] == "π©" { | |
| continue; | |
| } | |
| for s_idx in 0..self.secret.len() { | |
| // only process secret digits we haven't already dealt with | |
| if secret_handled[s_idx] { | |
| continue; | |
| } | |
| if guess[g_idx] == self.secret[s_idx] { | |
| // put a yellow block in for this guess | |
| result[g_idx] = "π¨"; | |
| // never match this secret digit again | |
| secret_handled[s_idx] = true; | |
| // stop comparing this guessed digit to any other secret digits | |
| continue 'guess; | |
| } | |
| } | |
| } | |
| result.join("") | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! Start a network server to play Green and Yellow | |
| use std::{io::prelude::*, sync::Mutex}; | |
| fn main() -> anyhow::Result<()> { | |
| let listener = std::net::TcpListener::bind("127.0.0.1:8000")?; | |
| let best_score_so_far = Mutex::new(u32::MAX); | |
| std::thread::scope(|s| { | |
| for maybe_stream in listener.incoming() { | |
| println!("New player! Starting game..."); | |
| let Ok(stream) = maybe_stream else { | |
| continue; | |
| }; | |
| s.spawn(|| { | |
| match handle_stream(stream, &best_score_so_far) { | |
| Ok(()) => { | |
| // cool | |
| } | |
| Err(e) => { | |
| eprintln!("Stream failed: {}", e); | |
| } | |
| } | |
| }); | |
| } | |
| }); | |
| Ok(()) | |
| } | |
| fn handle_stream(stream: std::net::TcpStream, locked_best_score_so_far: &Mutex<u32>) -> anyhow::Result<()> { | |
| writeln!(&stream, "Hello player")?; | |
| let game = network_green_yellow::Game::new(); | |
| // writeln!(&stream, "The solution is {:?}", game)?; | |
| let buf_reader = std::io::BufReader::new(&stream); | |
| let mut score = 0; | |
| let mut line_iter = buf_reader.lines(); | |
| 'guess_loop: loop { | |
| writeln!(&stream, "Please enter your guess:")?; | |
| let Some(line) = line_iter.next() else { | |
| println!("Ran out of lines?"); | |
| anyhow::bail!("Ran out of input"); | |
| }; | |
| let line = line?; | |
| writeln!(&stream, "You said: {line:?}")?; | |
| let mut guess = [0u8; 4]; | |
| for (part, guess_digit) in line.split_ascii_whitespace().zip(guess.iter_mut()) { | |
| let Ok(digit) = part.parse::<u8>() else { | |
| writeln!(&stream, "I don't like that line. Try again.")?; | |
| continue 'guess_loop; | |
| }; | |
| if digit > 9 { | |
| writeln!(&stream, "I don't like that line. Try again.")?; | |
| continue 'guess_loop; | |
| } | |
| *guess_digit = digit; | |
| } | |
| writeln!(&stream, "You guessed: {:?}", guess)?; | |
| score += 1; | |
| let blocks = game.calc_green_and_yellow(&guess); | |
| writeln!(&stream, "You got {}", blocks)?; | |
| if blocks == "π©π©π©π©" { | |
| let mut best_score_so_far = locked_best_score_so_far.lock().unwrap(); | |
| if score < *best_score_so_far { | |
| *best_score_so_far = score; | |
| writeln!( | |
| &stream, | |
| "Well done! You took {} attempts and got a new high score!", | |
| score | |
| )?; | |
| } else { | |
| writeln!( | |
| &stream, | |
| "Well done! You took {} attempts but that sucks. Try again.", | |
| score | |
| )?; | |
| } | |
| break; | |
| } | |
| } | |
| Ok(()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment