docs: api docs for the challenges

This commit is contained in:
Christoph J. Scherr 2024-09-06 23:27:42 +02:00
parent bfa532cbd2
commit ca97fd7012
4 changed files with 79 additions and 7 deletions

View File

@ -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 async_trait::async_trait;
use libpt::log::{info, warn}; use libpt::log::{info, warn};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
@ -8,13 +13,12 @@ use crate::config::Config;
use crate::has_won; use crate::has_won;
use crate::vault::VaultRef; use crate::vault::VaultRef;
/// This struct holds the configuration and vault for the challenge.
pub struct C1 { pub struct C1 {
config: Config, config: Config,
vault: VaultRef, vault: VaultRef,
} }
impl C1 {}
#[async_trait] #[async_trait]
impl Challenge for C1 { impl Challenge for C1 {
fn new(config: Config, vault: VaultRef) -> Self { fn new(config: Config, vault: VaultRef) -> Self {

View File

@ -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 anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use libpt::log::{info, warn}; use libpt::log::{info, warn};
@ -9,12 +15,18 @@ use crate::config::Config;
use crate::has_won; use crate::has_won;
use crate::vault::VaultRef; use crate::vault::VaultRef;
/// This struct holds the configuration and vault for the challenge.
pub struct C2 { pub struct C2 {
config: Config, config: Config,
vault: VaultRef, vault: VaultRef,
} }
impl C2 { impl C2 {
/// Trigger the winning mechanism.
///
/// # Errors
///
/// Will error when writing or shutting down the [TcpStream] fails.
async fn win( async fn win(
vault: &VaultRef, vault: &VaultRef,
stream: &mut TcpStream, stream: &mut TcpStream,

View File

@ -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::fmt::Display;
use std::net::SocketAddr; use std::net::SocketAddr;
@ -15,8 +34,12 @@ use crate::config::Config;
use crate::has_won; use crate::has_won;
use crate::vault::VaultRef; use crate::vault::VaultRef;
const NEEDED_CORRECT: usize = 16; /// Amount of questions required in the time limit to win.
pub const NEEDED_CORRECT: usize = 16;
/// Amount of milliseconds allotted for each question, used to calculate the time limit.
pub const MILLIS_PER_QUESTION: u128 = 200;
/// Signifies the operations that the randomiser can come up with.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum Operation { enum Operation {
Add, Add,
@ -26,7 +49,10 @@ enum Operation {
} }
impl 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 { match self {
Self::Add => a.overflowing_add(b).0, Self::Add => a.overflowing_add(b).0,
Self::Sub => a.overflowing_sub(b).0, Self::Sub => a.overflowing_sub(b).0,
@ -63,13 +89,26 @@ impl Distribution<Operation> for Standard {
} }
} }
/// This struct holds the configuration and vault for the challenge.
pub struct C3 { pub struct C3 {
config: Config, config: Config,
vault: VaultRef, vault: VaultRef,
} }
impl C3 { 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 mut rng = rand::thread_rng();
let a: i32 = rng.gen_range(0i32..i16::MAX as i32); let a: i32 = rng.gen_range(0i32..i16::MAX as i32);
let b: i32 = rng.gen_range(0i32..i16::MAX as i32); let b: i32 = rng.gen_range(0i32..i16::MAX as i32);
@ -81,7 +120,20 @@ impl C3 {
(question, solution) (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
/// [randomly 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 mut stream = peer.0;
let addr = peer.1; let addr = peer.1;
let mut correct: usize = 0; let mut correct: usize = 0;
@ -98,7 +150,7 @@ impl C3 {
Self::win(vault, &mut stream, &addr).await?; Self::win(vault, &mut stream, &addr).await?;
break; 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; correct = 0;
debug!("{addr} was too slow, resetting"); debug!("{addr} was too slow, resetting");
stream_write stream_write

View File

@ -58,6 +58,10 @@ where
/// # Returns /// # Returns
/// ///
/// A result indicating whether the challenge was successful ended. /// 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<()>; async fn serve(self) -> anyhow::Result<()>;
} }