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
ask - ask someone for cards
status - ask the bot to show the game general status
mystatus - ask the bot to send you your status
help - display commands info

View file

@ -1,7 +1,8 @@
use super::messages::GameCommand;
use crate::alias::Cx;
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::{actors::game::messages::Message, entities::game::Game};
use anyhow::Result;
@ -16,14 +17,14 @@ impl GameActor {
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)?;
cx.answer(GAME_STARTED).await?;
self.send_status_to_players(cx).await?;
Ok(())
self.send_status_to_players(&cx, &self.game.players).await?;
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();
self.game.execute(Action::Join(
@ -40,25 +41,132 @@ impl GameActor {
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(())
}
async fn status(&self, cx: Cx) -> Result<()> {
async fn status(&self, cx: &Cx) -> Result<()> {
Ok(())
}
async fn my_status(&self, cx: Cx) -> Result<()> {
async fn end(&mut self, cx: &Cx) -> Result<()> {
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(())
}
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;
for player in &self.game.players {
for player in players {
bot.send_message(
player.id.clone(),
format!(
@ -86,12 +194,11 @@ impl AsyncActor<Message> for GameActor {
async fn handle(&mut self, Message(cx, command): Message) -> Result<Self::Output> {
let result = match command {
GameCommand::Ask(to, card) => self.ask(cx, to, card).await,
GameCommand::End => self.end(cx).await,
GameCommand::Join => self.join(cx).await,
GameCommand::Start => self.start(cx).await,
GameCommand::Status => self.status(cx).await,
GameCommand::MyStatus => self.my_status(cx).await,
GameCommand::Ask(to, card) => self.ask(&cx, to, card).await,
GameCommand::End => self.end(&cx).await,
GameCommand::Join => self.join(&cx).await,
GameCommand::Start => self.start(&cx).await,
GameCommand::Status => self.status(&cx).await,
};
// Check errors ocurred
Ok(())

View file

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

View file

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

View file

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

View file

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