Testbed Bot Plugins

# Testbed Bot Plugins

## Architecture Overview
### Testbed

Manages the game engine, tables, and bots.

Reads a configuration file to determine which bots to create and seat at tables.

Calls bot functions directly for maximum performance.

### Bots

Implemented as Rust modules or dynamic libraries.

Expose a standard interface for decision-making.

Can be compiled independently and loaded at runtime.

### Configuration File

Specifies which bots to create and their parameters.

Can be in JSON, TOML, or any other format.

### Dynamic Loading

Uses Rust's libloading crate to load bot implementations at runtime.

## Bot Interface
Define a trait that all bots must implement. This ensures a consistent interface for the testbed to interact with bots.

`pub trait PokerBot {
    fn make_decision(&self, game_state: &GameState) -> Action;
    fn name(&self) -> &str;
}`

## Dynamic Bot Loading
Use the libloading crate to load bot implementations dynamically at runtime. This allows the testbed to load bots specified in the configuration file without needing to know about them at compile time.

### Example Bot Implementation

`// bots/example_bot.rs
use super::{PokerBot, GameState, Action};

pub struct ExampleBot;

impl PokerBot for ExampleBot {
    fn make_decision(&self, game_state: &GameState) -> Action {
        // Implement decision-making logic here
        Action::Call
    }

    fn name(&self) -> &str {
        "ExampleBot"
    }
}

// Expose a function to create the bot
#[no_mangle]
pub fn create_bot() -> Box<dyn PokerBot> {
    Box::new(ExampleBot)
}`

### Loading the Bot

`use libloading::{Library, Symbol};

fn load_bot(lib_path: &str) -> Box<dyn PokerBot> {
    let lib = Library::new(lib_path).expect("Failed to load bot library");
    let create_bot: Symbol<unsafe extern "C" fn() -> Box<dyn PokerBot>> =
        unsafe { lib.get(b"create_bot").expect("Failed to load create_bot symbol") };
    unsafe { create_bot() }
}`

## Configuration File
Use a configuration file (e.g., config.toml) to specify which bots to load and their parameters.

### Example config.toml

`[[tables]]
bots = [
    { name = "ExampleBot", library_path = "./bots/libexample_bot.so" },
    { name = "AnotherBot", library_path = "./bots/libanother_bot.so" },
]`

### Parsing the Configuration

`use serde::Deserialize;
use std::fs;

#[derive(Debug, Deserialize)]
struct BotConfig {
    name: String,
    library_path: String,
}

#[derive(Debug, Deserialize)]
struct TableConfig {
    bots: Vec<BotConfig>,
}

#[derive(Debug, Deserialize)]
struct Config {
    tables: Vec<TableConfig>,
}

fn load_config(path: &str) -> Config {
    let config_str = fs::read_to_string(path).expect("Failed to read config file");
    toml::from_str(&config_str).expect("Failed to parse config file")
}`

## Testbed Implementation
The testbed reads the configuration file, loads the bots, and manages the game.

### Example Testbed

`struct Testbed {
    tables: Vec<Table>,
}

impl Testbed {
    pub fn new(config_path: &str) -> Self {
        let config = load_config(config_path);
        let tables = config.tables.into_iter().map(|table_config| {
            let bots = table_config.bots.into_iter().map(|bot_config| {
                load_bot(&bot_config.library_path)
            }).collect();
            Table::new(bots)
        }).collect();
        Self { tables }
    }

    pub fn run(&self) {
        for table in &self.tables {
            table.play_round();
        }
    }
}

struct Table {
    bots: Vec<Box<dyn PokerBot>>,
}

impl Table {
    pub fn new(bots: Vec<Box<dyn PokerBot>>) -> Self {
        Self { bots }
    }

    pub fn play_round(&self) {
        let game_state = GameState::new(); // Simulate game state
        for bot in &self.bots {
            let action = bot.make_decision(&game_state);
            println!("{} decided to {:?}", bot.name(), action);
        }
    }
}`


id: 7eaeded6dff941db802b8a4f2b3e3e0d
parent_id: ac48627b91354a148d2fe76acb6703d1
created_time: 2025-02-13T06:01:49.870Z
updated_time: 2025-02-13T06:06:41.944Z
is_conflict: 0
latitude: 48.20817430
longitude: 16.37381890
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 0
user_created_time: 2025-02-13T06:01:49.870Z
user_updated_time: 2025-02-13T06:06:41.944Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1