From bea146d4391566619a6a1e5f9a0c8a94f2f72e6e Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Fri, 9 Jan 2026 06:54:17 -0800 Subject: [PATCH 01/10] shuffle --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/game/mod.rs | 16 +++++++++++++--- src/game/wall.rs | 21 +++++++++++++++++---- src/lib.rs | 2 ++ src/main.rs | 9 +++------ src/tiles.rs | 28 +++++++++++++--------------- src/tui/console.rs | 10 ++++------ src/tui/mod.rs | 39 ++++++++++++++++++++++++++++++++------- 9 files changed, 88 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49cf5ed..02ab506 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3274,8 +3274,10 @@ dependencies = [ "bevy_ratatui", "clap", "log", + "rand 0.9.2", "ratatui", "strum", + "tracing", "tracing-subscriber", "tui-logger", ] diff --git a/Cargo.toml b/Cargo.toml index fe51bfd..8ba3c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,10 @@ log = { version = "0.4.29", features = [ "release_max_level_error", "max_level_trace", ] } +rand = "0.9.2" ratatui = "0.30.0" strum = { version = "0.27.2", features = ["derive"] } +tracing = "0.1.44" tracing-subscriber = "0.3.22" tui-logger = { version = "0.18.0", features = ["tracing-support", "crossterm"] } diff --git a/src/game/mod.rs b/src/game/mod.rs index 14eeaf2..577e397 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,10 +1,9 @@ - use bevy::prelude::*; use crate::tiles::{self, *}; mod player; -pub(crate) mod wall; +pub mod wall; pub struct Riichi; @@ -13,10 +12,21 @@ impl Plugin for Riichi { app.init_resource::() .add_systems(Startup, init_match) .add_systems(Startup, tiles::init_tiles) - .add_systems(Startup, wall::build_wall); + .add_systems(Update, wall::build_wall) + // semicolon stopper + ; } } +#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)] +enum GameState { + #[default] + Setup, + Deal, + Play, + Score, +} + #[derive(Component)] pub(crate) struct Dice(u8, u8); diff --git a/src/game/wall.rs b/src/game/wall.rs index 2692952..4de0558 100644 --- a/src/game/wall.rs +++ b/src/game/wall.rs @@ -1,11 +1,24 @@ - +use bevy::log::tracing::instrument; use bevy::prelude::*; +use rand::seq::SliceRandom; +use crate::game::InWall; use crate::tiles::Tile; #[derive(Component)] -pub(crate) struct Wall(Vec); +#[relationship_target(relationship = InWall)] +pub struct Wall(Vec); -pub(crate) fn build_wall(_tiles: Query<&Tile>) { - info!("built a wall!") +#[instrument(level = "trace", skip_all)] +pub(crate) fn build_wall(mut commands: Commands, tiles: Query>) -> Result { + let mut rng = rand::rng(); + + let mut wall = commands.spawn(Wall(vec![])); + + let mut shuffled = tiles.iter().collect::>(); + shuffled.shuffle(&mut rng); + + wall.replace_children(&shuffled); + + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 8a1fb07..1740455 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + pub mod tiles; pub mod yakus; diff --git a/src/main.rs b/src/main.rs index 641d6df..5c8cbe2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ +#![allow(unused)] + use bevy::prelude::*; use clap::{Parser, Subcommand}; -use tracing_subscriber::{layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod gui; mod tui; @@ -18,11 +20,6 @@ enum Mode { } fn main() { - // tracing_subscriber::fmt() - // .with_writer(std::io::stderr) - // .with_env_filter("warn,jong=trace") - // .init(); - let args = Args::parse(); let mut app = App::new(); diff --git a/src/tiles.rs b/src/tiles.rs index c00ef9c..7716df0 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,24 +1,22 @@ use bevy::{ecs::entity::MapEntities, prelude::*}; use strum::FromRepr; - -// #[derive(Component)] -// #[derive(relasionship(re))] -// pub struct TileEntity { -// #[relationship] -// pub parent: Entity, - -// r#type: Tile, -// } - -struct Hand; +use crate::game::wall::Wall; #[derive(Component)] +#[relationship(relationship_target = Wall)] +pub struct InWall(pub Entity); + +// #[derive(Component)] +// #[relationship(relationship_target = WallTiles)] +// pub(crate) struct InHand(pub Entity); + +#[derive(Component, Debug)] pub struct Tile { suit: Suit, } -#[derive(MapEntities)] +#[derive(MapEntities, Debug)] pub enum Suit { Pin(Rank), Sou(Rank), @@ -27,10 +25,10 @@ pub enum Suit { Dragon(Dragon), } -#[derive(Deref, DerefMut)] +#[derive(Deref, DerefMut, Debug)] pub struct Rank(u8); -#[derive(FromRepr)] +#[derive(FromRepr, Debug)] pub enum Wind { Ton, Nan, @@ -38,7 +36,7 @@ pub enum Wind { Pei, } -#[derive(FromRepr)] +#[derive(Debug, FromRepr)] pub enum Dragon { Haku, Hatsu, diff --git a/src/tui/console.rs b/src/tui/console.rs index db20cf8..97b346c 100644 --- a/src/tui/console.rs +++ b/src/tui/console.rs @@ -1,13 +1,12 @@ -use tui_logger::TuiLoggerWidget; - -use bevy_ratatui::RatatuiContext; - use bevy::input::keyboard::Key; +use bevy::prelude::*; +use bevy_ratatui::RatatuiContext; +use tui_logger::TuiLoggerWidget; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] pub(crate) enum ConsoleState { - Open, #[default] + Open, Closed, } @@ -28,7 +27,6 @@ pub(crate) fn toggle_console( mut next_state: ResMut>, ) { if input.just_pressed(Key::Character("`".into())) { - trace!("toggled"); next_state.set(!*curr_state.get()); } } diff --git a/src/tui/mod.rs b/src/tui/mod.rs index d797779..d960f2e 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,10 +1,9 @@ use std::time::Duration; -use bevy::{app::ScheduleRunnerPlugin, input::keyboard::Key, prelude::*, state::app::StatesPlugin}; +use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; use bevy_ratatui::{RatatuiContext, RatatuiPlugins}; -use jong::tiles::Tile; -use tui_logger::TuiLoggerSmartWidget; - +use jong::{game::wall::Wall, tiles::Tile}; +use ratatui::widgets::Paragraph; mod console; mod input; @@ -40,10 +39,36 @@ impl Plugin for RiichiTui { // InGame, // } -pub(crate) fn draw_system(mut tui_ctx: ResMut, query: Query<&Tile>) -> Result { +pub(crate) fn draw_system( + // mut commands: Commands, + wall: Option>, + tiles: Option>, + mut tui_ctx: ResMut, +) -> Result { + let title = ratatui::text::Text::raw("tiny riichi"); + let wall = if let Some(wall) = wall { + let text = (*wall) + .iter() + .map(|c| { + tiles + .as_ref() + .unwrap() + .get(c) + .map(|tile| format!("{tile:?}")) + }) + .collect::>()?; + + Some(Paragraph::new(text)) + } else { + None + }; + tui_ctx.draw(|frame| { - let text = ratatui::text::Text::raw("tiny riichi"); - frame.render_widget(text, frame.area()); + frame.render_widget(title, frame.area()); + + if let Some(wall) = wall { + frame.render_widget(wall, frame.area()); + } })?; Ok(()) From d506a25716848ae396aa1f8aa457b08d90660484 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Fri, 9 Jan 2026 23:14:29 -0800 Subject: [PATCH 02/10] render some tiles --- src/game/mod.rs | 4 +-- src/game/wall.rs | 17 +++++++------ src/main.rs | 17 ++++++++++--- src/tiles.rs | 9 ++++--- src/tui/console.rs | 2 +- src/tui/mod.rs | 55 +++++++++-------------------------------- src/tui/render.rs | 55 +++++++++++++++++++++++++++++++++++++++++ src/tui/render/tiles.rs | 30 ++++++++++++++++++++++ 8 files changed, 129 insertions(+), 60 deletions(-) create mode 100644 src/tui/render.rs create mode 100644 src/tui/render/tiles.rs diff --git a/src/game/mod.rs b/src/game/mod.rs index 577e397..7f65cd7 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use tracing::instrument; use crate::tiles::{self, *}; @@ -11,8 +12,7 @@ impl Plugin for Riichi { fn build(&self, app: &mut App) { app.init_resource::() .add_systems(Startup, init_match) - .add_systems(Startup, tiles::init_tiles) - .add_systems(Update, wall::build_wall) + .add_systems(Startup, (tiles::init_tiles, wall::build_wall).chain()) // semicolon stopper ; } diff --git a/src/game/wall.rs b/src/game/wall.rs index 4de0558..ce54ec5 100644 --- a/src/game/wall.rs +++ b/src/game/wall.rs @@ -6,19 +6,22 @@ use crate::game::InWall; use crate::tiles::Tile; #[derive(Component)] -#[relationship_target(relationship = InWall)] -pub struct Wall(Vec); +#[relationship_target(relationship = InWall, linked_spawn)] +pub struct WallTiles(Vec); + +#[derive(Component)] +pub struct Wall; -#[instrument(level = "trace", skip_all)] pub(crate) fn build_wall(mut commands: Commands, tiles: Query>) -> Result { let mut rng = rand::rng(); - let mut wall = commands.spawn(Wall(vec![])); - - let mut shuffled = tiles.iter().collect::>(); + let mut shuffled = tiles + .iter() + .inspect(|e| debug!("{e:?}")) + .collect::>(); shuffled.shuffle(&mut rng); - wall.replace_children(&shuffled); + let mut wall = commands.spawn((Wall, WallTiles(shuffled))); Ok(()) } diff --git a/src/main.rs b/src/main.rs index 5c8cbe2..1a8dd9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ #![allow(unused)] -use bevy::prelude::*; +use bevy::{log::LogPlugin, prelude::*}; use clap::{Parser, Subcommand}; +use tracing::Level; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod gui; @@ -19,18 +20,28 @@ enum Mode { RunTui, } +const FILTERSTRING: &str = "warn,jong=trace"; + fn main() { let args = Args::parse(); let mut app = App::new(); let app = match args.mode { - Mode::RunGui => app.add_plugins(DefaultPlugins), + Mode::RunGui => { + app.add_plugins(DefaultPlugins.set(LogPlugin { + filter: FILTERSTRING.into(), + level: Level::TRACE, + // custom_layer: todo!(), + // fmt_layer: todo!(), + ..Default::default() + })) + } Mode::RunTui => { tracing_subscriber::registry() .with(tui_logger::TuiTracingSubscriberLayer) .init(); tui_logger::init_logger(tui_logger::LevelFilter::Trace).unwrap(); - tui_logger::set_env_filter_from_string("warn,jong=trace"); + tui_logger::set_env_filter_from_string(FILTERSTRING); app.add_plugins(tui::RiichiTui) } diff --git a/src/tiles.rs b/src/tiles.rs index 7716df0..4c6eb3c 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,10 +1,11 @@ use bevy::{ecs::entity::MapEntities, prelude::*}; use strum::FromRepr; +use tracing::instrument; -use crate::game::wall::Wall; +use crate::game::wall::WallTiles; #[derive(Component)] -#[relationship(relationship_target = Wall)] +#[relationship(relationship_target = WallTiles)] pub struct InWall(pub Entity); // #[derive(Component)] @@ -13,7 +14,7 @@ pub struct InWall(pub Entity); #[derive(Component, Debug)] pub struct Tile { - suit: Suit, + pub suit: Suit, } #[derive(MapEntities, Debug)] @@ -26,7 +27,7 @@ pub enum Suit { } #[derive(Deref, DerefMut, Debug)] -pub struct Rank(u8); +pub struct Rank(pub u8); #[derive(FromRepr, Debug)] pub enum Wind { diff --git a/src/tui/console.rs b/src/tui/console.rs index 97b346c..8112010 100644 --- a/src/tui/console.rs +++ b/src/tui/console.rs @@ -6,8 +6,8 @@ use tui_logger::TuiLoggerWidget; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] pub(crate) enum ConsoleState { #[default] - Open, Closed, + Open, } impl std::ops::Not for ConsoleState { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index d960f2e..6b10813 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,11 +1,13 @@ use std::time::Duration; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; -use bevy_ratatui::{RatatuiContext, RatatuiPlugins}; -use jong::{game::wall::Wall, tiles::Tile}; -use ratatui::widgets::Paragraph; +use bevy_ratatui::RatatuiPlugins; +use jong::tiles::InWall; +use ratatui::{text::ToSpan, widgets::Paragraph}; + mod console; mod input; +mod render; pub struct RiichiTui; @@ -26,50 +28,17 @@ impl Plugin for RiichiTui { .init_state::() .add_systems(Update, console::toggle_console) .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) + .init_state::() .add_systems(Update, input::keyboard_input_system) - .add_systems(Update, draw_system) + .add_systems(Update, render::draw_ingame.run_if(in_state(TuiState::InGame))) // semicolon stopper ; } } -// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States)] -// enum TuiState { -// MainMenu, -// InGame, -// } - -pub(crate) fn draw_system( - // mut commands: Commands, - wall: Option>, - tiles: Option>, - mut tui_ctx: ResMut, -) -> Result { - let title = ratatui::text::Text::raw("tiny riichi"); - let wall = if let Some(wall) = wall { - let text = (*wall) - .iter() - .map(|c| { - tiles - .as_ref() - .unwrap() - .get(c) - .map(|tile| format!("{tile:?}")) - }) - .collect::>()?; - - Some(Paragraph::new(text)) - } else { - None - }; - - tui_ctx.draw(|frame| { - frame.render_widget(title, frame.area()); - - if let Some(wall) = wall { - frame.render_widget(wall, frame.area()); - } - })?; - - Ok(()) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] +enum TuiState { + MainMenu, + #[default] + InGame, } diff --git a/src/tui/render.rs b/src/tui/render.rs new file mode 100644 index 0000000..a7bece1 --- /dev/null +++ b/src/tui/render.rs @@ -0,0 +1,55 @@ +use std::marker::PhantomData; + +use bevy::ecs::system::SystemParam; +use bevy::prelude::*; +use bevy_ratatui::RatatuiContext; +use ratatui::widgets::Paragraph; + +use jong::game::wall::Wall; +use jong::game::wall::WallTiles; +use jong::tiles::Tile; + +mod tiles; + +pub(crate) fn draw_ingame( + tiles: Query<&Tile>, + wall: Option>>, + mut tui_ctx: ResMut, +) -> Result { + use ratatui::layout::Flex; + use ratatui::prelude::*; + + let title = ratatui::text::Text::raw("tiny riichi"); + + let wall = if let Some(wall) = wall { + let wall = wall + .iter() + .map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) }) + .collect::>>()?; + + // let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true }); + Some(wall) + } else { + None + }; + + tui_ctx.draw(|frame| { + // frame.render_widget(title, frame.area()); + debug!("{}", frame.area()); + + if let Some(wall) = wall { + // let tile_area = Rect::new(0, 0, 5, 4); + let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); + let mut area = frame.area(); + area.height = 4; + let areas = layout.areas::<13>(area); + // debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len()); + for (tile, rect) in wall.iter().zip(areas.iter()) { + // debug!("{rect:?}"); + frame.render_widget(tile, *rect); + } + } + })?; + + Ok(()) +} diff --git a/src/tui/render/tiles.rs b/src/tui/render/tiles.rs new file mode 100644 index 0000000..802b38d --- /dev/null +++ b/src/tui/render/tiles.rs @@ -0,0 +1,30 @@ +use ratatui::widgets::Paragraph; + +use jong::tiles::Tile; + +pub(crate) fn draw_tile(tile: &Tile) -> Paragraph<'_> { + use ratatui::prelude::*; + + let block = ratatui::widgets::Block::bordered(); + + Paragraph::new(match &tile.suit { + jong::tiles::Suit::Pin(rank) => format!("{}\np", rank.0), + jong::tiles::Suit::Sou(rank) => format!("{}\ns", rank.0), + jong::tiles::Suit::Man(rank) => format!("{}\nm", rank.0), + jong::tiles::Suit::Wind(wind) => (match wind { + jong::tiles::Wind::Ton => "e\nw", + jong::tiles::Wind::Nan => "s\nw", + jong::tiles::Wind::Shaa => "w\nw", + jong::tiles::Wind::Pei => "n\nw", + }) + .into(), + jong::tiles::Suit::Dragon(dragon) => (match dragon { + jong::tiles::Dragon::Haku => "w\nd", + jong::tiles::Dragon::Hatsu => "g\nd", + jong::tiles::Dragon::Chun => "r\nd", + }) + .into(), + }) + .block(block) + .centered() +} From 3417384b866ecebfa4b365baa2064609d1ca6bb1 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 11 Jan 2026 20:10:30 -0800 Subject: [PATCH 03/10] detect hand change and render it --- src/game/mod.rs | 26 +++++++------ src/game/player.rs | 35 +++++++++++++++++ src/game/wall.rs | 13 ++++--- src/main.rs | 2 +- src/tiles.rs | 10 +---- src/tui/console.rs | 4 +- src/tui/mod.rs | 29 +++++++++----- src/tui/render.rs | 85 +++++++++++++++++++++++++++-------------- src/tui/render/tiles.rs | 2 +- 9 files changed, 138 insertions(+), 68 deletions(-) diff --git a/src/game/mod.rs b/src/game/mod.rs index 7f65cd7..09bf4e6 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -3,30 +3,32 @@ use tracing::instrument; use crate::tiles::{self, *}; -mod player; +pub mod player; pub mod wall; -pub struct Riichi; +#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)] +pub enum GameState { + #[default] + None, + Setup, + // Deal, + Play, + Score, +} +pub struct Riichi; impl Plugin for Riichi { fn build(&self, app: &mut App) { app.init_resource::() .add_systems(Startup, init_match) - .add_systems(Startup, (tiles::init_tiles, wall::build_wall).chain()) + .add_systems(Startup, tiles::init_tiles) + .init_state::() + .add_systems(OnEnter(GameState::Setup), (wall::build_wall, player::deal_hands).chain()) // semicolon stopper ; } } -#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)] -enum GameState { - #[default] - Setup, - Deal, - Play, - Score, -} - #[derive(Component)] pub(crate) struct Dice(u8, u8); diff --git a/src/game/player.rs b/src/game/player.rs index 45d3c04..17b4c66 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -1,9 +1,44 @@ use bevy::prelude::*; +use crate::{ + game::wall::{Wall, WallTiles}, + tiles::Tile, +}; + #[derive(Component)] pub(crate) struct Player { pub(crate) name: String, } +fn spawn_players(mut commands: Commands) {} + #[derive(Component)] pub(crate) struct Points(pub isize); + +#[derive(Component)] +pub struct Hand; + +#[derive(Component)] +#[relationship_target(relationship = InHand, linked_spawn)] +pub struct HandTiles(Vec); + +#[derive(Component)] +#[relationship(relationship_target = HandTiles)] +pub struct InHand(pub Entity); + +pub(crate) fn deal_hands( + mut commands: Commands, + wall: Single>, + wall_tiles: Query>, + tiles: Query>, +) -> Result { + let hand = wall_tiles.iter().collect::>(); + + commands + .get_entity(*wall)? + .remove_children(hand.last_chunk::<13>().unwrap()); + + commands.spawn((Hand, HandTiles(hand))); + + Ok(()) +} diff --git a/src/game/wall.rs b/src/game/wall.rs index ce54ec5..96a4e6e 100644 --- a/src/game/wall.rs +++ b/src/game/wall.rs @@ -2,17 +2,20 @@ use bevy::log::tracing::instrument; use bevy::prelude::*; use rand::seq::SliceRandom; -use crate::game::InWall; use crate::tiles::Tile; +#[derive(Component)] +pub struct Wall; + #[derive(Component)] #[relationship_target(relationship = InWall, linked_spawn)] pub struct WallTiles(Vec); #[derive(Component)] -pub struct Wall; +#[relationship(relationship_target = WallTiles)] +pub struct InWall(pub Entity); -pub(crate) fn build_wall(mut commands: Commands, tiles: Query>) -> Result { +pub(crate) fn build_wall(mut commands: Commands, tiles: Query>) { let mut rng = rand::rng(); let mut shuffled = tiles @@ -21,7 +24,5 @@ pub(crate) fn build_wall(mut commands: Commands, tiles: Query .collect::>(); shuffled.shuffle(&mut rng); - let mut wall = commands.spawn((Wall, WallTiles(shuffled))); - - Ok(()) + commands.spawn((Wall, WallTiles(shuffled))); } diff --git a/src/main.rs b/src/main.rs index 1a8dd9a..0481b30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,7 @@ fn main() { tui_logger::init_logger(tui_logger::LevelFilter::Trace).unwrap(); tui_logger::set_env_filter_from_string(FILTERSTRING); - app.add_plugins(tui::RiichiTui) + app.add_plugins(tui::RiichiTui::default()) } }; diff --git a/src/tiles.rs b/src/tiles.rs index 4c6eb3c..655f74d 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -2,15 +2,7 @@ use bevy::{ecs::entity::MapEntities, prelude::*}; use strum::FromRepr; use tracing::instrument; -use crate::game::wall::WallTiles; - -#[derive(Component)] -#[relationship(relationship_target = WallTiles)] -pub struct InWall(pub Entity); - -// #[derive(Component)] -// #[relationship(relationship_target = WallTiles)] -// pub(crate) struct InHand(pub Entity); +use crate::game::{player::HandTiles, wall::WallTiles}; #[derive(Component, Debug)] pub struct Tile { diff --git a/src/tui/console.rs b/src/tui/console.rs index 8112010..0b1ceab 100644 --- a/src/tui/console.rs +++ b/src/tui/console.rs @@ -1,6 +1,7 @@ use bevy::input::keyboard::Key; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; +use ratatui::widgets::Block; use tui_logger::TuiLoggerWidget; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] @@ -33,7 +34,8 @@ pub(crate) fn toggle_console( pub(crate) fn draw_console(mut tui_ctx: ResMut) -> Result { tui_ctx.draw(|frame| { - frame.render_widget(TuiLoggerWidget::default(), frame.area()); + let block = Block::bordered().title("console"); + frame.render_widget(TuiLoggerWidget::default().block(block), frame.area()); })?; Ok(()) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 6b10813..37c9315 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -2,14 +2,26 @@ use std::time::Duration; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; use bevy_ratatui::RatatuiPlugins; -use jong::tiles::InWall; use ratatui::{text::ToSpan, widgets::Paragraph}; +use jong::game::GameState; +use jong::game::wall::InWall; + mod console; mod input; mod render; -pub struct RiichiTui; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] +enum TuiState { + MainMenu, + #[default] + InGame, +} + +#[derive(Default)] +pub struct RiichiTui { + // player_names: Vec, +} impl Plugin for RiichiTui { fn build(&self, app: &mut App) { @@ -25,20 +37,19 @@ impl Plugin for RiichiTui { }, )) .add_plugins(StatesPlugin) + + // setup console .init_state::() .add_systems(Update, console::toggle_console) .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) + .init_state::() - .add_systems(Update, input::keyboard_input_system) + // .add_systems(Update, input::keyboard_input_system) .add_systems(Update, render::draw_ingame.run_if(in_state(TuiState::InGame))) + .add_systems(Update,render::render_changed_hand.run_if(in_state(GameState::Play))) // semicolon stopper ; } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] -enum TuiState { - MainMenu, - #[default] - InGame, -} +// fn prompt_names() {} diff --git a/src/tui/render.rs b/src/tui/render.rs index a7bece1..e3fe942 100644 --- a/src/tui/render.rs +++ b/src/tui/render.rs @@ -1,54 +1,81 @@ -use std::marker::PhantomData; - use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; use ratatui::widgets::Paragraph; -use jong::game::wall::Wall; -use jong::game::wall::WallTiles; -use jong::tiles::Tile; +use jong::{ + game::{ + player::{Hand, HandTiles}, + wall::{Wall, WallTiles}, + }, + tiles::Tile, +}; mod tiles; -pub(crate) fn draw_ingame( +#[derive(Component)] +pub(crate) struct RenderedHand<'a>(Vec>); + +pub(crate) fn render_changed_hand( + hand: Single<&HandTiles, Changed>, + // hand_tiles: Query<&HandTiles, With>, tiles: Query<&Tile>, - wall: Option>>, + mut target: Single<&'static mut RenderedHand>, +) -> Result { + let hand_tiles = hand + .iter() + .map(|inhand| -> Result<_> { Ok(tiles.get(inhand).map(tiles::draw_tile)?) }) + .collect::>>()?; + + target.0 = hand_tiles; + + Ok(()) +} + +pub(crate) fn draw_ingame( + // tiles: Query<&Tile>, + // wall_tiles: Option>>, + // hand_tiles: Query<&HandTiles, With>, + rendered_hand: Single<&'static RenderedHand>, mut tui_ctx: ResMut, ) -> Result { use ratatui::layout::Flex; use ratatui::prelude::*; - let title = ratatui::text::Text::raw("tiny riichi"); + // let title = ratatui::text::Text::raw("tiny riichi"); - let wall = if let Some(wall) = wall { - let wall = wall - .iter() - .map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) }) - .collect::>>()?; + // let wall = if let Some(wall_tiles) = wall_tiles { + // let wall_tiles = wall_tiles + // .iter() + // .map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) }) + // .collect::>>()?; - // let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true }); - Some(wall) - } else { - None - }; + // // let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true }); + // Some(wall_tiles) + // } else { + // None + // }; tui_ctx.draw(|frame| { // frame.render_widget(title, frame.area()); debug!("{}", frame.area()); - if let Some(wall) = wall { - // let tile_area = Rect::new(0, 0, 5, 4); - let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); - let mut area = frame.area(); - area.height = 4; - let areas = layout.areas::<13>(area); - // debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len()); - for (tile, rect) in wall.iter().zip(areas.iter()) { - // debug!("{rect:?}"); - frame.render_widget(tile, *rect); - } + // if let Some(wall) = wall { + // // let tile_area = Rect::new(0, 0, 5, 4); + let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); + let mut area = frame.area(); + area.height = 4; + let areas = layout.areas::<13>(area); + for (tile, area) in rendered_hand.0.iter().zip(areas.iter()) { + frame.render_widget(tile, *area); } + + // // debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len()); + // for (tile, rect) in wall.iter().zip(areas.iter()) { + // // debug!("{rect:?}"); + // frame.render_widget(tile, *rect); + // } + // } })?; Ok(()) diff --git a/src/tui/render/tiles.rs b/src/tui/render/tiles.rs index 802b38d..620a2d1 100644 --- a/src/tui/render/tiles.rs +++ b/src/tui/render/tiles.rs @@ -2,7 +2,7 @@ use ratatui::widgets::Paragraph; use jong::tiles::Tile; -pub(crate) fn draw_tile(tile: &Tile) -> Paragraph<'_> { +pub(crate) fn draw_tile(tile: &Tile) -> Paragraph<'static> { use ratatui::prelude::*; let block = ratatui::widgets::Block::bordered(); From 130bb38725e11c25499fe2bc9ce435366acca63d Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:32:30 -0800 Subject: [PATCH 04/10] reorder stuff for tui main menu --- src/tui/mod.rs | 8 +++--- src/tui/render/hand.rs | 26 +++++++++++++++++++ src/tui/{render.rs => render/ingame.rs} | 33 ++----------------------- src/tui/render/mod.rs | 27 ++++++++++++++++++++ 4 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 src/tui/render/hand.rs rename src/tui/{render.rs => render/ingame.rs} (68%) create mode 100644 src/tui/render/mod.rs diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 37c9315..1c3e510 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -13,8 +13,8 @@ mod render; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] enum TuiState { - MainMenu, #[default] + MainMenu, InGame, } @@ -44,9 +44,11 @@ impl Plugin for RiichiTui { .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) .init_state::() + .add_systems(Update, render::draw_mainmenu.run_if(in_state(TuiState::MainMenu))) // .add_systems(Update, input::keyboard_input_system) - .add_systems(Update, render::draw_ingame.run_if(in_state(TuiState::InGame))) - .add_systems(Update,render::render_changed_hand.run_if(in_state(GameState::Play))) + // .add_systems() + .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(TuiState::InGame))) + .add_systems(Update, render::hand::render_changed_hand.run_if(in_state(GameState::Play))) // semicolon stopper ; } diff --git a/src/tui/render/hand.rs b/src/tui/render/hand.rs new file mode 100644 index 0000000..dc6cce5 --- /dev/null +++ b/src/tui/render/hand.rs @@ -0,0 +1,26 @@ +use bevy::prelude::*; +use ratatui::widgets::Paragraph; + +use jong::game::player::HandTiles; +use jong::tiles::Tile; + +use crate::tui::render::tiles; + +#[derive(Component)] +pub(crate) struct RenderedHand<'a>(pub(crate) Vec>); + +pub(crate) fn render_changed_hand( + hand: Single<&HandTiles, Changed>, + // hand_tiles: Query<&HandTiles, With>, + tiles: Query<&Tile>, + mut target: Single<&'static mut RenderedHand>, +) -> Result { + let hand_tiles = hand + .iter() + .map(|inhand| -> Result<_> { Ok(tiles.get(inhand).map(tiles::draw_tile)?) }) + .collect::>>()?; + + target.0 = hand_tiles; + + Ok(()) +} diff --git a/src/tui/render.rs b/src/tui/render/ingame.rs similarity index 68% rename from src/tui/render.rs rename to src/tui/render/ingame.rs index e3fe942..66fbbb3 100644 --- a/src/tui/render.rs +++ b/src/tui/render/ingame.rs @@ -1,42 +1,13 @@ -use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; -use ratatui::widgets::Paragraph; -use jong::{ - game::{ - player::{Hand, HandTiles}, - wall::{Wall, WallTiles}, - }, - tiles::Tile, -}; - -mod tiles; - -#[derive(Component)] -pub(crate) struct RenderedHand<'a>(Vec>); - -pub(crate) fn render_changed_hand( - hand: Single<&HandTiles, Changed>, - // hand_tiles: Query<&HandTiles, With>, - tiles: Query<&Tile>, - mut target: Single<&'static mut RenderedHand>, -) -> Result { - let hand_tiles = hand - .iter() - .map(|inhand| -> Result<_> { Ok(tiles.get(inhand).map(tiles::draw_tile)?) }) - .collect::>>()?; - - target.0 = hand_tiles; - - Ok(()) -} +use crate::tui::render::hand; pub(crate) fn draw_ingame( // tiles: Query<&Tile>, // wall_tiles: Option>>, // hand_tiles: Query<&HandTiles, With>, - rendered_hand: Single<&'static RenderedHand>, + rendered_hand: Single<&'static hand::RenderedHand>, mut tui_ctx: ResMut, ) -> Result { use ratatui::layout::Flex; diff --git a/src/tui/render/mod.rs b/src/tui/render/mod.rs new file mode 100644 index 0000000..fc30799 --- /dev/null +++ b/src/tui/render/mod.rs @@ -0,0 +1,27 @@ +use bevy::ecs::system::SystemParam; +use bevy::prelude::*; +use bevy_ratatui::RatatuiContext; +use ratatui::widgets::Paragraph; + +use jong::{ + game::{ + GameState, + player::Hand, + wall::{Wall, WallTiles}, + }, + tiles::Tile, +}; + +use crate::tui::TuiState; + +pub(crate) mod hand; +pub(crate) mod ingame; +mod tiles; + +pub(crate) fn draw_mainmenu( + mut tui_ctx: ResMut, + mut tui_state: ResMut>, + mut game_state: ResMut>, +) { + tui_ctx.draw(|frame| {}); +} From bc3421a3715c0bd0c35f593a92ca916678467a45 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 11 Jan 2026 23:41:27 -0800 Subject: [PATCH 05/10] why we crashin here? --- src/game/player.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/player.rs b/src/game/player.rs index 17b4c66..e14b982 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -29,8 +29,8 @@ pub struct InHand(pub Entity); pub(crate) fn deal_hands( mut commands: Commands, wall: Single>, - wall_tiles: Query>, - tiles: Query>, + wall_tiles: Populated>, + tiles: Populated>, ) -> Result { let hand = wall_tiles.iter().collect::>(); @@ -40,5 +40,6 @@ pub(crate) fn deal_hands( commands.spawn((Hand, HandTiles(hand))); + trace!("dealt hands"); Ok(()) } From 759ff410c2883c5c57cbf1b4b645706d8eba8339 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Sun, 11 Jan 2026 23:41:27 -0800 Subject: [PATCH 06/10] basic main menu (change this to generic overlay menu later?) --- src/game/mod.rs | 3 +-- src/tui/console.rs | 14 +++++++++--- src/tui/input.rs | 22 ------------------- src/tui/menu.rs | 50 +++++++++++++++++++++++++++++++++++++++++++ src/tui/mod.rs | 32 ++++++++++++++++++--------- src/tui/render/mod.rs | 13 ++++------- 6 files changed, 88 insertions(+), 46 deletions(-) delete mode 100644 src/tui/input.rs create mode 100644 src/tui/menu.rs diff --git a/src/game/mod.rs b/src/game/mod.rs index 09bf4e6..e1eec8b 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -6,10 +6,9 @@ use crate::tiles::{self, *}; pub mod player; pub mod wall; -#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)] +#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq, Copy)] pub enum GameState { #[default] - None, Setup, // Deal, Play, diff --git a/src/tui/console.rs b/src/tui/console.rs index 0b1ceab..0d4ac88 100644 --- a/src/tui/console.rs +++ b/src/tui/console.rs @@ -1,6 +1,8 @@ use bevy::input::keyboard::Key; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; +use bevy_ratatui::event::KeyMessage; +use ratatui::crossterm::event::KeyCode; use ratatui::widgets::Block; use tui_logger::TuiLoggerWidget; @@ -23,12 +25,18 @@ impl std::ops::Not for ConsoleState { } pub(crate) fn toggle_console( - input: Res>, + // input: Res>, + mut messages: MessageReader, curr_state: Res>, mut next_state: ResMut>, ) { - if input.just_pressed(Key::Character("`".into())) { - next_state.set(!*curr_state.get()); + // if input.just_pressed(Key::Character("`".into())) { + // next_state.set(!*curr_state.get()); + // } + for message in messages.read() { + if let KeyCode::Char('`') = message.code { + next_state.set(!*curr_state.get()); + } } } diff --git a/src/tui/input.rs b/src/tui/input.rs deleted file mode 100644 index 6d0d6e4..0000000 --- a/src/tui/input.rs +++ /dev/null @@ -1,22 +0,0 @@ -// use bevy::ecs::message::MessageReader; -use bevy::app::AppExit; -use bevy::input::keyboard::{Key, KeyboardInput}; -use bevy::prelude::*; - -use crate::tui::console::ConsoleState; - -pub(crate) fn keyboard_input_system( - // keycode_input: Option>>, - // key_input: Option>>, - // mut next_state: ResMut>, - mut keyboard_events: MessageReader, -) { - // if let Some(keycode_input) = keycode_input { - // if keycode_input.just_pressed(KeyCode::Backquote) { - // // console_state.set; - // } - // } - for keyboard_input in keyboard_events.read() { - trace!("{:?}", keyboard_input); - } -} diff --git a/src/tui/menu.rs b/src/tui/menu.rs new file mode 100644 index 0000000..72e0301 --- /dev/null +++ b/src/tui/menu.rs @@ -0,0 +1,50 @@ +use bevy::prelude::*; +use bevy_ratatui::RatatuiContext; +use bevy_ratatui::event::KeyMessage; +use ratatui::crossterm::event::KeyCode; +use ratatui::layout::Constraint; +use ratatui::layout::Layout; + +use jong::game::GameState; + +use crate::tui::TuiState; + +const MAINMENU_OPTIONS: [&str; 2] = ["(p)lay", "(q)uit"]; +// const MAINMENU_INPUTS: [char;2] = ['p', 'q']; + +pub(crate) fn draw_mainmenu( + mut tui_ctx: ResMut, + // mut tui_state: ResMut>, + // mut game_state: ResMut>, +) { + let options = MAINMENU_OPTIONS; + let layout = Layout::vertical(vec![Constraint::Min(1); options.len()]); + + tui_ctx.draw(|frame| { + let areas = layout.split(frame.area()); + for (opt, area) in options.into_iter().zip(areas.iter()) { + frame.render_widget(opt, *area) + } + }); +} + +pub(crate) fn mainmenu_input( + // input: Res>, + mut messages: MessageReader, + mut next_tuistate: ResMut>, + mut next_gamestate: ResMut>, + mut exit: MessageWriter, +) { + for message in messages.read() { + match message.code { + KeyCode::Char('p') => { + next_tuistate.set(TuiState::InGame); + next_gamestate.set(GameState::default()); + } + KeyCode::Char('q') => { + exit.write_default(); + } + _ => {} + } + } +} diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 1c3e510..35b0c94 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -8,7 +8,7 @@ use jong::game::GameState; use jong::game::wall::InWall; mod console; -mod input; +mod menu; mod render; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] @@ -31,27 +31,39 @@ impl Plugin for RiichiTui { ))), RatatuiPlugins { // enable_kitty_protocol: todo!(), - // enable_mouse_capture: todo!(), - enable_input_forwarding: true, + enable_mouse_capture: true, + // enable_input_forwarding: false, ..Default::default() }, )) .add_plugins(StatesPlugin) - // setup console .init_state::() .add_systems(Update, console::toggle_console) .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) - + // other setup .init_state::() - .add_systems(Update, render::draw_mainmenu.run_if(in_state(TuiState::MainMenu))) - // .add_systems(Update, input::keyboard_input_system) - // .add_systems() + .add_computed_state::() + // main menu + .add_systems(Update, (menu::draw_mainmenu,menu::mainmenu_input).run_if(in_state(TuiState::MainMenu))) + // gaming .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(TuiState::InGame))) - .add_systems(Update, render::hand::render_changed_hand.run_if(in_state(GameState::Play))) + .add_systems(Update, render::hand::render_changed_hand.run_if(in_state(InGame).and(in_state(GameState::Play)))) // semicolon stopper ; } } -// fn prompt_names() {} +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +struct InGame; + +impl ComputedStates for InGame { + type SourceStates = TuiState; + + fn compute(sources: Self::SourceStates) -> Option { + match sources { + TuiState::MainMenu => None, + TuiState::InGame => Some(Self), + } + } +} diff --git a/src/tui/render/mod.rs b/src/tui/render/mod.rs index fc30799..1c8b3e4 100644 --- a/src/tui/render/mod.rs +++ b/src/tui/render/mod.rs @@ -1,11 +1,13 @@ use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; -use ratatui::widgets::Paragraph; +use ratatui::{ + layout::{}, + widgets::Paragraph, +}; use jong::{ game::{ - GameState, player::Hand, wall::{Wall, WallTiles}, }, @@ -18,10 +20,3 @@ pub(crate) mod hand; pub(crate) mod ingame; mod tiles; -pub(crate) fn draw_mainmenu( - mut tui_ctx: ResMut, - mut tui_state: ResMut>, - mut game_state: ResMut>, -) { - tui_ctx.draw(|frame| {}); -} From 65ea256436dccb95536a276de280a38e36297804 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:08:27 -0800 Subject: [PATCH 07/10] fix deal crash --- src/game/mod.rs | 7 ++++++- src/game/player.rs | 11 +++++------ src/game/wall.rs | 7 +++---- src/tui/menu.rs | 2 +- src/tui/render/ingame.rs | 24 ------------------------ src/tui/render/mod.rs | 19 ------------------- 6 files changed, 15 insertions(+), 55 deletions(-) diff --git a/src/game/mod.rs b/src/game/mod.rs index e1eec8b..95b20e1 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -9,6 +9,7 @@ pub mod wall; #[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq, Copy)] pub enum GameState { #[default] + None, Setup, // Deal, Play, @@ -22,12 +23,16 @@ impl Plugin for Riichi { .add_systems(Startup, init_match) .add_systems(Startup, tiles::init_tiles) .init_state::() - .add_systems(OnEnter(GameState::Setup), (wall::build_wall, player::deal_hands).chain()) + .add_systems(OnEnter(GameState::Setup), (wall::build_wall, player::deal_hands, setup_done).chain()) // semicolon stopper ; } } +fn setup_done(mut next: ResMut>) { + next.set(GameState::Play); +} + #[derive(Component)] pub(crate) struct Dice(u8, u8); diff --git a/src/game/player.rs b/src/game/player.rs index e14b982..d1d263f 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use crate::{ - game::wall::{Wall, WallTiles}, + game::wall::{InWall, Wall, WallTiles}, tiles::Tile, }; @@ -28,14 +28,13 @@ pub struct InHand(pub Entity); pub(crate) fn deal_hands( mut commands: Commands, - wall: Single>, - wall_tiles: Populated>, - tiles: Populated>, + inwalls: Single<&WallTiles>, + walltiles: Single>, ) -> Result { - let hand = wall_tiles.iter().collect::>(); + let hand = inwalls.iter().collect::>(); commands - .get_entity(*wall)? + .get_entity(*walltiles)? .remove_children(hand.last_chunk::<13>().unwrap()); commands.spawn((Hand, HandTiles(hand))); diff --git a/src/game/wall.rs b/src/game/wall.rs index 96a4e6e..5202d0e 100644 --- a/src/game/wall.rs +++ b/src/game/wall.rs @@ -18,11 +18,10 @@ pub struct InWall(pub Entity); pub(crate) fn build_wall(mut commands: Commands, tiles: Query>) { let mut rng = rand::rng(); - let mut shuffled = tiles - .iter() - .inspect(|e| debug!("{e:?}")) - .collect::>(); + let mut shuffled = tiles.iter().collect::>(); shuffled.shuffle(&mut rng); commands.spawn((Wall, WallTiles(shuffled))); + + trace!("build_wall"); } diff --git a/src/tui/menu.rs b/src/tui/menu.rs index 72e0301..65da70b 100644 --- a/src/tui/menu.rs +++ b/src/tui/menu.rs @@ -39,7 +39,7 @@ pub(crate) fn mainmenu_input( match message.code { KeyCode::Char('p') => { next_tuistate.set(TuiState::InGame); - next_gamestate.set(GameState::default()); + next_gamestate.set(GameState::Setup); } KeyCode::Char('q') => { exit.write_default(); diff --git a/src/tui/render/ingame.rs b/src/tui/render/ingame.rs index 66fbbb3..642c3f8 100644 --- a/src/tui/render/ingame.rs +++ b/src/tui/render/ingame.rs @@ -13,26 +13,9 @@ pub(crate) fn draw_ingame( use ratatui::layout::Flex; use ratatui::prelude::*; - // let title = ratatui::text::Text::raw("tiny riichi"); - - // let wall = if let Some(wall_tiles) = wall_tiles { - // let wall_tiles = wall_tiles - // .iter() - // .map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) }) - // .collect::>>()?; - - // // let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true }); - // Some(wall_tiles) - // } else { - // None - // }; - tui_ctx.draw(|frame| { - // frame.render_widget(title, frame.area()); debug!("{}", frame.area()); - // if let Some(wall) = wall { - // // let tile_area = Rect::new(0, 0, 5, 4); let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); let mut area = frame.area(); area.height = 4; @@ -40,13 +23,6 @@ pub(crate) fn draw_ingame( for (tile, area) in rendered_hand.0.iter().zip(areas.iter()) { frame.render_widget(tile, *area); } - - // // debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len()); - // for (tile, rect) in wall.iter().zip(areas.iter()) { - // // debug!("{rect:?}"); - // frame.render_widget(tile, *rect); - // } - // } })?; Ok(()) diff --git a/src/tui/render/mod.rs b/src/tui/render/mod.rs index 1c8b3e4..fc6612a 100644 --- a/src/tui/render/mod.rs +++ b/src/tui/render/mod.rs @@ -1,22 +1,3 @@ -use bevy::ecs::system::SystemParam; -use bevy::prelude::*; -use bevy_ratatui::RatatuiContext; -use ratatui::{ - layout::{}, - widgets::Paragraph, -}; - -use jong::{ - game::{ - player::Hand, - wall::{Wall, WallTiles}, - }, - tiles::Tile, -}; - -use crate::tui::TuiState; - pub(crate) mod hand; pub(crate) mod ingame; mod tiles; - From 59399c3590f6d3a2c6876c4ad2b62abdbc53ac82 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:54:59 -0800 Subject: [PATCH 08/10] we start printing again --- src/game/hand.rs | 54 ++++++++++++++++++++++++++++++++++++++++ src/game/mod.rs | 5 +++- src/game/player.rs | 29 +-------------------- src/tiles.rs | 12 ++++----- src/tui/mod.rs | 35 +++++++++++++++++++++----- src/tui/render/hand.rs | 26 +++++++++++++------ src/tui/render/ingame.rs | 5 +--- 7 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 src/game/hand.rs diff --git a/src/game/hand.rs b/src/game/hand.rs new file mode 100644 index 0000000..4daa915 --- /dev/null +++ b/src/game/hand.rs @@ -0,0 +1,54 @@ +use bevy::prelude::*; + +use crate::{game::wall::WallTiles, tiles::Tile}; + +#[derive(Component)] +pub struct Hand; + +#[derive(Component)] +#[relationship(relationship_target = HandTiles)] +pub struct InHand(pub Entity); + +#[derive(Component)] +#[relationship_target(relationship = InHand, linked_spawn)] +pub struct HandTiles(Vec); + +pub(crate) fn deal_hands( + mut commands: Commands, + walltiles: Single<&WallTiles>, + walltiles_entity: Single>, +) -> Result { + let hand = walltiles.iter().collect::>(); + + commands + .get_entity(*walltiles_entity)? + .remove_children(hand.last_chunk::<13>().unwrap()); + + commands.spawn((Hand, HandTiles(hand))); + + trace!("dealt hands"); + Ok(()) +} + +pub(crate) fn sort_hand( + mut commands: Commands, + tiles: Populated<&Tile>, + handtiles_entity: Single>, + handtiles: Single<&HandTiles, Changed>, +) -> Result { + let mut hand: Vec<_> = handtiles + .iter() + .map(|e| -> Result<(_, _)> { Ok((tiles.get(e)?, e)) }) + .collect::>()?; + + hand.sort_by_key(|(t, _)| t.suit); + + let hand: Vec<_> = hand.iter().map(|(_, e)| *e).collect(); + + commands + .get_entity(*handtiles_entity)? + .replace_children(&hand); + + trace!("sort_hand"); + Ok(()) +} diff --git a/src/game/mod.rs b/src/game/mod.rs index 95b20e1..bf41e6c 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -3,6 +3,7 @@ use tracing::instrument; use crate::tiles::{self, *}; +pub mod hand; pub mod player; pub mod wall; @@ -23,7 +24,8 @@ impl Plugin for Riichi { .add_systems(Startup, init_match) .add_systems(Startup, tiles::init_tiles) .init_state::() - .add_systems(OnEnter(GameState::Setup), (wall::build_wall, player::deal_hands, setup_done).chain()) + .add_systems(OnEnter(GameState::Setup), (wall::build_wall, hand::deal_hands, setup_done).chain()) + // .add_systems(Update, systems) // semicolon stopper ; } @@ -31,6 +33,7 @@ impl Plugin for Riichi { fn setup_done(mut next: ResMut>) { next.set(GameState::Play); + trace!("setup_done"); } #[derive(Component)] diff --git a/src/game/player.rs b/src/game/player.rs index d1d263f..e4e3323 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use crate::{ - game::wall::{InWall, Wall, WallTiles}, + game::wall::{InWall, Wall}, tiles::Tile, }; @@ -15,30 +15,3 @@ fn spawn_players(mut commands: Commands) {} #[derive(Component)] pub(crate) struct Points(pub isize); -#[derive(Component)] -pub struct Hand; - -#[derive(Component)] -#[relationship_target(relationship = InHand, linked_spawn)] -pub struct HandTiles(Vec); - -#[derive(Component)] -#[relationship(relationship_target = HandTiles)] -pub struct InHand(pub Entity); - -pub(crate) fn deal_hands( - mut commands: Commands, - inwalls: Single<&WallTiles>, - walltiles: Single>, -) -> Result { - let hand = inwalls.iter().collect::>(); - - commands - .get_entity(*walltiles)? - .remove_children(hand.last_chunk::<13>().unwrap()); - - commands.spawn((Hand, HandTiles(hand))); - - trace!("dealt hands"); - Ok(()) -} diff --git a/src/tiles.rs b/src/tiles.rs index 655f74d..634e115 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -2,26 +2,26 @@ use bevy::{ecs::entity::MapEntities, prelude::*}; use strum::FromRepr; use tracing::instrument; -use crate::game::{player::HandTiles, wall::WallTiles}; +use crate::game::{hand::HandTiles, wall::WallTiles}; #[derive(Component, Debug)] pub struct Tile { pub suit: Suit, } -#[derive(MapEntities, Debug)] +#[derive(MapEntities, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)] pub enum Suit { + Man(Rank), Pin(Rank), Sou(Rank), - Man(Rank), Wind(Wind), Dragon(Dragon), } -#[derive(Deref, DerefMut, Debug)] +#[derive(Deref, DerefMut, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)] pub struct Rank(pub u8); -#[derive(FromRepr, Debug)] +#[derive(FromRepr, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)] pub enum Wind { Ton, Nan, @@ -29,7 +29,7 @@ pub enum Wind { Pei, } -#[derive(Debug, FromRepr)] +#[derive(Debug, FromRepr, PartialEq, PartialOrd, Eq, Ord, Clone, Copy)] pub enum Dragon { Haku, Hatsu, diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 35b0c94..639b23a 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -32,28 +32,51 @@ impl Plugin for RiichiTui { RatatuiPlugins { // enable_kitty_protocol: todo!(), enable_mouse_capture: true, - // enable_input_forwarding: false, + enable_input_forwarding: true, ..Default::default() }, )) .add_plugins(StatesPlugin) - // setup console + + // console .init_state::() - .add_systems(Update, console::toggle_console) .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) - // other setup + + // general setup .init_state::() .add_computed_state::() + .add_systems(Update, curr_state) + // main menu - .add_systems(Update, (menu::draw_mainmenu,menu::mainmenu_input).run_if(in_state(TuiState::MainMenu))) + .add_systems(Update, (menu::draw_mainmenu, menu::mainmenu_input).run_if(in_state(TuiState::MainMenu))) + // gaming - .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(TuiState::InGame))) + .init_resource::() + .add_systems(OnEnter(GameState::Play), render::hand::render_hand) .add_systems(Update, render::hand::render_changed_hand.run_if(in_state(InGame).and(in_state(GameState::Play)))) + .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(TuiState::InGame))) + // semicolon stopper ; } } +fn curr_state( + curr_gamestate: Option>>, + curr_tuistate: Res>, + curr_ingame: Option>>, +) { + if let Some(curr_gamestate) = curr_gamestate && curr_gamestate.is_changed(){ + trace!("GameState: {curr_gamestate:?}") + } + if curr_tuistate.is_changed() { + trace!("TuiState: {curr_tuistate:?}") + } + if let Some(curr_ingame) = curr_ingame && curr_ingame.is_changed(){ + trace!("InGame: {curr_ingame:?}") + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] struct InGame; diff --git a/src/tui/render/hand.rs b/src/tui/render/hand.rs index dc6cce5..9c76106 100644 --- a/src/tui/render/hand.rs +++ b/src/tui/render/hand.rs @@ -1,25 +1,37 @@ use bevy::prelude::*; use ratatui::widgets::Paragraph; -use jong::game::player::HandTiles; +use jong::game::hand::HandTiles; use jong::tiles::Tile; use crate::tui::render::tiles; -#[derive(Component)] -pub(crate) struct RenderedHand<'a>(pub(crate) Vec>); +#[derive(Resource, Default)] +pub(crate) struct RenderedHand(pub(crate) Vec>); pub(crate) fn render_changed_hand( hand: Single<&HandTiles, Changed>, - // hand_tiles: Query<&HandTiles, With>, - tiles: Query<&Tile>, - mut target: Single<&'static mut RenderedHand>, + tiles: Populated<&Tile>, + mut target: ResMut, ) -> Result { + trace!("render_changed_hand"); + + render_hand(hand, tiles, target)?; + + Ok(()) +} + +pub(crate) fn render_hand( + hand: Single<&HandTiles, Changed>, + tiles: Populated<&Tile>, + mut target: ResMut, +) -> Result { + trace!("render_hand"); + let hand_tiles = hand .iter() .map(|inhand| -> Result<_> { Ok(tiles.get(inhand).map(tiles::draw_tile)?) }) .collect::>>()?; - target.0 = hand_tiles; Ok(()) diff --git a/src/tui/render/ingame.rs b/src/tui/render/ingame.rs index 642c3f8..3d407f8 100644 --- a/src/tui/render/ingame.rs +++ b/src/tui/render/ingame.rs @@ -4,10 +4,7 @@ use bevy_ratatui::RatatuiContext; use crate::tui::render::hand; pub(crate) fn draw_ingame( - // tiles: Query<&Tile>, - // wall_tiles: Option>>, - // hand_tiles: Query<&HandTiles, With>, - rendered_hand: Single<&'static hand::RenderedHand>, + rendered_hand: Res, mut tui_ctx: ResMut, ) -> Result { use ratatui::layout::Flex; From 81cb5c24d4e72791622c67c2ab318004ad3b0fae Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:56:30 -0800 Subject: [PATCH 09/10] cleanup --- src/game/mod.rs | 3 +-- src/game/wall.rs | 1 - src/lib.rs | 3 +-- src/tiles.rs | 3 --- src/tui/mod.rs | 50 +++++++++++++++--------------------------------- 5 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/game/mod.rs b/src/game/mod.rs index bf41e6c..b099460 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,5 +1,4 @@ use bevy::prelude::*; -use tracing::instrument; use crate::tiles::{self, *}; @@ -25,7 +24,7 @@ impl Plugin for Riichi { .add_systems(Startup, tiles::init_tiles) .init_state::() .add_systems(OnEnter(GameState::Setup), (wall::build_wall, hand::deal_hands, setup_done).chain()) - // .add_systems(Update, systems) + .add_systems(Update, (hand::sort_hand).run_if(in_state(GameState::Play))) // semicolon stopper ; } diff --git a/src/game/wall.rs b/src/game/wall.rs index 5202d0e..69e4fd7 100644 --- a/src/game/wall.rs +++ b/src/game/wall.rs @@ -1,4 +1,3 @@ -use bevy::log::tracing::instrument; use bevy::prelude::*; use rand::seq::SliceRandom; diff --git a/src/lib.rs b/src/lib.rs index 1740455..45412c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![allow(unused)] +pub mod game; pub mod tiles; pub mod yakus; - -pub mod game; diff --git a/src/tiles.rs b/src/tiles.rs index 634e115..5147233 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,8 +1,5 @@ use bevy::{ecs::entity::MapEntities, prelude::*}; use strum::FromRepr; -use tracing::instrument; - -use crate::game::{hand::HandTiles, wall::WallTiles}; #[derive(Component, Debug)] pub struct Tile { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 639b23a..5ccd9bf 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -18,11 +18,22 @@ enum TuiState { InGame, } -#[derive(Default)] -pub struct RiichiTui { - // player_names: Vec, +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +struct InGame; + +impl ComputedStates for InGame { + type SourceStates = TuiState; + + fn compute(sources: Self::SourceStates) -> Option { + match sources { + TuiState::MainMenu => None, + TuiState::InGame => Some(Self), + } + } } +#[derive(Default)] +pub struct RiichiTui; impl Plugin for RiichiTui { fn build(&self, app: &mut App) { app.add_plugins(( @@ -45,48 +56,17 @@ impl Plugin for RiichiTui { // general setup .init_state::() .add_computed_state::() - .add_systems(Update, curr_state) // main menu .add_systems(Update, (menu::draw_mainmenu, menu::mainmenu_input).run_if(in_state(TuiState::MainMenu))) // gaming .init_resource::() - .add_systems(OnEnter(GameState::Play), render::hand::render_hand) + .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(InGame))) .add_systems(Update, render::hand::render_changed_hand.run_if(in_state(InGame).and(in_state(GameState::Play)))) - .add_systems(Update, render::ingame::draw_ingame.run_if(in_state(TuiState::InGame))) // semicolon stopper ; } } -fn curr_state( - curr_gamestate: Option>>, - curr_tuistate: Res>, - curr_ingame: Option>>, -) { - if let Some(curr_gamestate) = curr_gamestate && curr_gamestate.is_changed(){ - trace!("GameState: {curr_gamestate:?}") - } - if curr_tuistate.is_changed() { - trace!("TuiState: {curr_tuistate:?}") - } - if let Some(curr_ingame) = curr_ingame && curr_ingame.is_changed(){ - trace!("InGame: {curr_ingame:?}") - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -struct InGame; - -impl ComputedStates for InGame { - type SourceStates = TuiState; - - fn compute(sources: Self::SourceStates) -> Option { - match sources { - TuiState::MainMenu => None, - TuiState::InGame => Some(Self), - } - } -} From 5504db4e0fdbad2c53465f4fcd88dd3af35100c0 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Mon, 12 Jan 2026 21:07:34 -0800 Subject: [PATCH 10/10] big ass input system --- src/tui/console.rs | 19 -------------- src/tui/menu.rs | 21 ---------------- src/tui/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++-- src/tui/render/ingame.rs | 2 +- 4 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/tui/console.rs b/src/tui/console.rs index 0d4ac88..f668331 100644 --- a/src/tui/console.rs +++ b/src/tui/console.rs @@ -1,8 +1,5 @@ -use bevy::input::keyboard::Key; use bevy::prelude::*; use bevy_ratatui::RatatuiContext; -use bevy_ratatui::event::KeyMessage; -use ratatui::crossterm::event::KeyCode; use ratatui::widgets::Block; use tui_logger::TuiLoggerWidget; @@ -24,22 +21,6 @@ impl std::ops::Not for ConsoleState { } } -pub(crate) fn toggle_console( - // input: Res>, - mut messages: MessageReader, - curr_state: Res>, - mut next_state: ResMut>, -) { - // if input.just_pressed(Key::Character("`".into())) { - // next_state.set(!*curr_state.get()); - // } - for message in messages.read() { - if let KeyCode::Char('`') = message.code { - next_state.set(!*curr_state.get()); - } - } -} - pub(crate) fn draw_console(mut tui_ctx: ResMut) -> Result { tui_ctx.draw(|frame| { let block = Block::bordered().title("console"); diff --git a/src/tui/menu.rs b/src/tui/menu.rs index 65da70b..3ee27e5 100644 --- a/src/tui/menu.rs +++ b/src/tui/menu.rs @@ -27,24 +27,3 @@ pub(crate) fn draw_mainmenu( } }); } - -pub(crate) fn mainmenu_input( - // input: Res>, - mut messages: MessageReader, - mut next_tuistate: ResMut>, - mut next_gamestate: ResMut>, - mut exit: MessageWriter, -) { - for message in messages.read() { - match message.code { - KeyCode::Char('p') => { - next_tuistate.set(TuiState::InGame); - next_gamestate.set(GameState::Setup); - } - KeyCode::Char('q') => { - exit.write_default(); - } - _ => {} - } - } -} diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 5ccd9bf..e90a87f 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -2,17 +2,20 @@ use std::time::Duration; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; use bevy_ratatui::RatatuiPlugins; +use bevy_ratatui::event::KeyMessage; use ratatui::{text::ToSpan, widgets::Paragraph}; use jong::game::GameState; use jong::game::wall::InWall; +use crate::tui::console::ConsoleState; + mod console; mod menu; mod render; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] -enum TuiState { +pub(crate) enum TuiState { #[default] MainMenu, InGame, @@ -56,9 +59,10 @@ impl Plugin for RiichiTui { // general setup .init_state::() .add_computed_state::() + .add_systems(Update, input_system) // main menu - .add_systems(Update, (menu::draw_mainmenu, menu::mainmenu_input).run_if(in_state(TuiState::MainMenu))) + .add_systems(Update, menu::draw_mainmenu.run_if(in_state(TuiState::MainMenu))) // gaming .init_resource::() @@ -70,3 +74,49 @@ impl Plugin for RiichiTui { } } +#[allow(clippy::too_many_arguments)] +pub(crate) fn input_system( + mut messages: MessageReader, + + curr_tuistate: Res>, + curr_consolestate: Res>, + curr_gamestate: Res>, + + mut next_tuistate: ResMut>, + mut next_consolestate: ResMut>, + mut next_gamestate: ResMut>, + + mut exit: MessageWriter, +) { + use bevy_ratatui::crossterm::event::KeyCode; + + let (ts, cs, gs) = (curr_tuistate.get(), curr_consolestate.get(), curr_gamestate.get()); + + for message in messages.read() { + if let KeyCode::Char('`') = message.code { + next_consolestate.set(!*curr_consolestate.get()); + continue + } + + match ts { + TuiState::MainMenu => match message.code { + KeyCode::Char('p') => { + next_tuistate.set(TuiState::InGame); + next_gamestate.set(GameState::Setup); + } + KeyCode::Char('q') => { + exit.write_default(); + } + _ => {} + }, + TuiState::InGame => match gs { + // GameState::None => todo!(), + GameState::Setup => todo!(), + GameState::Play => todo!(), + GameState::Score => todo!(), + _ => todo!() + }, + } + } +} + diff --git a/src/tui/render/ingame.rs b/src/tui/render/ingame.rs index 3d407f8..6a30d4e 100644 --- a/src/tui/render/ingame.rs +++ b/src/tui/render/ingame.rs @@ -11,7 +11,7 @@ pub(crate) fn draw_ingame( use ratatui::prelude::*; tui_ctx.draw(|frame| { - debug!("{}", frame.area()); + // debug!("{}", frame.area()); let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); let mut area = frame.area();