Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Most implementations of 2048 use a matrix to represent the locations and values of different cells. To implement board shifts they rotate the matrix and apply the same function, then rotate it back. With the way we're using Bevy's ECS we are going to take a different approach and we'll be using simpler math. The algorithm we'll be using will order all of the tiles from the bottom left to the top right. So if every grid location is filled with a tile, we'd get the tile at `0,0`, then `1,0`, then `2,0`, then `3,0`. After reaching the end we go up one level and the fifth tile is `1,0`, and so on. With this arrangement of tiles, we can check the current tile and the next tile and see if they should be merged. If they're on different rows, or if their points values are different we skip forward to the next tile, otherwise we merge them. We'll add a query for all the tile entities to the `board_shift` system. We are going to potentially mutate the `Position` and `Points` components on the tile entity depending so they need to be mutable references. ```rust fn board_shift( input: Res<Input<KeyCode>>, mut tiles: Query<(Entity, &mut Position, &mut Points)>, ) ``` The first direction we're going to implement is `BoardShift::Left`. To start off, we'll make sure the tiles are sorted into the order we talked about earlier. The `tiles` query is mutable, so we need to use `[iter_mut](https://docs.rs/bevy/0.10.0/bevy/ecs/prelude/struct.Query.html#method.iter_mut)` to get an iterator. After getting an iterator, we can use `[.sorted_by](https://docs.rs/itertools/0.10.5/itertools/trait.Itertools.html#method.sorted_by)` to first sort by row and then column of the grid. `.sorted_by` is from the Itertools crate. `.sorted_by` expects an `[Ordering](https://doc.rust-lang.org/std/cmp/enum.Ordering.html)` value as the return type. `[Ordering](https://doc.rust-lang.org/std/cmp/enum.Ordering.html)` is an enum that has `Less`, `Equal`, and `Greater` variants. `[Ord](https://doc.rust-lang.org/std/cmp/trait.Ord.html)` is a trait that can be implemented for any type. It requires implementing the `cmp` method, which compares two values of the given type. In our case, `u8` already has an `Ord` implementation, so we can use `Ord::cmp` to determine if any two `y` values in a tile's `Position` component are equal or different. If they're equal, that means the two tiles are in the same row and we need to do the same for the `x` value. If they're not equal, we can pass the return value back to `sorted_by` because it will be an `Ordering` variant. ```rust Some(BoardShift::Left) => { dbg!("left"); let mut it = tiles.iter_mut().sorted_by(|a, b| { match Ord::cmp(&a.1.y, &b.1.y) { Ordering::Equal => { Ord::cmp(&a.1.x, &b.1.x) } ordering => ordering, } }); } ``` While the `Ord` trait is part of Rust's prelude and is already in scope for use to use: We need to bring `Ordering` ourselves, which we can add next to the `convert::TryFrom`. You can think of this as being like destructured imports in JavaScript. ```rust use std::{cmp::Ordering, convert::TryFrom}; ``` \## Aside: Debugging If this is confusing, you can do something to help. Add the following line after the sorting code. This will collect the iterator into a `Vec` and debug it to the console for you to look at when you hit the left arrow key. The `_` in the type signature tells Rust to "figure out what the type should be here", which it will be able to grab from our query. ```rust dbg!(it.collect::<Vec<_>>()); ``` If you have not done so already, you will also need to add a `Debug` representation to the `Points` and `Position` structs to use the above `dbg!` code. ```rust \#[derive(Debug, Component)] struct Points { value: u32, } \#[derive(Debug, Component)] struct Position { x: u8, y: u8, } ``` This prints out a tuple for each tile that matches what we asked for in the query type signature: `(Entity, &mut Position, &mut Points)`. If you run this a few times you will be able to see the tiles on the screen and compare them to the output we're getting from the sorting. ```rust [src/main.rs:247] "left" = "left" [src/main.rs:257] it.collect::<Vec<_>>() = [ ( 21v0, Mut( Position { x: 1, y: 0, }, ), Mut( Points { value: 2, }, ), ), ( 19v0, Mut( Position { x: 0, y: 2, }, ), Mut( Points { value: 2, }, ), ), ] ``` After you're done debugging, remember to remove that `dbg!` line of code or you'll start seeing some "borrow of moved value: `it`" warnings as we continue. The derive lines can stay.
- Loading branch information