use serde::{Serialize, Deserialize};
use super::super::error::*;
use super::board::{Board, OverlaidWord, BoardWithOverlay};
use super::player::Player;
use super::direction::*;
use super::tile::{TileBag, Tile};
use std::fmt;
#[derive(Debug)]
pub struct PlayWordResult {
pub words: Vec<String>,
pub score: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Game {
pub board: Board,
pub players: Vec<Player>,
pub turn: u32,
pub tile_bag: TileBag,
pub has_word_been_played: bool,
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Board:")?;
self.board.fmt(f)?;
writeln!(f, "Players:")?;
for (idx, ref player) in self.players.iter().enumerate() {
writeln!(
f,
"{}: score: {}, pieces: {:?}",
idx, player.score, player.hand
)?;
}
writeln!(f, "Other:")?;
writeln!(
f,
"turn: {}, has_word_been_played:{}, tile_bag: {}",
self.turn, self.has_word_been_played, self.tile_bag
)?;
Ok(())
}
}
fn ensure_play_builds_on_other_words(
original_word: &str,
main_line_word: &OverlaidWord,
branching_words: &[OverlaidWord],
used_tiles: &[Tile],
) -> Result<()> {
if used_tiles.is_empty() {
Err(Error::NoLettersUsed.into())
} else if main_line_word.len() == original_word.len()
&& branching_words.is_empty()
&& used_tiles.len() == original_word.len()
{
Err(Error::WordDoesNotIntersect.into())
} else {
Ok(())
}
}
impl Game {
fn increment_turn(&mut self) {
self.turn += 1;
}
pub fn get_current_player_idx(&self) -> usize {
(self.turn as usize) % self.players.len()
}
pub fn get_current_player(&mut self) -> &mut Player {
let idx = self.get_current_player_idx();
&mut self.players[idx]
}
pub fn new(player_count: usize) -> Game {
let board = Board::new();
let mut tile_bag = TileBag::new();
let mut players = Vec::with_capacity(player_count);
for _ in 0..player_count {
players.push(Player::new(&mut tile_bag));
}
Game {
board,
players,
turn: 0,
tile_bag,
has_word_been_played: false,
}
}
pub fn play_word(&mut self, start: Point, dir: Direction, word: &str) -> Result<PlayWordResult> {
let mut game = self.clone();
let board_overlay =
BoardWithOverlay::try_overlay(game.board.clone(), start, dir, word)?;
let (main_line_word, branching_words) = board_overlay.get_formed_words();
let needed_tiles = board_overlay.get_overlaid_letters();
if !game.has_word_been_played {
main_line_word.ensure_word_covering_starting_spot()?;
game.has_word_been_played = true;
} else {
ensure_play_builds_on_other_words(
word,
&main_line_word,
&branching_words,
&needed_tiles,
)?;
}
let mut total_score = 0u32;
let mut total_formed_words = Vec::<String>::new();
let (word, score) = main_line_word.calculate_word_and_score()?;
total_formed_words.push(word);
total_score += score;
for branching_word in branching_words.iter() {
let (word, score) = branching_word.calculate_word_and_score()?;
total_formed_words.push(word);
total_score += score;
}
let new_tiles = game.tile_bag.draw_upto(needed_tiles.len());
let player = game.get_current_player();
player.remove_tiles_from_hand(&needed_tiles)?;
player.add_tiles_to_hand(new_tiles);
player.add_score(total_score);
game.board = board_overlay.apply_to_board();
game.increment_turn();
*self = game;
Ok(PlayWordResult{words: total_formed_words, score: total_score})
}
pub fn serialize(&self) -> String {
serde_json::to_string(self).unwrap()
}
pub fn from_serialized(serialized: &str) -> serde_json::Result<Self> {
serde_json::from_str(serialized)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn player_count() {
assert_eq!(Game::new(4).players.len(), 4);
assert_eq!(Game::new(2).players.len(), 2);
}
#[test]
fn board_size() {
assert_eq!(Game::new(1).board.cells.len(), 225);
}
#[test]
fn tile_bag_different() {
assert_ne!(Game::new(1).tile_bag.tiles, Game::new(1).tile_bag.tiles);
}
#[test]
fn serialization() {
let game = Game::new(2);
let serialized = game.serialize();
let deserialized = Game::from_serialized(&serialized).unwrap();
assert_eq!(game.players.len(), deserialized.players.len());
}
}