From 87288601cab72bfc0599f3e612eae9e3d3c54712 Mon Sep 17 00:00:00 2001 From: YK Date: Sun, 14 Jul 2024 20:25:02 +0300 Subject: [PATCH] feat: core gameplay --- src/common.rs | 8 +++- src/game.rs | 124 +++++++++++++++++++++++++++++++++++++++---------- src/main.rs | 7 +-- src/prelude.rs | 5 +- 4 files changed, 114 insertions(+), 30 deletions(-) diff --git a/src/common.rs b/src/common.rs index 2b5b773..2adc795 100644 --- a/src/common.rs +++ b/src/common.rs @@ -16,7 +16,13 @@ pub fn button_system ( } } -pub fn despawn_screen (to_despawn: Query>, mut commands: Commands) { +pub fn despawn_screen (to_despawn: Query>, commands: &mut Commands) { + for entity in &to_despawn { + commands.entity(entity).despawn_recursive(); + } +} + +pub fn despawn_screen_move (to_despawn: Query>, mut commands: Commands) { for entity in &to_despawn { commands.entity(entity).despawn_recursive(); } diff --git a/src/game.rs b/src/game.rs index 9df12da..5cd86e7 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,11 +1,13 @@ -use crate::prelude::*; +use itertools::Itertools; +use rand::{distributions::{Distribution, WeightedIndex}, seq::IteratorRandom}; + +use crate::{common::despawn_screen, prelude::*}; #[derive(Component)] pub struct FieldView; - pub fn enter ( mut commands: Commands, asset_server: Res, @@ -80,10 +82,10 @@ pub fn enter ( let mut start = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]; start.shuffle(thread); - *field = Field(start); + *field = Field(start, 4); - render_field(commands, asset_server, field.into()); + // render_field(commands, asset_server, field.into(), q); } pub fn update_scoreboard (score: Res, mut query: Query<&mut Text, With>) { @@ -95,30 +97,39 @@ pub fn render_field ( mut commands: Commands, asset_server: Res, field: Res, + mut prev_field: ResMut, + q: Query>, ) { - for (idx, &val) in field.iter().enumerate() { - if val > 0 { - spawn_square_at(&mut commands, val, idx, asset_server.clone()); + if prev_field.0 != field.0 { + despawn_screen::(q, &mut commands); + + for (idx, &val) in field.iter().enumerate() { + if val > 0 { + spawn_square_at(&mut commands, val, idx, asset_server.clone()); + } } + + prev_field.0 = field.0.clone(); } + } -fn spawn_square_at (commands: &mut Commands, level: u32, position: usize, asset_server: AssetServer) { +fn spawn_square_at (commands: &mut Commands, level: u8, position: usize, asset_server: AssetServer) { let font = asset_server.load("proxima_thin.ttf"); let Some(&(x, y)) = sq_origins.get(position) else { return; }; - let text = 2i32.pow(level); + let text = 2i32.pow(level as u32).to_string(); let label = Text2dBundle { - text: Text::from_section(text.to_string(), TextStyle { + text: Text::from_section(text, TextStyle { font, font_size: 44., color: Color::WHITE, }), transform: Transform { - translation: Vec3::new(x, y, 0.0), + translation: Vec3::new(x, y, 1.), ..Default::default() }, ..Default::default() @@ -150,19 +161,21 @@ pub fn process_inputs ( score: ResMut, ) { + let mover = |dir: MoveDirection| move_field(dir, field, score); + for key in kb.get_just_pressed() { match key { KeyCode::ArrowUp | KeyCode::KeyW => { - return move_field(MoveDirection::Up, field, score); + return mover(MoveDirection::Up); }, KeyCode::ArrowDown | KeyCode::KeyS => { - return move_field(MoveDirection::Down, field, score); + return mover(MoveDirection::Down); }, KeyCode::ArrowLeft | KeyCode::KeyA => { - return move_field(MoveDirection::Left, field, score); + return mover(MoveDirection::Left); }, KeyCode::ArrowRight | KeyCode::KeyD => { - return move_field(MoveDirection::Right, field, score); + return mover(MoveDirection::Right); }, _ => () } @@ -179,28 +192,89 @@ pub fn process_inputs ( // controls (with custom actuation point) // https://bevy-cheatbook.github.io/input/gamepad.html#gamepad-settings if icgbp(GBT::DPadLeft) { - return move_field(MoveDirection::Left, field, score); + return mover(MoveDirection::Left); } else if icgbp(GBT::DPadRight) { - return move_field(MoveDirection::Right, field, score); + return mover(MoveDirection::Right); } else if icgbp(GBT::DPadUp) { - return move_field(MoveDirection::Up, field, score); + return mover(MoveDirection::Up); } else if icgbp(GBT::DPadDown) { - return move_field(MoveDirection::Down, field, score); + return mover(MoveDirection::Down); } } } -pub fn move_field ( +fn move_field ( direction: MoveDirection, - field: ResMut, - score: ResMut + mut field: ResMut, + mut score: ResMut, ) { - - println!("{:?} {:?}", direction, score); + let (new, score_incr) = calculate_move(&field.0, field.1, direction); + if new != field.0 { + field.0 = new; + score.0 += score_incr; + spawn_new_square(field); + } } -pub fn redraw_field () { +fn calculate_move (source: &[u8], size: usize, dir: MoveDirection) -> (Vec, usize) { + let mut score_changes = vec![]; + let mut target = vec![0; source.len()]; + let mut qs = Vec::with_capacity(size); + + for x in 0..size { + let mut col = Vec::with_capacity(size); + for y in 0..size { + if dir == MoveDirection::Up || dir == MoveDirection::Down { + col.push(y * size + x); + } else { + col.push(x * size + y); + } + } + + if dir == MoveDirection::Down || dir == MoveDirection::Right { + col.reverse(); + } + + qs.push(col); + } + + + for q in qs { + let mut write_cursor = 0; + let mut cursor = 0; + let _in = q.iter().map(|&idx| source[idx]).filter(|&e| e != 0).collect_vec(); + + while let Some(&cur) = _in.get(cursor) { + let next = _in.get(cursor + 1); + + match (cur, next) { + (a, Some(b)) if a == *b => { + target[q[write_cursor]] = cur + 1; + score_changes.push(cur as usize); + write_cursor += 1; + cursor += 2; + }, + _ => { + target[q[write_cursor]] = cur; + write_cursor += 1; + cursor += 1; + } + } + } + + } + + (target, score_changes.iter().sum()) +} + +fn spawn_new_square (mut field: ResMut) { + let rng = &mut thread_rng(); + let Some(empty) = field.0.iter().enumerate().filter(|&(_, &val)| val == 0).map(|(idx, _)| idx).choose(rng) else { return }; + let choices = [1, 2, 3]; + let weights = [8, 4, 1]; + let Ok(index) = WeightedIndex::new(&weights) else { return }; + field.0[empty] = choices[index.sample(rng)]; } diff --git a/src/main.rs b/src/main.rs index 52f72d9..decb62d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,12 +17,13 @@ impl Plugin for SqPlugin { .insert_state(State4096::Game) .insert_state(InGameState::Static) .insert_resource(Score(0)) - .insert_resource(Field(vec![])) + .insert_resource(Field(vec![], 0)) + .insert_resource(PrevField(vec![])) .add_systems(Startup, setup::setup) - .add_systems(Update, (game::update_scoreboard, game::process_inputs).run_if(in_state(State4096::Game))) + .add_systems(Update, (game::render_field, game::update_scoreboard, game::process_inputs).run_if(in_state(State4096::Game))) .add_systems(Update, (menu::actions, common::button_system).run_if(in_state(State4096::Menu))) .add_systems(OnEnter(State4096::Menu), menu::enter) - .add_systems(OnExit(State4096::Menu), common::despawn_screen::) + .add_systems(OnExit(State4096::Menu), common::despawn_screen_move::) .add_systems(OnEnter(State4096::Game), game::enter) ; } diff --git a/src/prelude.rs b/src/prelude.rs index 0e82304..7d291bc 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,7 +9,10 @@ pub struct ScoreboardUi; #[derive(Resource, Deref, DerefMut)] -pub struct Field (pub Vec); +pub struct Field (#[deref] pub Vec, pub usize); + +#[derive(Resource, Deref, DerefMut)] +pub struct PrevField (pub Vec); #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum MoveDirection {