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
|
||||
Cargo.lock
|
||||
.vs-code
|
||||
launch.json
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod uno_logic;
|
||||
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() {
|
||||
}
|
||||
|
|
|
|||
592
src/uno_logic.rs
592
src/uno_logic.rs
|
|
@ -1,14 +1,15 @@
|
|||
extern crate rand;
|
||||
use std::{
|
||||
io::{ BufReader, BufWriter }, net::TcpStream, usize, vec
|
||||
};
|
||||
use std::{ io::{ Read, Write }, net::TcpStream };
|
||||
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
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Eq)]
|
||||
#[derive(Hash)]
|
||||
pub enum CardValue {
|
||||
PLUS_TWO,
|
||||
PLUS_FOUR,
|
||||
|
|
@ -28,6 +29,9 @@ pub enum CardValue {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Eq)]
|
||||
#[derive(Hash)]
|
||||
pub enum CardColors {
|
||||
RED,
|
||||
BLUE,
|
||||
|
|
@ -41,65 +45,67 @@ pub enum Direction {
|
|||
COUNTER_CLOCKWISE,
|
||||
}
|
||||
|
||||
pub enum TURN_RESULT{
|
||||
pub enum TURN_RESULT {
|
||||
NEXT_TURN,
|
||||
REPEAT_TURN,
|
||||
GAME_OVER
|
||||
GAME_OVER,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerState<'a> {
|
||||
pub player_name: &'a str,
|
||||
pub cards: Vec<Card>,
|
||||
pub said_uno : bool,
|
||||
pub said_uno: bool,
|
||||
}
|
||||
|
||||
pub struct PlayerConnection<'a>{
|
||||
pub player_name : &'a str,
|
||||
pub stream : TcpStream,
|
||||
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: CardColors,
|
||||
}
|
||||
|
||||
pub struct GameState<'a>{
|
||||
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>>,
|
||||
pub current_color : CardColors,
|
||||
pub current_direction : Direction,
|
||||
plus_twos : u32,
|
||||
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: CardColors,
|
||||
pub current_direction: Direction,
|
||||
plus_twos: u32,
|
||||
}
|
||||
|
||||
type Deck = Vec<Card>;
|
||||
|
||||
// PartialEq trait implements
|
||||
|
||||
impl PartialEq for CardValue{
|
||||
impl PartialEq for CardValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
core::mem::discriminant(self) == core::mem::discriminant(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for CardColors{
|
||||
impl PartialEq for CardColors {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
core::mem::discriminant(self) == core::mem::discriminant(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Direction{
|
||||
impl PartialEq for Direction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
core::mem::discriminant(self) == core::mem::discriminant(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Card{
|
||||
impl PartialEq for Card {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value && self.color == other.color
|
||||
}
|
||||
|
|
@ -109,7 +115,7 @@ impl PartialEq for Card{
|
|||
|
||||
impl Clone for Card {
|
||||
fn clone(&self) -> Self {
|
||||
Self {value: self.value.clone(), color: self.color.clone()}
|
||||
Self { value: self.value.clone(), color: self.color.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +128,7 @@ impl Copy for CardValue {}
|
|||
//struct implementations
|
||||
|
||||
impl CardValue {
|
||||
pub fn to_string<'a>(self) -> &'a str{
|
||||
pub fn to_string<'a>(self) -> &'a str {
|
||||
match self {
|
||||
CardValue::PLUS_FOUR => "PLUS_FOUR",
|
||||
CardValue::CHANGE_COLOR => "CHANGE_COLOR",
|
||||
|
|
@ -138,237 +144,410 @@ impl CardValue {
|
|||
CardValue::SIX => "SIX",
|
||||
CardValue::SEVEN => "SEVEN",
|
||||
CardValue::EIGHT => "EIGHT",
|
||||
CardValue::NINE => "NINE"
|
||||
CardValue::NINE => "NINE",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CardColors {
|
||||
pub fn to_string<'a>(self) -> &'a str{
|
||||
pub fn to_string<'a>(self) -> &'a str {
|
||||
match self {
|
||||
CardColors::BLACK => "BLACK",
|
||||
CardColors::BLUE => "BLUE",
|
||||
CardColors::GREEN => "GREEN",
|
||||
CardColors::YELLOW => "YELLOW",
|
||||
CardColors::RED => "RED"
|
||||
CardColors::RED => "RED",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerState<'_> {
|
||||
pub fn new(player_name : &str, cards : Vec<Card>) -> PlayerState{
|
||||
pub fn new(player_name: &str, cards: Vec<Card>) -> PlayerState {
|
||||
PlayerState {
|
||||
player_name: player_name,
|
||||
cards: cards,
|
||||
said_uno : false,
|
||||
said_uno: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerConnection<'_> {
|
||||
pub fn new(player_name : &str, stream : TcpStream) -> PlayerConnection{
|
||||
PlayerConnection {
|
||||
player_name : player_name,
|
||||
stream : stream
|
||||
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 GameState<'_>{
|
||||
|
||||
fn cards_left(player : &PlayerState) -> usize{
|
||||
impl<S> GameState<'_, S> where S: Read + Write {
|
||||
fn cards_left(player: &PlayerState) -> usize {
|
||||
return player.cards.len();
|
||||
}
|
||||
|
||||
fn next_player(&mut self) {
|
||||
if self.current_direction == Direction::CLOCKWISE{
|
||||
if self.current_turn+1 >= self.player_states.len() as u32{
|
||||
self.current_turn = 0;
|
||||
return;
|
||||
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;
|
||||
}
|
||||
self.current_turn +=1;
|
||||
}
|
||||
else {
|
||||
if self.current_turn == 0{
|
||||
self.current_turn = (self.player_states.len()-1) as u32;
|
||||
return;
|
||||
Direction::COUNTER_CLOCKWISE => {
|
||||
if self.current_turn == 0 {
|
||||
self.current_turn = (self.player_states.len() -1) as u32;
|
||||
return;
|
||||
}
|
||||
self.current_turn -= 1;
|
||||
}
|
||||
self.current_turn -=1;
|
||||
}
|
||||
}
|
||||
|
||||
fn game_init(&mut self){
|
||||
let mut players : Vec<PlayerState> = vec![];
|
||||
for player in &self.player_states{
|
||||
players.push(player.to_owned());
|
||||
fn game_init(&mut self) {
|
||||
let players = self.player_states.clone();
|
||||
for player in players {
|
||||
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{
|
||||
for card in player.cards{
|
||||
if GameState::check_if_legal(self.current_card.clone(), card, self.current_color.clone()){
|
||||
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){
|
||||
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 {
|
||||
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 : 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 {
|
||||
fn check_if_legal(
|
||||
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;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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()){
|
||||
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::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();
|
||||
return TURN_RESULT::NEXT_TURN;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
self.draw(player.get_mut(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR), 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.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::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();
|
||||
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();
|
||||
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.turns+=1;
|
||||
return TURN_RESULT::NEXT_TURN
|
||||
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::cards_left(player.get(self.current_turn as usize).expect(&GAME_INTEGRITY_ERROR)) == 1{
|
||||
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.turns+=1;
|
||||
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{
|
||||
if card_to_be_played.value == CardValue::REVERSE {
|
||||
if self.current_direction == Direction::CLOCKWISE {
|
||||
self.current_direction = Direction::COUNTER_CLOCKWISE;
|
||||
}
|
||||
else {
|
||||
} 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{
|
||||
} 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::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.turns+=1;
|
||||
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>>) -> GameState<'a>{
|
||||
GameState{
|
||||
turns : 0,
|
||||
current_turn : 0,
|
||||
deck : GameState::base_deck(),
|
||||
trash : vec![],
|
||||
current_card : Card{ value : CardValue::ZERO, color : CardColors::RED},
|
||||
player_states : player_states.clone(),
|
||||
player_connections : player_connections,
|
||||
current_color : CardColors::RED,
|
||||
current_direction : Direction::CLOCKWISE,
|
||||
plus_twos : 0,
|
||||
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: CardColors::RED },
|
||||
player_states: player_states.clone(),
|
||||
player_connections: player_connections,
|
||||
current_color: CardColors::RED,
|
||||
current_direction: Direction::CLOCKWISE,
|
||||
plus_twos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
new_game.current_color = new_game.current_card.color.clone();
|
||||
new_game.current_color = new_game.current_card.color;
|
||||
return new_game;
|
||||
}
|
||||
|
||||
fn get_currently_held(&mut self) -> Vec<Card>{
|
||||
fn get_currently_held(&mut self) -> Vec<Card> {
|
||||
let mut currently_held = vec![];
|
||||
for hand in &mut self.player_states{
|
||||
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>) -> Deck{
|
||||
self.deck = GameState::base_deck();
|
||||
for current_cart in currently_held{
|
||||
self.deck.remove(self.deck.iter().position(|x| *x == current_cart).expect(&GAME_INTEGRITY_ERROR));
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
|
||||
fn base_deck()-> Vec<Card>{
|
||||
let mut deck : Deck = vec![];
|
||||
fn reset_deck(&mut self, currently_held: Vec<Card>) {
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for color in [CardColors::BLACK, CardColors::BLUE, 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].contains(&value) {
|
||||
for _ in 0..1 {deck.push(Card{value : value.clone(), color : color.clone()})};
|
||||
fn base_deck() -> Vec<Card> {
|
||||
let mut deck: Deck = vec![];
|
||||
|
||||
for color in [
|
||||
CardColors::BLACK,
|
||||
CardColors::BLUE,
|
||||
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 && color != CardColors::BLACK{
|
||||
deck.push(Card { value: value, color: color });
|
||||
} else {
|
||||
for _ in 0..4 {
|
||||
deck.push(Card { value: value, color: color });
|
||||
}
|
||||
}
|
||||
else if value == CardValue::ZERO
|
||||
{
|
||||
deck.push(Card{value : value, color : color.clone()});
|
||||
}
|
||||
else {
|
||||
for _ in 0..4 {deck.push(Card {value : value.clone(), color : color.clone()})};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//CHANGE COLOR
|
||||
|
|
@ -441,28 +620,141 @@ impl GameState<'_>{
|
|||
deck.push(Card{ value : CardValue::ZERO, color : CardColors::GREEN, color_change : CardColors::BLACK});
|
||||
deck.push(Card{ value : CardValue::ZERO, color : CardColors::YELLOW, color_change : CardColors::BLACK}); */
|
||||
|
||||
return deck;
|
||||
return deck;
|
||||
}
|
||||
|
||||
fn get_cards(&mut self, count : u32) -> Vec<Card>{
|
||||
fn get_cards(&mut self, count: u32) -> Vec<Card> {
|
||||
let mut cards_drawn = vec![];
|
||||
for _ in 0..count{
|
||||
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));
|
||||
}
|
||||
return cards_drawn
|
||||
cards_drawn
|
||||
}
|
||||
|
||||
fn draw(&mut self, player : &mut PlayerState, count : u32) -> Vec<Card>{
|
||||
if self.deck.len() <= count as usize{
|
||||
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);
|
||||
player.cards.append(&mut cards_drawn);
|
||||
return cards_drawn;
|
||||
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};
|
||||
|
||||
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,41 +3,20 @@ use std::{
|
|||
net::TcpStream,
|
||||
};
|
||||
|
||||
use crate::uno_logic::PlayerConnection;
|
||||
use crate::uno_logic::{PlayerConnection, PlayerState};
|
||||
|
||||
const PROTOCOL_ERROR_STRING: &str = "Client/Server protocol mismatch";
|
||||
|
||||
pub fn register_player<'a>(mut stream : TcpStream) -> Result<PlayerConnection<'a>, 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});
|
||||
pub struct PlayerConfiguration<'a> {
|
||||
pub player_connection : PlayerConnection<'a, TcpStream>,
|
||||
pub is_owner : bool
|
||||
}
|
||||
|
||||
impl PlayerConfiguration<'_>{
|
||||
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){
|
||||
//
|
||||
// }
|
||||
Loading…
Reference in New Issue
Block a user