feat: add starting the game

This commit is contained in:
2026-08-17 15:50:52 +02:00
parent a12f1fce4d
commit 4f024cae34
9 changed files with 190 additions and 46 deletions

View File

@@ -72,6 +72,12 @@ impl Board {
Board { board, size } Board { board, size }
} }
/// Returns the clone of the board inner vec. Meant for `BoardView`. This vec shouldn't be
/// manipulated by hand
pub fn get_board(&self) -> Vec<Field> {
self.board.clone()
}
/// For testing /// For testing
fn new_empty(player1: &mut Player, player2: &mut Player, size: Option<usize>) -> Self { fn new_empty(player1: &mut Player, player2: &mut Player, size: Option<usize>) -> Self {
debug!("Creating new empty board!"); debug!("Creating new empty board!");
@@ -527,8 +533,8 @@ mod tests {
fn new_board_test() { fn new_board_test() {
init_logger(); init_logger();
let mut player1: Player = Player::new("Morbius".to_string()); let mut player1: Player = Player::new("Morbius");
let mut player2: Player = Player::new("Milo".to_string()); let mut player2: Player = Player::new("Milo");
let test_board: Board = Board::new(&mut player1, &mut player2, None); let test_board: Board = Board::new(&mut player1, &mut player2, None);
let size: usize = test_board.size; let size: usize = test_board.size;
@@ -575,8 +581,8 @@ mod tests {
fn test_overloaded_operators() { fn test_overloaded_operators() {
init_logger(); init_logger();
let mut player1: Player = Player::new("Morbius".to_string()); let mut player1: Player = Player::new("Morbius");
let mut player2: Player = Player::new("Milo".to_string()); let mut player2: Player = Player::new("Milo");
let test_board: Board = Board::new(&mut player1, &mut player2, None); let test_board: Board = Board::new(&mut player1, &mut player2, None);
@@ -607,8 +613,8 @@ mod tests {
fn test_moving_pawn() { fn test_moving_pawn() {
init_logger(); init_logger();
let mut player1: Player = Player::new("Morbius".to_string()); let mut player1: Player = Player::new("Morbius");
let mut player2: Player = Player::new("Milo".to_string()); let mut player2: Player = Player::new("Milo");
let mut test_board: Board = Board::new(&mut player1, &mut player2, None); let mut test_board: Board = Board::new(&mut player1, &mut player2, None);
@@ -642,8 +648,8 @@ mod tests {
fn test_getting_pawn_moves() { fn test_getting_pawn_moves() {
init_logger(); init_logger();
let mut p1: Player = Player::new("Morbius".to_string()); let mut p1: Player = Player::new("Morbius");
let mut p2: Player = Player::new("Milo".to_string()); let mut p2: Player = Player::new("Milo");
let mut board: Board = Board::new_empty(&mut p1, &mut p2, None); let mut board: Board = Board::new_empty(&mut p1, &mut p2, None);
@@ -676,8 +682,8 @@ mod tests {
fn test_getting_player_pawns_positions() { fn test_getting_player_pawns_positions() {
init_logger(); init_logger();
let mut p1: Player = Player::new("Morbius".to_string()); let mut p1: Player = Player::new("Morbius");
let mut p2: Player = Player::new("Milo".to_string()); let mut p2: Player = Player::new("Milo");
let test_board: Board = Board::new(&mut p1, &mut p2, None); let test_board: Board = Board::new(&mut p1, &mut p2, None);
let expected_positions: Vec<Position> = (0..test_board.size) let expected_positions: Vec<Position> = (0..test_board.size)
@@ -707,8 +713,8 @@ mod tests {
fn test_is_movable() { fn test_is_movable() {
init_logger(); init_logger();
let mut p1: Player = Player::new("Morbius".to_string()); let mut p1: Player = Player::new("Morbius");
let mut p2: Player = Player::new("Milo".to_string()); let mut p2: Player = Player::new("Milo");
let test_board: Board = Board::new(&mut p1, &mut p2, None); let test_board: Board = Board::new(&mut p1, &mut p2, None);
assert!(test_board.is_pawn_movable(Position { row: 2, column: 1 }, &p1)); assert!(test_board.is_pawn_movable(Position { row: 2, column: 1 }, &p1));
@@ -726,8 +732,8 @@ mod tests {
fn test_getting_captures_path() { fn test_getting_captures_path() {
init_logger(); init_logger();
let mut p1 = Player::new("Morbius".to_string()); let mut p1 = Player::new("Morbius");
let mut p2 = Player::new("Milo".to_string()); let mut p2 = Player::new("Milo");
let mut board = Board::new_empty(&mut p1, &mut p2, None); let mut board = Board::new_empty(&mut p1, &mut p2, None);
@@ -758,8 +764,8 @@ mod tests {
fn test_capturing() { fn test_capturing() {
init_logger(); init_logger();
let mut p1 = Player::new("Morbius".to_string()); let mut p1 = Player::new("Morbius");
let mut p2 = Player::new("Milo".to_string()); let mut p2 = Player::new("Milo");
let mut board = Board::new_empty(&mut p1, &mut p2, None); let mut board = Board::new_empty(&mut p1, &mut p2, None);

View File

@@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
use crate::logic::board::{board::Board, field::Field, pawn::Pawn};
/// View of the board, without any methods. Meant for sending over network
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct BoardView {
pub board: Vec<Field>,
pub size: usize,
}
impl From<Board> for BoardView {
fn from(value: Board) -> Self {
Self {
board: value.get_board(),
size: value.size,
}
}
}

View File

@@ -1,3 +1,4 @@
pub mod board; pub mod board;
pub mod board_view;
mod field; mod field;
pub mod pawn; pub mod pawn;

View File

@@ -1,9 +1,11 @@
use log::{info, warn}; use log::{info, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::logic::{ use crate::logic::{
board::{ board::{
board::Board, board::Board,
board_view::BoardView,
pawn::{CapturePath, MovePath}, pawn::{CapturePath, MovePath},
}, },
math::position::Position, math::position::Position,
@@ -23,7 +25,7 @@ struct PlayerList {
} }
impl PlayerList { impl PlayerList {
pub fn new(player1_name: String, player2_name: String) -> Self { pub fn new(player1_name: &str, player2_name: &str) -> Self {
PlayerList { PlayerList {
players: [Player::new(player1_name), Player::new(player2_name)], players: [Player::new(player1_name), Player::new(player2_name)],
current_turn: 0, current_turn: 0,
@@ -50,7 +52,7 @@ pub struct GameMaster {
} }
impl GameMaster { impl GameMaster {
pub fn new(player1_name: String, player2_name: String) -> Self { pub fn new(player1_name: &str, player2_name: &str) -> Self {
let mut players: PlayerList = PlayerList::new(player1_name, player2_name); let mut players: PlayerList = PlayerList::new(player1_name, player2_name);
let [player1, player2] = &mut players.players; let [player1, player2] = &mut players.players;
@@ -59,6 +61,19 @@ impl GameMaster {
GameMaster { players, board } GameMaster { players, board }
} }
pub fn get_players_names_and_ids(&self) -> Vec<(String, Uuid)> {
let mut result: Vec<(String, Uuid)> = vec![];
for player in &self.players.players {
result.push((player.name.clone(), player.id));
}
result
}
pub fn get_board_view(&self) -> BoardView {
BoardView::from(self.board.clone())
}
pub fn get_current_player_captures(&self) -> Vec<CapturePath> { pub fn get_current_player_captures(&self) -> Vec<CapturePath> {
self.board self.board
.get_player_pawns_positions(self.players.get_current_turn()) .get_player_pawns_positions(self.players.get_current_turn())
@@ -167,7 +182,7 @@ mod tests {
use rand::seq::IndexedRandom; use rand::seq::IndexedRandom;
fn run_game() { fn run_game() {
let mut master: GameMaster = GameMaster::new("Morbius".to_string(), "Milo".to_string()); let mut master: GameMaster = GameMaster::new("Morbius", "Milo");
let mut turns = 0; let mut turns = 0;
let max_turns = 2000; let max_turns = 2000;

View File

@@ -12,9 +12,9 @@ pub struct Player {
} }
impl Player { impl Player {
pub fn new(name: String) -> Self { pub fn new(name: &str) -> Self {
Player { Player {
name, name: name.to_string(),
id: Uuid::new_v4(), id: Uuid::new_v4(),
end_row: None, end_row: None,
vertical_direction: None, vertical_direction: None,

View File

@@ -3,11 +3,15 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use log::{debug, error, info}; use log::{debug, error, info};
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
use crate::network::{ use crate::{
connection::{Connection, ConnectionType}, logic::board::board_view::BoardView,
message::{ network::{
ClientMessage::{self, SignalReadiness, TextMessage}, connection::{Connection, ConnectionType},
Message, ServerMessage, message::{
ClientMessage::{self, SignalReadiness, TextMessage},
Message, ServerMessage,
},
network_identity::NetworkIdentity,
}, },
}; };
@@ -21,6 +25,10 @@ pub struct Client {
conn_reciever_incoming: Option<Receiver<(SocketAddr, Message<ServerMessage>)>>, conn_reciever_incoming: Option<Receiver<(SocketAddr, Message<ServerMessage>)>>,
conn_sender_outgoing: Sender<Message<ClientMessage>>, conn_sender_outgoing: Sender<Message<ClientMessage>>,
settings: ClientSettings, settings: ClientSettings,
identity: Option<NetworkIdentity>,
board_view: Option<BoardView>,
update_reciever: Option<Receiver<Message<ServerMessage>>>,
} }
impl Client { impl Client {
@@ -51,6 +59,9 @@ impl Client {
conn_reciever_incoming: Some(conn_reciever), conn_reciever_incoming: Some(conn_reciever),
conn_sender_outgoing, conn_sender_outgoing,
settings, settings,
identity: None,
board_view: None,
update_reciever: None,
}) })
} }
@@ -64,6 +75,9 @@ impl Client {
let settings = self.settings.clone(); let settings = self.settings.clone();
let (update_sender, update_reciever) = mpsc::channel::<Message<ServerMessage>>(1024);
self.update_reciever = Some(update_reciever);
tokio::spawn(async move { tokio::spawn(async move {
loop { loop {
if let Some(oldest_msg) = conn_reciever_incoming.recv().await { if let Some(oldest_msg) = conn_reciever_incoming.recv().await {
@@ -79,13 +93,28 @@ impl Client {
let _ = sender.send(msg.unwrap()).await; let _ = sender.send(msg.unwrap()).await;
} }
ServerMessage::AcceptHandshake => { ServerMessage::AcceptHandshake => {
info!("Server accepted us! Yay :D"); info!("Server accepted us! Yay :D");
} }
ServerMessage::DeclineHandshake { reason } => { ServerMessage::DeclineHandshake { reason } => {
error!("Server declined connection: {}", reason); error!("Server declined connection: {}", reason);
break; break;
} }
ServerMessage::GameStart {
identity,
board_view,
} => {
info!("Server started the game!");
info!("Our identity is: {}", identity);
/*
self.board_view = Some(board_view);
self.identity = Some(identity);
*/
}
ServerMessage::AvailableCaptures { captures } => todo!(), ServerMessage::AvailableCaptures { captures } => todo!(),
ServerMessage::AvailableMoves { moves } => todo!(), ServerMessage::AvailableMoves { moves } => todo!(),
ServerMessage::BroadcastBoardState { board } => todo!(), ServerMessage::BroadcastBoardState { board } => todo!(),

View File

@@ -3,13 +3,17 @@ use std::net::SocketAddr;
use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde::{Deserialize, Serialize, de::DeserializeOwned};
use uuid::Uuid; use uuid::Uuid;
use crate::logic::{ use crate::{
board::{ logic::{
board::Board, board::{
pawn::{CapturePath, MovePath}, board::Board,
board_view::BoardView,
pawn::{CapturePath, MovePath},
},
game_master::GameResult,
math::position::Position,
}, },
game_master::GameResult, network::network_identity::NetworkIdentity,
math::position::Position,
}; };
use postcard::{from_bytes, to_allocvec}; use postcard::{from_bytes, to_allocvec};
@@ -38,15 +42,32 @@ where
pub enum ServerMessage { pub enum ServerMessage {
RequestHandshake, RequestHandshake,
AcceptHandshake, AcceptHandshake,
DeclineHandshake { reason: String }, DeclineHandshake {
reason: String,
},
AvailableCaptures { captures: Vec<CapturePath> }, GameStart {
AvailableMoves { moves: Vec<MovePath> }, identity: NetworkIdentity,
board_view: BoardView,
},
BroadcastBoardState { board: Board }, AvailableCaptures {
BroadcastCurrentTurn { active_player: Uuid }, captures: Vec<CapturePath>,
},
AvailableMoves {
moves: Vec<MovePath>,
},
GameEnd { result: GameResult }, BroadcastBoardState {
board: Board,
},
BroadcastCurrentTurn {
active_player: Uuid,
},
GameEnd {
result: GameResult,
},
} }
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]

View File

@@ -1,15 +1,23 @@
use std::fmt::Display;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use crate::network::message::{Message, ServerMessage}; use crate::network::message::{Message, ServerMessage};
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
pub struct NetworkIdentity { pub struct NetworkIdentity {
pub name: String, pub name: String,
pub id: uuid::Uuid, pub id: uuid::Uuid,
pub is_ready: bool, pub is_ready: bool,
} }
impl Display for NetworkIdentity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(Name: {}, id: {})", self.name, self.id)
}
}
impl Default for NetworkIdentity { impl Default for NetworkIdentity {
fn default() -> Self { fn default() -> Self {
Self { Self {

View File

@@ -7,9 +7,14 @@ use tokio::{
net::TcpListener, net::TcpListener,
sync::mpsc::{Receiver, Sender}, sync::mpsc::{Receiver, Sender},
}; };
use uuid::Uuid;
use crate::{ use crate::{
logic::{board::pawn::CapturePath, game_master::GameMaster, math::position::Position}, logic::{
board::{board_view::BoardView, pawn::CapturePath},
game_master::GameMaster,
math::position::Position,
},
network::{ network::{
connection::{Connection, ConnectionType}, connection::{Connection, ConnectionType},
message::{ message::{
@@ -83,7 +88,7 @@ impl Server {
.expect("Could not start the server"), .expect("Could not start the server"),
), // TODO! Handle this nicely, as this will ), // TODO! Handle this nicely, as this will
// crash gui app // crash gui app
connections: HashMap::new(), connections: HashMap::with_capacity(2),
sender, sender,
reciever, reciever,
welcoming_reciever: None, welcoming_reciever: None,
@@ -186,7 +191,7 @@ impl Server {
}, },
ClientMessage::SignalReadiness => { ClientMessage::SignalReadiness => {
self.handle_readiness(addr) self.handle_readiness(addr).await
}, },
ClientMessage::RequestCapture { capture_path } => { ClientMessage::RequestCapture { capture_path } => {
@@ -210,8 +215,48 @@ impl Server {
} }
} }
fn start_game(&self) { async fn start_game(&mut self) {
todo!(); info!("Starting the game!");
let mut names: Vec<String> = vec![];
for networkidentity in self.connections.values() {
if networkidentity.identity.is_ready {
names.push(networkidentity.identity.name.clone());
}
}
if names.len() != self.settings.max_connections {
error!("Tried starting the game, but could not gather enough ready player names");
return;
}
let game_master = GameMaster::new(&names[0], &names[1]);
let names_and_ids: Vec<(String, Uuid)> = game_master.get_players_names_and_ids();
let board_view: BoardView = game_master.get_board_view();
let mut msgs: Vec<(SocketAddr, Result<Message<ServerMessage>, postcard::Error>)> = vec![];
for (name, id) in names_and_ids {
for (addr, networkidentity) in self.connections.iter_mut() {
if networkidentity.identity.name == name {
networkidentity.identity.id = id;
msgs.push((
*addr,
Message::new(ServerMessage::GameStart {
identity: networkidentity.identity.clone(),
board_view: board_view.clone(),
}),
));
}
}
}
for (addr, msg) in msgs {
self.send_message(addr, msg).await;
}
self.game_master = Some(game_master);
} }
/* ======= Processing messages :D helper functions ======== */ /* ======= Processing messages :D helper functions ======== */
@@ -293,7 +338,7 @@ impl Server {
}; };
} }
fn handle_readiness(&mut self, addr: SocketAddr) { async fn handle_readiness(&mut self, addr: SocketAddr) {
self.state.ready_count += 1; self.state.ready_count += 1;
info!( info!(
"{} signaled readiness. Currently ready: {}/{}", "{} signaled readiness. Currently ready: {}/{}",
@@ -304,7 +349,7 @@ impl Server {
if self.state.ready_count == self.settings.max_connections { if self.state.ready_count == self.settings.max_connections {
info!("All players are ready! Changing state to ServerStage::Game"); info!("All players are ready! Changing state to ServerStage::Game");
self.state.stage = ServerStage::Game; self.state.stage = ServerStage::Game;
self.start_game(); self.start_game().await;
} }
} }