diff --git a/src/challenge/c1.rs b/src/challenge/c1.rs index 4f5c8f9..f6737b4 100644 --- a/src/challenge/c1.rs +++ b/src/challenge/c1.rs @@ -1,3 +1,8 @@ +//! The first challenge, in which the contestant only needs to connect by TCP to be sent the secret. +//! +//! This challenge is designed to be simple and straightforward, requiring the contestant to only +//! establish a TCP connection to the server to receive the secret. + use async_trait::async_trait; use libpt::log::{info, warn}; use tokio::io::AsyncWriteExt; @@ -8,13 +13,12 @@ use crate::config::Config; use crate::has_won; use crate::vault::VaultRef; +/// This struct holds the configuration and vault for the challenge. pub struct C1 { config: Config, vault: VaultRef, } -impl C1 {} - #[async_trait] impl Challenge for C1 { fn new(config: Config, vault: VaultRef) -> Self { diff --git a/src/challenge/c2.rs b/src/challenge/c2.rs index bd85148..39aa30b 100644 --- a/src/challenge/c2.rs +++ b/src/challenge/c2.rs @@ -1,3 +1,9 @@ +//! The second challenge, in which the contestant needs to connect by TCP and write a the [u16] +//! `1337` to win. +//! +//! This challenge is designed to be simple and straightforward, but still requires some thinkering +//! for newbies, especially those that sill only think in text. + use anyhow::Result; use async_trait::async_trait; use libpt::log::{info, warn}; @@ -9,12 +15,18 @@ use crate::config::Config; use crate::has_won; use crate::vault::VaultRef; +/// This struct holds the configuration and vault for the challenge. pub struct C2 { config: Config, vault: VaultRef, } impl C2 { + /// Trigger the winning mechanism. + /// + /// # Errors + /// + /// Will error when writing or shutting down the [TcpStream] fails. async fn win( vault: &VaultRef, stream: &mut TcpStream, diff --git a/src/challenge/c3.rs b/src/challenge/c3.rs index 3ee28aa..f87fb7b 100644 --- a/src/challenge/c3.rs +++ b/src/challenge/c3.rs @@ -1,3 +1,22 @@ +//! The third challenge, in which the contestant must answer a series of math questions within a time limit. +//! +//! This challenge requires the contestant to establish a TCP connection to the server and answer a +//! series of math questions that are randomly generated by the server. The contestant must answer +//! [NEEDED_CORRECT] questions correctly within a time limit of [MILLIS_PER_QUESTION] milliseconds +//! per question. If the contestant fails to answer a question correctly or runs out of time, their +//! counter of correct answers will reset. +//! +//! The math questions are generated randomly and have the form "What is x \[op\] y?", where \[op\] is one +//! of the four basic arithmetic operations (+, -, *, /) and x and y are random integers between +//! 0 and 2^16-1. +//! +//! The contestant must send their answers as bytes (not text) over the TCP connection. +//! +//! It is expected that no human can calculate the questions fast enough, so scripting a math +//! solver is needed. +//! +//! See the [sample solution for c3](https://git.cscherr.de/PlexSheep/wooly-vault/src/branch/master/solutions/3.py). + use std::fmt::Display; use std::net::SocketAddr; @@ -15,8 +34,10 @@ use crate::config::Config; use crate::has_won; use crate::vault::VaultRef; -const NEEDED_CORRECT: usize = 16; +pub const NEEDED_CORRECT: usize = 16; +pub const MILLIS_PER_QUESTION: u128 = 200; +/// Signifies the operations that the randomizer can come up with. #[derive(Copy, Clone)] enum Operation { Add, @@ -26,7 +47,10 @@ enum Operation { } impl Operation { - pub(crate) fn calc(self, a: i32, b: i32) -> i32 { + /// Calculate the result of two [i32] with this [Operation]. + /// + /// This will not error from overflows. + pub fn calc(self, a: i32, b: i32) -> i32 { match self { Self::Add => a.overflowing_add(b).0, Self::Sub => a.overflowing_sub(b).0, @@ -63,13 +87,26 @@ impl Distribution for Standard { } } +/// This struct holds the configuration and vault for the challenge. pub struct C3 { config: Config, vault: VaultRef, } impl C3 { - fn mk_question() -> (String, String) { + /// Create a new randomized math question. + /// + /// Questions will always have the same structure: + /// + /// * "What is x  y?" + /// + /// $x$ and $y$ will always be a random integer between 0 and 2^16-1. + /// + /// # Returns + /// + /// This function returns both the question and the solution to that question as a tuple. The + /// first is the question and the second is the solution. + pub fn mk_question() -> (String, String) { let mut rng = rand::thread_rng(); let a: i32 = rng.gen_range(0i32..i16::MAX as i32); let b: i32 = rng.gen_range(0i32..i16::MAX as i32); @@ -81,7 +118,20 @@ impl C3 { (question, solution) } - async fn handler(peer: (TcpStream, SocketAddr), vault: &VaultRef) -> Result<()> { + /// Handle a singular contestant + /// + /// Messages sent by Wooly Vault [C3] will always be terminated with a newline. [C3] will + /// [randomlly generate questions](Self::mk_question) that the contestant needs to solve in + /// a given time limit. If the contestant fails to correctly answer [NEEDED_CORRECT] questions + /// in the given time limit, their counter of correct answer will reset. + /// + /// The time limit is [the amount of required correct answers](NEEDED_CORRECT) times [the + /// milliseconds per question](MILLIS_PER_QUESTION). + /// + /// # Errors + /// + /// This function will return an error if using the [TcpStream] fails. + pub async fn handler(peer: (TcpStream, SocketAddr), vault: &VaultRef) -> Result<()> { let mut stream = peer.0; let addr = peer.1; let mut correct: usize = 0; @@ -98,7 +148,7 @@ impl C3 { Self::win(vault, &mut stream, &addr).await?; break; } - if last_reset.elapsed().as_millis() > NEEDED_CORRECT as u128 * 200 { + if last_reset.elapsed().as_millis() > NEEDED_CORRECT as u128 * MILLIS_PER_QUESTION { correct = 0; debug!("{addr} was too slow, resetting"); stream_write diff --git a/src/challenge/mod.rs b/src/challenge/mod.rs index 18d2184..ed9f886 100644 --- a/src/challenge/mod.rs +++ b/src/challenge/mod.rs @@ -58,6 +58,10 @@ where /// # Returns /// /// A result indicating whether the challenge was successful ended. + /// + /// # Errors + /// + /// Will error when the challenge errors, for example when the network adress cannot be bound. async fn serve(self) -> anyhow::Result<()>; }