initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+6128
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "audiopoker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.19", features = ["webgl2"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
web-sys = { version = "0.3", features = ["SpeechRecognition", "SpeechSynthesis", "Window", "HtmlInputElement"] }
|
||||||
|
js-sys = "0.3"
|
||||||
|
futures = "0.3"
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
||||||
|
[target.wasm]
|
||||||
|
edition = 2024
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Projektplan: Audiopoker (Bevy + Wasm)
|
||||||
|
|
||||||
|
## Zielsetzung
|
||||||
|
Ein barrierefreies Multiplayer-Pokergame für blinde Spieler, das primär über Audio (TTS & SFX) bedient wird. Das Spiel wird via Webbrowser aufgerufen und auf WebAssembly kompiliert.
|
||||||
|
|
||||||
|
## Technologie-Stack
|
||||||
|
- **Engine:** Bevy (Rust)
|
||||||
|
- **Target:** WebAssembly (Wasm)
|
||||||
|
- **Networking:** WebSockets für die Synchronisation des Spielzustands.
|
||||||
|
- **Audio-Output:** Web Speech API (via JS-Bindings) für Text-to-Speech, `bevy_audio` für Soundeffekte.
|
||||||
|
- **Input:** Tastatur-zentrierte Steuerung (Tab/Space/Enter).
|
||||||
|
|
||||||
|
## Modul-Struktur
|
||||||
|
- `core::logic`: Reine Poker-Logik (Deck, Hand-Evaluation, Betting) ohne grafische Abhängigkeiten.
|
||||||
|
- `core::network`: Netzwerkprotokoll und WebSocket-Handhabung für Multiplayer-Synchronisation.
|
||||||
|
- `audio::tts`: Interface zur Web Speech API für dynamisches Sprechen von Spielereignissen.
|
||||||
|
- `audio::sfx`: Sound-Manager für atmosphärische Effekte (Chips, Karten mischen).
|
||||||
|
- `ui_minimal`: Minimalistische Darstellung, optimiert für Barrierefreiheit und geringen Ressourcenverbrauch.
|
||||||
|
|
||||||
|
## Implementierungsphasen
|
||||||
|
|
||||||
|
### Phase 1: Fundament & Core Logic (MVP)
|
||||||
|
- [ ] Projektstruktur aufsetzen (`Cargo.toml`, Bevy Config).
|
||||||
|
- [ ] **Poker Engine**: Hand-Rankings, Deck-Shuffling und Betting-Logik implementieren.
|
||||||
|
- [ ] **Audio-Grundlagen**: Integration der Web Speech API in die Rust-Umgebung.
|
||||||
|
|
||||||
|
### Phase 2: Networking & Multiplayer
|
||||||
|
- [ ] Server-Architektur für Spielräume und Spielerverwaltung aufbauen.
|
||||||
|
- [ ] Netzwerkprotokoll für Aktionen (Deal, Bet, Fold) definieren.
|
||||||
|
- [ ] Multiplayer-Lobby und Tischsuche implementieren.
|
||||||
|
|
||||||
|
### Phase 3: Audio Experience & UI
|
||||||
|
- [ ] TTS-Logik verfeinern (Kontextuelle Ausgaben).
|
||||||
|
- [ ] Soundeffekte einbinden.
|
||||||
|
- [ ] Minimalistisches, barrierefreies User Interface erstellen.
|
||||||
|
|
||||||
|
### Phase 4: Polishing & Testing
|
||||||
|
- [ ] Barrierefreiheits-Audit (Tastatur-Flow ohne Maus).
|
||||||
|
- [ ] Latenztests im Multiplayer.
|
||||||
|
- [ ] WebAssembly Deployment Vorbereitung.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
pub trait Speaker {
|
||||||
|
fn speak(&self, text: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DummySpeaker;
|
||||||
|
|
||||||
|
impl Speaker for DummySpeaker {
|
||||||
|
fn speak(&self, text: &str) {
|
||||||
|
println!("TTS (Dummy): {}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub struct WebSpeechSpeaker;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl WebSpeechSpeaker {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
WebSpeechSpeaker
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn speak(&self, text: &str) {
|
||||||
|
let window = web_sys::window().expect("No global window found");
|
||||||
|
let speech = window.speech_synthesis();
|
||||||
|
// We need to create an Utterance object.
|
||||||
|
// This requires `web-sys` with `SpeechSynthesisUtterance` feature.
|
||||||
|
// It's also a bit tricky because `SpeechSynthesisUtterance` has many required fields.
|
||||||
|
// For now, let's just have the structure ready.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub type SpeakerImpl = DummySpeaker;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub type SpeakerImpl = WebSpeechSpeaker;
|
||||||
|
|
||||||
|
pub fn get_speaker() -> Box<dyn Speaker> {
|
||||||
|
// This is a bit simplified, usually you'd use some factory or trait object pattern.
|
||||||
|
// Because of `wasm_bindgen` constraints on Trait Objects with methods taking &str...
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub mod tts;
|
||||||
|
pub mod sfx;
|
||||||
|
|
||||||
|
pub struct AudioEngine {
|
||||||
|
pub tts: tts::TtsEngine,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioEngine {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
tts: tts::TtsEngine::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
pub struct SfxEngine;
|
||||||
|
|
||||||
|
impl SfxEngine {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_sound(&self, _effect_id: &str) {
|
||||||
|
println!("Playing SFX: {}", effect_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
use web_sys::window;
|
||||||
|
|
||||||
|
pub struct TtsEngine;
|
||||||
|
|
||||||
|
impl TtsEngine {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn speak(&self, text: &str) {
|
||||||
|
if let Some(window) = window() {
|
||||||
|
let speech_synth = web_sys::speech_synthesis::SpeechSynthesis::new().unwrap();
|
||||||
|
// We need a SpeechSynthesisUtterance object. This requires some JS binding or manual construction via web-sys/js-sys.
|
||||||
|
// For now, just log that we would speak here.
|
||||||
|
println!("TTS Speaking: {}", text);
|
||||||
|
} else {
|
||||||
|
println!("No window found for TTS.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Suit {
|
||||||
|
Spades,
|
||||||
|
Hearts,
|
||||||
|
Diamonds,
|
||||||
|
Clubs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Rank(pub u8); // 2-14 (11=J, 12=Q, 13=K, 14=A)
|
||||||
|
|
||||||
|
impl Rank {
|
||||||
|
pub fn new(value: u8) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Card {
|
||||||
|
pub suit: Suit,
|
||||||
|
pub rank: Rank,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Card {
|
||||||
|
pub fn new(suit: Suit, rank_val: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
suit,
|
||||||
|
rank: Rank::new(rank_val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::core::logic::card::{Card, Rank};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Hand {
|
||||||
|
pub cards: Vec<Card>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hand {
|
||||||
|
pub fn new(cards: Vec<Card>) -> Self {
|
||||||
|
Self { cards }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the hand and returns a score or ranking.
|
||||||
|
/// This is currently a placeholder for the poker logic implementation.
|
||||||
|
pub fn evaluate(&self) -> u32 {
|
||||||
|
// TODO: Implement evaluation logic based on standard poker rules
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
pub mod card;
|
||||||
|
pub mod hand;
|
||||||
|
|
||||||
|
use card::Card;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
|
pub struct Deck {
|
||||||
|
pub cards: Vec<Card>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deck {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut cards = Vec::new();
|
||||||
|
let suits = [card::Suit::Spades, card::Suit::Hearts, card::Suit::Diamonds, card::Suit::Clubs];
|
||||||
|
for suit in suits.iter() {
|
||||||
|
for rank in 2..=14 {
|
||||||
|
cards.push(Card::new(*suit, rank));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self { cards }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shuffle(&mut self) {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
self.cards.shuffle(&mut rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
impl Deck {
|
||||||
|
pub fn deal_hand(&mut self, count: usize) -> Vec<Card> {
|
||||||
|
(0..count).filter_map(|_| self.draw()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be implemented in src/game.rs or similar?
|
||||||
|
// Let's add a Game manager to logic for now as the plan is minimal.
|
||||||
|
pub struct GameManager;
|
||||||
|
|
||||||
|
impl GameManager {
|
||||||
|
pub fn start_new_game(player_names: Vec<String>) -> GameState {
|
||||||
|
let mut players = Vec::new();
|
||||||
|
for (i, name) in player_names.into_iter().enumerate() {
|
||||||
|
players.push(Player::new(i as u32, &name));
|
||||||
|
}
|
||||||
|
|
||||||
|
GameState {
|
||||||
|
players,
|
||||||
|
pot: 0,
|
||||||
|
dealer_idx: 0,
|
||||||
|
community_cards: Vec::new(),
|
||||||
|
current_round: Round::PreFlop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user