use crate::core::{Board, BoardSize, GameMode, Piece, Player};
use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct PlayerScore {
pub pieces: Vec<Piece>,
pub total: i32,
}
impl PlayerScore {
pub fn new() -> Self {
Self {
pieces: Vec::new(),
total: 0,
}
}
pub fn add_piece(&mut self, piece: Piece) {
if let Piece::Number(value) = piece {
self.total += value;
}
self.pieces.push(piece);
}
}
#[derive(Debug, Clone)]
pub struct GameSession {
pub board: Board,
pub current_player: Player,
pub scores: HashMap<Player, PlayerScore>,
pub round: usize,
pub total_scores: HashMap<Player, i32>,
pub game_mode: GameMode,
pub players: Vec<Player>,
}
impl GameSession {
pub fn new(size: BoardSize, game_mode: GameMode) -> Self {
let mut scores = HashMap::new();
let mut total_scores = HashMap::new();
let players = match game_mode {
GameMode::TwoPlayers => vec![Player::First, Player::Second],
GameMode::FourPlayers => {
vec![Player::First, Player::Second, Player::Third, Player::Fourth]
}
};
for player in &players {
scores.insert(*player, PlayerScore::new());
total_scores.insert(*player, 0);
}
Self {
board: Board::new(size),
current_player: Player::First,
scores,
round: 1,
total_scores,
game_mode,
players,
}
}
pub fn new_with_board(board: Board, game_mode: GameMode) -> Self {
let mut scores = HashMap::new();
let mut total_scores = HashMap::new();
let players = match game_mode {
GameMode::TwoPlayers => vec![Player::First, Player::Second],
GameMode::FourPlayers => {
vec![Player::First, Player::Second, Player::Third, Player::Fourth]
}
};
for player in &players {
scores.insert(*player, PlayerScore::new());
total_scores.insert(*player, 0);
}
Self {
board,
current_player: Player::First,
scores,
round: 1,
total_scores,
game_mode,
players,
}
}
pub fn process_move(&mut self, target: (usize, usize)) -> Result<(), String> {
let result = self.board.make_move(self.current_player, target);
match result {
Ok(piece) => {
if let Piece::Number(_) = piece {
self.scores
.get_mut(&self.current_player)
.unwrap()
.add_piece(piece);
}
self.current_player = self.current_player.next_for_mode(self.game_mode);
Ok(())
}
Err(e) => Err(e),
}
}
pub fn is_round_over(&self) -> bool {
self.board.is_game_over()
}
pub fn get_round_winner(&self) -> Option<Player> {
if !self.is_round_over() {
return None;
}
let mut highest_score = i32::MIN;
let mut winner: Option<Player> = None;
let mut is_tie = false;
for player in &self.players {
let score = self.scores.get(player).unwrap().total;
match score.cmp(&highest_score) {
std::cmp::Ordering::Greater => {
highest_score = score;
winner = Some(*player);
is_tie = false;
}
std::cmp::Ordering::Equal => {
is_tie = true;
}
std::cmp::Ordering::Less => {}
}
}
if is_tie {
None } else {
winner
}
}
pub fn start_next_round(&mut self) {
for player in &self.players {
let round_score = self.scores.get(player).unwrap().total;
*self.total_scores.get_mut(player).unwrap() += round_score;
}
self.board = Board::new(self.board.size);
for player in &self.players {
self.scores.insert(*player, PlayerScore::new());
}
self.round += 1;
if self.round > 1 {
let index = (self.round - 1) % self.players.len();
self.current_player = self.players[index];
}
}
pub fn get_overall_winner(&self) -> Option<Player> {
let mut highest_total = i32::MIN;
let mut winner: Option<Player> = None;
let mut is_tie = false;
for player in &self.players {
let total = *self.total_scores.get(player).unwrap();
match total.cmp(&highest_total) {
std::cmp::Ordering::Greater => {
highest_total = total;
winner = Some(*player);
is_tie = false;
}
std::cmp::Ordering::Equal => {
is_tie = true;
}
std::cmp::Ordering::Less => {}
}
}
if is_tie {
None } else {
winner
}
}
pub fn get_player_name(&self, player: Player) -> String {
match self.game_mode {
GameMode::TwoPlayers => {
match player {
Player::First => "プレイヤー1 (横)".to_string(),
Player::Second => "プレイヤー2 (縦)".to_string(),
_ => format!("{:?}", player), }
}
GameMode::FourPlayers => match player {
Player::First => "プレイヤー1 (横)".to_string(),
Player::Second => "プレイヤー2 (縦)".to_string(),
Player::Third => "プレイヤー3 (横)".to_string(),
Player::Fourth => "プレイヤー4 (縦)".to_string(),
},
}
}
}
#[derive(Debug, Clone)]
pub enum GameEvent {
GameStarted,
RoundStarted(usize),
MoveMade(Player, (usize, usize), Piece),
InvalidMove(Player, (usize, usize), String),
RoundEnded(Option<Player>, HashMap<Player, i32>),
GameEnded(Option<Player>, HashMap<Player, i32>),
}
pub trait GameEventListener {
fn on_event(&mut self, event: GameEvent);
}
pub struct GameManager {
pub session: GameSession,
listeners: Vec<Box<dyn GameEventListener>>,
}
impl GameManager {
pub fn new(size: BoardSize, game_mode: GameMode) -> Self {
Self {
session: GameSession::new(size, game_mode),
listeners: Vec::new(),
}
}
pub fn new_with_board(board: Board, game_mode: GameMode) -> Self {
Self {
session: GameSession::new_with_board(board, game_mode),
listeners: Vec::new(),
}
}
pub fn add_listener(&mut self, listener: Box<dyn GameEventListener>) {
self.listeners.push(listener);
}
fn notify(&mut self, event: GameEvent) {
for listener in &mut self.listeners {
listener.on_event(event.clone());
}
}
pub fn start_game(&mut self) {
self.notify(GameEvent::GameStarted);
self.notify(GameEvent::RoundStarted(self.session.round));
}
pub fn make_move(&mut self, target: (usize, usize)) {
let current_player = self.session.current_player;
match self.session.process_move(target) {
Ok(()) => {
let pieces = &self.session.scores.get(¤t_player).unwrap().pieces;
let last_piece = pieces.last().unwrap_or(&Piece::Empty);
self.notify(GameEvent::MoveMade(current_player, target, *last_piece));
if self.session.is_round_over() {
let winner = self.session.get_round_winner();
let scores = self
.session
.scores
.iter()
.map(|(k, v)| (*k, v.total))
.collect();
self.notify(GameEvent::RoundEnded(winner, scores));
}
}
Err(e) => {
self.notify(GameEvent::InvalidMove(current_player, target, e));
}
}
}
pub fn start_next_round(&mut self) {
self.session.start_next_round();
self.notify(GameEvent::RoundStarted(self.session.round));
}
pub fn end_game(&mut self) {
let winner = self.session.get_overall_winner();
self.notify(GameEvent::GameEnded(
winner,
self.session.total_scores.clone(),
));
}
}