146 lines
4.6 KiB
Rust
146 lines
4.6 KiB
Rust
use axum::{
|
|
response::Html,
|
|
routing::{get, post},
|
|
Json, Router,
|
|
};
|
|
use once_cell::sync::Lazy;
|
|
use rand::{thread_rng, Rng};
|
|
use rand::seq::SliceRandom;
|
|
use serde::Deserialize;
|
|
use std::net::SocketAddr;
|
|
use tokio::sync::Mutex;
|
|
use log::debug;
|
|
use log::error;
|
|
use log::info;
|
|
use log::warn;
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
env_logger::init();
|
|
|
|
info!("let's go!");
|
|
// build our application with some routes
|
|
let app = Router::new()
|
|
.route("/", get(|| async { Html(INDEX_HTML.as_str()) }))
|
|
.route("/api", post(input));
|
|
|
|
// run it with hyper
|
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3067));
|
|
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
axum::serve(listener, app.into_make_service())
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
static INDEX_HTML: Lazy<String> = Lazy::new(|| {
|
|
let content = include_str!("index.html");
|
|
let options: String = everyone()
|
|
.into_iter()
|
|
.map(|person| format!(r#"<option value="{person}">{person}</option>"#))
|
|
.collect();
|
|
content.replace("__OPTIONS__", &options)
|
|
});
|
|
|
|
static STATE: Lazy<Mutex<State>> = Lazy::new(Mutex::default);
|
|
|
|
struct State {
|
|
participants: Vec<String>,
|
|
remaining: Vec<String>,
|
|
}
|
|
|
|
|
|
fn has_forbidden_adjacent(participants: &Vec<String>) -> bool {
|
|
let forbidden_pairs = vec![("Alice".into(), "Bob".into())];
|
|
// Check pairs of adjacent elements, including the circular pair (last, first)
|
|
((1..participants.len()).any(|i| {
|
|
match (participants[i - 1].as_str(), participants[i].as_str()) {
|
|
// Check if the pair is forbidden
|
|
(a, b) if forbidden_pairs.contains(&(a.to_string(), b.to_string())) ||
|
|
forbidden_pairs.contains(&(b.to_string(), a.to_string())) => {
|
|
info!("forbidden pair detected : {:?}/{:?} in {:?}", a, b, participants.join(" => "));
|
|
return true;
|
|
},
|
|
_ => false,
|
|
}
|
|
}
|
|
) || // Check the pair (last, first) for circular adjacency
|
|
match (participants.last().unwrap().as_str(), participants[0].as_str()) {
|
|
(a, b) if forbidden_pairs.contains(&(a.to_string(), b.to_string())) ||
|
|
forbidden_pairs.contains(&(b.to_string(), a.to_string())) => {
|
|
info!("forbidden pair detected : {:?}/{:?} in {:?}", a, b, participants.join(" => "));
|
|
return true;
|
|
},
|
|
_ => false,
|
|
}
|
|
)
|
|
}
|
|
|
|
fn everyone() -> Vec<String> {
|
|
vec!["Alice".into(), "Bob".into(), "Carol".into(), "Dave".into(), "Edgar".into(), "France".into()]
|
|
}
|
|
|
|
impl Default for State {
|
|
fn default() -> Self {
|
|
let mut p = everyone();
|
|
let mut tries = 0;
|
|
while tries < 100 {
|
|
p.shuffle(&mut thread_rng());
|
|
|
|
if !has_forbidden_adjacent(&p) {
|
|
info!("distribution : {:?}", p.join(" => "));
|
|
return Self {
|
|
participants: p,
|
|
remaining: everyone()
|
|
}
|
|
} else {
|
|
}
|
|
|
|
tries += 1;
|
|
}
|
|
|
|
eprintln!("ERROR (could not generate a correct list)");
|
|
std::process::exit(1)
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
struct Input {
|
|
person: String,
|
|
}
|
|
|
|
async fn input(Json(input): Json<Input>) -> String {
|
|
let mut state = STATE.lock().await;
|
|
|
|
if state.remaining.is_empty() {
|
|
return "... He mais tout le monde a déja pioché !!".into();
|
|
}
|
|
info!("joueurs qui restent : {:?}", state.remaining.join(","));
|
|
match state.remaining.iter().position(|p| input.person == *p).map(|e| state.remaining.remove(e)) {
|
|
Some(rem) => {
|
|
match state.participants.iter().position(|p| input.person == *p) {
|
|
Some(pos) => {
|
|
info!("joueur qui pioche : {:?}", state.participants[pos]);
|
|
return match state.participants.iter().nth(pos+1) {
|
|
Some(x) => {
|
|
info!("joueur qui suit : {:?}", x);
|
|
return x.to_string();
|
|
}
|
|
None => return match state.participants.first() {
|
|
Some(x) => {
|
|
info!("joueur qui suit : {:?}", x);
|
|
return x.to_string();
|
|
}
|
|
None => "ERROR".to_string()
|
|
}
|
|
}
|
|
}
|
|
None => "ERROR".to_string()
|
|
}
|
|
}
|
|
None => "... Hem mais tu as déja pioché !".to_string()
|
|
}
|
|
}
|