use cardpack::prelude::BasicPile;
use serde::{Deserialize, Serialize};
use crate::error::GfError;
use super::Player;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AskEntry {
pub asker: usize,
pub rank: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlayerView {
pub index: usize,
pub name: String,
pub hand_size: usize,
pub hand: Option<BasicPile>,
pub books: usize,
pub completed_book_ranks: Vec<String>,
}
impl PlayerView {
pub fn from_perspective(
players: &[Player],
observer: usize,
) -> Result<Vec<PlayerView>, GfError> {
if !players.is_empty() && observer >= players.len() {
return Err(GfError::InvalidTarget);
}
Ok(players
.iter()
.enumerate()
.map(|(index, player)| {
let hand = if index == observer {
Some(player.hand().clone())
} else {
None
};
PlayerView {
index,
name: player.name.clone(),
hand_size: player.hand_size(),
hand,
books: player.book_count(),
completed_book_ranks: player.book_ranks(),
}
})
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
use cardpack::prelude::FrenchBasicCard;
fn make_players() -> Vec<Player> {
let mut alice = Player::new("Alice");
alice.receive_card(FrenchBasicCard::ACE_SPADES);
let mut bob = Player::new("Bob");
bob.receive_card(FrenchBasicCard::KING_HEARTS);
bob.receive_card(FrenchBasicCard::KING_SPADES);
vec![alice, bob]
}
#[test]
fn test_view_from_perspective_observer_sees_own_hand() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views.len(), 2);
assert!(views[0].hand.is_some());
assert_eq!(views[0].hand.as_ref().map(BasicPile::len), Some(1));
}
#[test]
fn test_view_from_perspective_others_hand_is_none() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert!(views[1].hand.is_none());
}
#[test]
fn test_view_hand_size_always_visible() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views[0].hand_size, 1);
assert_eq!(views[1].hand_size, 2);
}
#[test]
fn test_view_indices_correct() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views[0].index, 0);
assert_eq!(views[1].index, 1);
}
#[test]
fn test_view_names_correct() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views[0].name, "Alice");
assert_eq!(views[1].name, "Bob");
}
#[test]
fn test_view_book_count() {
let mut players = make_players();
let book = BasicPile::from(vec![
FrenchBasicCard::ACE_SPADES,
FrenchBasicCard::ACE_HEARTS,
FrenchBasicCard::ACE_DIAMONDS,
FrenchBasicCard::ACE_CLUBS,
]);
players[0].add_book(book);
let views = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views[0].books, 1);
assert_eq!(views[1].books, 0);
assert_eq!(views[0].completed_book_ranks, vec!["A".to_string()]);
assert!(views[1].completed_book_ranks.is_empty());
}
#[test]
fn test_view_bob_as_observer() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 1).unwrap();
assert!(views[0].hand.is_none());
assert!(views[1].hand.is_some());
}
#[test]
fn test_view_empty_players() {
let views = PlayerView::from_perspective(&[], 0).unwrap();
assert!(views.is_empty());
}
#[test]
fn test_view_invalid_observer_returns_err() {
let players = make_players();
let result = PlayerView::from_perspective(&players, 99);
assert_eq!(result, Err(GfError::InvalidTarget));
}
#[test]
fn test_ask_entry_fields() {
let entry = AskEntry {
asker: 3,
rank: "7".to_string(),
};
assert_eq!(entry.asker, 3);
assert_eq!(entry.rank, "7");
}
#[test]
fn test_ask_entry_partial_eq() {
let a = AskEntry {
asker: 0,
rank: "A".to_string(),
};
let b = AskEntry {
asker: 0,
rank: "A".to_string(),
};
let c = AskEntry {
asker: 1,
rank: "K".to_string(),
};
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_view_serialization_round_trip() {
let players = make_players();
let views = PlayerView::from_perspective(&players, 0).unwrap();
let json = serde_json::to_string(&views[0]).expect("serialize");
let back: PlayerView = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.name, "Alice");
assert_eq!(back.hand_size, 1);
}
#[test]
fn test_player_view_partial_eq() {
let players = make_players();
let views1 = PlayerView::from_perspective(&players, 0).unwrap();
let views2 = PlayerView::from_perspective(&players, 0).unwrap();
assert_eq!(views1[0], views2[0]);
assert_ne!(views1[0], views1[1]);
}
}