id: f110f4403a77491eb68bc44127d61ab9
parent_id: 
item_type: 1
item_id: a19d6f572de548d2bcdf7d9635cf1b4d
item_updated_time: 1780221411096
title_diff: "[{\"diffs\":[[1,\"Testbed Bot Plugins\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":19}]"
body_diff: "[{\"diffs\":[[1,\"# Testbed Bot Plugins\\\n\\\n## Architecture Overview\\\n### Testbed\\\n\\\nManages the game engine, tables, and bots.\\\n\\\nReads a configuration file to determine which bots to create and seat at tables.\\\n\\\nCalls bot functions directly for maximum performance.\\\n\\\n### Bots\\\n\\\nImplemented as Rust modules or dynamic libraries.\\\n\\\nExpose a standard interface for decision-making.\\\n\\\nCan be compiled independently and loaded at runtime.\\\n\\\n### Configuration File\\\n\\\nSpecifies which bots to create and their parameters.\\\n\\\nCan be in JSON, TOML, or any other format.\\\n\\\n### Dynamic Loading\\\n\\\nUses Rust's libloading crate to load bot implementations at runtime.\\\n\\\n## Bot Interface\\\nDefine a trait that all bots must implement. This ensures a consistent interface for the testbed to interact with bots.\\\n\\\n`pub trait PokerBot {\\\n    fn make_decision(&self, game_state: &GameState) -> Action;\\\n    fn name(&self) -> &str;\\\n}`\\\n\\\n## Dynamic Bot Loading\\\nUse 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.\\\n\\\n### Example Bot Implementation\\\n\\\n`// bots/example_bot.rs\\\nuse super::{PokerBot, GameState, Action};\\\n\\\npub struct ExampleBot;\\\n\\\nimpl PokerBot for ExampleBot {\\\n    fn make_decision(&self, game_state: &GameState) -> Action {\\\n        // Implement decision-making logic here\\\n        Action::Call\\\n    }\\\n\\\n    fn name(&self) -> &str {\\\n        \\\"ExampleBot\\\"\\\n    }\\\n}\\\n\\\n// Expose a function to create the bot\\\n#[no_mangle]\\\npub fn create_bot() -> Box<dyn PokerBot> {\\\n    Box::new(ExampleBot)\\\n}`\\\n\\\n### Loading the Bot\\\n\\\n`use libloading::{Library, Symbol};\\\n\\\nfn load_bot(lib_path: &str) -> Box<dyn PokerBot> {\\\n    let lib = Library::new(lib_path).expect(\\\"Failed to load bot library\\\");\\\n    let create_bot: Symbol<unsafe extern \\\"C\\\" fn() -> Box<dyn PokerBot>> =\\\n        unsafe { lib.get(b\\\"create_bot\\\").expect(\\\"Failed to load create_bot symbol\\\") };\\\n    unsafe { create_bot() }\\\n}`\\\n\\\n## Configuration File\\\nUse a configuration file (e.g., config.toml) to specify which bots to load and their parameters.\\\n\\\n### Example config.toml\\\n\\\n`[[tables]]\\\nbots = [\\\n    { name = \\\"ExampleBot\\\", library_path = \\\"./bots/libexample_bot.so\\\" },\\\n    { name = \\\"AnotherBot\\\", library_path = \\\"./bots/libanother_bot.so\\\" },\\\n]`\\\n\\\n### Parsing the Configuration\\\n\\\n`use serde::Deserialize;\\\nuse std::fs;\\\n\\\n#[derive(Debug, Deserialize)]\\\nstruct BotConfig {\\\n    name: String,\\\n    library_path: String,\\\n}\\\n\\\n#[derive(Debug, Deserialize)]\\\nstruct TableConfig {\\\n    bots: Vec<BotConfig>,\\\n}\\\n\\\n#[derive(Debug, Deserialize)]\\\nstruct Config {\\\n    tables: Vec<TableConfig>,\\\n}\\\n\\\nfn load_config(path: &str) -> Config {\\\n    let config_str = fs::read_to_string(path).expect(\\\"Failed to read config file\\\");\\\n    toml::from_str(&config_str).expect(\\\"Failed to parse config file\\\")\\\n}`\\\n\\\n## Testbed Implementation\\\nThe testbed reads the configuration file, loads the bots, and manages the game.\\\n\\\n### Example Testbed\\\n\\\n`struct Testbed {\\\n    tables: Vec<Table>,\\\n}\\\n\\\nimpl Testbed {\\\n    pub fn new(config_path: &str) -> Self {\\\n        let config = load_config(config_path);\\\n        let tables = config.tables.into_iter().map(|table_config| {\\\n            let bots = table_config.bots.into_iter().map(|bot_config| {\\\n                load_bot(&bot_config.library_path)\\\n            }).collect();\\\n            Table::new(bots)\\\n        }).collect();\\\n        Self { tables }\\\n    }\\\n\\\n    pub fn run(&self) {\\\n        for table in &self.tables {\\\n            table.play_round();\\\n        }\\\n    }\\\n}\\\n\\\nstruct Table {\\\n    bots: Vec<Box<dyn PokerBot>>,\\\n}\\\n\\\nimpl Table {\\\n    pub fn new(bots: Vec<Box<dyn PokerBot>>) -> Self {\\\n        Self { bots }\\\n    }\\\n\\\n    pub fn play_round(&self) {\\\n        let game_state = GameState::new(); // Simulate game state\\\n        for bot in &self.bots {\\\n            let action = bot.make_decision(&game_state);\\\n            println!(\\\"{} decided to {:?}\\\", bot.name(), action);\\\n        }\\\n    }\\\n}`\\\n\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":3897}]"
metadata_diff: {"new":{"id":"a19d6f572de548d2bcdf7d9635cf1b4d","parent_id":"2508f7a375a148eaa747c8f5c2147160","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_updated_time":1739426801944,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":1780221411096},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-05-31T10:06:29.021Z
created_time: 2026-05-31T10:06:29.021Z
type_: 13