GameState::to_string_id
This commit is contained in:
parent
6fe5a268da
commit
25a5470a12
8 changed files with 206 additions and 298 deletions
|
|
@ -5,11 +5,12 @@ use crate::player::{Color, Player, PlayerId};
|
|||
use crate::Error;
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
// use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::{fmt, vec};
|
||||
use std::{fmt, vec, str};
|
||||
|
||||
type TGPN = [u8];
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
|
||||
/// The different stages a game can be in. (not to be confused with the entire "GameState")
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
|
@ -74,6 +75,10 @@ impl GameState {
|
|||
GameState::default()
|
||||
}
|
||||
|
||||
fn add_player(&mut self, player_id: PlayerId, player: Player) {
|
||||
self.players.insert(player_id, player);
|
||||
}
|
||||
|
||||
/// Format to TGPN notation (Tables games position notation)
|
||||
// fn toTGPN(&self, f: &mut fmt::Formatter) -> TGPN {
|
||||
pub fn toTGPN(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
@ -83,52 +88,67 @@ impl GameState {
|
|||
}
|
||||
|
||||
/// Calculate game state id :
|
||||
pub fn to_string_id(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
pub fn to_string_id(&self) -> String {
|
||||
// Pieces placement -> 77 bits (24 + 23 + 30 max)
|
||||
let mut pos_bits = self.board.toGnupgPosId();
|
||||
let mut pos_bits = self.board.to_gnupg_pos_id();
|
||||
|
||||
// active player -> 1 bit
|
||||
// white : 0 (false)
|
||||
// black : 1 (true)
|
||||
pos_bits.push(
|
||||
self.who_plays()
|
||||
.map(|player| player.color == Color::Black)
|
||||
.unwrap_or(false), // White by default
|
||||
.map(|player| if player.color == Color::Black { '1'} else {'0'})
|
||||
.unwrap_or('0'), // White by default
|
||||
);
|
||||
|
||||
// step -> 2 bits
|
||||
// * roll dice
|
||||
// * mark points (jeton & fichet) & set bredouille markers (3rd jeton & pavillon)
|
||||
// * move pieces
|
||||
let mut step_bits = match self.turn_stage {
|
||||
TurnStage::RollDice => [false, false],
|
||||
TurnStage::MarkPoints => [false, true],
|
||||
TurnStage::Move => [true, false],
|
||||
let step_bits = match self.turn_stage {
|
||||
TurnStage::RollDice => "01",
|
||||
TurnStage::MarkPoints => "01",
|
||||
TurnStage::Move => "10",
|
||||
};
|
||||
pos_bits.append(&mut step_bits.into());
|
||||
pos_bits.push_str(step_bits);
|
||||
|
||||
// dice roll -> 6 bits
|
||||
let dice_bits = self.dices.to_bits_string();
|
||||
pos_bits.push_str(&dice_bits);
|
||||
|
||||
// dice roll -> 4 bits
|
||||
let mut dice_bits = match self.dices {
|
||||
TurnStage::RollDice => [false, false],
|
||||
TurnStage::MarkPoints => [false, true],
|
||||
TurnStage::Move => [true, false],
|
||||
};
|
||||
pos_bits.append(&mut step_bits.into());
|
||||
// points 10bits x2 joueurs = 20bits
|
||||
// * points -> 4bits
|
||||
// * trous -> 4bits
|
||||
// * bredouille possible 1bit
|
||||
// * grande bredouille possible 1bit
|
||||
let white_bits = self.get_white_player().unwrap().to_bits_string();
|
||||
let black_bits = self.get_black_player().unwrap().to_bits_string();
|
||||
pos_bits.push_str(&white_bits);
|
||||
pos_bits.push_str(&black_bits);
|
||||
|
||||
let mut s = String::new();
|
||||
// s.push_str(&format!("Dices: {:?}\n", self.dices));
|
||||
write!(f, "{}", s)
|
||||
pos_bits = format!("{:0>108}", pos_bits);
|
||||
// println!("{}", pos_bits);
|
||||
let pos_u8 = pos_bits
|
||||
.as_bytes().chunks(6)
|
||||
.map(|chunk| str::from_utf8(chunk).unwrap())
|
||||
.map(|chunk| u8::from_str_radix(chunk, 2).unwrap())
|
||||
.collect::<Vec<u8>>();
|
||||
general_purpose::STANDARD.encode(pos_u8)
|
||||
}
|
||||
|
||||
pub fn who_plays(&self) -> Option<&Player> {
|
||||
self.players.get(&self.active_player_id)
|
||||
}
|
||||
|
||||
pub fn get_white_player(&self) -> Option<&Player> {
|
||||
self.players
|
||||
.iter()
|
||||
.filter(|(_id, player)| player.color == Color::White)
|
||||
.map(|(_id, player)| player)
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn get_black_player(&self) -> Option<&Player> {
|
||||
self.players
|
||||
.iter()
|
||||
.filter(|(_id, player)| player.color == Color::Black)
|
||||
.map(|(_id, player)| player)
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn switch_active_player(&mut self) {
|
||||
let other_player_id = self
|
||||
.players
|
||||
|
|
@ -252,6 +272,10 @@ impl GameState {
|
|||
Player {
|
||||
name: name.to_string(),
|
||||
color,
|
||||
holes: 0,
|
||||
points: 0,
|
||||
can_bredouille: true,
|
||||
can_big_bredouille: true
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -322,10 +346,6 @@ pub enum GameEvent {
|
|||
|
||||
impl Roll for GameState {
|
||||
fn roll(&mut self) -> Result<&mut Self, Error> {
|
||||
if !self.dices.consumed.0 && !self.dices.consumed.1 {
|
||||
return Err(Error::MoveFirst);
|
||||
}
|
||||
|
||||
self.dices = self.dices.roll();
|
||||
if self.who_plays().is_none() {
|
||||
let diff = self.dices.values.0 - self.dices.values.1;
|
||||
|
|
@ -344,36 +364,20 @@ impl Move for GameState {
|
|||
// 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);
|
||||
}
|
||||
|
||||
// remove checker from old position
|
||||
self.board.set(player, from, -1)?;
|
||||
|
||||
// move checker to new position, in case it is reaching the off position, set it off
|
||||
let new_position = from as i8 - dice as i8;
|
||||
if new_position < 0 {
|
||||
self.board.set_off(player, 1)?;
|
||||
// self.board.set_off(player, 1)?;
|
||||
} else {
|
||||
self.board.set(player, new_position as usize, 1)?;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// self.board.set(player, new_position as usize, 1)?;
|
||||
}
|
||||
|
||||
// switch to other player if all dices have been consumed
|
||||
if self.dices.consumed.0 && self.dices.consumed.1 {
|
||||
self.switch_active_player();
|
||||
self.roll_first = true;
|
||||
}
|
||||
self.switch_active_player();
|
||||
self.roll_first = true;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
|
@ -403,4 +407,21 @@ impl Move for GameState {
|
|||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_string_id() {
|
||||
let mut state = GameState::default();
|
||||
state.add_player(1, Player::new("player1".into(), Color::White));
|
||||
state.add_player(2, Player::new("player2".into(), Color::Black));
|
||||
let string_id = state.to_string_id();
|
||||
// println!("string_id : {}", string_id);
|
||||
assert!(string_id == "Dz8+AAAAAT8/MAAAAAQAADAD");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue