diff --git a/Cargo.lock b/Cargo.lock index 186a878..8400afd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,8 +9,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "coup-cli" -version = "0.1.0" +name = "coup" +version = "0.0.1" dependencies = [ "rand", ] diff --git a/Cargo.toml b/Cargo.toml index 83e17ee..32f5444 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "coup-cli" -version = "0.1.0" +name = "coup" +version = "0.0.1" edition = "2021" authors = ["Dominik Wilkowski "] license = "GPL-3.0-or-later" diff --git a/src/bot.rs b/src/bot.rs index 746da8b..4e2092f 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -1,7 +1,15 @@ +//! The bot trait [BotInterface] and a couple types that help with the bot implementation + use std::fmt; use crate::{Action, Card, Counter, History, Score}; +/// A bot struct requires three public fields: +/// ```rust +/// pub name: String, +/// pub coins: u8, +/// pub cards: Vec, +/// ``` #[derive(Debug, Clone)] pub struct Bot { pub name: String, @@ -19,21 +27,37 @@ impl Bot { } } +/// A type to describe other bots still in the game #[derive(Debug, Clone)] pub struct OtherBot { + /// The name of the bot used to identify it in [Action] and [Counter] pub name: String, + /// The amount of coins this bot has pub coins: u8, + /// The amount of [Card] this bot still have pub cards: u8, } +/// The context struct is what is passed into each of the [BotInterface] methods +/// as arguments so the bot knows the context of the current move #[derive(Debug, Clone)] pub struct Context<'a> { + /// A list of all other bots minus the yourself pub other_bots: &'a [OtherBot], + /// A list of all discarded [Card] so far in the game pub discard_pile: &'a [Card], + /// A list of each events that have happened in this game so far pub history: &'a [History], + /// The current score of the game pub score: &'a Score, } +/// The BotInterface trait is what drives your bot. +/// You need to store a couple things for yourself which is what the getter and +/// setter methods are for and then implement each method below that defines +/// the behavior of your bot. +/// The default implementation is a static implementation of a bot like the +/// pre-build [crate::bots::StaticBot]. pub trait BotInterface { fn get_name(&self) -> String; fn get_coins(&self) -> u8; @@ -94,6 +118,21 @@ pub trait BotInterface { } } +/// The debug trait has been implemented to support both format and alternate +/// format which means you can print a bot instance with: +/// ```rust +/// let mut bot = Box::new(StaticBot::new(String::from("My static bot"))) as Box; +/// println!("{:?}", bot); +/// // Bot { name: "My static bot", coins: 2, cards: [] } +/// +/// // or +/// println!("{:#?}", bot); +/// // Bot { +/// // name: "My static bot" +/// // coins: 2 +/// // cards: [] +/// // } +/// ``` impl fmt::Debug for dyn BotInterface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { @@ -114,6 +153,14 @@ impl fmt::Debug for dyn BotInterface { } } +/// The display trait has been implemented which means you can print the avatar +/// of a bot instance with: +/// ```rust +/// let mut bot = Box::new(StaticBot::new(String::from("My static bot"))) as Box; +/// println!("{}", bot); +/// // [My static bot โ™กโ™ก ๐Ÿ’ฐ2] +/// ``` + impl fmt::Display for dyn BotInterface { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/src/bots/honest_bot.rs b/src/bots/honest_bot.rs index fd9deaa..8b8fc17 100644 --- a/src/bots/honest_bot.rs +++ b/src/bots/honest_bot.rs @@ -1,8 +1,14 @@ +//! A honest bot implementation for you to use to test your own bot with. + use crate::{ bot::{BotInterface, Context}, Action, Card, }; +/// The honest bot will try to take all actions it should take without being too +/// smart or strategic thinking. It will act on it's own cards, counter other +/// bots if they do something that it can counter based on its cards and will +/// never bluff itself pub struct HonestBot { pub name: String, pub coins: u8, @@ -41,11 +47,12 @@ impl BotInterface for HonestBot { } fn on_turn(&self, context: Context) -> Action { - // if self.get_coins() >= 10 { - let target = context.other_bots.iter().min_by_key(|bot| bot.cards).unwrap(); - Action::Coup(target.name.clone()) - // } else { - // Action::Income - // } + if self.get_coins() >= 10 { + let target = + context.other_bots.iter().min_by_key(|bot| bot.cards).unwrap(); + Action::Coup(target.name.clone()) + } else { + Action::Income + } } } diff --git a/src/bots/mod.rs b/src/bots/mod.rs index 615b258..e439271 100644 --- a/src/bots/mod.rs +++ b/src/bots/mod.rs @@ -1,3 +1,5 @@ +//! A collection of pre-built bots to test with + pub mod honest_bot; pub mod static_bot; // TODO: pub mod random_bot; diff --git a/src/bots/static_bot.rs b/src/bots/static_bot.rs index 82ae556..c1db3ac 100644 --- a/src/bots/static_bot.rs +++ b/src/bots/static_bot.rs @@ -1,5 +1,10 @@ +//! A static bot implementation for you to use to test your own bot with. + use crate::{bot::BotInterface, Card}; +/// The static bot only takes [crate::Action::Income] on turns and eventually is forced +/// by the engine to coup another bot. It won't challenge, counter or act on its +/// own cards at all. pub struct StaticBot { pub name: String, pub coins: u8, diff --git a/src/lib.rs b/src/lib.rs index 89bbce3..cb3f5bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,21 @@ pub mod bots; use crate::bot::{BotInterface, Context, OtherBot}; +/// One of the five cards you get in the game of Coup #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Card { + /// - [Action::Swapping] โ€“ Draw two character cards from the deck, choose which (if any) to exchange with your cards, then return two
+ /// - [Counter::Stealing] โ€“ Block someone from stealing coins from you Ambassador, + /// - [Action::Assassination] โ€“ Pay three coins and try to assassinate another player's character Assassin, + /// - [Action::Stealing] โ€“ Take two coins from another player + /// - [Counter::Stealing] โ€“ Block someone from stealing coins from you Captain, + /// - [Counter::Assassination] โ€“ Block an assassination attempt against yourself. Contessa, + /// - [Action::Tax] โ€“ Take three coins from the treasury
+ /// - [Counter::ForeignAid] โ€“ Block someone from taking foreign aid Duke, } @@ -42,14 +51,13 @@ pub enum Counter { Assassination, /// Block foreign aid with your [Card::Duke] ForeignAid, - /// Block stealing with your [Card::Captain] - StealingCaptain, - /// Block stealing with your [Card::Ambassador] - StealingAmbassador, - /// block tax with your [Card::Duke] + /// Block stealing with your [Card::Captain] or your [Card::Ambassador] + Stealing, + /// Block tax with your [Card::Duke] Tax, } +/// A collection on all possible moves in the game for bots to analyze #[derive(Debug, Clone, PartialEq, Eq)] pub enum History { ActionAssassination { by: String, target: String }, @@ -71,17 +79,19 @@ pub enum History { CounterTax { by: String, target: String }, } +/// The score of the game for all bots pub type Score = Vec<(String, u64)>; +/// The Coup game engine pub struct Coup { - pub bots: Vec>, - pub playing_bots: Vec, - pub deck: Vec, - pub discard_pile: Vec, - pub history: Vec, - pub score: Score, - pub turn: usize, - pub moves: usize, + bots: Vec>, + playing_bots: Vec, + deck: Vec, + discard_pile: Vec, + history: Vec, + score: Score, + turn: usize, + moves: usize, } impl Coup { @@ -101,7 +111,9 @@ impl Coup { } } - /// A public method to get a new deck + /// A public method to get a new deck. + /// This can be used by bots to make sure you get the same amount of cards as + /// the engine does pub fn new_deck() -> Vec { let mut deck = vec![ Card::Ambassador, @@ -147,7 +159,8 @@ impl Coup { > 1 } - /// Starting a round which means we setup the table, give each bots their cards and coins + /// Playing a game which means we setup the table, give each bots their cards + /// and coins and start the game loop pub fn play(&mut self) { // A fresh deck let mut deck = Coup::new_deck(); @@ -477,6 +490,17 @@ impl Coup { } } +/// The debug trait has been implemented to support both format and alternate +/// format which means you can print a game instance with: +/// ```rust +/// let mut my_coup = Coup::new(vec![]); +/// println!("{:?}", my_coup); +/// ``` +/// and +/// ```rust +/// let mut my_coup = Coup::new(vec![]); +/// println!("{:#?}", my_coup); +/// ``` impl fmt::Debug for Coup { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { diff --git a/src/main.rs b/src/main.rs index b630cff..89c03e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ -use coup_cli::{ +use coup::{ bots::{HonestBot, StaticBot}, Coup, }; fn main() { let mut coup_game = Coup::new(vec![ - Box::new(StaticBot::new(String::from("Bot 1"))), - Box::new(HonestBot::new(String::from("Bot 2"))), - Box::new(StaticBot::new(String::from("Bot 3"))), + Box::new(StaticBot::new(String::from("Charles"))), + Box::new(HonestBot::new(String::from("Tici"))), + Box::new(StaticBot::new(String::from("Novini"))), + Box::new(HonestBot::new(String::from("Dom"))), ]); coup_game.play();