server (from tic-tac example)
This commit is contained in:
parent
7c4d0a19a4
commit
e9d4f04044
58
Cargo.lock
generated
58
Cargo.lock
generated
|
|
@ -223,6 +223,24 @@ dependencies = [
|
||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
|
|
@ -296,12 +314,43 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.190"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.190"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -314,6 +363,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trictrac"
|
name = "trictrac"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trictrac-client"
|
name = "trictrac-client"
|
||||||
|
|
@ -338,6 +390,12 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
store = { path = "../store" }
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
pico-args = "0.5.0"
|
pico-args = "0.5.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
use std::net::{SocketAddr, UdpSocket, IpAddr, Ipv4Addr};
|
use std::net::{SocketAddr, UdpSocket, IpAddr, Ipv4Addr};
|
||||||
use std::time::{Duration, Instant, SystemTime};
|
use std::time::{Duration, Instant, SystemTime};
|
||||||
|
use store::EndGameReason;
|
||||||
|
|
||||||
use renet::{
|
use renet::{
|
||||||
transport::{
|
transport::{
|
||||||
|
|
@ -45,13 +46,80 @@ fn main() {
|
||||||
while let Some(event) = server.get_event() {
|
while let Some(event) = server.get_event() {
|
||||||
match event {
|
match event {
|
||||||
ServerEvent::ClientConnected { client_id } => {
|
ServerEvent::ClientConnected { client_id } => {
|
||||||
|
let user_data = transport.user_data(client_id).unwrap();
|
||||||
info!("🎉 Client {} connected.", client_id);
|
info!("🎉 Client {} connected.", client_id);
|
||||||
|
|
||||||
|
// Tell the recently joined player about the other player
|
||||||
|
for (player_id, player) in game_state.players.iter() {
|
||||||
|
let event = store::GameEvent::PlayerJoined {
|
||||||
|
player_id: *player_id,
|
||||||
|
name: player.name.clone(),
|
||||||
|
};
|
||||||
|
server.send_message(client_id, 0, bincode::serialize(&event).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new player to the game
|
||||||
|
let event = store::GameEvent::PlayerJoined {
|
||||||
|
player_id: client_id,
|
||||||
|
name: name_from_user_data(&user_data),
|
||||||
|
};
|
||||||
|
game_state.consume(&event);
|
||||||
|
|
||||||
|
// Tell all players that a new player has joined
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
|
||||||
|
info!("Client {} connected.", id);
|
||||||
|
// In TicTacTussle the game can begin once two players has joined
|
||||||
|
if game_state.players.len() == 2 {
|
||||||
|
let event = store::GameEvent::BeginGame { goes_first: client_id };
|
||||||
|
game_state.consume(&event);
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
trace!("The game gas begun");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ServerEvent::ClientDisconnected { client_id, reason } => {
|
ServerEvent::ClientDisconnected { client_id, reason } => {
|
||||||
info!("👋 Client {} disconnected", client_id);
|
// First consume a disconnect event
|
||||||
|
let event = store::GameEvent::PlayerDisconnected { player_id: client_id };
|
||||||
|
game_state.consume(&event);
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
info!("Client {} disconnected", client_id);
|
||||||
|
|
||||||
|
// Then end the game, since tic tac toe can't go on with a single player
|
||||||
|
let event = store::GameEvent::EndGame {
|
||||||
|
reason: EndGameReason::PlayerLeft { player_id: client_id },
|
||||||
|
};
|
||||||
|
game_state.consume(&event);
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
|
||||||
|
// NOTE: Since we don't authenticate users we can't do any reconnection attempts.
|
||||||
|
// We simply have no way to know if the next user is the same as the one that disconnected.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::thread::sleep(Duration::from_millis(50));
|
|
||||||
|
// Receive GameEvents from clients. Broadcast valid events.
|
||||||
|
for client_id in server.clients_id().into_iter() {
|
||||||
|
while let Some(message) = server.receive_message(client_id, 0) {
|
||||||
|
if let Ok(event) = bincode::deserialize::<store::GameEvent>(&message) {
|
||||||
|
if game_state.validate(&event) {
|
||||||
|
game_state.consume(&event);
|
||||||
|
trace!("Player {} sent:\n\t{:#?}", client_id, event);
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
|
||||||
|
// Determine if a player has won the game
|
||||||
|
if let Some(winner) = game_state.determine_winner() {
|
||||||
|
let event = store::GameEvent::EndGame {
|
||||||
|
reason: store::EndGameReason::PlayerWon { winner },
|
||||||
|
};
|
||||||
|
server.broadcast_message(0, bincode::serialize(&event).unwrap());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Player {} sent invalid event:\n\t{:#?}", client_id, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.send_packets().unwrap();
|
||||||
|
thread::sleep(Duration::from_millis(50));}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::Player;
|
use crate::player::{Player, Color};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Represents the Backgammon board
|
/// Represents the Backgammon board
|
||||||
///
|
///
|
||||||
/// A Backgammon board consists of 24 fields, each of which can hold 0 or more checkers. In
|
/// A Tric-Trac board consists of 24 fields, each of which can hold 0 or more checkers.
|
||||||
/// addition there is a bar to hold checkers that have been hit and an off area to hold checkers
|
|
||||||
/// that have been removed from the board.
|
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn foo() {}
|
/// # fn foo() {}
|
||||||
|
|
@ -16,7 +14,7 @@ use serde::{Deserialize, Serialize};
|
||||||
/// // | X O | | O | +-------+
|
/// // | X O | | O | +-------+
|
||||||
/// // | X | | O |
|
/// // | X | | O |
|
||||||
/// // | X | | O |
|
/// // | X | | O |
|
||||||
/// // | |BAR| |
|
/// // | | | |
|
||||||
/// // | O | | X |
|
/// // | O | | X |
|
||||||
/// // | O | | X |
|
/// // | O | | X |
|
||||||
/// // | O X | | X | +-------+
|
/// // | O X | | X | +-------+
|
||||||
|
|
@ -64,7 +62,6 @@ impl Board {
|
||||||
|
|
||||||
BoardDisplay {
|
BoardDisplay {
|
||||||
board,
|
board,
|
||||||
bar: self.get_bar(),
|
|
||||||
off: self.get_off(),
|
off: self.get_off(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,32 +89,25 @@ impl Board {
|
||||||
return Err(Error::FieldBlocked);
|
return Err(Error::FieldBlocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
match player {
|
match player.color {
|
||||||
Player::Player0 => {
|
Color::White => {
|
||||||
let new = self.raw_board.0.board[field] as i8 + amount;
|
let new = self.raw_board.0.board[field] as i8 + amount;
|
||||||
if new < 0 {
|
if new < 0 {
|
||||||
return Err(Error::MoveInvalid);
|
return Err(Error::MoveInvalid);
|
||||||
}
|
}
|
||||||
self.raw_board.0.board[field] = new as u8;
|
self.raw_board.0.board[field] = new as u8;
|
||||||
|
|
||||||
// in case one opponent's checker is hit, move it to the bar
|
|
||||||
self.raw_board.1.bar += self.raw_board.1.board[23 - field];
|
|
||||||
self.raw_board.1.board[23 - field] = 0;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Player::Player1 => {
|
Color::Black => {
|
||||||
let new = self.raw_board.1.board[field] as i8 + amount;
|
let new = self.raw_board.1.board[field] as i8 + amount;
|
||||||
if new < 0 {
|
if new < 0 {
|
||||||
return Err(Error::MoveInvalid);
|
return Err(Error::MoveInvalid);
|
||||||
}
|
}
|
||||||
self.raw_board.1.board[field] = new as u8;
|
self.raw_board.1.board[field] = new as u8;
|
||||||
|
|
||||||
// in case one opponent's checker is hit, move it to the bar
|
|
||||||
self.raw_board.0.bar += self.raw_board.0.board[23 - field];
|
|
||||||
self.raw_board.0.board[23 - field] = 0;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Player::Nobody => Err(Error::PlayerInvalid),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,64 +117,38 @@ impl Board {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
match player {
|
match player.color {
|
||||||
Player::Player0 => {
|
Color::White => {
|
||||||
if self.raw_board.1.board[23 - field] > 1 {
|
if self.raw_board.1.board[23 - field] > 1 {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Player::Player1 => {
|
Color::Black => {
|
||||||
if self.raw_board.0.board[23 - field] > 1 {
|
if self.raw_board.0.board[23 - field] > 1 {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Player::Nobody => Err(Error::PlayerInvalid),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set checkers for a player on the bar. This method adds amount to the already existing
|
|
||||||
/// checkers there.
|
|
||||||
pub fn set_bar(&mut self, player: Player, amount: i8) -> Result<(), Error> {
|
|
||||||
match player {
|
|
||||||
Player::Player0 => {
|
|
||||||
let new = self.raw_board.0.bar as i8 + amount;
|
|
||||||
if new < 0 {
|
|
||||||
return Err(Error::MoveInvalid);
|
|
||||||
}
|
|
||||||
self.raw_board.0.bar = new as u8;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Player::Player1 => {
|
|
||||||
let new = self.raw_board.1.bar as i8 + amount;
|
|
||||||
if new < 0 {
|
|
||||||
return Err(Error::MoveInvalid);
|
|
||||||
}
|
|
||||||
self.raw_board.1.bar = new as u8;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Player::Nobody => Err(Error::PlayerInvalid),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set checkers for a player off the board. This method adds amount to the already existing
|
/// Set checkers for a player off the board. This method adds amount to the already existing
|
||||||
/// checkers there.
|
/// checkers there.
|
||||||
pub fn set_off(&mut self, player: Player, amount: u8) -> Result<(), Error> {
|
pub fn set_off(&mut self, player: Player, amount: u8) -> Result<(), Error> {
|
||||||
match player {
|
match player.color {
|
||||||
Player::Player0 => {
|
Color::White => {
|
||||||
let new = self.raw_board.0.off + amount;
|
let new = self.raw_board.0.off + amount;
|
||||||
self.raw_board.0.off = new;
|
self.raw_board.0.off = new;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Player::Player1 => {
|
Color::Black => {
|
||||||
let new = self.raw_board.1.off + amount;
|
let new = self.raw_board.1.off + amount;
|
||||||
self.raw_board.1.off = new;
|
self.raw_board.1.off = new;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Player::Nobody => Err(Error::PlayerInvalid),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +157,6 @@ impl Board {
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
struct PlayerBoard {
|
struct PlayerBoard {
|
||||||
board: [u8; 24],
|
board: [u8; 24],
|
||||||
bar: u8,
|
|
||||||
off: u8,
|
off: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +166,6 @@ impl Default for PlayerBoard {
|
||||||
board: [
|
board: [
|
||||||
0, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
0, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
||||||
],
|
],
|
||||||
bar: 0,
|
|
||||||
off: 0,
|
off: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,11 +178,6 @@ pub trait Move {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
/// Move a checker from bar
|
|
||||||
fn move_checker_from_bar(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// Move permitted
|
/// Move permitted
|
||||||
fn move_permitted(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error>
|
fn move_permitted(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error>
|
||||||
where
|
where
|
||||||
|
|
@ -243,7 +200,6 @@ mod tests {
|
||||||
PlayerBoard::default(),
|
PlayerBoard::default(),
|
||||||
PlayerBoard {
|
PlayerBoard {
|
||||||
board: [0, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,],
|
board: [0, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,],
|
||||||
bar: 0,
|
|
||||||
off: 0
|
off: 0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -258,18 +214,11 @@ mod tests {
|
||||||
board: [
|
board: [
|
||||||
-2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, -5, 5, 0, 0, 0, -3, 0, -5, 0, 0, 0, 0, 2,
|
-2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, -5, 5, 0, 0, 0, -3, 0, -5, 0, 0, 0, 0, 2,
|
||||||
],
|
],
|
||||||
bar: (0, 0),
|
|
||||||
off: (0, 0)
|
off: (0, 0)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_bar() {
|
|
||||||
let board = Board::new();
|
|
||||||
assert_eq!(board.get_bar(), (0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_off() {
|
fn get_off() {
|
||||||
let board = Board::new();
|
let board = Board::new();
|
||||||
|
|
@ -279,7 +228,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_player0() -> Result<(), Error> {
|
fn set_player0() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 1, 1)?;
|
let player = Player {name: "".into(), color: Color::White};
|
||||||
|
board.set(player, 1, 1)?;
|
||||||
assert_eq!(board.get().board[1], 1);
|
assert_eq!(board.get().board[1], 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -287,31 +237,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_player1() -> Result<(), Error> {
|
fn set_player1() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player1, 2, 1)?;
|
let player = Player {name: "".into(), color: Color::Black};
|
||||||
|
board.set(player, 2, 1)?;
|
||||||
assert_eq!(board.get().board[21], -1);
|
assert_eq!(board.get().board[21], -1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_player0_bar() -> Result<(), Error> {
|
|
||||||
let mut board = Board::new();
|
|
||||||
board.set_bar(Player::Player0, 1)?;
|
|
||||||
assert_eq!(board.get().bar.0, 1);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_player1_bar() -> Result<(), Error> {
|
|
||||||
let mut board = Board::new();
|
|
||||||
board.set_bar(Player::Player1, 1)?;
|
|
||||||
assert_eq!(board.get().bar.1, 1);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_player0_off() -> Result<(), Error> {
|
fn set_player0_off() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set_off(Player::Player0, 1)?;
|
let player = Player {name: "".into(), color: Color::White};
|
||||||
|
board.set_off(player, 1)?;
|
||||||
assert_eq!(board.get().off.0, 1);
|
assert_eq!(board.get().off.0, 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -319,7 +255,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_player1_off() -> Result<(), Error> {
|
fn set_player1_off() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set_off(Player::Player1, 1)?;
|
let player = Player {name: "".into(), color: Color::Black};
|
||||||
|
board.set_off(player, 1)?;
|
||||||
assert_eq!(board.get().off.1, 1);
|
assert_eq!(board.get().off.1, 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -327,108 +264,89 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_player1_off1() -> Result<(), Error> {
|
fn set_player1_off1() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set_off(Player::Player1, 1)?;
|
let player = Player {name: "".into(), color: Color::Black};
|
||||||
board.set_off(Player::Player1, 1)?;
|
board.set_off(player, 1)?;
|
||||||
|
board.set_off(player, 1)?;
|
||||||
assert_eq!(board.get().off.1, 2);
|
assert_eq!(board.get().off.1, 2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_invalid_player() {
|
|
||||||
let mut board = Board::new();
|
|
||||||
assert!(board.set(Player::Nobody, 0, 1).is_err());
|
|
||||||
assert!(board.set_bar(Player::Nobody, 1).is_err());
|
|
||||||
assert!(board.set_off(Player::Nobody, 1).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_player0() -> Result<(), Error> {
|
fn blocked_player0() -> Result<(), Error> {
|
||||||
let board = Board::new();
|
let board = Board::new();
|
||||||
assert!(board.blocked(Player::Player0, 0)?);
|
assert!(board.blocked(Player { name:"".into(), color: Color::White }, 0)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_player1() -> Result<(), Error> {
|
fn blocked_player1() -> Result<(), Error> {
|
||||||
let board = Board::new();
|
let board = Board::new();
|
||||||
assert!(board.blocked(Player::Player1, 0)?);
|
assert!(board.blocked(Player { name:"".into(), color: Color::Black }, 0)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_player0_a() -> Result<(), Error> {
|
fn blocked_player0_a() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player1, 1, 2)?;
|
board.set(Player { name:"".into(), color: Color::Black }, 1, 2)?;
|
||||||
assert!(board.blocked(Player::Player0, 22)?);
|
assert!(board.blocked(Player { name:"".into(), color: Color::White }, 22)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_player1_a() -> Result<(), Error> {
|
fn blocked_player1_a() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 1, 2)?;
|
board.set(Player { name:"".into(), color: Color::White }, 1, 2)?;
|
||||||
assert!(board.blocked(Player::Player1, 22)?);
|
assert!(board.blocked(Player { name:"".into(), color: Color::Black }, 22)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn blocked_invalid_player() {
|
|
||||||
let board = Board::new();
|
|
||||||
assert!(board.blocked(Player::Nobody, 0).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_invalid_field() {
|
fn blocked_invalid_field() {
|
||||||
let board = Board::new();
|
let board = Board::new();
|
||||||
assert!(board.blocked(Player::Player0, 24).is_err());
|
assert!(board.blocked(Player { name:"".into(), color: Color::White }, 24).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_1_checker_player0_a() -> Result<(), Error> {
|
fn set_field_with_1_checker_player0_a() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 1, 1)?;
|
board.set(Player { name:"".into(), color: Color::White }, 1, 1)?;
|
||||||
board.set(Player::Player1, 22, 1)?;
|
board.set(Player { name:"".into(), color: Color::Black }, 22, 1)?;
|
||||||
assert_eq!(board.get().board[1], -1);
|
assert_eq!(board.get().board[1], -1);
|
||||||
assert_eq!(board.get().bar.0, 1);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_1_checker_player0_b() -> Result<(), Error> {
|
fn set_field_with_1_checker_player0_b() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 1, 1)?;
|
board.set(Player { name:"".into(), color: Color::White }, 1, 1)?;
|
||||||
board.set_bar(Player::Player0, 5)?;
|
board.set(Player { name:"".into(), color: Color::Black }, 22, 1)?;
|
||||||
board.set(Player::Player1, 22, 1)?;
|
|
||||||
assert_eq!(board.get().board[1], -1);
|
assert_eq!(board.get().board[1], -1);
|
||||||
assert_eq!(board.get().bar.0, 6);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_1_checker_player1_a() -> Result<(), Error> {
|
fn set_field_with_1_checker_player1_a() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player1, 1, 1)?;
|
board.set(Player { name:"".into(), color: Color::Black }, 1, 1)?;
|
||||||
board.set(Player::Player0, 22, 1)?;
|
board.set(Player { name:"".into(), color: Color::White }, 22, 1)?;
|
||||||
assert_eq!(board.get().board[22], 1);
|
assert_eq!(board.get().board[22], 1);
|
||||||
assert_eq!(board.get().bar.1, 1);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_1_checker_player1_b() -> Result<(), Error> {
|
fn set_field_with_1_checker_player1_b() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player1, 1, 1)?;
|
board.set(Player { name:"".into(), color: Color::Black }, 1, 1)?;
|
||||||
board.set_bar(Player::Player1, 5)?;
|
board.set(Player { name:"".into(), color: Color::White }, 22, 1)?;
|
||||||
board.set(Player::Player0, 22, 1)?;
|
|
||||||
assert_eq!(board.get().board[22], 1);
|
assert_eq!(board.get().board[22], 1);
|
||||||
assert_eq!(board.get().bar.1, 6);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_2_checkers_player0_a() -> Result<(), Error> {
|
fn set_field_with_2_checkers_player0_a() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 23, 2)?;
|
board.set(Player { name:"".into(), color: Color::White }, 23, 2)?;
|
||||||
assert_eq!(board.get().board[23], 4);
|
assert_eq!(board.get().board[23], 4);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -436,7 +354,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_with_2_checkers_player0_b() -> Result<(), Error> {
|
fn set_field_with_2_checkers_player0_b() -> Result<(), Error> {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
board.set(Player::Player0, 23, -1)?;
|
board.set(Player { name:"".into(), color: Color::White }, 23, -1)?;
|
||||||
assert_eq!(board.get().board[23], 1);
|
assert_eq!(board.get().board[23], 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -444,24 +362,24 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_field_blocked() {
|
fn set_field_blocked() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
assert!(board.set(Player::Player0, 0, 2).is_err());
|
assert!(board.set(Player { name:"".into(), color: Color::White }, 0, 2).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_wrong_field1() {
|
fn set_wrong_field1() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
assert!(board.set(Player::Player0, 50, 2).is_err());
|
assert!(board.set(Player { name:"".into(), color: Color::White }, 50, 2).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_wrong_amount0() {
|
fn set_wrong_amount0() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
assert!(board.set(Player::Player0, 23, -3).is_err());
|
assert!(board.set(Player { name:"".into(), color: Color::White }, 23, -3).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_wrong_amount1() {
|
fn set_wrong_amount1() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
assert!(board.set(Player::Player1, 23, -3).is_err());
|
assert!(board.set(Player { name:"".into(), color: Color::Black }, 23, -3).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
//! # Play a TricTrac Game
|
//! # Play a TricTrac Game
|
||||||
use crate::CurrentPlayer;
|
use crate::player::{Color, Player, PlayerId};
|
||||||
use crate::{Player, PlayerId};
|
use crate::board::{Board, Move};
|
||||||
use crate::{Board, Move};
|
use crate::dice::{Dices, Roll};
|
||||||
use crate::{Dices, Roll};
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -36,7 +35,7 @@ impl fmt::Display for GameState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s.push_str(&format!("Dices: {:?}\n", self.dices));
|
s.push_str(&format!("Dices: {:?}\n", self.dices));
|
||||||
s.push_str(&format!("Who plays: {}\n", self.who_plays));
|
s.push_str(&format!("Who plays: {}\n", self.who_plays().map(|player| player.name ).unwrap_or("".to_string())));
|
||||||
s.push_str(&format!("Board: {:?}\n", self.board.get()));
|
s.push_str(&format!("Board: {:?}\n", self.board.get()));
|
||||||
write!(f, "{}", s)
|
write!(f, "{}", s)
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +61,32 @@ impl GameState {
|
||||||
GameState::default()
|
GameState::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn who_plays(&self) -> &Option<&Player> {
|
||||||
|
&self.players.get(&self.active_player_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_active_player(&mut self) {
|
||||||
|
let other_player_id = self.players.iter()
|
||||||
|
.filter(|(id, player)| **id != self.active_player_id )
|
||||||
|
.map(|(id, player)| *id )
|
||||||
|
.next();
|
||||||
|
self.active_player_id = other_player_id.unwrap_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn player_id_by_color(&self, color: Color) -> Option<&PlayerId> {
|
||||||
|
self.players.iter()
|
||||||
|
.filter(|(id, player)| player.color == color)
|
||||||
|
.map(|(id, player)| id )
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn player_id(&self, player: &Player) -> Option<&PlayerId> {
|
||||||
|
self.players.iter()
|
||||||
|
.filter(|(id, candidate)| player.color == candidate.color)
|
||||||
|
.map(|(id, candidate)| id )
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines whether an event is valid considering the current GameState
|
/// Determines whether an event is valid considering the current GameState
|
||||||
pub fn validate(&self, event: &GameEvent) -> bool {
|
pub fn validate(&self, event: &GameEvent) -> bool {
|
||||||
use GameEvent::*;
|
use GameEvent::*;
|
||||||
|
|
@ -109,7 +134,7 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Move { player_id, at } => {
|
Move { player_id, from, to } => {
|
||||||
// Check player exists
|
// Check player exists
|
||||||
if !self.players.contains_key(player_id) {
|
if !self.players.contains_key(player_id) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -120,7 +145,7 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the tile index is inside the board
|
// Check that the tile index is inside the board
|
||||||
if *at > 23 {
|
if *to > 23 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,10 +166,12 @@ impl GameState {
|
||||||
}
|
}
|
||||||
EndGame { reason: _ } => self.stage = Stage::Ended,
|
EndGame { reason: _ } => self.stage = Stage::Ended,
|
||||||
PlayerJoined { player_id, name } => {
|
PlayerJoined { player_id, name } => {
|
||||||
|
let color = if self.players.len() > 0 { Color::White } else { Color::Black };
|
||||||
self.players.insert(
|
self.players.insert(
|
||||||
*player_id,
|
*player_id,
|
||||||
Player {
|
Player {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
color
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -155,8 +182,8 @@ impl GameState {
|
||||||
}
|
}
|
||||||
Move { player_id, from, to } => {
|
Move { player_id, from, to } => {
|
||||||
let player = self.players.get(player_id).unwrap();
|
let player = self.players.get(player_id).unwrap();
|
||||||
self.board.set(player, at, 1);
|
self.board.set(*player, *from, 0 as i8);
|
||||||
self.board.set(player, at, 1);
|
self.board.set(*player, *to, 1 as i8);
|
||||||
self.active_player_id = self
|
self.active_player_id = self
|
||||||
.players
|
.players
|
||||||
.keys()
|
.keys()
|
||||||
|
|
@ -168,8 +195,13 @@ impl GameState {
|
||||||
|
|
||||||
self.history.push(valid_event.clone());
|
self.history.push(valid_event.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines if someone has won the game
|
||||||
|
pub fn determine_winner(&self) -> Option<PlayerId> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// The reasons why a game could end
|
/// The reasons why a game could end
|
||||||
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Deserialize)]
|
||||||
|
|
@ -188,11 +220,9 @@ pub enum GameEvent {
|
||||||
PlayerJoined { player_id: PlayerId, name: String },
|
PlayerJoined { player_id: PlayerId, name: String },
|
||||||
PlayerDisconnected { player_id: PlayerId },
|
PlayerDisconnected { player_id: PlayerId },
|
||||||
Roll { player_id: PlayerId },
|
Roll { player_id: PlayerId },
|
||||||
Move { player_id: PlayerId, at: usize },
|
Move { player_id: PlayerId, from: usize, to: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Roll for GameState {
|
impl Roll for GameState {
|
||||||
fn roll(&mut self) -> Result<&mut Self, Error> {
|
fn roll(&mut self) -> Result<&mut Self, Error> {
|
||||||
if !self.dices.consumed.0
|
if !self.dices.consumed.0
|
||||||
|
|
@ -202,18 +232,12 @@ impl Roll for GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dices = self.dices.roll();
|
self.dices = self.dices.roll();
|
||||||
if self.who_plays == Player::Nobody {
|
if self.who_plays().is_none() {
|
||||||
let diff = self.dices.values.0 - self.dices.values.1;
|
let diff = self.dices.values.0 - self.dices.values.1;
|
||||||
match diff {
|
let active_color = if diff < 0 { Color::Black } else { Color::White };
|
||||||
0 => {
|
let color_player_id = self.player_id_by_color(active_color);
|
||||||
self.who_plays = Player::Nobody;
|
if color_player_id.is_some(){
|
||||||
}
|
self.active_player_id = *color_player_id.unwrap();
|
||||||
_ if diff > 0 => {
|
|
||||||
self.who_plays = Player::Player0;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.who_plays = Player::Player1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
@ -254,36 +278,7 @@ impl Move for GameState {
|
||||||
if self.dices.consumed.0
|
if self.dices.consumed.0
|
||||||
&& self.dices.consumed.1
|
&& self.dices.consumed.1
|
||||||
{
|
{
|
||||||
self.who_plays = self.who_plays.other();
|
self.switch_active_player();
|
||||||
self.roll_first = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_checker_from_bar(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error> {
|
|
||||||
// check if move is permitted
|
|
||||||
let _ = self.move_permitted(player, dice)?;
|
|
||||||
|
|
||||||
// check if the dice value has been consumed
|
|
||||||
if (dice == self.dices.values.0 && self.dices.consumed.0)
|
|
||||||
|| (dice == self.dices.values.1 && self.dices.consumed.1)
|
|
||||||
{
|
|
||||||
return Err(Error::MoveInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set dice value to consumed
|
|
||||||
if dice == self.dices.values.0 && !self.dices.consumed.0 {
|
|
||||||
self.dices.consumed.0 = true;
|
|
||||||
} else if dice == self.dices.values.1 && !self.dices.consumed.1 {
|
|
||||||
self.dices.consumed.1 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch to other player if all dices have been consumed
|
|
||||||
if self.dices.consumed.0
|
|
||||||
&& self.dices.consumed.1
|
|
||||||
{
|
|
||||||
self.who_plays = self.who_plays.other();
|
|
||||||
self.roll_first = true;
|
self.roll_first = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,13 +287,14 @@ impl Move for GameState {
|
||||||
|
|
||||||
/// Implements checks to validate if the player is allowed to move
|
/// Implements checks to validate if the player is allowed to move
|
||||||
fn move_permitted(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error> {
|
fn move_permitted(&mut self, player: Player, dice: u8) -> Result<&mut Self, Error> {
|
||||||
|
let maybe_player_id = self.player_id(&player);
|
||||||
// check if player is allowed to move
|
// check if player is allowed to move
|
||||||
if player != self.who_plays {
|
if maybe_player_id != Some(&self.active_player_id) {
|
||||||
return Err(Error::NotYourTurn);
|
return Err(Error::NotYourTurn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if player is nobody, you can not play and have to roll first
|
// if player is nobody, you can not play and have to roll first
|
||||||
if self.who_plays == Player::Nobody {
|
if maybe_player_id.is_none() {
|
||||||
return Err(Error::RollFirst);
|
return Err(Error::RollFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,19 +311,3 @@ impl Move for GameState {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
// Test Display trait for Game
|
|
||||||
#[test]
|
|
||||||
fn test_display() {
|
|
||||||
let g = Game::new();
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", g),
|
|
||||||
"Rules: Points: 7\nDices: Dices { values: (0, 0), consumed: (false, false, false, false) }\nWho plays: Nobody\nBoard: BoardDisplay { board: [-2, 0, 0, 0, 0, 5, 0, 3, 0, 0, 0, -5, 5, 0, 0, 0, -3, 0, -5, 0, 0, 0, 0, 2], bar: (0, 0), off: (0, 0) }\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
mod game;
|
mod game;
|
||||||
pub use game::Game;
|
|
||||||
|
|
||||||
mod player;
|
mod player;
|
||||||
pub use player::Player;
|
pub use player::Player;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
|
mod board;
|
||||||
|
mod dice;
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ use std::fmt;
|
||||||
|
|
||||||
|
|
||||||
// This just makes it easier to dissern between a player id and any ol' u64
|
// This just makes it easier to dissern between a player id and any ol' u64
|
||||||
type PlayerId = u64;
|
pub type PlayerId = u64;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
White,
|
White,
|
||||||
Black,
|
Black,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue