diff --git a/.beads/.gitignore b/.beads/.gitignore deleted file mode 100644 index d27a1db..0000000 --- a/.beads/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# SQLite databases -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm - -# Daemon runtime files -daemon.lock -daemon.log -daemon.pid -bd.sock -sync-state.json -last-touched - -# Local version tracking (prevents upgrade notification spam after git ops) -.local_version - -# Legacy database files -db.sqlite -bd.db - -# Worktree redirect file (contains relative path to main repo's .beads/) -# Must not be committed as paths would be wrong in other clones -redirect - -# Merge artifacts (temporary files from 3-way merge) -beads.base.jsonl -beads.base.meta.json -beads.left.jsonl -beads.left.meta.json -beads.right.jsonl -beads.right.meta.json - -# Sync state (local-only, per-machine) -# These files are machine-specific and should not be shared across clones -.sync.lock -sync_base.jsonl - -# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. -# They would override fork protection in .git/info/exclude, allowing -# contributors to accidentally commit upstream issue databases. -# The JSONL files (issues.jsonl, interactions.jsonl) and config files -# are tracked by git by default since no pattern above ignores them. diff --git a/.beads/README.md b/.beads/README.md deleted file mode 100644 index 50f281f..0000000 --- a/.beads/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Beads - AI-Native Issue Tracking - -Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. - -## What is Beads? - -Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. - -**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) - -## Quick Start - -### Essential Commands - -```bash -# Create new issues -bd create "Add user authentication" - -# View all issues -bd list - -# View issue details -bd show - -# Update issue status -bd update --status in_progress -bd update --status done - -# Sync with git remote -bd sync -``` - -### Working with Issues - -Issues in Beads are: -- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code -- **AI-friendly**: CLI-first design works perfectly with AI coding agents -- **Branch-aware**: Issues can follow your branch workflow -- **Always in sync**: Auto-syncs with your commits - -## Why Beads? - -✨ **AI-Native Design** -- Built specifically for AI-assisted development workflows -- CLI-first interface works seamlessly with AI coding agents -- No context switching to web UIs - -🚀 **Developer Focused** -- Issues live in your repo, right next to your code -- Works offline, syncs when you push -- Fast, lightweight, and stays out of your way - -🔧 **Git Integration** -- Automatic sync with git commits -- Branch-aware issue tracking -- Intelligent JSONL merge resolution - -## Get Started with Beads - -Try Beads in your own projects: - -```bash -# Install Beads -curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash - -# Initialize in your repo -bd init - -# Create your first issue -bd create "Try out Beads" -``` - -## Learn More - -- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) -- **Quick Start Guide**: Run `bd quickstart` -- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) - ---- - -*Beads: Issue tracking that moves at the speed of thought* ⚡ diff --git a/.beads/config.yaml b/.beads/config.yaml deleted file mode 100644 index 1de3590..0000000 --- a/.beads/config.yaml +++ /dev/null @@ -1,62 +0,0 @@ -# Beads Configuration File -# This file configures default behavior for all bd commands in this repository -# All settings can also be set via environment variables (BD_* prefix) -# or overridden with command-line flags - -# Issue prefix for this repository (used by bd init) -# If not set, bd init will auto-detect from directory name -# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. -# issue-prefix: "" - -# Use no-db mode: load from JSONL, no SQLite, write back after each command -# When true, bd will use .beads/issues.jsonl as the source of truth -# instead of SQLite database -# no-db: false - -# Disable daemon for RPC communication (forces direct database access) -# no-daemon: false - -# Disable auto-flush of database to JSONL after mutations -# no-auto-flush: false - -# Disable auto-import from JSONL when it's newer than database -# no-auto-import: false - -# Enable JSON output by default -# json: false - -# Default actor for audit trails (overridden by BD_ACTOR or --actor) -# actor: "" - -# Path to database (overridden by BEADS_DB or --db) -# db: "" - -# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON) -# auto-start-daemon: true - -# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE) -# flush-debounce: "5s" - -# Git branch for beads commits (bd sync will commit to this branch) -# IMPORTANT: Set this for team projects so all clones use the same sync branch. -# This setting persists across clones (unlike database config which is gitignored). -# Can also use BEADS_SYNC_BRANCH env var for local override. -# If not set, bd sync will require you to run 'bd config set sync.branch '. -sync-branch: "beads-sync" - -# Multi-repo configuration (experimental - bd-307) -# Allows hydrating from multiple repositories and routing writes to the correct JSONL -# repos: -# primary: "." # Primary repo (where this database lives) -# additional: # Additional repos to hydrate from (read-only) -# - ~/beads-planning # Personal planning repo -# - ~/work-planning # Work planning repo - -# Integration settings (access with 'bd config get/set') -# These are stored in the database, not in this file: -# - jira.url -# - jira.project -# - linear.url -# - linear.api-key -# - github.org -# - github.repo \ No newline at end of file diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl deleted file mode 100644 index e69de29..0000000 diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl deleted file mode 100644 index e69de29..0000000 diff --git a/.beads/metadata.json b/.beads/metadata.json deleted file mode 100644 index c787975..0000000 --- a/.beads/metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "database": "beads.db", - "jsonl_export": "issues.jsonl" -} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 807d598..0000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ - -# Use bd merge for beads JSONL files -.beads/issues.jsonl merge=beads diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 296820f..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,24 +0,0 @@ -# Agent Instructions - -This project uses **bd** (beads) for issue tracking. - -Run `bd prime` for workflow context, or install hooks (`bd hooks install`) for auto-injection. - -## Quick Reference - -- `bd ready` - Find unblocked work -- `bd create "Title" --type task --priority 2` - Create issue -- `bd update --status in_progress` # Claim work -- `bd close ` - Complete work -- `bd sync` - Sync with git (run at session end) - -## Landing the Plane (Session Completion) - -**When ending a work session**, you MUST complete ALL steps below. - -**MANDATORY WORKFLOW:** - -1. **File issues for remaining work** - Create issues for anything that needs follow-up -2. **Run quality gates** (if code changed) - Tests, linters, builds -3. **Update issue status** - Close finished work, update in-progress items -4. **Hand off** - Provide context for next session diff --git a/store/pyproject.toml b/bot/pyproject.toml similarity index 100% rename from store/pyproject.toml rename to bot/pyproject.toml diff --git a/bot/src/training_common.rs b/bot/src/training_common.rs index 3754086..8c85021 100644 --- a/bot/src/training_common.rs +++ b/bot/src/training_common.rs @@ -15,8 +15,7 @@ pub const ACTION_SPACE_SIZE: usize = 514; pub enum TrictracAction { /// Lancer les dés Roll, - /// Faire un nouveau 'relevé' (repositionnement des dames à l'état de départ) après avoir gagné un trou, - /// au lieu de continuer dans la position courante + /// Continuer après avoir gagné un trou Go, /// Effectuer un mouvement de pions Move { diff --git a/doc/ai/history/beads.md b/doc/ai/history/beads.md deleted file mode 100644 index dad9923..0000000 --- a/doc/ai/history/beads.md +++ /dev/null @@ -1,24 +0,0 @@ -```sh -❯ bd init - Repository ID: d5459d4d - Clone ID: 73ab432945c43882 - ✓ Created AGENTS.md with landing-the-plane instructions - -✓ bd initialized successfully! - - Database: .beads/beads.db - Issue prefix: trictrac - Issues will be named: trictrac- (e.g., trictrac-a3f2dd) - -Run bd quickstart to get started. - -⚠ Setup incomplete. Some issues were detected: - • Git Hooks: Missing 1 recommended hook(s) - • Sync Divergence: 1 sync divergence issue(s) detected - • Claude Integration: Not configured - • Git Working Tree: Uncommitted changes present - • Version Tracking: Version tracking not initialized - • Sync Branch Config: sync-branch not configured - -Run bd doctor --fix to see details and fix these issues. -``` diff --git a/store/src/pyengine.rs b/store/src/pyengine.rs index b436baa..af2b650 100644 --- a/store/src/pyengine.rs +++ b/store/src/pyengine.rs @@ -3,7 +3,7 @@ use pyo3::prelude::*; use pyo3::types::PyDict; use crate::board::CheckerMove; -use crate::dice::{Dice, DiceRoller}; +use crate::dice::Dice; use crate::game::{GameEvent, GameState, Stage, TurnStage}; use crate::game_rules_moves::MoveRules; use crate::game_rules_points::PointsRules; @@ -24,7 +24,7 @@ impl TricTrac { // Initialiser 2 joueurs game_state.init_player("player1"); - game_state.init_player("player2"); + game_state.init_player("bot"); // Commencer la partie avec le joueur 1 game_state.consume(&GameEvent::BeginGame { goes_first: 1 }); @@ -36,81 +36,50 @@ impl TricTrac { } } - /// Obtenir l'état du jeu sous forme de dictionnaire - fn get_state_dict<'py>(&self, py: Python<'py>) -> PyResult> { - let dict = PyDict::new(py); - dict.set_item("stage", format!("{:?}", self.game_state.stage))?; - dict.set_item("turn_stage", format!("{:?}", self.game_state.turn_stage))?; - dict.set_item("active_player_id", self.game_state.active_player_id)?; - - // Board - let board_list = self.game_state.board.to_vec(); // returns Vec - dict.set_item("board", board_list)?; - - // Dice - dict.set_item("dice", (self.game_state.dice.values.0, self.game_state.dice.values.1))?; - - // Players - let players_dict = PyDict::new(py); - for (id, player) in &self.game_state.players { - let p_dict = PyDict::new(py); - p_dict.set_item("color", format!("{:?}", player.color))?; - p_dict.set_item("holes", player.holes)?; - p_dict.set_item("points", player.points)?; - p_dict.set_item("can_bredouille", player.can_bredouille)?; - p_dict.set_item("dice_roll_count", player.dice_roll_count)?; - players_dict.set_item(id, p_dict)?; - } - dict.set_item("players", players_dict)?; - - Ok(dict) - } - - /// Lance les dés ou utilise la séquence prédéfinie - fn roll_dice(&mut self) -> PyResult<(u8, u8)> { - let player_id = self.game_state.active_player_id; - - if self.game_state.turn_stage != TurnStage::RollDice { - return Err(pyo3::exceptions::PyRuntimeError::new_err("Not in RollDice stage")); - } - - self.game_state.consume(&GameEvent::Roll { player_id }); - - let dice = if self.current_dice_index < self.dice_roll_sequence.len() { - let vals = self.dice_roll_sequence[self.current_dice_index]; - self.current_dice_index += 1; - Dice { values: vals } - } else { - DiceRoller::default().roll() - }; - - self.game_state.consume(&GameEvent::RollResult { player_id, dice }); - - Ok(dice.values) - } - - /// Applique un mouvement (deux déplacements de dames) - fn apply_move(&mut self, from1: usize, to1: usize, from2: usize, to2: usize) -> PyResult<()> { - let player_id = self.game_state.active_player_id; - - let m1 = CheckerMove::new(from1, to1).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?; - let m2 = CheckerMove::new(from2, to2).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?; - - let moves = (m1, m2); - - if !self.game_state.validate(&GameEvent::Move { player_id, moves }) { - return Err(pyo3::exceptions::PyValueError::new_err("Invalid move")); - } - - self.game_state.consume(&GameEvent::Move { player_id, moves }); - Ok(()) - } - /// Obtenir l'état du jeu sous forme de chaîne de caractères compacte fn get_state_id(&self) -> String { self.game_state.to_string_id() } + /// Obtenir l'état du jeu sous forme de dictionnaire pour faciliter l'entrainement + fn get_state_dict(&self) -> PyResult> { + Python::with_gil(|py| { + let state_dict = PyDict::new(py); + + // Informations essentielles sur l'état du jeu + state_dict.set_item("active_player", self.game_state.active_player_id)?; + state_dict.set_item("stage", format!("{:?}", self.game_state.stage))?; + state_dict.set_item("turn_stage", format!("{:?}", self.game_state.turn_stage))?; + + // Dés + let (dice1, dice2) = self.game_state.dice.values; + state_dict.set_item("dice", (dice1, dice2))?; + + // Points des joueurs + if let Some(white_player) = self.game_state.get_white_player() { + state_dict.set_item("white_points", white_player.points)?; + state_dict.set_item("white_holes", white_player.holes)?; + } + + if let Some(black_player) = self.game_state.get_black_player() { + state_dict.set_item("black_points", black_player.points)?; + state_dict.set_item("black_holes", black_player.holes)?; + } + + // Positions des pièces + let white_positions = self.get_checker_positions(Color::White); + let black_positions = self.get_checker_positions(Color::Black); + + state_dict.set_item("white_positions", white_positions)?; + state_dict.set_item("black_positions", black_positions)?; + + // État compact pour la comparaison d'états + state_dict.set_item("state_id", self.game_state.to_string_id())?; + + Ok(state_dict.into()) + }) + } + /// Renvoie les positions des pièces pour un joueur spécifique fn get_checker_positions(&self, color: Color) -> Vec<(usize, i8)> { self.game_state.board.get_color_fields(color) @@ -146,6 +115,144 @@ impl TricTrac { .collect() } + /// Jouer un coup ((from1, to1), (from2, to2)) + fn play_move(&mut self, moves: ((usize, usize), (usize, usize))) -> bool { + let ((from1, to1), (from2, to2)) = moves; + + // Vérifier que c'est au tour du joueur de jouer + if self.game_state.turn_stage != TurnStage::Move + && self.game_state.turn_stage != TurnStage::HoldOrGoChoice + { + return false; + } + + let move1 = CheckerMove::new(from1, to1).unwrap_or_default(); + let move2 = CheckerMove::new(from2, to2).unwrap_or_default(); + + let event = GameEvent::Move { + player_id: self.game_state.active_player_id, + moves: (move1, move2), + }; + + // Vérifier si le mouvement est valide + if !self.game_state.validate(&event) { + return false; + } + + // Exécuter le mouvement + self.game_state.consume(&event); + + // Si l'autre joueur doit lancer les dés maintenant, simuler ce lancement + if self.game_state.turn_stage == TurnStage::RollDice { + self.roll_dice(); + } + + true + } + + /// Lancer les dés (soit aléatoirement, soit en utilisant une séquence prédéfinie) + fn roll_dice(&mut self) -> (u8, u8) { + // Vérifier que c'est au bon moment pour lancer les dés + if self.game_state.turn_stage != TurnStage::RollDice + && self.game_state.turn_stage != TurnStage::RollWaiting + { + return self.game_state.dice.values; + } + + // Simuler un lancer de dés + let dice_values = if !self.dice_roll_sequence.is_empty() + && self.current_dice_index < self.dice_roll_sequence.len() + { + // Utiliser la séquence prédéfinie + let dice = self.dice_roll_sequence[self.current_dice_index]; + self.current_dice_index += 1; + dice + } else { + // Générer aléatoirement + ( + (1 + (rand::random::() % 6)), + (1 + (rand::random::() % 6)), + ) + }; + + // Envoyer les événements appropriés + let roll_event = GameEvent::Roll { + player_id: self.game_state.active_player_id, + }; + + if self.game_state.validate(&roll_event) { + self.game_state.consume(&roll_event); + } + + let roll_result_event = GameEvent::RollResult { + player_id: self.game_state.active_player_id, + dice: Dice { + values: dice_values, + }, + }; + + if self.game_state.validate(&roll_result_event) { + self.game_state.consume(&roll_result_event); + } + + dice_values + } + + /// Marquer des points + fn mark_points(&mut self, points: u8) -> bool { + // Vérifier que c'est au bon moment pour marquer des points + if self.game_state.turn_stage != TurnStage::MarkPoints + && self.game_state.turn_stage != TurnStage::MarkAdvPoints + { + return false; + } + + let event = GameEvent::Mark { + player_id: self.game_state.active_player_id, + points, + }; + + // Vérifier si l'événement est valide + if !self.game_state.validate(&event) { + return false; + } + + // Exécuter l'événement + self.game_state.consume(&event); + + // Si l'autre joueur doit lancer les dés maintenant, simuler ce lancement + if self.game_state.turn_stage == TurnStage::RollDice { + self.roll_dice(); + } + + true + } + + /// Choisir de "continuer" (Go) après avoir gagné un trou + fn choose_go(&mut self) -> bool { + // Vérifier que c'est au bon moment pour choisir de continuer + if self.game_state.turn_stage != TurnStage::HoldOrGoChoice { + return false; + } + + let event = GameEvent::Go { + player_id: self.game_state.active_player_id, + }; + + // Vérifier si l'événement est valide + if !self.game_state.validate(&event) { + return false; + } + + // Exécuter l'événement + self.game_state.consume(&event); + + // Simuler le lancer de dés pour le prochain tour + self.roll_dice(); + + true + } + /// Calcule les points maximaux que le joueur actif peut obtenir avec les dés actuels fn calculate_points(&self) -> u8 { let active_player = self @@ -173,7 +280,7 @@ impl TricTrac { // Initialiser 2 joueurs self.game_state.init_player("player1"); - self.game_state.init_player("player2"); + self.game_state.init_player("bot"); // Commencer la partie avec le joueur 1 self.game_state