actor almost done

This commit is contained in:
Victor Martinez Montane 2021-08-27 15:35:59 +02:00
parent 6a8d741622
commit eb7098161a
6 changed files with 165 additions and 72 deletions

View file

@ -4,5 +4,4 @@ start - start the game
endgame - stop the game endgame - stop the game
ask - ask someone for cards ask - ask someone for cards
status - ask the bot to show the game general status status - ask the bot to show the game general status
mystatus - ask the bot to send you your status
help - display commands info help - display commands info

View file

@ -1,7 +1,8 @@
use super::messages::GameCommand; use super::messages::GameCommand;
use crate::alias::Cx; use crate::alias::Cx;
use crate::entities::actor::AsyncActor; use crate::entities::actor::AsyncActor;
use crate::entities::game::Action; use crate::entities::game::{Action, GameResults, GameState, TurnEvent};
use crate::entities::player::Player;
use crate::templates::*; use crate::templates::*;
use crate::{actors::game::messages::Message, entities::game::Game}; use crate::{actors::game::messages::Message, entities::game::Game};
use anyhow::Result; use anyhow::Result;
@ -16,14 +17,14 @@ impl GameActor {
Self { game: Game::new() } Self { game: Game::new() }
} }
async fn start(&mut self, cx: Cx) -> Result<()> { async fn start(&mut self, cx: &Cx) -> Result<()> {
self.game.execute(Action::Start)?; self.game.execute(Action::Start)?;
cx.answer(GAME_STARTED).await?; cx.answer(GAME_STARTED).await?;
self.send_status_to_players(cx).await?; self.send_status_to_players(&cx, &self.game.players).await?;
Ok(()) self.check_game_state(&cx).await
} }
async fn join(&mut self, cx: Cx) -> Result<()> { async fn join(&mut self, cx: &Cx) -> Result<()> {
let user = cx.update.from().unwrap(); let user = cx.update.from().unwrap();
self.game.execute(Action::Join( self.game.execute(Action::Join(
@ -40,25 +41,132 @@ impl GameActor {
Ok(()) Ok(())
} }
async fn ask(&mut self, cx: Cx, to: usize, card: usize) -> Result<()> { async fn ask(&mut self, cx: &Cx, to: usize, card: usize) -> Result<()> {
let from = cx.update.from().unwrap();
let events = self
.game
.execute(Action::Ask(format!("{}", from.id), to, card as u8))?;
for event in events {
match event {
TurnEvent::Took(quantity) => {
if quantity == 0 {
cx.answer(format!(
"{} had no cards with that number, lets draw!",
&self.game.players[to].name
))
.await?;
} else {
cx.answer(format!(
"{} had {} cards with the number {}, keep asking!",
&self.game.players[to].name, quantity, card
))
.await?;
self.send_asking_data(cx, &from.first_name).await?;
}
}
TurnEvent::Group(card) => {
cx.answer(format!(
"{} has made a group of four {}",
from.first_name, card
))
.await?;
}
_ => {}
}
}
if let GameState::Drawing(_) = self.game.state {
self.draw(cx, card).await?;
}
self.check_game_state(&cx).await
}
async fn draw(&mut self, cx: &Cx, card: usize) -> Result<()> {
let from = cx.update.from().unwrap();
let events = self
.game
.execute(Action::Draw(format!("{}", from.id), card as u8))?;
for event in events {
match event {
TurnEvent::Drawn(drawn) => {
cx.answer(format!("{} has drawn a card", &from.first_name))
.await?;
if drawn == (card as u8) {
cx.answer(format!(
"{} has drawn a {}!! Keep asking!",
&from.first_name, card
))
.await?;
}
}
TurnEvent::DeckEmpty => {
cx.answer("The deck is empty!!!").await?;
}
TurnEvent::Group(card) => {
cx.answer(format!(
"{} has made a group of four {}",
from.first_name, card
))
.await?;
}
_ => {}
}
}
Ok(()) Ok(())
} }
async fn status(&self, cx: Cx) -> Result<()> { async fn status(&self, cx: &Cx) -> Result<()> {
Ok(()) Ok(())
} }
async fn my_status(&self, cx: Cx) -> Result<()> { async fn end(&mut self, cx: &Cx) -> Result<()> {
Ok(()) Ok(())
} }
async fn end(&mut self, cx: Cx) -> Result<()> { async fn check_game_state(&self, cx: &Cx) -> Result<()> {
match &self.game.state {
GameState::Waiting => Ok(()),
GameState::Drawing(_) => Ok(()),
GameState::Asking(index) => {
self.send_asking_data(&cx, &self.game.players[*index].name)
.await
}
GameState::GameOver(GameResults { winners, score }) => {
self.send_game_over(&cx, winners, *score).await
}
}
}
async fn send_game_over(&self, cx: &Cx, winners: &[String], score: u8) -> Result<()> {
cx.answer(format!(
"Game Over!\n\tWinners 👑: {}\n\tScore: {}",
winners.join(", "),
score
))
.await?;
Ok(()) Ok(())
} }
async fn send_status_to_players(&self, cx: Cx) -> Result<()> { async fn send_asking_data(&self, cx: &Cx, name: &str) -> Result<()> {
let players_data: Vec<(usize, String)> = self
.game
.players
.iter()
.enumerate()
.map(|(index, player)| (index, player.name.clone()))
.collect();
cx.answer(
format!(
"{} lets ask someone for a card😇:\n\nType '/ask <option> <card> with one of the following options:\n\n{}'",
name,
players_data.iter().map(|(index, name)| format!("{}) {}", index, name)).collect::<Vec<String>>().join("\n")
)
).await?;
Ok(())
}
async fn send_status_to_players(&self, cx: &Cx, players: &[Player]) -> Result<()> {
let bot = &cx.requester; let bot = &cx.requester;
for player in &self.game.players { for player in players {
bot.send_message( bot.send_message(
player.id.clone(), player.id.clone(),
format!( format!(
@ -86,12 +194,11 @@ impl AsyncActor<Message> for GameActor {
async fn handle(&mut self, Message(cx, command): Message) -> Result<Self::Output> { async fn handle(&mut self, Message(cx, command): Message) -> Result<Self::Output> {
let result = match command { let result = match command {
GameCommand::Ask(to, card) => self.ask(cx, to, card).await, GameCommand::Ask(to, card) => self.ask(&cx, to, card).await,
GameCommand::End => self.end(cx).await, GameCommand::End => self.end(&cx).await,
GameCommand::Join => self.join(cx).await, GameCommand::Join => self.join(&cx).await,
GameCommand::Start => self.start(cx).await, GameCommand::Start => self.start(&cx).await,
GameCommand::Status => self.status(cx).await, GameCommand::Status => self.status(&cx).await,
GameCommand::MyStatus => self.my_status(cx).await,
}; };
// Check errors ocurred // Check errors ocurred
Ok(()) Ok(())

View file

@ -5,7 +5,6 @@ pub enum GameCommand {
Start, Start,
Join, Join,
Status, Status,
MyStatus,
Ask(usize, usize), Ask(usize, usize),
End, End,
} }
@ -18,7 +17,6 @@ impl From<Command> for GameCommand {
Command::EndGame => GameCommand::End, Command::EndGame => GameCommand::End,
Command::Ask { to, card } => GameCommand::Ask(to, card), Command::Ask { to, card } => GameCommand::Ask(to, card),
Command::Status => GameCommand::Status, Command::Status => GameCommand::Status,
Command::MyStatus => GameCommand::MyStatus,
_ => panic!("Cannot convert Command to GameCommand"), _ => panic!("Cannot convert Command to GameCommand"),
} }
} }

View file

@ -28,8 +28,6 @@ pub enum Command {
Ask { to: usize, card: usize }, Ask { to: usize, card: usize },
#[command(description = "ask the bot to show the game general status")] #[command(description = "ask the bot to show the game general status")]
Status, Status,
#[command(description = "ask the bot to send you your status")]
MyStatus,
#[command(description = "Show bot commands")] #[command(description = "Show bot commands")]
Help, Help,
} }

View file

@ -5,7 +5,7 @@ use rand::seq::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use serde::Serialize; use serde::Serialize;
pub enum TurnEvents { pub enum TurnEvent {
Started, Started,
Joined, Joined,
// Group(card) // Group(card)
@ -14,11 +14,7 @@ pub enum TurnEvents {
Drawn(u8), Drawn(u8),
// Took(quantity) // Took(quantity)
Took(u8), Took(u8),
// GameOver(winners, score)
GameOver(Vec<String>, u8),
DeckEmpty, DeckEmpty,
// NextTurn(username)
NextTurn(String),
} }
pub enum Action { pub enum Action {
@ -31,11 +27,18 @@ pub enum Action {
Draw(String, u8), Draw(String, u8),
} }
#[derive(Serialize, PartialEq, Clone, Debug)]
pub struct GameResults {
pub winners: Vec<String>,
pub score: u8,
}
#[derive(Serialize, PartialEq, Clone, Debug)] #[derive(Serialize, PartialEq, Clone, Debug)]
pub enum GameState { pub enum GameState {
Waiting, Waiting,
Asking(usize), Asking(usize),
Drawing(usize), Drawing(usize),
GameOver(GameResults),
} }
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
@ -54,7 +57,7 @@ impl Game {
} }
} }
pub fn execute(&mut self, action: Action) -> Result<Vec<TurnEvents>> { pub fn execute(&mut self, action: Action) -> Result<Vec<TurnEvent>> {
let events = match action { let events = match action {
Action::Start => self.start_game()?, Action::Start => self.start_game()?,
Action::Join(id, name) => self.join_player(&id, &name)?, Action::Join(id, name) => self.join_player(&id, &name)?,
@ -64,7 +67,7 @@ impl Game {
Ok(events) Ok(events)
} }
fn join_player(&mut self, player_id: &str, name: &str) -> Result<Vec<TurnEvents>> { fn join_player(&mut self, player_id: &str, name: &str) -> Result<Vec<TurnEvent>> {
if self.has_started() { if self.has_started() {
return Err(GameAlreadyStarted.into()); return Err(GameAlreadyStarted.into());
} }
@ -78,10 +81,10 @@ impl Game {
name: name.into(), name: name.into(),
id: player_id.into(), id: player_id.into(),
}); });
Ok(vec![TurnEvents::Joined]) Ok(vec![TurnEvent::Joined])
} }
fn start_game(&mut self) -> Result<Vec<TurnEvents>> { fn start_game(&mut self) -> Result<Vec<TurnEvent>> {
if self.has_started() { if self.has_started() {
return Err(GameAlreadyStarted.into()); return Err(GameAlreadyStarted.into());
} }
@ -91,10 +94,10 @@ impl Game {
player.cards.extend(self.deck.draw_n(7)); player.cards.extend(self.deck.draw_n(7));
} }
self.state = GameState::Asking(0); self.state = GameState::Asking(0);
return Ok(vec![TurnEvents::Started]); return Ok(vec![TurnEvent::Started]);
} }
pub fn ask_to(&mut self, player_id: String, to: usize, card: u8) -> Result<Vec<TurnEvents>> { pub fn ask_to(&mut self, player_id: String, to: usize, card: u8) -> Result<Vec<TurnEvent>> {
let mut events = vec![]; let mut events = vec![];
let index = self let index = self
.get_player_index(&player_id) .get_player_index(&player_id)
@ -106,7 +109,7 @@ impl Game {
return Err(InvalidQuestion(to, card).into()); return Err(InvalidQuestion(to, card).into());
} }
let cards = self.take_cards_from(to, card); let cards = self.take_cards_from(to, card);
events.push(TurnEvents::Took(cards.len() as u8)); events.push(TurnEvent::Took(cards.len() as u8));
let player = &mut self.players[index]; let player = &mut self.players[index];
// Set player state to drawing if no cards were taken // Set player state to drawing if no cards were taken
if cards.len() > 0 { if cards.len() > 0 {
@ -114,25 +117,20 @@ impl Game {
let groups = player.reduce_groups(); let groups = player.reduce_groups();
// Group player cards // Group player cards
for group in groups { for group in groups {
events.push(TurnEvents::Group(group)) events.push(TurnEvent::Group(group))
} }
} else { } else {
self.state = GameState::Drawing(index); self.state = GameState::Drawing(index);
} }
if !player.has_cards() || !self.players[to].has_cards() { if !player.has_cards() || !self.players[to].has_cards() {
let winners = self.get_winners(); self.game_over();
events.push(TurnEvents::GameOver(
winners.iter().map(|p| p.name.clone()).collect(),
winners[0].score,
));
self.end_game();
} }
Ok(events) Ok(events)
} }
pub fn draw_card(&mut self, player_id: String, chosen_card: u8) -> Result<Vec<TurnEvents>> { pub fn draw_card(&mut self, player_id: String, chosen_card: u8) -> Result<Vec<TurnEvent>> {
let mut events = vec![]; let mut events = vec![];
let index = self let index = self
.get_player_index(&player_id) .get_player_index(&player_id)
@ -143,36 +141,26 @@ impl Game {
let player = &mut self.players[index]; let player = &mut self.players[index];
let drawn = self.deck.draw_n(1); let drawn = self.deck.draw_n(1);
if drawn.len() == 0 { if drawn.len() == 0 {
events.push(TurnEvents::DeckEmpty); events.push(TurnEvent::DeckEmpty);
} }
if let Some(&card) = drawn.first() { if let Some(&card) = drawn.first() {
player.add_cards(&drawn); player.add_cards(&drawn);
events.push(TurnEvents::Drawn(card)); events.push(TurnEvent::Drawn(card));
let groups = player.reduce_groups();
for card in groups {
events.push(TurnEvent::Group(card));
}
if card != chosen_card { if card != chosen_card {
self.end_turn(); self.end_turn();
events.push(TurnEvents::NextTurn(
self.get_curr_player().unwrap().name.clone(),
))
} else { } else {
let groups = player.reduce_groups(); self.state = GameState::Asking(index);
for card in groups {
events.push(TurnEvents::Group(card));
}
if !player.has_cards() { if !player.has_cards() {
let winners = self.get_winners(); self.game_over();
events.push(TurnEvents::GameOver(
winners.iter().map(|p| p.name.clone()).collect(),
winners[0].score,
));
self.end_game();
} }
} }
} else { } else {
self.end_turn(); self.end_turn();
events.push(TurnEvents::NextTurn(
self.get_curr_player().unwrap().name.clone(),
))
} }
Ok(events) Ok(events)
@ -187,9 +175,13 @@ impl Game {
self.state = GameState::Asking(new_index); self.state = GameState::Asking(new_index);
} }
fn end_game(&mut self) { fn game_over(&mut self) {
let winners = self.get_winners();
self.state = GameState::GameOver(GameResults {
winners: winners.iter().map(|p| p.name.clone()).collect(),
score: winners[0].score,
});
self.deck = Deck::new(); self.deck = Deck::new();
self.state = GameState::Waiting;
self.players = vec![] self.players = vec![]
} }

View file

@ -44,16 +44,15 @@ impl Player {
.into_iter() .into_iter()
.filter(|&c| counter[c as usize - 1] < 4) .filter(|&c| counter[c as usize - 1] < 4)
.collect(); .collect();
let groups: Vec<u8> = let groups: Vec<u8> = counter
counter .iter()
.into_iter() .enumerate()
.enumerate() .fold(vec![], |mut acc, (index, curr)| {
.fold(vec![], |mut acc, (index, curr)| { if *curr >= 4 {
if *curr >= 4 { acc.push((index as u8) + 1);
acc.push((index as u8) + 1); }
} acc
acc });
});
self.score += groups.len() as u8; self.score += groups.len() as u8;
groups groups
} }