challenge: 12
This commit is contained in:
parent
2b614ee8c4
commit
81263f7803
@ -13,6 +13,7 @@ tokio = "1.28.2"
|
|||||||
cargo-manifest = { version = "0.17.0", optional = true }
|
cargo-manifest = { version = "0.17.0", optional = true }
|
||||||
serde_yml = { version = "0.0.12", optional = true }
|
serde_yml = { version = "0.0.12", optional = true }
|
||||||
toml = { version = "0.8.19", optional = true }
|
toml = { version = "0.8.19", optional = true }
|
||||||
|
rand = { version = "0.8.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum-test = "16.4.0"
|
axum-test = "16.4.0"
|
||||||
@ -20,7 +21,7 @@ axum-test = "16.4.0"
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
task1-9 = ["cargo-manifest", "serde_yml", "toml"]
|
task1-9 = ["cargo-manifest", "serde_yml", "toml"]
|
||||||
task12 = []
|
task12 = ["rand"]
|
||||||
task16 = []
|
task16 = []
|
||||||
task19 = []
|
task19 = []
|
||||||
task23 = []
|
task23 = []
|
||||||
|
22
src/lib.rs
22
src/lib.rs
@ -1,17 +1,20 @@
|
|||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
|
// use axum::routing::{get, post};
|
||||||
|
#[cfg(feature = "task12")]
|
||||||
|
use routes::{board, place, random_board, reset, Board};
|
||||||
#[cfg(feature = "task1-9")]
|
#[cfg(feature = "task1-9")]
|
||||||
use routes::{
|
use routes::{
|
||||||
hello_bird, hello_world, ipv4_dest, ipv4_key, ipv6_dest, ipv6_key, manifest, milk, minus_one,
|
hello_bird, hello_world, ipv4_dest, ipv4_key, ipv6_dest, ipv6_key, manifest, milk, minus_one,
|
||||||
refill, MilkFactory,
|
refill, MilkFactory,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub fn router() -> axum::Router {
|
pub fn router() -> axum::Router {
|
||||||
#[cfg(feature = "task1-9")]
|
use axum::{
|
||||||
use axum::routing::get;
|
routing::{get, post},
|
||||||
#[cfg(feature = "task1-9")]
|
Router,
|
||||||
use axum::routing::post;
|
};
|
||||||
use axum::Router;
|
|
||||||
|
|
||||||
#[cfg(feature = "task1-9")]
|
#[cfg(feature = "task1-9")]
|
||||||
let milk_factory = MilkFactory::new();
|
let milk_factory = MilkFactory::new();
|
||||||
@ -30,6 +33,13 @@ pub fn router() -> axum::Router {
|
|||||||
.with_state(milk_factory)
|
.with_state(milk_factory)
|
||||||
.route("/", get(hello_bird));
|
.route("/", get(hello_bird));
|
||||||
|
|
||||||
// #[cfg(feature="task12")]
|
#[cfg(feature = "task12")]
|
||||||
|
Router::new()
|
||||||
|
.route("/12/board", get(board))
|
||||||
|
.route("/12/reset", post(reset))
|
||||||
|
.route("/12/place/:team/:column", post(place))
|
||||||
|
.route("/12/random-board", get(random_board))
|
||||||
|
.with_state(Board::new());
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,38 @@
|
|||||||
#![cfg(feature = "task1-9")]
|
#[cfg(feature = "task12")]
|
||||||
|
mod task_twelve;
|
||||||
|
#[cfg(feature = "task12")]
|
||||||
|
pub use task_twelve::{board, game::Board, place, random_board, reset};
|
||||||
|
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod hello_bird;
|
mod hello_bird;
|
||||||
|
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod hello_world;
|
mod hello_world;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod minus_one;
|
mod minus_one;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod task_five;
|
mod task_five;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod task_nine;
|
mod task_nine;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
mod task_two;
|
mod task_two;
|
||||||
|
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use hello_bird::hello_bird;
|
pub use hello_bird::hello_bird;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use hello_world::hello_world;
|
pub use hello_world::hello_world;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use minus_one::minus_one;
|
pub use minus_one::minus_one;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_five::manifest;
|
pub use task_five::manifest;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_two::ipv4_dest;
|
pub use task_two::ipv4_dest;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_two::ipv4_key;
|
pub use task_two::ipv4_key;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_two::ipv6_dest;
|
pub use task_two::ipv6_dest;
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_two::ipv6_key;
|
pub use task_two::ipv6_key;
|
||||||
|
|
||||||
|
#[cfg(feature = "task1-9")]
|
||||||
pub use task_nine::{milk, refill, MilkFactory};
|
pub use task_nine::{milk, refill, MilkFactory};
|
||||||
|
38
src/routes/task_twelve/error.rs
Normal file
38
src/routes/task_twelve/error.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum GameError {
|
||||||
|
ColumnFull,
|
||||||
|
GameOver,
|
||||||
|
InvalidColumn,
|
||||||
|
InvalidTeam,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for GameError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::ColumnFull => write!(f, "Column is full"),
|
||||||
|
Self::InvalidColumn => write!(f, "Invalid column"),
|
||||||
|
Self::GameOver => write!(f, "Game is over"),
|
||||||
|
Self::InvalidTeam => write!(f, "Invalid team"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for GameError {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
match self {
|
||||||
|
Self::ColumnFull | Self::GameOver | Self::InvalidColumn => (
|
||||||
|
axum::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||||
|
self.to_string(),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
Self::InvalidTeam => {
|
||||||
|
(axum::http::StatusCode::BAD_REQUEST, self.to_string()).into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
196
src/routes/task_twelve/game/board.rs
Normal file
196
src/routes/task_twelve/game/board.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DEFAULT_SEED: u64 = 2024;
|
||||||
|
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||||
|
|
||||||
|
use super::{super::error::GameError, column::Column, Slot, EMPTY, WALL};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Board {
|
||||||
|
columns: Arc<Mutex<[[Option<Slot>; 4]; 4]>>,
|
||||||
|
seed: Arc<Mutex<StdRng>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
columns: Arc::new(Mutex::new([[None; 4]; 4])),
|
||||||
|
seed: Arc::new(Mutex::new(StdRng::seed_from_u64(DEFAULT_SEED))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{}\n",
|
||||||
|
self.to_string()
|
||||||
|
.split_terminator('\n')
|
||||||
|
.take(5)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_seed(&self) -> Arc<Mutex<StdRng>> {
|
||||||
|
self.seed.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random(&self, random: &mut StdRng) {
|
||||||
|
let mut columns: [[Option<Slot>; 4]; 4] = [[None; 4]; 4];
|
||||||
|
// let mut seed = self.seed.lock().unwrap();
|
||||||
|
for i in (0..4).rev() {
|
||||||
|
(0..4).for_each(|j| {
|
||||||
|
let random = random.gen::<bool>();
|
||||||
|
let slot = if random {
|
||||||
|
Some(Slot::Cookie)
|
||||||
|
} else {
|
||||||
|
Some(Slot::Milk)
|
||||||
|
};
|
||||||
|
columns[j][i] = slot;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// for column in &mut columns.iter_mut() {
|
||||||
|
// for slot in column.iter_mut() {
|
||||||
|
// let random = random.gen::<bool>();
|
||||||
|
// if random {
|
||||||
|
// *slot = Some(Slot::Cookie);
|
||||||
|
// } else {
|
||||||
|
// *slot = Some(Slot::Milk);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// drop(seed);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cols = self.columns.lock().unwrap();
|
||||||
|
*cols = columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_full(&self) -> bool {
|
||||||
|
let columns = self.columns.lock().unwrap();
|
||||||
|
columns
|
||||||
|
.iter()
|
||||||
|
.all(|column| column.iter().all(std::option::Option::is_some))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self) -> Option<Slot> {
|
||||||
|
let columns = self.columns.lock().unwrap();
|
||||||
|
|
||||||
|
// Check rows and columns
|
||||||
|
for i in 0..4 {
|
||||||
|
if columns[i][0].is_some()
|
||||||
|
&& columns[i][0] == columns[i][1]
|
||||||
|
&& columns[i][0] == columns[i][2]
|
||||||
|
&& columns[i][0] == columns[i][3]
|
||||||
|
{
|
||||||
|
return columns[i][0];
|
||||||
|
}
|
||||||
|
if columns[0][i].is_some()
|
||||||
|
&& columns[0][i] == columns[1][i]
|
||||||
|
&& columns[0][i] == columns[2][i]
|
||||||
|
&& columns[0][i] == columns[3][i]
|
||||||
|
{
|
||||||
|
return columns[0][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check diagonals
|
||||||
|
if columns[0][0].is_some()
|
||||||
|
&& columns[0][0] == columns[1][1]
|
||||||
|
&& columns[0][0] == columns[2][2]
|
||||||
|
&& columns[0][0] == columns[3][3]
|
||||||
|
{
|
||||||
|
return columns[0][0];
|
||||||
|
}
|
||||||
|
if columns[0][3].is_some()
|
||||||
|
&& columns[0][3] == columns[1][2]
|
||||||
|
&& columns[0][3] == columns[2][1]
|
||||||
|
&& columns[0][3] == columns[3][0]
|
||||||
|
{
|
||||||
|
return columns[0][3];
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn reset(&self) {
|
||||||
|
{
|
||||||
|
let mut columns = self.columns.lock().unwrap();
|
||||||
|
for i in 0..4 {
|
||||||
|
for j in 0..4 {
|
||||||
|
columns[i][j] = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut seed = self.seed.lock().unwrap();
|
||||||
|
*seed = StdRng::seed_from_u64(DEFAULT_SEED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn insert(&self, column: Column, slot: Slot) -> Result<(), GameError> {
|
||||||
|
if self.check().is_some() {
|
||||||
|
return Err(GameError::GameOver);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let column: usize = column.into();
|
||||||
|
let column = column - 1;
|
||||||
|
let mut columns = self.columns.lock().unwrap();
|
||||||
|
|
||||||
|
if column > columns.len() - 1 {
|
||||||
|
return Err(GameError::InvalidColumn);
|
||||||
|
}
|
||||||
|
for i in 0..4 {
|
||||||
|
if columns[column][i].is_none() {
|
||||||
|
columns[column][i] = Some(slot);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(columns);
|
||||||
|
}
|
||||||
|
Err(GameError::ColumnFull)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Board {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
{
|
||||||
|
let columns = self.columns.lock().unwrap();
|
||||||
|
for j in (0..4).rev() {
|
||||||
|
write!(f, "{WALL}")?;
|
||||||
|
for i in 0..4 {
|
||||||
|
match columns[i][j] {
|
||||||
|
Some(slot) => write!(f, "{slot}")?,
|
||||||
|
None => write!(f, "{EMPTY}")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f, "{WALL}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _ in 0..6 {
|
||||||
|
write!(f, "{WALL}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(f)?;
|
||||||
|
if let Some(winner) = self.check() {
|
||||||
|
writeln!(f, "{winner} wins!")?;
|
||||||
|
} else if self.is_full() {
|
||||||
|
writeln!(f, "No winner.")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for Board {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
(axum::http::StatusCode::OK, self.to_string()).into_response()
|
||||||
|
}
|
||||||
|
}
|
47
src/routes/task_twelve/game/column.rs
Normal file
47
src/routes/task_twelve/game/column.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use serde::de::{self, Deserializer, Visitor};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Column(usize);
|
||||||
|
|
||||||
|
impl Display for Column {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Column> for usize {
|
||||||
|
fn from(column: Column) -> Self {
|
||||||
|
column.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Column {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct ColumnVisitor;
|
||||||
|
|
||||||
|
impl Visitor<'_> for ColumnVisitor {
|
||||||
|
type Value = Column;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("an integer between 1 and 4")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
1..=4 => Ok(Column(value as usize)),
|
||||||
|
_ => Err(de::Error::custom("value must be between 1 and 4")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_u8(ColumnVisitor)
|
||||||
|
}
|
||||||
|
}
|
28
src/routes/task_twelve/game/mod.rs
Normal file
28
src/routes/task_twelve/game/mod.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
pub mod board;
|
||||||
|
pub mod column;
|
||||||
|
pub mod slot;
|
||||||
|
|
||||||
|
pub use board::Board;
|
||||||
|
pub use slot::Slot;
|
||||||
|
|
||||||
|
const EMPTY: &str = "⬛";
|
||||||
|
const COOKIE: &str = "🍪";
|
||||||
|
const MILK: &str = "🥛";
|
||||||
|
const WALL: &str = "⬜";
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use super::*;
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn test_board() {
|
||||||
|
// let board = Board::new();
|
||||||
|
// println!("{board}");
|
||||||
|
|
||||||
|
// for _ in 0..4 {
|
||||||
|
// assert!(board.insert(0, Slot::Milk).is_ok());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// println!("{board}");
|
||||||
|
// }
|
||||||
|
// }
|
34
src/routes/task_twelve/game/slot.rs
Normal file
34
src/routes/task_twelve/game/slot.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use super::{super::error::GameError, COOKIE, MILK};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum Slot {
|
||||||
|
Milk,
|
||||||
|
Cookie,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Slot {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Milk => write!(f, "{MILK}"),
|
||||||
|
Self::Cookie => write!(f, "{COOKIE}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Slot {
|
||||||
|
type Error = GameError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
"milk" => Ok(Self::Milk),
|
||||||
|
"cookie" => Ok(Self::Cookie),
|
||||||
|
_ => Err(GameError::InvalidTeam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/routes/task_twelve/mod.rs
Normal file
44
src/routes/task_twelve/mod.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::{Path, State},
|
||||||
|
response::IntoResponse,
|
||||||
|
};
|
||||||
|
use game::{column::Column, Board, Slot};
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod game;
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
pub async fn board(State(board): State<Board>) -> impl IntoResponse {
|
||||||
|
board.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
pub async fn reset(State(board): State<Board>) -> impl IntoResponse {
|
||||||
|
board.reset();
|
||||||
|
board.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
pub async fn place(
|
||||||
|
State(board): State<Board>,
|
||||||
|
Path((team, column)): Path<(Slot, Column)>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
board.insert(column, team).map_err(|err| match err {
|
||||||
|
error::GameError::ColumnFull | error::GameError::GameOver => (
|
||||||
|
axum::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||||
|
board.to_string(),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
e => e.into_response(),
|
||||||
|
})?;
|
||||||
|
Ok::<_, axum::response::Response>(board.into_response())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
pub async fn random_board(State(board): State<Board>) -> impl IntoResponse {
|
||||||
|
// let mut random = rand::rngs::StdRng::seed_from_u64(2024);
|
||||||
|
let seed = board.get_seed();
|
||||||
|
let mut random = seed.lock().unwrap();
|
||||||
|
board.random(&mut random);
|
||||||
|
board.display().into_response()
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#[cfg(feature = "task12")]
|
#![cfg(feature = "task12")]
|
||||||
|
#[cfg(test)]
|
||||||
mod task_twelve {
|
mod task_twelve {
|
||||||
use axum_test::TestServer;
|
use axum_test::TestServer;
|
||||||
use itsscb_shuttlings_cch24::router;
|
use itsscb_shuttlings_cch24::router;
|
||||||
@ -6,4 +7,214 @@ mod task_twelve {
|
|||||||
fn test_server() -> TestServer {
|
fn test_server() -> TestServer {
|
||||||
TestServer::new(router()).unwrap()
|
TestServer::new(router()).unwrap()
|
||||||
}
|
}
|
||||||
|
const EMPTY_BOARD: &str = "⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_task_1() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server.get("/12/board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_task_2() {
|
||||||
|
let server = test_server();
|
||||||
|
let response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜⬛⬛⬛⬛⬜
|
||||||
|
⬜🍪⬛⬛⬛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
|
||||||
|
let response = server.post("/12/place/cookie/1").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🍪⬛⬛⬛⬜
|
||||||
|
⬜🍪⬛⬛⬛⬜
|
||||||
|
⬜🍪⬛⬛⬛⬜
|
||||||
|
⬜🍪⬛⬛⬛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
🍪 wins!
|
||||||
|
";
|
||||||
|
|
||||||
|
let response = server.post("/12/place/cookie/1").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
|
||||||
|
let response = server.post("/12/place/cookie/1").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
let response = server.post("/12/place/cookie/1").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let response = server.post("/12/place/milk/2").await;
|
||||||
|
response.assert_status_service_unavailable();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let mut response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let want = "⬜🥛🍪🥛🍪⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
No winner.
|
||||||
|
";
|
||||||
|
|
||||||
|
for i in 1..5 {
|
||||||
|
for _ in 0..3 {
|
||||||
|
let slot = if i % 2 == 0 { "milk" } else { "cookie" };
|
||||||
|
response = server.post(&format!("/12/place/{slot}/{i}")).await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 1..5 {
|
||||||
|
let slot = if i % 2 == 0 { "cookie" } else { "milk" };
|
||||||
|
|
||||||
|
response = server.post(&format!("/12/place/{slot}/{i}")).await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
}
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let response = server.post("/12/place/milk/1").await;
|
||||||
|
response.assert_status_service_unavailable();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let want = "⬜⬛⬛⬛🍪⬜
|
||||||
|
⬜⬛⬛🍪🥛⬜
|
||||||
|
⬜⬛🍪🥛🥛⬜
|
||||||
|
⬜🍪🥛🥛🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
🍪 wins!
|
||||||
|
";
|
||||||
|
|
||||||
|
let response = server.post("/12/place/cookie/1").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
|
||||||
|
for i in 2..5 {
|
||||||
|
let response = server.post(&format!("/12/place/milk/{i}")).await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
}
|
||||||
|
let response = server.post("/12/place/cookie/2").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
|
||||||
|
for i in 3..5 {
|
||||||
|
let response = server.post(&format!("/12/place/milk/{i}")).await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
}
|
||||||
|
let response = server.post("/12/place/cookie/3").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
|
||||||
|
// for i in 4..5 {
|
||||||
|
let response = server.post("/12/place/milk/4").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
// }
|
||||||
|
let response = server.post("/12/place/cookie/4").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
|
||||||
|
response.assert_text(want);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_task_3() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🍪🍪🍪🍪⬜
|
||||||
|
⬜🥛🍪🍪🥛⬜
|
||||||
|
⬜🥛🥛🥛🥛⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
// dbg!(response.text());
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🍪🥛🍪🍪⬜
|
||||||
|
⬜🥛🍪🥛🍪⬜
|
||||||
|
⬜🥛🍪🍪🍪⬜
|
||||||
|
⬜🍪🥛🥛🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🍪🍪🥛🍪⬜
|
||||||
|
⬜🍪🥛🍪🍪⬜
|
||||||
|
⬜🥛🍪🍪🥛⬜
|
||||||
|
⬜🍪🥛🍪🍪⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🥛🍪🍪🥛⬜
|
||||||
|
⬜🥛🍪🍪🍪⬜
|
||||||
|
⬜🍪🥛🥛🥛⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🥛🥛🥛🍪⬜
|
||||||
|
⬜🍪🍪🍪🥛⬜
|
||||||
|
⬜🥛🍪🍪🥛⬜
|
||||||
|
⬜🍪🥛🥛🍪⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
|
||||||
|
let response = server.post("/12/reset").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(EMPTY_BOARD);
|
||||||
|
|
||||||
|
let want = "\
|
||||||
|
⬜🍪🍪🍪🍪⬜
|
||||||
|
⬜🥛🍪🍪🥛⬜
|
||||||
|
⬜🥛🥛🥛🥛⬜
|
||||||
|
⬜🍪🥛🍪🥛⬜
|
||||||
|
⬜⬜⬜⬜⬜⬜
|
||||||
|
";
|
||||||
|
let response = server.get("/12/random-board").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text(want);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user