836 lines
31 KiB
Rust
836 lines
31 KiB
Rust
extern crate rand;
|
|
use std::{ io::{ Read, Write }, net::TcpStream };
|
|
use rand::Rng;
|
|
const GAME_INTEGRITY_ERROR: &'static str = "game integrity compromised";
|
|
const PROTOCOL_ERROR_STRING: &str = "Client/Server protocol mismatch";
|
|
|
|
//enums, structs and types
|
|
|
|
#[derive(Clone)]
|
|
#[derive(Debug)]
|
|
#[derive(Eq)]
|
|
#[derive(Hash)]
|
|
pub enum CardValue {
|
|
PLUS_TWO,
|
|
PLUS_FOUR,
|
|
ZERO,
|
|
ONE,
|
|
TWO,
|
|
THREE,
|
|
FOUR,
|
|
FIVE,
|
|
SIX,
|
|
SEVEN,
|
|
EIGHT,
|
|
NINE,
|
|
REVERSE,
|
|
SKIP,
|
|
CHANGE_COLOR,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
#[derive(Debug)]
|
|
#[derive(Eq)]
|
|
#[derive(Hash)]
|
|
pub enum CardColor {
|
|
RED,
|
|
BLUE,
|
|
GREEN,
|
|
YELLOW,
|
|
BLACK,
|
|
}
|
|
|
|
pub enum Direction {
|
|
CLOCKWISE,
|
|
COUNTER_CLOCKWISE,
|
|
}
|
|
#[derive(PartialEq)]
|
|
#[derive(Debug)]
|
|
pub enum TURN_RESULT {
|
|
NEXT_TURN,
|
|
REPEAT_TURN,
|
|
GAME_OVER,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PlayerState<'a> {
|
|
pub player_name: &'a str,
|
|
pub cards: Vec<Card>,
|
|
pub said_uno: bool,
|
|
}
|
|
|
|
pub struct PlayerConnection<'a, S> where S: Read + Write {
|
|
pub player_name: &'a str,
|
|
pub stream: S,
|
|
}
|
|
#[derive(Debug)]
|
|
#[derive(Eq)]
|
|
#[derive(Hash)]
|
|
pub struct Card {
|
|
pub value: CardValue,
|
|
pub color: CardColor,
|
|
}
|
|
|
|
pub struct GameState<'a, S: Read + Write> {
|
|
pub turns: u64,
|
|
pub current_turn: u32,
|
|
deck: Deck,
|
|
trash: Deck,
|
|
pub current_card: Card,
|
|
pub player_states: Vec<PlayerState<'a>>,
|
|
pub player_connections: Vec<PlayerConnection<'a, S>>,
|
|
pub current_color: CardColor,
|
|
pub current_direction: Direction,
|
|
plus_twos: u32,
|
|
}
|
|
|
|
type Deck = Vec<Card>;
|
|
|
|
// PartialEq trait implements
|
|
|
|
impl PartialEq for CardValue {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
core::mem::discriminant(self) == core::mem::discriminant(other)
|
|
}
|
|
}
|
|
|
|
impl PartialEq for CardColor {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
core::mem::discriminant(self) == core::mem::discriminant(other)
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Direction {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
core::mem::discriminant(self) == core::mem::discriminant(other)
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Card {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.value == other.value && self.color == other.color
|
|
}
|
|
}
|
|
|
|
//Clone trait implements
|
|
|
|
impl Clone for Card {
|
|
fn clone(&self) -> Self {
|
|
Self { value: self.value.clone(), color: self.color.clone() }
|
|
}
|
|
}
|
|
|
|
//Copy trait implements
|
|
|
|
impl Copy for Card {}
|
|
impl Copy for CardColor {}
|
|
impl Copy for CardValue {}
|
|
|
|
//struct implementations
|
|
|
|
impl CardValue {
|
|
pub fn to_string<'a>(self) -> &'a str {
|
|
match self {
|
|
CardValue::PLUS_FOUR => "PLUS_FOUR",
|
|
CardValue::CHANGE_COLOR => "CHANGE_COLOR",
|
|
CardValue::PLUS_TWO => "PLUS_TWO",
|
|
CardValue::SKIP => "SKIP",
|
|
CardValue::REVERSE => "REVERSE",
|
|
CardValue::ZERO => "ZERO",
|
|
CardValue::ONE => "ONE",
|
|
CardValue::TWO => "TWO",
|
|
CardValue::THREE => "THREE",
|
|
CardValue::FOUR => "FOUR",
|
|
CardValue::FIVE => "FIVE",
|
|
CardValue::SIX => "SIX",
|
|
CardValue::SEVEN => "SEVEN",
|
|
CardValue::EIGHT => "EIGHT",
|
|
CardValue::NINE => "NINE",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CardColor {
|
|
pub fn to_string<'a>(self) -> &'a str {
|
|
match self {
|
|
CardColor::BLACK => "BLACK",
|
|
CardColor::BLUE => "BLUE",
|
|
CardColor::GREEN => "GREEN",
|
|
CardColor::YELLOW => "YELLOW",
|
|
CardColor::RED => "RED",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PlayerState<'_> {
|
|
pub fn new(player_name: &str, cards: Vec<Card>) -> PlayerState {
|
|
PlayerState {
|
|
player_name: player_name,
|
|
cards: cards,
|
|
said_uno: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PlayerConnection<'_, TcpStream> {
|
|
pub fn register_player<'a>(
|
|
mut stream: TcpStream
|
|
) -> Result<PlayerConnection<'a, TcpStream>, String> {
|
|
let buffer: &mut [u8] = &mut [];
|
|
match stream.read(buffer) {
|
|
Ok(_) => (),
|
|
Err(_) => {
|
|
return Err("could not read from socket".to_owned());
|
|
}
|
|
}
|
|
let msg = match std::str::from_utf8(buffer) {
|
|
Ok(msg) => msg,
|
|
Err(_) => {
|
|
return Err("could not parse UTF-8".to_owned());
|
|
}
|
|
};
|
|
|
|
let mut lines = msg.lines();
|
|
let is_valid1 = lines.next().unwrap_or_else(|| &PROTOCOL_ERROR_STRING) == "app:uno";
|
|
let is_valid2 = lines.next().unwrap_or_else(|| &PROTOCOL_ERROR_STRING) == "version:0.1";
|
|
let player_name: &str = lines.next().unwrap_or_else(|| &PROTOCOL_ERROR_STRING);
|
|
let is_valid3 = if player_name != "invalid" && player_name.contains("player_name:") {
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
if lines.next() != None || !(is_valid1 && is_valid2 && is_valid3) {
|
|
stream.write_all(PROTOCOL_ERROR_STRING.as_bytes()).unwrap();
|
|
stream.flush().unwrap();
|
|
return Err(PROTOCOL_ERROR_STRING.to_owned());
|
|
}
|
|
stream.write_all("Ok".as_bytes()).unwrap();
|
|
stream.flush().unwrap();
|
|
return Ok(PlayerConnection {
|
|
player_name: player_name[..].split(':').collect::<Vec<_>>()[1],
|
|
stream: stream,
|
|
});
|
|
}
|
|
}
|
|
|
|
impl<S> GameState<'_, S> where S: Read + Write {
|
|
fn cards_left(player: &PlayerState) -> usize {
|
|
return player.cards.len();
|
|
}
|
|
|
|
fn next_player(&mut self) {
|
|
match self.current_direction {
|
|
Direction::CLOCKWISE => {
|
|
if self.current_turn + 1 >= (self.player_states.len() as u32) {
|
|
self.current_turn = 0;
|
|
return;
|
|
}
|
|
self.current_turn += 1;
|
|
}
|
|
Direction::COUNTER_CLOCKWISE => {
|
|
if self.current_turn == 0 {
|
|
self.current_turn = (self.player_states.len() -1) as u32;
|
|
return;
|
|
}
|
|
self.current_turn -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn game_init(&mut self) {
|
|
let players = self.player_states.clone();
|
|
for player in players {
|
|
self.draw(player.player_name, 7);
|
|
}
|
|
self.turns += 1;
|
|
}
|
|
|
|
fn has_any_moves(&self, player: PlayerState) -> bool {
|
|
for card in player.cards {
|
|
if GameState::<S>::check_if_legal(
|
|
self.current_card,
|
|
card,
|
|
self.current_color
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn add_to_trash(&mut self, card: Card) {
|
|
self.trash.push(card);
|
|
}
|
|
|
|
fn play(player: &mut PlayerState, card_to_be_played: Card) -> Result<Card, String> {
|
|
if player.cards.contains(&card_to_be_played) {
|
|
return Ok(
|
|
player.cards.remove(
|
|
player.cards
|
|
.iter()
|
|
.position(|x| *x == card_to_be_played)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
)
|
|
);
|
|
} else {
|
|
return Err(String::from("Card not held by player"));
|
|
}
|
|
}
|
|
|
|
fn check_if_legal(
|
|
current_card: Card,
|
|
card_to_be_played: Card,
|
|
current_color: CardColor
|
|
) -> bool {
|
|
if
|
|
card_to_be_played.color == CardColor::BLACK ||
|
|
card_to_be_played.color == current_color ||
|
|
card_to_be_played.value == current_card.value
|
|
{
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
pub fn next_turn(&mut self, card_to_be_played: Card) -> TURN_RESULT {
|
|
if self.turns == 0 {
|
|
self.game_init();
|
|
return TURN_RESULT::NEXT_TURN;
|
|
}
|
|
|
|
if !GameState::has_any_moves(&self,
|
|
self.player_states
|
|
.get(self.current_turn as usize)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
.to_owned()
|
|
){
|
|
let player = &mut self.player_states.clone();
|
|
if !GameState::<S>::check_if_legal(self.current_card,self
|
|
.draw(
|
|
player
|
|
.get_mut(self.current_turn as usize)
|
|
.expect(&GAME_INTEGRITY_ERROR).player_name,
|
|
1
|
|
).get(0)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
.to_owned(),
|
|
self.current_color
|
|
){
|
|
self.next_player();
|
|
return TURN_RESULT::NEXT_TURN;
|
|
}
|
|
let mut player = self.player_states.clone();
|
|
self.add_to_trash(
|
|
GameState::<S>
|
|
::play(
|
|
player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR),
|
|
card_to_be_played
|
|
)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
);
|
|
}
|
|
|
|
if
|
|
!GameState::<S>::check_if_legal(
|
|
self.current_card,
|
|
card_to_be_played,
|
|
self.current_color
|
|
)
|
|
{
|
|
return TURN_RESULT::REPEAT_TURN;
|
|
}
|
|
|
|
if
|
|
self.current_card.value == CardValue::PLUS_TWO &&
|
|
card_to_be_played.value != CardValue::PLUS_TWO
|
|
{
|
|
let player = &mut self.player_states.clone();
|
|
self.draw(
|
|
player
|
|
.get_mut(self.current_turn as usize)
|
|
.expect(&GAME_INTEGRITY_ERROR).player_name,
|
|
self.plus_twos
|
|
);
|
|
} else if
|
|
self.current_card.value == CardValue::PLUS_TWO &&
|
|
card_to_be_played.value == CardValue::PLUS_TWO
|
|
{
|
|
self.plus_twos += 1;
|
|
if self.current_color != card_to_be_played.color {
|
|
self.current_color = card_to_be_played.color;
|
|
}
|
|
let mut player = self.player_states.clone();
|
|
self.add_to_trash(
|
|
GameState::<S>
|
|
::play(
|
|
player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR),
|
|
card_to_be_played
|
|
)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
);
|
|
self.next_player();
|
|
return TURN_RESULT::NEXT_TURN;
|
|
}
|
|
|
|
if self.current_card.value == CardValue::PLUS_FOUR {
|
|
let mut player = self.player_states.clone();
|
|
self.draw(
|
|
player
|
|
.get_mut(self.current_turn as usize)
|
|
.expect(&GAME_INTEGRITY_ERROR).player_name,
|
|
4
|
|
);
|
|
}
|
|
|
|
if card_to_be_played.value == CardValue::SKIP {
|
|
self.current_card = card_to_be_played;
|
|
for _ in 0 .. 2 { self.next_player()};
|
|
self.turns += 1;
|
|
return TURN_RESULT::NEXT_TURN;
|
|
}
|
|
let mut player = self.player_states.clone();
|
|
if
|
|
player.get(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR).said_uno == true &&
|
|
GameState::<S>::cards_left(
|
|
player.get(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR)
|
|
) == 1
|
|
{
|
|
self.add_to_trash(
|
|
GameState::<S>
|
|
::play(
|
|
player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR),
|
|
card_to_be_played
|
|
)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
);
|
|
self.turns += 1;
|
|
return TURN_RESULT::GAME_OVER;
|
|
}
|
|
|
|
if card_to_be_played.value == CardValue::REVERSE {
|
|
if self.current_direction == Direction::CLOCKWISE {
|
|
self.current_direction = Direction::COUNTER_CLOCKWISE;
|
|
} else {
|
|
self.current_direction = Direction::CLOCKWISE;
|
|
}
|
|
}
|
|
|
|
if [CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR].contains(&card_to_be_played.value) {
|
|
self.current_color = card_to_be_played.color;
|
|
} else if
|
|
[
|
|
CardValue::EIGHT,
|
|
CardValue::FIVE,
|
|
CardValue::FOUR,
|
|
CardValue::NINE,
|
|
CardValue::ONE,
|
|
CardValue::SEVEN,
|
|
CardValue::SIX,
|
|
CardValue::THREE,
|
|
CardValue::TWO,
|
|
CardValue::ZERO,
|
|
CardValue::PLUS_TWO,
|
|
CardValue::SKIP,
|
|
CardValue::REVERSE,
|
|
CardValue::PLUS_TWO,
|
|
].contains(&card_to_be_played.value)
|
|
{
|
|
if card_to_be_played.color != self.current_color {
|
|
self.current_color = card_to_be_played.color;
|
|
}
|
|
}
|
|
|
|
let mut player = self.player_states.clone();
|
|
self.add_to_trash(
|
|
GameState::<S>
|
|
::play(
|
|
player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR),
|
|
card_to_be_played
|
|
)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
);
|
|
self.current_card = card_to_be_played;
|
|
self.turns += 1;
|
|
self.next_player();
|
|
return TURN_RESULT::NEXT_TURN;
|
|
}
|
|
|
|
fn new_game_helper<'a>(
|
|
player_states: Vec<PlayerState<'a>>,
|
|
player_connections: Vec<PlayerConnection<'a, S>>
|
|
) -> GameState<'a, S> {
|
|
GameState {
|
|
turns: 0,
|
|
current_turn: 0,
|
|
deck: GameState::<S>::base_deck(),
|
|
trash: vec![],
|
|
current_card: Card { value: CardValue::ZERO, color: CardColor::RED },
|
|
player_states: player_states.clone(),
|
|
player_connections: player_connections,
|
|
current_color: CardColor::RED,
|
|
current_direction: Direction::CLOCKWISE,
|
|
plus_twos: 0,
|
|
}
|
|
}
|
|
|
|
pub fn new_game<'a>(
|
|
player_states: Vec<PlayerState<'a>>,
|
|
player_connections: Vec<PlayerConnection<'a, S>>
|
|
) -> GameState<'a, S> {
|
|
let mut new_game = GameState::new_game_helper(player_states, player_connections);
|
|
new_game.current_color = new_game.current_card.color;
|
|
return new_game;
|
|
}
|
|
|
|
fn get_currently_held(&mut self) -> Vec<Card> {
|
|
let mut currently_held = vec![];
|
|
for hand in &mut self.player_states {
|
|
currently_held.append(&mut hand.cards);
|
|
}
|
|
return currently_held;
|
|
}
|
|
|
|
fn reset_deck(&mut self, currently_held: Vec<Card>) {
|
|
self.trash.clear();
|
|
self.deck = GameState::<S>::base_deck();
|
|
for current_cart in currently_held {
|
|
self.deck.remove(
|
|
self.deck
|
|
.iter()
|
|
.position(|x| *x == current_cart)
|
|
.expect(&GAME_INTEGRITY_ERROR)
|
|
);
|
|
}
|
|
}
|
|
|
|
fn base_deck() -> Vec<Card> {
|
|
let mut deck: Deck = vec![];
|
|
|
|
for color in [
|
|
CardColor::BLACK,
|
|
CardColor::BLUE,
|
|
CardColor::GREEN,
|
|
CardColor::RED,
|
|
CardColor::YELLOW,
|
|
] {
|
|
for value in [
|
|
CardValue::CHANGE_COLOR,
|
|
CardValue::PLUS_FOUR,
|
|
CardValue::ONE,
|
|
CardValue::TWO,
|
|
CardValue::THREE,
|
|
CardValue::FOUR,
|
|
CardValue::FIVE,
|
|
CardValue::SIX,
|
|
CardValue::SEVEN,
|
|
CardValue::EIGHT,
|
|
CardValue::NINE,
|
|
CardValue::ZERO,
|
|
CardValue::PLUS_TWO,
|
|
CardValue::REVERSE,
|
|
CardValue::SKIP,
|
|
] {
|
|
if ![CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR, CardValue::ZERO].contains(&value) && color != CardColor::BLACK {
|
|
for _ in 0..2 {
|
|
deck.push(Card { value: value, color: color });
|
|
}
|
|
} else if value == CardValue::ZERO && color != CardColor::BLACK{
|
|
deck.push(Card { value: value, color: color });
|
|
} else if [CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR].contains(&value) && color == CardColor::BLACK {
|
|
for _ in 0..4 {
|
|
deck.push(Card { value: value, color: color });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//CHANGE COLOR
|
|
/* for _ in 0..3 {deck.push(Card {value : CardValue::CHANGE_COLOR, color : CardColor::BLACK, color_change : CardColor::BLACK})};
|
|
//PLUS FOUR
|
|
for _ in 0..3 {deck.push(Card{ value : CardValue::PLUS_FOUR, color : CardColor::BLACK, color_change : CardColor::BLACK})};
|
|
//PLUS TWO
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::PLUS_TWO, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::PLUS_TWO, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::PLUS_TWO, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::PLUS_TWO, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//REVERSE
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::REVERSE, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::REVERSE, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::REVERSE, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::REVERSE, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//SKIP
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SKIP, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SKIP, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SKIP, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SKIP, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//NINE
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::NINE, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::NINE, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::NINE, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::NINE, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//EIGHT
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::EIGHT, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::EIGHT, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::EIGHT, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::EIGHT, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//SEVEN
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SEVEN, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SEVEN, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SEVEN, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SEVEN, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//SIX
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SIX, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SIX, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SIX, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::SIX, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//FIVE
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FIVE, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FIVE, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FIVE, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FIVE, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//FOUR
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FOUR, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FOUR, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FOUR, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::FOUR, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//THREE
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::THREE, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::THREE, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::THREE, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::THREE, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//TWO
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::TWO, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::TWO, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::TWO, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::TWO, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//ONE
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::ONE, color : CardColor::RED, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::ONE, color : CardColor::BLUE, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::ONE, color : CardColor::GREEN, color_change : CardColor::BLACK})};
|
|
for _ in 0..1 {deck.push(Card{ value : CardValue::ONE, color : CardColor::YELLOW, color_change : CardColor::BLACK})};
|
|
//ZERO
|
|
deck.push(Card{ value : CardValue::ZERO, color : CardColor::RED, color_change : CardColor::BLACK});
|
|
deck.push(Card{ value : CardValue::ZERO, color : CardColor::BLUE, color_change : CardColor::BLACK});
|
|
deck.push(Card{ value : CardValue::ZERO, color : CardColor::GREEN, color_change : CardColor::BLACK});
|
|
deck.push(Card{ value : CardValue::ZERO, color : CardColor::YELLOW, color_change : CardColor::BLACK}); */
|
|
|
|
return deck;
|
|
}
|
|
|
|
fn get_cards(&mut self, count: u32) -> Vec<Card> {
|
|
let mut cards_drawn = vec![];
|
|
for _ in 0..count {
|
|
let mut rng = rand::thread_rng();
|
|
let n: usize = rng.gen_range(0..self.deck.len());
|
|
cards_drawn.push(self.deck.remove(n));
|
|
}
|
|
cards_drawn
|
|
}
|
|
|
|
fn draw(&mut self, player_name: &str, count: u32) -> Vec<Card> {
|
|
if self.deck.len() <= (count as usize) {
|
|
let currently_held = self.get_currently_held();
|
|
self.reset_deck(currently_held);
|
|
}
|
|
let mut cards_drawn = self.get_cards(count);
|
|
for player in &mut self.player_states {
|
|
if player.player_name == player_name {
|
|
player.cards.append(&mut cards_drawn);
|
|
}
|
|
}
|
|
cards_drawn
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::{collections::{HashSet, VecDeque}, iter::Skip};
|
|
|
|
use super::*;
|
|
|
|
fn get_card_set() -> Vec<Card>{
|
|
let mut set = HashSet::new();
|
|
let mut unique_vec = vec![];
|
|
GameState::<VecDeque<u8>>::base_deck().into_iter().for_each(|c| {
|
|
set.insert(c);
|
|
});
|
|
set.into_iter().for_each(|c| unique_vec.push(c.to_owned()));
|
|
unique_vec
|
|
}
|
|
|
|
fn get_card(card_value : CardValue, card_color : CardColor) -> Card{
|
|
for card in get_card_set(){
|
|
if card_value == card.value && card_color == card.color {
|
|
return card;
|
|
}
|
|
}
|
|
return Card {value : CardValue::ZERO, color : CardColor::BLACK};
|
|
}
|
|
|
|
fn get_test_gs<'a>() -> GameState<'a, VecDeque<u8>> {
|
|
let fake_stream_detlef: VecDeque<u8> = VecDeque::new();
|
|
let fake_stream_dieter: VecDeque<u8> = VecDeque::new();
|
|
let fake_stream_doerte: VecDeque<u8> = VecDeque::new();
|
|
let player_states: Vec<PlayerState> = vec![
|
|
PlayerState::new("detlef", vec![]),
|
|
PlayerState::new("dieter", vec![]),
|
|
PlayerState::new("dorte", vec![])
|
|
];
|
|
let player_connections = vec![
|
|
PlayerConnection { player_name: "detlef", stream: fake_stream_detlef },
|
|
PlayerConnection { player_name: "dieter", stream: fake_stream_dieter },
|
|
PlayerConnection { player_name: "doerte", stream: fake_stream_doerte }
|
|
];
|
|
GameState::new_game(player_states, player_connections)
|
|
}
|
|
|
|
#[test]
|
|
fn test_card_value_to_string() {
|
|
let cv: CardValue = CardValue::NINE;
|
|
assert_eq!(cv.to_string(), "NINE");
|
|
}
|
|
|
|
#[test]
|
|
fn test_card_color_to_string() {
|
|
let cc: CardColor = CardColor::BLUE;
|
|
assert_eq!(cc.to_string(), "BLUE");
|
|
}
|
|
|
|
#[test]
|
|
fn test_game_init() {
|
|
let mut gs = get_test_gs();
|
|
gs.game_init();
|
|
for player_state in &mut gs.player_states {
|
|
// println!("{:#?}", player_state.cards);
|
|
assert_eq!(player_state.cards.len(), 7);
|
|
}
|
|
assert_eq!(
|
|
gs.deck.len(),
|
|
GameState::<VecDeque<u8>>::base_deck().len() - gs.player_states.len() * 7
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_player_cw() {
|
|
let mut gs = get_test_gs();
|
|
gs.current_direction = Direction::CLOCKWISE;
|
|
gs.next_player();
|
|
assert_eq!(gs.current_turn, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_player_ccw() {
|
|
let mut gs = get_test_gs();
|
|
gs.current_direction = Direction::COUNTER_CLOCKWISE;
|
|
gs.next_player();
|
|
assert_eq!(gs.current_turn, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_has_any_moves_none(){
|
|
let mut gs = get_test_gs();
|
|
//no moves
|
|
gs.current_card = Card {value : CardValue::TWO, color : CardColor::GREEN};
|
|
gs.current_color = CardColor::GREEN;
|
|
let player = PlayerState::new("detlef", [get_card(CardValue::FOUR, CardColor::RED), get_card(CardValue::EIGHT, CardColor::YELLOW)].to_vec());
|
|
assert_eq!(gs.has_any_moves(player), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_has_any_moves_some(){
|
|
let mut gs = get_test_gs();
|
|
//has moves
|
|
gs.current_card = Card {value : CardValue::TWO, color : CardColor::GREEN};
|
|
gs.current_color = CardColor::GREEN;
|
|
let player = PlayerState::new("detlef", [get_card(CardValue::FOUR, CardColor::RED), get_card(CardValue::EIGHT, CardColor::GREEN)].to_vec());
|
|
}
|
|
|
|
#[test]
|
|
fn test_check_if_legal_false(){
|
|
//illegal
|
|
let current_card = Card {value : CardValue::TWO, color : CardColor::GREEN};
|
|
assert_eq!(GameState::<VecDeque<u8>>::check_if_legal(current_card, get_card(CardValue::FOUR, CardColor::RED), CardColor::GREEN), false);
|
|
assert_eq!(GameState::<VecDeque<u8>>::check_if_legal(current_card, get_card(CardValue::EIGHT, CardColor::YELLOW), CardColor::GREEN), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_add_to_trash(){
|
|
let mut gs = get_test_gs();
|
|
gs.add_to_trash(get_card(CardValue::ONE, CardColor::YELLOW));
|
|
assert_eq!(gs.trash, vec![get_card(CardValue::ONE, CardColor::YELLOW)]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_play_card_held(){
|
|
let mut gs = get_test_gs();
|
|
gs.player_states.get_mut(0).unwrap().cards.push(get_card(CardValue::NINE, CardColor::BLUE));
|
|
assert_eq!(GameState::<VecDeque<u8>>::play(gs.player_states.get_mut(0).unwrap(),get_card(CardValue::NINE, CardColor::BLUE)).unwrap(), get_card(CardValue::NINE, CardColor::BLUE));
|
|
}
|
|
|
|
#[test]
|
|
fn test_play_card_not_held(){
|
|
let mut gs = get_test_gs();
|
|
gs.player_states.get_mut(0).unwrap().cards.push(get_card(CardValue::NINE, CardColor::BLUE));
|
|
assert_eq!(GameState::<VecDeque<u8>>::play(gs.player_states.get_mut(0).unwrap(),get_card(CardValue::EIGHT, CardColor::BLUE)), Err("Card not held by player".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_red_two(){
|
|
let mut gs = get_test_gs();
|
|
gs.game_init();
|
|
gs.current_card = get_card(CardValue::TWO, CardColor::GREEN);
|
|
gs.current_color = CardColor::GREEN;
|
|
gs.player_states.get_mut(0).unwrap().cards.push(get_card(CardValue::TWO, CardColor::RED));
|
|
assert_eq!(gs.next_turn(get_card(CardValue::TWO, CardColor::RED)), TURN_RESULT::NEXT_TURN);
|
|
assert_eq!(gs.current_color, CardColor::RED);
|
|
assert_eq!(gs.current_card, get_card(CardValue::TWO, CardColor::RED));
|
|
assert_eq!(gs.plus_twos, 0);
|
|
assert_eq!(gs.turns, 2);
|
|
assert_eq!(gs.current_turn, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_yellow_four(){
|
|
let mut gs = get_test_gs();
|
|
gs.game_init();
|
|
gs.current_card = get_card(CardValue::TWO, CardColor::GREEN);
|
|
gs.current_color = CardColor::GREEN;
|
|
gs.player_states.get_mut(0).unwrap().cards.push(get_card(CardValue::FOUR, CardColor::YELLOW));
|
|
assert_eq!(gs.next_turn(get_card(CardValue::FOUR, CardColor::YELLOW)), TURN_RESULT::REPEAT_TURN);
|
|
assert_eq!(gs.current_color, CardColor::GREEN);
|
|
assert_eq!(gs.current_card, get_card(CardValue::TWO, CardColor::GREEN));
|
|
assert_eq!(gs.turns, 1);
|
|
assert_eq!(gs.current_turn, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_green_skip(){
|
|
let mut gs = get_test_gs();
|
|
gs.game_init();
|
|
gs.current_card = get_card(CardValue::TWO, CardColor::GREEN);
|
|
gs.current_color = CardColor::GREEN;
|
|
gs.player_states.get_mut(0).unwrap().cards.push(get_card(CardValue::SKIP, CardColor::GREEN));
|
|
assert_eq!(gs.next_turn(get_card(CardValue::SKIP, CardColor::GREEN)), TURN_RESULT::NEXT_TURN);
|
|
assert_eq!(gs.current_color, CardColor::GREEN);
|
|
assert_eq!(gs.current_card, get_card(CardValue::SKIP, CardColor::GREEN));
|
|
assert_eq!(gs.turns, 2);
|
|
assert_eq!(gs.current_turn, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_plus_four(){
|
|
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_green_reverse(){
|
|
|
|
}
|
|
|
|
#[test]
|
|
fn test_next_turn_green_two_to_change_color(){
|
|
|
|
}
|
|
}
|