number of changes, added testing
This commit is contained in:
parent
7400786b4c
commit
7f99c65f0a
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
.vs-code
|
.vs-code
|
||||||
|
launch.json
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod uno_logic;
|
pub mod uno_logic;
|
||||||
pub mod uno_protocol;
|
pub mod uno_protocol;
|
||||||
pub mod uno_tcp;
|
pub mod uno_tcp;
|
||||||
|
// pub mod uno_logic::tests;
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
|
mod uno_server;
|
||||||
fn main() {
|
fn main() {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
440
src/uno_logic.rs
440
src/uno_logic.rs
|
|
@ -1,14 +1,15 @@
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
use std::{
|
use std::{ io::{ Read, Write }, net::TcpStream };
|
||||||
io::{ BufReader, BufWriter }, net::TcpStream, usize, vec
|
|
||||||
};
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
const GAME_INTEGRITY_ERROR: &'static str = "game integrity compromised";
|
const GAME_INTEGRITY_ERROR: &'static str = "game integrity compromised";
|
||||||
|
const PROTOCOL_ERROR_STRING: &str = "Client/Server protocol mismatch";
|
||||||
|
|
||||||
//enums, structs and types
|
//enums, structs and types
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Eq)]
|
||||||
|
#[derive(Hash)]
|
||||||
pub enum CardValue {
|
pub enum CardValue {
|
||||||
PLUS_TWO,
|
PLUS_TWO,
|
||||||
PLUS_FOUR,
|
PLUS_FOUR,
|
||||||
|
|
@ -28,6 +29,9 @@ pub enum CardValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Eq)]
|
||||||
|
#[derive(Hash)]
|
||||||
pub enum CardColors {
|
pub enum CardColors {
|
||||||
RED,
|
RED,
|
||||||
BLUE,
|
BLUE,
|
||||||
|
|
@ -44,7 +48,7 @@ pub enum Direction {
|
||||||
pub enum TURN_RESULT {
|
pub enum TURN_RESULT {
|
||||||
NEXT_TURN,
|
NEXT_TURN,
|
||||||
REPEAT_TURN,
|
REPEAT_TURN,
|
||||||
GAME_OVER
|
GAME_OVER,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -54,24 +58,26 @@ pub struct PlayerState<'a> {
|
||||||
pub said_uno: bool,
|
pub said_uno: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlayerConnection<'a>{
|
pub struct PlayerConnection<'a, S> where S: Read + Write {
|
||||||
pub player_name: &'a str,
|
pub player_name: &'a str,
|
||||||
pub stream : TcpStream,
|
pub stream: S,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Eq)]
|
||||||
|
#[derive(Hash)]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
pub value: CardValue,
|
pub value: CardValue,
|
||||||
pub color: CardColors,
|
pub color: CardColors,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GameState<'a>{
|
pub struct GameState<'a, S: Read + Write> {
|
||||||
pub turns: u64,
|
pub turns: u64,
|
||||||
pub current_turn: u32,
|
pub current_turn: u32,
|
||||||
deck: Deck,
|
deck: Deck,
|
||||||
trash: Deck,
|
trash: Deck,
|
||||||
pub current_card: Card,
|
pub current_card: Card,
|
||||||
pub player_states: Vec<PlayerState<'a>>,
|
pub player_states: Vec<PlayerState<'a>>,
|
||||||
pub player_connections : Vec<PlayerConnection<'a>>,
|
pub player_connections: Vec<PlayerConnection<'a, S>>,
|
||||||
pub current_color: CardColors,
|
pub current_color: CardColors,
|
||||||
pub current_direction: Direction,
|
pub current_direction: Direction,
|
||||||
plus_twos: u32,
|
plus_twos: u32,
|
||||||
|
|
@ -138,7 +144,7 @@ impl CardValue {
|
||||||
CardValue::SIX => "SIX",
|
CardValue::SIX => "SIX",
|
||||||
CardValue::SEVEN => "SEVEN",
|
CardValue::SEVEN => "SEVEN",
|
||||||
CardValue::EIGHT => "EIGHT",
|
CardValue::EIGHT => "EIGHT",
|
||||||
CardValue::NINE => "NINE"
|
CardValue::NINE => "NINE",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +156,7 @@ impl CardColors {
|
||||||
CardColors::BLUE => "BLUE",
|
CardColors::BLUE => "BLUE",
|
||||||
CardColors::GREEN => "GREEN",
|
CardColors::GREEN => "GREEN",
|
||||||
CardColors::YELLOW => "YELLOW",
|
CardColors::YELLOW => "YELLOW",
|
||||||
CardColors::RED => "RED"
|
CardColors::RED => "RED",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -165,30 +171,62 @@ impl PlayerState<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerConnection<'_> {
|
impl PlayerConnection<'_, TcpStream> {
|
||||||
pub fn new(player_name : &str, stream : TcpStream) -> PlayerConnection{
|
pub fn register_player<'a>(
|
||||||
PlayerConnection {
|
mut stream: TcpStream
|
||||||
player_name : player_name,
|
) -> Result<PlayerConnection<'a, TcpStream>, String> {
|
||||||
stream : stream
|
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 GameState<'_>{
|
impl<S> GameState<'_, S> where S: Read + Write {
|
||||||
|
|
||||||
fn cards_left(player: &PlayerState) -> usize {
|
fn cards_left(player: &PlayerState) -> usize {
|
||||||
return player.cards.len();
|
return player.cards.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_player(&mut self) {
|
fn next_player(&mut self) {
|
||||||
if self.current_direction == Direction::CLOCKWISE{
|
match self.current_direction {
|
||||||
if self.current_turn+1 >= self.player_states.len() as u32{
|
Direction::CLOCKWISE => {
|
||||||
|
if self.current_turn + 1 >= (self.player_states.len() as u32) {
|
||||||
self.current_turn = 0;
|
self.current_turn = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.current_turn += 1;
|
self.current_turn += 1;
|
||||||
}
|
}
|
||||||
else {
|
Direction::COUNTER_CLOCKWISE => {
|
||||||
if self.current_turn == 0 {
|
if self.current_turn == 0 {
|
||||||
self.current_turn = (self.player_states.len() -1) as u32;
|
self.current_turn = (self.player_states.len() -1) as u32;
|
||||||
return;
|
return;
|
||||||
|
|
@ -196,21 +234,24 @@ impl GameState<'_>{
|
||||||
self.current_turn -= 1;
|
self.current_turn -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn game_init(&mut self) {
|
fn game_init(&mut self) {
|
||||||
let mut players : Vec<PlayerState> = vec![];
|
let players = self.player_states.clone();
|
||||||
for player in &self.player_states{
|
for player in players {
|
||||||
players.push(player.to_owned());
|
self.draw(player.player_name, 7);
|
||||||
}
|
|
||||||
for mut player in &mut players{
|
|
||||||
self.draw(&mut player, 7);
|
|
||||||
}
|
}
|
||||||
self.turns += 1;
|
self.turns += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_any_moves(&self, player: PlayerState) -> bool {
|
fn has_any_moves(&self, player: PlayerState) -> bool {
|
||||||
for card in player.cards {
|
for card in player.cards {
|
||||||
if GameState::check_if_legal(self.current_card.clone(), card, self.current_color.clone()){
|
if GameState::<S>::check_if_legal(
|
||||||
|
self.current_card,
|
||||||
|
card,
|
||||||
|
self.current_color
|
||||||
|
)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,15 +264,29 @@ impl GameState<'_>{
|
||||||
|
|
||||||
fn play(player: &mut PlayerState, card_to_be_played: Card) -> Result<Card, String> {
|
fn play(player: &mut PlayerState, card_to_be_played: Card) -> Result<Card, String> {
|
||||||
if player.cards.contains(&card_to_be_played) {
|
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)));
|
return Ok(
|
||||||
}
|
player.cards.remove(
|
||||||
else {
|
player.cards
|
||||||
|
.iter()
|
||||||
|
.position(|x| *x == card_to_be_played)
|
||||||
|
.expect(&GAME_INTEGRITY_ERROR)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
return Err(String::from("Card not held by player"));
|
return Err(String::from("Card not held by player"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_legal(current_card : Card, card_to_be_played : Card, current_color : CardColors) -> bool{
|
fn check_if_legal(
|
||||||
if card_to_be_played.color == CardColors::BLACK || card_to_be_played.color == current_color || card_to_be_played.value == current_card.value {
|
current_card: Card,
|
||||||
|
card_to_be_played: Card,
|
||||||
|
current_color: CardColors
|
||||||
|
) -> bool {
|
||||||
|
if
|
||||||
|
card_to_be_played.color == CardColors::BLACK ||
|
||||||
|
card_to_be_played.color == current_color ||
|
||||||
|
card_to_be_played.value == current_card.value
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -239,54 +294,123 @@ impl GameState<'_>{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_turn(mut self, card_to_be_played: Card) -> TURN_RESULT {
|
pub fn next_turn(mut self, card_to_be_played: Card) -> TURN_RESULT {
|
||||||
|
|
||||||
if self.turns == 0 {
|
if self.turns == 0 {
|
||||||
self.game_init();
|
self.game_init();
|
||||||
return TURN_RESULT::NEXT_TURN;
|
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()){
|
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();
|
let player = &mut self.player_states.clone();
|
||||||
if !GameState::check_if_legal(self.current_card, self.draw(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), 1).get(0).expect(&GAME_INTEGRITY_ERROR).to_owned(), self.current_color){
|
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();
|
self.next_player();
|
||||||
return TURN_RESULT::NEXT_TURN;
|
return TURN_RESULT::NEXT_TURN;
|
||||||
}
|
}
|
||||||
let mut player = self.player_states.clone();
|
let mut player = self.player_states.clone();
|
||||||
self.add_to_trash(GameState::play(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), card_to_be_played).expect(&GAME_INTEGRITY_ERROR));
|
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::check_if_legal(self.current_card, card_to_be_played, self.current_color){
|
if
|
||||||
|
!GameState::<S>::check_if_legal(
|
||||||
|
self.current_card,
|
||||||
|
card_to_be_played,
|
||||||
|
self.current_color
|
||||||
|
)
|
||||||
|
{
|
||||||
return TURN_RESULT::REPEAT_TURN;
|
return TURN_RESULT::REPEAT_TURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current_card.value == CardValue::PLUS_TWO && card_to_be_played.value != CardValue::PLUS_TWO{
|
if
|
||||||
|
self.current_card.value == CardValue::PLUS_TWO &&
|
||||||
|
card_to_be_played.value != CardValue::PLUS_TWO
|
||||||
|
{
|
||||||
let player = &mut self.player_states.clone();
|
let player = &mut self.player_states.clone();
|
||||||
self.draw(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), self.plus_twos);
|
self.draw(
|
||||||
}
|
player
|
||||||
else if self.current_card.value == CardValue::PLUS_TWO && card_to_be_played.value == CardValue::PLUS_TWO {
|
.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;
|
self.plus_twos += 1;
|
||||||
if self.current_color != card_to_be_played.color {
|
if self.current_color != card_to_be_played.color {
|
||||||
self.current_color = card_to_be_played.color;
|
self.current_color = card_to_be_played.color;
|
||||||
}
|
}
|
||||||
let mut player = self.player_states.clone();
|
let mut player = self.player_states.clone();
|
||||||
self.add_to_trash(GameState::play(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), card_to_be_played).expect(&GAME_INTEGRITY_ERROR));
|
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();
|
self.next_player();
|
||||||
return TURN_RESULT::NEXT_TURN;
|
return TURN_RESULT::NEXT_TURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current_card.value == CardValue::PLUS_FOUR {
|
if self.current_card.value == CardValue::PLUS_FOUR {
|
||||||
let mut player = self.player_states.clone();
|
let mut player = self.player_states.clone();
|
||||||
self.draw(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), 4);
|
self.draw(
|
||||||
|
player
|
||||||
|
.get_mut(self.current_turn as usize)
|
||||||
|
.expect(&GAME_INTEGRITY_ERROR).player_name,
|
||||||
|
4
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.current_card.value == CardValue::SKIP {
|
if self.current_card.value == CardValue::SKIP {
|
||||||
self.next_player();
|
self.next_player();
|
||||||
self.turns += 1;
|
self.turns += 1;
|
||||||
return TURN_RESULT::NEXT_TURN
|
return TURN_RESULT::NEXT_TURN;
|
||||||
}
|
}
|
||||||
let mut player = self.player_states.clone();
|
let mut player = self.player_states.clone();
|
||||||
if player.get(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR).said_uno == true && GameState::cards_left(player.get(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR)) == 1{
|
if
|
||||||
self.add_to_trash(GameState::play(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), card_to_be_played).expect(&GAME_INTEGRITY_ERROR));
|
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;
|
self.turns += 1;
|
||||||
return TURN_RESULT::GAME_OVER;
|
return TURN_RESULT::GAME_OVER;
|
||||||
}
|
}
|
||||||
|
|
@ -294,33 +418,59 @@ impl GameState<'_>{
|
||||||
if card_to_be_played.value == CardValue::REVERSE {
|
if card_to_be_played.value == CardValue::REVERSE {
|
||||||
if self.current_direction == Direction::CLOCKWISE {
|
if self.current_direction == Direction::CLOCKWISE {
|
||||||
self.current_direction = Direction::COUNTER_CLOCKWISE;
|
self.current_direction = Direction::COUNTER_CLOCKWISE;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
self.current_direction = Direction::CLOCKWISE;
|
self.current_direction = Direction::CLOCKWISE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if [CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR].contains(&card_to_be_played.value) {
|
if [CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR].contains(&card_to_be_played.value) {
|
||||||
self.current_color = card_to_be_played.color;
|
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) {
|
} 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 {
|
if card_to_be_played.color != self.current_color {
|
||||||
self.current_color = card_to_be_played.color;
|
self.current_color = card_to_be_played.color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut player = self.player_states.clone();
|
let mut player = self.player_states.clone();
|
||||||
self.add_to_trash(GameState::play(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), card_to_be_played).expect(&GAME_INTEGRITY_ERROR));
|
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.current_card = card_to_be_played;
|
||||||
self.turns += 1;
|
self.turns += 1;
|
||||||
self.next_player();
|
self.next_player();
|
||||||
return TURN_RESULT::NEXT_TURN;
|
return TURN_RESULT::NEXT_TURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_game_helper<'a>(player_states: Vec<PlayerState<'a>>, player_connections: Vec<PlayerConnection<'a>>) -> GameState<'a>{
|
fn new_game_helper<'a>(
|
||||||
|
player_states: Vec<PlayerState<'a>>,
|
||||||
|
player_connections: Vec<PlayerConnection<'a, S>>
|
||||||
|
) -> GameState<'a, S> {
|
||||||
GameState {
|
GameState {
|
||||||
turns: 0,
|
turns: 0,
|
||||||
current_turn: 0,
|
current_turn: 0,
|
||||||
deck : GameState::base_deck(),
|
deck: GameState::<S>::base_deck(),
|
||||||
trash: vec![],
|
trash: vec![],
|
||||||
current_card: Card { value: CardValue::ZERO, color: CardColors::RED },
|
current_card: Card { value: CardValue::ZERO, color: CardColors::RED },
|
||||||
player_states: player_states.clone(),
|
player_states: player_states.clone(),
|
||||||
|
|
@ -331,9 +481,12 @@ impl GameState<'_>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_game<'a>(player_states : Vec<PlayerState<'a>>, player_connections: Vec<PlayerConnection<'a>>) -> GameState<'a>{
|
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);
|
let mut new_game = GameState::new_game_helper(player_states, player_connections);
|
||||||
new_game.current_color = new_game.current_card.color.clone();
|
new_game.current_color = new_game.current_card.color;
|
||||||
return new_game;
|
return new_game;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,30 +498,56 @@ impl GameState<'_>{
|
||||||
return currently_held;
|
return currently_held;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_deck(&mut self, currently_held : Vec<Card>) -> Deck{
|
fn reset_deck(&mut self, currently_held: Vec<Card>) {
|
||||||
self.deck = GameState::base_deck();
|
self.deck = GameState::<S>::base_deck();
|
||||||
for current_cart in currently_held {
|
for current_cart in currently_held {
|
||||||
self.deck.remove(self.deck.iter().position(|x| *x == current_cart).expect(&GAME_INTEGRITY_ERROR));
|
self.deck.remove(
|
||||||
|
self.deck
|
||||||
|
.iter()
|
||||||
|
.position(|x| *x == current_cart)
|
||||||
|
.expect(&GAME_INTEGRITY_ERROR)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return vec![];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_deck() -> Vec<Card> {
|
fn base_deck() -> Vec<Card> {
|
||||||
let mut deck: Deck = vec![];
|
let mut deck: Deck = vec![];
|
||||||
|
|
||||||
for color in [CardColors::BLACK, CardColors::BLUE, CardColors::GREEN, CardColors::RED, CardColors::YELLOW]{
|
for color in [
|
||||||
for value in [CardValue::CHANGE_COLOR, CardValue::EIGHT, CardValue::FIVE, CardValue::FOUR, CardValue::NINE, CardValue::ONE, CardValue::PLUS_FOUR, CardValue::PLUS_TWO, CardValue::REVERSE, CardValue::SEVEN, CardValue::SIX, CardValue::SKIP, CardValue::THREE, CardValue::TWO, CardValue::ZERO]{
|
CardColors::BLACK,
|
||||||
if ![CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR].contains(&value) {
|
CardColors::BLUE,
|
||||||
for _ in 0..1 {deck.push(Card{value : value.clone(), color : color.clone()})};
|
CardColors::GREEN,
|
||||||
|
CardColors::RED,
|
||||||
|
CardColors::YELLOW,
|
||||||
|
] {
|
||||||
|
for value in [
|
||||||
|
CardValue::CHANGE_COLOR,
|
||||||
|
CardValue::EIGHT,
|
||||||
|
CardValue::FIVE,
|
||||||
|
CardValue::FOUR,
|
||||||
|
CardValue::NINE,
|
||||||
|
CardValue::ONE,
|
||||||
|
CardValue::PLUS_FOUR,
|
||||||
|
CardValue::PLUS_TWO,
|
||||||
|
CardValue::REVERSE,
|
||||||
|
CardValue::SEVEN,
|
||||||
|
CardValue::SIX,
|
||||||
|
CardValue::SKIP,
|
||||||
|
CardValue::THREE,
|
||||||
|
CardValue::TWO,
|
||||||
|
CardValue::ZERO,
|
||||||
|
] {
|
||||||
|
if ![CardValue::CHANGE_COLOR, CardValue::PLUS_FOUR, CardValue::ZERO].contains(&value) && color != CardColors::BLACK {
|
||||||
|
for _ in 0..1 {
|
||||||
|
deck.push(Card { value: value, color: color });
|
||||||
}
|
}
|
||||||
else if value == CardValue::ZERO
|
} else if value == CardValue::ZERO && color != CardColors::BLACK{
|
||||||
{
|
deck.push(Card { value: value, color: color });
|
||||||
deck.push(Card{value : value, color : color.clone()});
|
} else {
|
||||||
|
for _ in 0..4 {
|
||||||
|
deck.push(Card { value: value, color: color });
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
for _ in 0..4 {deck.push(Card {value : value.clone(), color : color.clone()})};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//CHANGE COLOR
|
//CHANGE COLOR
|
||||||
|
|
@ -451,18 +630,131 @@ impl GameState<'_>{
|
||||||
let n: usize = rng.gen_range(0..self.deck.len());
|
let n: usize = rng.gen_range(0..self.deck.len());
|
||||||
cards_drawn.push(self.deck.remove(n));
|
cards_drawn.push(self.deck.remove(n));
|
||||||
}
|
}
|
||||||
return cards_drawn
|
cards_drawn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, player : &mut PlayerState, count : u32) -> Vec<Card>{
|
fn draw(&mut self, player_name: &str, count: u32) -> Vec<Card> {
|
||||||
if self.deck.len() <= count as usize{
|
if self.deck.len() <= (count as usize) {
|
||||||
let currently_held = self.get_currently_held();
|
let currently_held = self.get_currently_held();
|
||||||
self.reset_deck(currently_held);
|
self.reset_deck(currently_held);
|
||||||
}
|
}
|
||||||
let mut cards_drawn = self.get_cards(count);
|
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);
|
player.cards.append(&mut cards_drawn);
|
||||||
return cards_drawn;
|
}
|
||||||
|
}
|
||||||
|
cards_drawn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
|
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 : CardColors) -> Card{
|
||||||
|
for card in get_card_set(){
|
||||||
|
if card_value == card.value && card_color == card.color {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Card {value : CardValue::ZERO, color : CardColors::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: CardColors = CardColors::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 : CardColors::GREEN};
|
||||||
|
let player = PlayerState::new("detlef", [get_card(CardValue::FOUR, CardColors::RED), get_card(CardValue::EIGHT, CardColors::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 : CardColors::GREEN};
|
||||||
|
let player = PlayerState::new("detlef", [get_card(CardValue::FOUR, CardColors::RED), get_card(CardValue::EIGHT, CardColors::GREEN)].to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_if_legal_false(){
|
||||||
|
//illegal
|
||||||
|
let mut gs = get_test_gs();
|
||||||
|
let current_card = Card {value : CardValue::TWO, color : CardColors::GREEN};
|
||||||
|
assert_eq!(GameState::<VecDeque<u8>>::check_if_legal(current_card, get_card(CardValue::FOUR, CardColors::RED), CardColors::GREEN), false);
|
||||||
|
assert_eq!(GameState::<VecDeque<u8>>::check_if_legal(current_card, get_card(CardValue::EIGHT, CardColors::YELLOW), CardColors::GREEN), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
src/uno_server.rs
Normal file
18
src/uno_server.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
use std::net::{Incoming, TcpListener};
|
||||||
|
use Uno::uno_logic::PlayerConnection;
|
||||||
|
use Uno::uno_tcp::{self, PlayerConfiguration};
|
||||||
|
|
||||||
|
fn create_owner<'a>(incoming_stream : &mut Incoming) -> PlayerConfiguration<'a>{
|
||||||
|
Uno::uno_tcp::PlayerConfiguration::new(Uno::uno_logic::PlayerConnection::register_player(incoming_stream.next().unwrap().unwrap()).unwrap(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(){
|
||||||
|
let binding = TcpListener::bind("0.0.0.0:8000").unwrap();
|
||||||
|
let mut stream = binding.incoming();
|
||||||
|
let mut player_configurations = vec![];
|
||||||
|
if player_configurations.len() == 0{
|
||||||
|
player_configurations.push(create_owner(&mut stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,40 +3,19 @@ use std::{
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::uno_logic::PlayerConnection;
|
use crate::uno_logic::{PlayerConnection, PlayerState};
|
||||||
|
|
||||||
const PROTOCOL_ERROR_STRING: &str = "Client/Server protocol mismatch";
|
pub struct PlayerConfiguration<'a> {
|
||||||
|
pub player_connection : PlayerConnection<'a, TcpStream>,
|
||||||
pub fn register_player<'a>(mut stream : TcpStream) -> Result<PlayerConnection<'a>, String> {
|
pub is_owner : bool
|
||||||
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();
|
impl PlayerConfiguration<'_>{
|
||||||
return Ok(PlayerConnection { player_name : player_name[..].split(':').collect::<Vec<_>>()[1], stream : stream});
|
pub fn new<'a> (player_connection : PlayerConnection<'a, TcpStream>, is_owner : bool)-> PlayerConfiguration<'a> {
|
||||||
|
return PlayerConfiguration{player_connection : player_connection, is_owner : is_owner};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// fn register_player(player_name : &str, reader : BufReader, writer : BufWriter){
|
// fn register_player(player_name : &str, reader : BufReader, writer : BufWriter){
|
||||||
//
|
//
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user