mirror of
https://codeberg.org/JasterV/gofish_bot.git
synced 2026-04-26 18:10:09 +00:00
actor almost done
This commit is contained in:
parent
6a8d741622
commit
eb7098161a
6 changed files with 165 additions and 72 deletions
|
|
@ -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
|
||||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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![]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue