WIP: Add support for removing AIs
This commit is contained in:
parent
5f125dfb75
commit
12ce134047
5 changed files with 58 additions and 68 deletions
|
@ -66,7 +66,7 @@ enum RoomEvent {
|
||||||
PlayerJoined(Player),
|
PlayerJoined(Player),
|
||||||
PlayerLeft(Player),
|
PlayerLeft(Player),
|
||||||
AIJoined(Difficulty),
|
AIJoined(Difficulty),
|
||||||
//AILeft(Difficulty),
|
AILeft { index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Debug)]
|
#[derive(Clone, Serialize, Debug)]
|
||||||
|
@ -74,6 +74,7 @@ enum RoomEvent {
|
||||||
enum ServerToClientMessage {
|
enum ServerToClientMessage {
|
||||||
RoomChange { event: RoomEvent, info: PartyInfo },
|
RoomChange { event: RoomEvent, info: PartyInfo },
|
||||||
GameEvent { state: ApiState },
|
GameEvent { state: ApiState },
|
||||||
|
Invalid { reason: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -84,6 +85,7 @@ enum ClientToServerMessage {
|
||||||
StartGame,
|
StartGame,
|
||||||
GameMove,
|
GameMove,
|
||||||
AddAI { difficulty: Difficulty },
|
AddAI { difficulty: Difficulty },
|
||||||
|
RemoveAI { index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -99,6 +101,7 @@ async fn incoming_message_handler<E: std::fmt::Display>(
|
||||||
sender: &Sender<InnerRoomMessage>,
|
sender: &Sender<InnerRoomMessage>,
|
||||||
player: &Player,
|
player: &Player,
|
||||||
room: &Arc<RwLock<Room>>,
|
room: &Arc<RwLock<Room>>,
|
||||||
|
stream: &mut DuplexStream,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match message {
|
match message {
|
||||||
None => {
|
None => {
|
||||||
|
@ -184,6 +187,23 @@ async fn incoming_message_handler<E: std::fmt::Display>(
|
||||||
};
|
};
|
||||||
sender.send(InnerRoomMessage::PassThrough(event)).unwrap();
|
sender.send(InnerRoomMessage::PassThrough(event)).unwrap();
|
||||||
}
|
}
|
||||||
|
ClientToServerMessage::RemoveAI { index } => {
|
||||||
|
let mut room = room.write().await;
|
||||||
|
if index < room.party_info.ais.len() {
|
||||||
|
room.party_info.ais.remove(index);
|
||||||
|
let event = ServerToClientMessage::RoomChange {
|
||||||
|
event: RoomEvent::AILeft { index },
|
||||||
|
info: room.party_info.clone(),
|
||||||
|
};
|
||||||
|
sender.send(InnerRoomMessage::PassThrough(event)).unwrap();
|
||||||
|
} else {
|
||||||
|
let event = ServerToClientMessage::Invalid {
|
||||||
|
reason: format!("{index} is out of bounds"),
|
||||||
|
};
|
||||||
|
let event = serde_json::to_string(&event).unwrap();
|
||||||
|
let _ = stream.send(event.into()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -204,6 +224,7 @@ async fn outgoing_message_handler<E: std::fmt::Debug>(
|
||||||
stream: &mut DuplexStream,
|
stream: &mut DuplexStream,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let message = message.unwrap();
|
let message = message.unwrap();
|
||||||
|
println!("Inner room message - {:#?}", message);
|
||||||
let message = match message {
|
let message = match message {
|
||||||
InnerRoomMessage::PassThrough(event) => serde_json::to_string(&event).unwrap(),
|
InnerRoomMessage::PassThrough(event) => serde_json::to_string(&event).unwrap(),
|
||||||
InnerRoomMessage::GameEvent => {
|
InnerRoomMessage::GameEvent => {
|
||||||
|
@ -290,7 +311,7 @@ async fn chat(
|
||||||
// Rust formatter can't reach into this macro, hence we broke out the logic
|
// Rust formatter can't reach into this macro, hence we broke out the logic
|
||||||
// into sub-functions
|
// into sub-functions
|
||||||
message = incoming_message => {
|
message = incoming_message => {
|
||||||
if incoming_message_handler(message, &sender, &player, &room).await {
|
if incoming_message_handler(message, &sender, &player, &room, &mut stream).await {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -304,56 +325,6 @@ async fn chat(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
|
||||||
// ws.channel(move |mut stream| Box::pin(async move {
|
|
||||||
// let mut interval = interval(Duration::from_secs(10));
|
|
||||||
// while !stream.is_terminated(){ // always seems to return true?
|
|
||||||
// let ws_incoming = stream.next();
|
|
||||||
// let other_incoming = receiver.recv();
|
|
||||||
// let ping_tick = interval.tick();
|
|
||||||
//
|
|
||||||
// // pin_mut!(ws_incoming, other_incoming); // no clue what this does
|
|
||||||
//
|
|
||||||
// select! {
|
|
||||||
// message = ws_incoming => {
|
|
||||||
// if message.is_none() {
|
|
||||||
// println!("Websocket closed");
|
|
||||||
// return Ok(())
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// println!("websocket received a websocket message");
|
|
||||||
// let message = message.unwrap()?;
|
|
||||||
//
|
|
||||||
// if let ws::Message::Close(close_frame) = &message {
|
|
||||||
// println!("Received close message");
|
|
||||||
// println!("{close_frame:?}")
|
|
||||||
// } else if let ws::Message::Text(text) = &message {
|
|
||||||
// println!("Received text {text:?}");
|
|
||||||
// sender.send(text.to_string()).unwrap();
|
|
||||||
// } else {
|
|
||||||
// println!("Received non-text message: {message:?}")
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// message = other_incoming => {
|
|
||||||
// let message = message.unwrap();
|
|
||||||
// println!("Sending message \"{message}\" via websocket");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// let _ = stream.send(message.into()).await; // always seems to return Ok(()), even after a disconnection
|
|
||||||
// //println!("Message sent: {blat:?}");
|
|
||||||
// }
|
|
||||||
// _ = ping_tick => {
|
|
||||||
// println!("ping_tick");
|
|
||||||
// let message = Message::Ping(Vec::new());
|
|
||||||
// let _ = stream.send(message.into()).await;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Ok(())
|
|
||||||
//
|
|
||||||
// }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
|
|
|
@ -18,6 +18,16 @@ interface PartyInfo {
|
||||||
players: Player[]
|
players: Player[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LOGBASE = 10000;
|
||||||
|
function unprocessAIRandomness(processedAIRandomness: number): string {
|
||||||
|
const x = 100*(LOGBASE**processedAIRandomness -1) / (LOGBASE-1)
|
||||||
|
return x.toFixed(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function unprocessedAIProportion(processedProportion: number): string {
|
||||||
|
return (100 * (1-processedProportion)).toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
export function Menu(): React.JSX.Element {
|
export function Menu(): React.JSX.Element {
|
||||||
|
|
||||||
const [roomName, setRoomName] = useState<string>("");
|
const [roomName, setRoomName] = useState<string>("");
|
||||||
|
@ -31,8 +41,7 @@ export function Menu(): React.JSX.Element {
|
||||||
|
|
||||||
// Can change log scale to control shape of curve using following equation:
|
// Can change log scale to control shape of curve using following equation:
|
||||||
// aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1
|
// aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1
|
||||||
const logBase: number = 10000;
|
const processedAIRandomness = Math.log(1 + (LOGBASE - 1)*aiRandomness/100) / Math.log(LOGBASE);
|
||||||
const processedAIRandomness = Math.log(1 + (logBase - 1)*aiRandomness/100) / Math.log(logBase);
|
|
||||||
const processedProportionDictionary = 1.0 - proportionDictionary / 100;
|
const processedProportionDictionary = 1.0 - proportionDictionary / 100;
|
||||||
|
|
||||||
if(socket != null && partyInfo == null) {
|
if(socket != null && partyInfo == null) {
|
||||||
|
@ -42,8 +51,19 @@ export function Menu(): React.JSX.Element {
|
||||||
return <li key={x.id}>{x.name}</li>;
|
return <li key={x.id}>{x.name}</li>;
|
||||||
});
|
});
|
||||||
const ais = partyInfo.ais.map((x, i) => {
|
const ais = partyInfo.ais.map((x, i) => {
|
||||||
return <li key={i}>Proportion: {x.proportion} / Randomness: {x.randomness}</li>
|
return <li key={i}>
|
||||||
})
|
<span>Proportion: {unprocessedAIProportion(x.proportion)} / Randomness: {unprocessAIRandomness(x.randomness)}</span>
|
||||||
|
<button onClick={() => {
|
||||||
|
const event = {
|
||||||
|
type: "RemoveAI",
|
||||||
|
index: i
|
||||||
|
};
|
||||||
|
socket.send(JSON.stringify(event));
|
||||||
|
}
|
||||||
|
}>X
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
});
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<p>Connected to {roomName}</p>
|
<p>Connected to {roomName}</p>
|
||||||
|
|
|
@ -26,19 +26,19 @@ impl WasmAPI {
|
||||||
pub fn exchange(&mut self, selection: JsValue) -> JsValue {
|
pub fn exchange(&mut self, selection: JsValue) -> JsValue {
|
||||||
let selection: Vec<bool> = serde_wasm_bindgen::from_value(selection).unwrap();
|
let selection: Vec<bool> = serde_wasm_bindgen::from_value(selection).unwrap();
|
||||||
|
|
||||||
let result = self.0.exchange("Player".to_string(), selection);
|
let result = self.0.exchange("Player", selection);
|
||||||
|
|
||||||
serde_wasm_bindgen::to_value(&result).unwrap()
|
serde_wasm_bindgen::to_value(&result).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pass(&mut self) -> JsValue {
|
pub fn pass(&mut self) -> JsValue {
|
||||||
let result = self.0.pass("Player".to_string());
|
let result = self.0.pass("Player");
|
||||||
|
|
||||||
serde_wasm_bindgen::to_value(&result).unwrap()
|
serde_wasm_bindgen::to_value(&result).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&mut self) -> JsValue {
|
pub fn load(&mut self) -> JsValue {
|
||||||
let result = self.0.load("Player".to_string());
|
let result = self.0.load("Player");
|
||||||
serde_wasm_bindgen::to_value(&result).unwrap()
|
serde_wasm_bindgen::to_value(&result).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,13 +128,13 @@ impl APIGame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exchange(&mut self, player: String, tray_tiles: Vec<bool>) -> Result<ApiState, Error> {
|
pub fn exchange(&mut self, player: &str, tray_tiles: Vec<bool>) -> Result<ApiState, Error> {
|
||||||
self.player_exists_and_turn(&player)?;
|
self.player_exists_and_turn(player)?;
|
||||||
|
|
||||||
let (tray, turn_action, game_state) = self.0.exchange_tiles(tray_tiles)?;
|
let (tray, turn_action, game_state) = self.0.exchange_tiles(tray_tiles)?;
|
||||||
let update = Update {
|
let update = Update {
|
||||||
r#type: turn_action,
|
r#type: turn_action,
|
||||||
player,
|
player: player.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.1.push(update.clone());
|
self.1.push(update.clone());
|
||||||
|
@ -142,14 +142,14 @@ impl APIGame {
|
||||||
Ok(self.build_result(tray, Some(game_state), Some(update)))
|
Ok(self.build_result(tray, Some(game_state), Some(update)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pass(&mut self, player: String) -> Result<ApiState, Error> {
|
pub fn pass(&mut self, player: &str) -> Result<ApiState, Error> {
|
||||||
self.player_exists_and_turn(&player)?;
|
self.player_exists_and_turn(player)?;
|
||||||
|
|
||||||
let game_state = self.0.pass()?;
|
let game_state = self.0.pass()?;
|
||||||
let tray = self.0.player_states.get_tray(&player).unwrap().clone();
|
let tray = self.0.player_states.get_tray(player).unwrap().clone();
|
||||||
let update = Update {
|
let update = Update {
|
||||||
r#type: TurnAction::Pass,
|
r#type: TurnAction::Pass,
|
||||||
player,
|
player: player.to_string(),
|
||||||
};
|
};
|
||||||
self.1.push(update.clone());
|
self.1.push(update.clone());
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::player_interaction::ai::{Difficulty, AI};
|
||||||
use crate::player_interaction::Tray;
|
use crate::player_interaction::Tray;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use rand::rngs::SmallRng;
|
use rand::rngs::SmallRng;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::SeedableRng;
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
pub enum Player {
|
pub enum Player {
|
||||||
|
@ -204,7 +204,6 @@ pub struct Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
|
|
||||||
pub fn new_specific(
|
pub fn new_specific(
|
||||||
mut rng: SmallRng,
|
mut rng: SmallRng,
|
||||||
dictionary: DictionaryImpl,
|
dictionary: DictionaryImpl,
|
||||||
|
|
Loading…
Reference in a new issue