From a9e3d9cd70526a7d7a39aad2d2d704590be9c366 Mon Sep 17 00:00:00 2001 From: pythops Date: Wed, 12 Jun 2024 16:58:19 +0200 Subject: [PATCH 1/3] Add CI --- .github/workflows/ci.yaml | 28 ++++++++++++++++ .github/workflows/release.yaml | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..991fe55 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,28 @@ +--- +on: + push: + branches: + - "*" + tags: + - "!*" +name: CI +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: taiki-e/install-action@just + + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: clippy rustfmt + + - name: linting + run: | + cargo clippy --workspace --all-features -- -D warnings + cargo fmt --all -- --check + + - name: Debug builds + run: cargo build diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1306f18 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,61 @@ +--- +name: Release +on: + push: + tags: + - v[0-9]+.* +jobs: + build: + permissions: + contents: write + continue-on-error: false + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-apple-darwin + os: macos-latest + - target: x86_64-pc-windows-msvc + os: windows-latest + - target: aarch64-pc-windows-msvc + os: windows-latest + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Target + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install musl + if: matrix.target == 'x86_64-unknown-linux-musl' + run: | + sudo apt update + sudo apt install -y musl-tools gcc + + - name: Build + run: cargo build --release --target ${{ matrix.target }} + + - name: Strip Binary (linux) + if: matrix.target == 'x86_64-unknown-linux-gnu' || matrix.target == 'x86_64-unknown-linux-musl' + run: strip ./target/${{ matrix.target }}/release/ratatui-snake + + - name: Rename Binary (unix) + if: runner.os != 'Windows' + run: mv ./target/${{ matrix.target }}/release/ratatui-snake ./ratatui-snake-${{ matrix.target }} + + - name: Rename Binary (windows) + if: runner.os == 'Windows' + run: mv ./target/${{ matrix.target }}/release/ratatui-snake.exe ./ratatui-snake-${{ matrix.target }}.exe + + - name: Upload Binary + uses: softprops/action-gh-release@v2 + with: + files: "ratatui-snake*" From 4bceea092fd829a53076997d95eab1030c62f0a7 Mon Sep 17 00:00:00 2001 From: pythops Date: Wed, 12 Jun 2024 17:00:51 +0200 Subject: [PATCH 2/3] fix linting errors --- src/coord.rs | 21 +++- src/game.rs | 14 ++- src/letters.rs | 297 +++++++++++++++++++++++++------------------------ src/main.rs | 63 +++++------ 4 files changed, 206 insertions(+), 189 deletions(-) diff --git a/src/coord.rs b/src/coord.rs index 95f0539..df25799 100644 --- a/src/coord.rs +++ b/src/coord.rs @@ -6,10 +6,7 @@ pub struct Coord { impl Coord { pub fn new(x: f64, y: f64) -> Self { - Coord { - x, - y, - } + Coord { x, y } } pub fn move_left(&mut self, speed: f64) { @@ -44,8 +41,20 @@ impl Coord { } pub fn move_toward(&mut self, c: &Coord) { - let x_diff = if self.x < c.x { 1.0 } else if self.x > c.x { -1.0 } else { 0.0 }; - let y_diff = if self.y < c.y { 1.0 } else if self.y > c.y { -1.0 } else { 0.0 }; + let x_diff = if self.x < c.x { + 1.0 + } else if self.x > c.x { + -1.0 + } else { + 0.0 + }; + let y_diff = if self.y < c.y { + 1.0 + } else if self.y > c.y { + -1.0 + } else { + 0.0 + }; self.x += x_diff; self.y += y_diff; diff --git a/src/game.rs b/src/game.rs index 5bdd13a..f8a8388 100644 --- a/src/game.rs +++ b/src/game.rs @@ -52,7 +52,11 @@ impl Game { pub fn increase_frame_num(&mut self) { self.frame_num += { - if self.frame_num > 10 / (self.speed as i32) { -self.frame_num } else { 1 } + if self.frame_num > 10 / (self.speed as i32) { + -self.frame_num + } else { + 1 + } }; } @@ -91,7 +95,8 @@ impl Game { pub fn change_direction(&mut self, direction: Direction) { self.direction = direction; - self.corners.push(Coord::new(self.head_coord.x, self.head_coord.y)) + self.corners + .push(Coord::new(self.head_coord.x, self.head_coord.y)) } pub fn restart(&mut self) { @@ -115,9 +120,8 @@ impl Game { let end = &pair[1]; // check if the head coord is between start and end - if - (self.head_coord.x - start.x) * (self.head_coord.x - end.x) <= 0.0 && - (self.head_coord.y - start.y) * (self.head_coord.y - end.y) <= 0.0 + if (self.head_coord.x - start.x) * (self.head_coord.x - end.x) <= 0.0 + && (self.head_coord.y - start.y) * (self.head_coord.y - end.y) <= 0.0 { self.state = GameState::GameOver; } diff --git a/src/letters.rs b/src/letters.rs index 38d8ab1..aca2375 100644 --- a/src/letters.rs +++ b/src/letters.rs @@ -1,4 +1,7 @@ -use ratatui::{ style::Color, widgets::canvas::{ Line, Painter, Shape } }; +use ratatui::{ + style::Color, + widgets::canvas::{Line, Painter, Shape}, +}; pub struct Letter { pub lines: Vec>, @@ -15,7 +18,7 @@ impl Shape for Letter { line_points[1] * (self.scale as f64), line_points[2] * (self.scale as f64) + self.starting_x, line_points[3] * (self.scale as f64), - self.color + self.color, ); line.draw(painter); } @@ -26,177 +29,183 @@ impl Letter { pub fn new_letter(letter: char, starting_x: f64) -> Self { let lines = match letter { // A - 'a' => - vec![ - vec![1.0, 0.0, 2.5, 5.0], - vec![2.5, 5.0, 4.0, 0.0], - vec![1.25, 2.5, 3.75, 2.5] - ], + 'a' => vec![ + vec![1.0, 0.0, 2.5, 5.0], + vec![2.5, 5.0, 4.0, 0.0], + vec![1.25, 2.5, 3.75, 2.5], + ], // B - 'b' => - vec![ - vec![0.0, 0.0, 0.0, 5.0], - vec![0.0, 5.0, 3.0, 5.0], - vec![3.0, 5.0, 4.0, 4.0], - vec![4.0, 4.0, 4.0, 3.0], - vec![4.0, 3.0, 3.0, 2.5], - vec![3.0, 2.5, 4.0, 2.0], - vec![4.0, 2.0, 4.0, 1.0], - vec![4.0, 1.0, 3.0, 0.0], - vec![3.0, 0.0, 0.0, 0.0] - ], + 'b' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 3.0, 5.0], + vec![3.0, 5.0, 4.0, 4.0], + vec![4.0, 4.0, 4.0, 3.0], + vec![4.0, 3.0, 3.0, 2.5], + vec![3.0, 2.5, 4.0, 2.0], + vec![4.0, 2.0, 4.0, 1.0], + vec![4.0, 1.0, 3.0, 0.0], + vec![3.0, 0.0, 0.0, 0.0], + ], // C - 'c' => - vec![ - vec![4.0, 5.0, 1.0, 5.0], - vec![1.0, 5.0, 0.0, 4.0], - vec![0.0, 4.0, 0.0, 1.0], - vec![0.0, 1.0, 1.0, 0.0], - vec![1.0, 0.0, 4.0, 0.0] - ], + 'c' => vec![ + vec![4.0, 5.0, 1.0, 5.0], + vec![1.0, 5.0, 0.0, 4.0], + vec![0.0, 4.0, 0.0, 1.0], + vec![0.0, 1.0, 1.0, 0.0], + vec![1.0, 0.0, 4.0, 0.0], + ], // D - 'd' => - vec![ - vec![0.0, 0.0, 0.0, 5.0], - vec![0.0, 5.0, 3.0, 5.0], - vec![3.0, 5.0, 4.0, 4.0], - vec![4.0, 4.0, 4.0, 1.0], - vec![4.0, 1.0, 3.0, 0.0], - vec![3.0, 0.0, 0.0, 0.0] - ], + 'd' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 3.0, 5.0], + vec![3.0, 5.0, 4.0, 4.0], + vec![4.0, 4.0, 4.0, 1.0], + vec![4.0, 1.0, 3.0, 0.0], + vec![3.0, 0.0, 0.0, 0.0], + ], // E - 'e' => - vec![ - vec![4.0, 5.0, 0.0, 5.0], - vec![0.0, 5.0, 0.0, 0.0], - vec![0.0, 0.0, 4.0, 0.0], - vec![0.0, 2.5, 3.0, 2.5] - ], + 'e' => vec![ + vec![4.0, 5.0, 0.0, 5.0], + vec![0.0, 5.0, 0.0, 0.0], + vec![0.0, 0.0, 4.0, 0.0], + vec![0.0, 2.5, 3.0, 2.5], + ], // F - 'f' => - vec![vec![0.0, 5.0, 0.0, 0.0], vec![0.0, 5.0, 4.0, 5.0], vec![0.0, 2.5, 3.0, 2.5]], + 'f' => vec![ + vec![0.0, 5.0, 0.0, 0.0], + vec![0.0, 5.0, 4.0, 5.0], + vec![0.0, 2.5, 3.0, 2.5], + ], // G - 'g' => - vec![ - vec![4.0, 5.0, 1.0, 5.0], - vec![1.0, 5.0, 0.0, 4.0], - vec![0.0, 4.0, 0.0, 1.0], - vec![0.0, 1.0, 1.0, 0.0], - vec![1.0, 0.0, 4.0, 0.0], - vec![4.0, 0.0, 4.0, 2.5], - vec![4.0, 2.5, 2.5, 2.5] - ], + 'g' => vec![ + vec![4.0, 5.0, 1.0, 5.0], + vec![1.0, 5.0, 0.0, 4.0], + vec![0.0, 4.0, 0.0, 1.0], + vec![0.0, 1.0, 1.0, 0.0], + vec![1.0, 0.0, 4.0, 0.0], + vec![4.0, 0.0, 4.0, 2.5], + vec![4.0, 2.5, 2.5, 2.5], + ], // H - 'h' => - vec![vec![0.0, 0.0, 0.0, 5.0], vec![0.0, 2.5, 4.0, 2.5], vec![4.0, 0.0, 4.0, 5.0]], + 'h' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 2.5, 4.0, 2.5], + vec![4.0, 0.0, 4.0, 5.0], + ], // I - 'i' => - vec![vec![1.0, 5.0, 4.0, 5.0], vec![2.5, 5.0, 2.5, 0.0], vec![1.0, 0.0, 4.0, 0.0]], + 'i' => vec![ + vec![1.0, 5.0, 4.0, 5.0], + vec![2.5, 5.0, 2.5, 0.0], + vec![1.0, 0.0, 4.0, 0.0], + ], // J - 'j' => - vec![ - vec![1.0, 5.0, 4.0, 5.0], - vec![2.5, 5.0, 2.5, 1.0], - vec![2.5, 1.0, 1.5, 0.0], - vec![1.5, 0.0, 0.0, 0.0] - ], + 'j' => vec![ + vec![1.0, 5.0, 4.0, 5.0], + vec![2.5, 5.0, 2.5, 1.0], + vec![2.5, 1.0, 1.5, 0.0], + vec![1.5, 0.0, 0.0, 0.0], + ], // K - 'k' => - vec![vec![0.0, 0.0, 0.0, 5.0], vec![0.0, 2.5, 4.0, 5.0], vec![0.0, 2.5, 4.0, 0.0]], + 'k' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 2.5, 4.0, 5.0], + vec![0.0, 2.5, 4.0, 0.0], + ], // L 'l' => vec![vec![0.0, 5.0, 0.0, 0.0], vec![0.0, 0.0, 4.0, 0.0]], // M - 'm' => - vec![ - vec![0.0, 0.0, 0.0, 5.0], - vec![0.0, 5.0, 2.5, 2.5], - vec![2.5, 2.5, 5.0, 5.0], - vec![5.0, 5.0, 5.0, 0.0] - ], + 'm' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 2.5, 2.5], + vec![2.5, 2.5, 5.0, 5.0], + vec![5.0, 5.0, 5.0, 0.0], + ], // N - 'n' => - vec![vec![0.0, 0.0, 0.0, 5.0], vec![0.0, 5.0, 5.0, 0.0], vec![5.0, 0.0, 5.0, 5.0]], + 'n' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 5.0, 0.0], + vec![5.0, 0.0, 5.0, 5.0], + ], // O - 'o' => - vec![ - vec![1.0, 5.0, 4.0, 5.0], - vec![4.0, 5.0, 5.0, 4.0], - vec![5.0, 4.0, 5.0, 1.0], - vec![5.0, 1.0, 4.0, 0.0], - vec![4.0, 0.0, 1.0, 0.0], - vec![1.0, 0.0, 0.0, 1.0], - vec![0.0, 1.0, 0.0, 4.0], - vec![0.0, 4.0, 1.0, 5.0] - ], + 'o' => vec![ + vec![1.0, 5.0, 4.0, 5.0], + vec![4.0, 5.0, 5.0, 4.0], + vec![5.0, 4.0, 5.0, 1.0], + vec![5.0, 1.0, 4.0, 0.0], + vec![4.0, 0.0, 1.0, 0.0], + vec![1.0, 0.0, 0.0, 1.0], + vec![0.0, 1.0, 0.0, 4.0], + vec![0.0, 4.0, 1.0, 5.0], + ], // P - 'p' => - vec![ - vec![0.0, 0.0, 0.0, 5.0], - vec![0.0, 5.0, 4.0, 5.0], - vec![4.0, 5.0, 4.0, 2.5], - vec![4.0, 2.5, 0.0, 2.5] - ], + 'p' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 4.0, 5.0], + vec![4.0, 5.0, 4.0, 2.5], + vec![4.0, 2.5, 0.0, 2.5], + ], // Q - 'q' => - vec![ - vec![1.0, 5.0, 4.0, 5.0], - vec![4.0, 5.0, 5.0, 4.0], - vec![5.0, 4.0, 5.0, 1.0], - vec![5.0, 1.0, 4.0, 0.0], - vec![4.0, 0.0, 1.0, 0.0], - vec![1.0, 0.0, 0.0, 1.0], - vec![0.0, 1.0, 0.0, 4.0], - vec![0.0, 4.0, 1.0, 5.0], - vec![3.0, 2.0, 5.0, 0.0] - ], + 'q' => vec![ + vec![1.0, 5.0, 4.0, 5.0], + vec![4.0, 5.0, 5.0, 4.0], + vec![5.0, 4.0, 5.0, 1.0], + vec![5.0, 1.0, 4.0, 0.0], + vec![4.0, 0.0, 1.0, 0.0], + vec![1.0, 0.0, 0.0, 1.0], + vec![0.0, 1.0, 0.0, 4.0], + vec![0.0, 4.0, 1.0, 5.0], + vec![3.0, 2.0, 5.0, 0.0], + ], // R - 'r' => - vec![ - vec![0.0, 0.0, 0.0, 5.0], - vec![0.0, 5.0, 4.0, 5.0], - vec![4.0, 5.0, 4.0, 2.5], - vec![4.0, 2.5, 0.0, 2.5], - vec![0.0, 2.5, 4.0, 0.0] - ], + 'r' => vec![ + vec![0.0, 0.0, 0.0, 5.0], + vec![0.0, 5.0, 4.0, 5.0], + vec![4.0, 5.0, 4.0, 2.5], + vec![4.0, 2.5, 0.0, 2.5], + vec![0.0, 2.5, 4.0, 0.0], + ], // S - 's' => - vec![ - vec![4.0, 5.0, 1.0, 5.0], - vec![1.0, 5.0, 0.0, 4.0], - vec![0.0, 4.0, 4.0, 1.0], - vec![4.0, 1.0, 4.0, 0.0], - vec![4.0, 0.0, 1.0, 0.0], - vec![1.0, 0.0, 0.0, 1.0] - ], + 's' => vec![ + vec![4.0, 5.0, 1.0, 5.0], + vec![1.0, 5.0, 0.0, 4.0], + vec![0.0, 4.0, 4.0, 1.0], + vec![4.0, 1.0, 4.0, 0.0], + vec![4.0, 0.0, 1.0, 0.0], + vec![1.0, 0.0, 0.0, 1.0], + ], // T 't' => vec![vec![0.0, 5.0, 5.0, 5.0], vec![2.5, 5.0, 2.5, 0.0]], // U - 'u' => - vec![ - vec![0.0, 5.0, 0.0, 1.0], - vec![0.0, 1.0, 1.0, 0.0], - vec![1.0, 0.0, 4.0, 0.0], - vec![4.0, 0.0, 5.0, 1.0], - vec![5.0, 1.0, 5.0, 5.0] - ], + 'u' => vec![ + vec![0.0, 5.0, 0.0, 1.0], + vec![0.0, 1.0, 1.0, 0.0], + vec![1.0, 0.0, 4.0, 0.0], + vec![4.0, 0.0, 5.0, 1.0], + vec![5.0, 1.0, 5.0, 5.0], + ], // V 'v' => vec![vec![0.0, 5.0, 2.5, 0.0], vec![2.5, 0.0, 5.0, 5.0]], // W - 'w' => - vec![ - vec![0.0, 5.0, 1.5, 0.0], - vec![1.5, 0.0, 2.5, 2.5], - vec![2.5, 2.5, 3.5, 0.0], - vec![3.5, 0.0, 5.0, 5.0] - ], + 'w' => vec![ + vec![0.0, 5.0, 1.5, 0.0], + vec![1.5, 0.0, 2.5, 2.5], + vec![2.5, 2.5, 3.5, 0.0], + vec![3.5, 0.0, 5.0, 5.0], + ], // X 'x' => vec![vec![0.0, 0.0, 5.0, 5.0], vec![5.0, 0.0, 0.0, 5.0]], // Y - 'y' => - vec![vec![0.0, 5.0, 2.5, 2.5], vec![5.0, 5.0, 2.5, 2.5], vec![2.5, 2.5, 2.5, 0.0]], + 'y' => vec![ + vec![0.0, 5.0, 2.5, 2.5], + vec![5.0, 5.0, 2.5, 2.5], + vec![2.5, 2.5, 2.5, 0.0], + ], // Z - 'z' => - vec![vec![0.0, 5.0, 5.0, 5.0], vec![5.0, 5.0, 0.0, 0.0], vec![0.0, 0.0, 5.0, 0.0]], + 'z' => vec![ + vec![0.0, 5.0, 5.0, 5.0], + vec![5.0, 5.0, 0.0, 0.0], + vec![0.0, 0.0, 5.0, 0.0], + ], // Space ' ' => vec![], diff --git a/src/main.rs b/src/main.rs index b9dd289..e3de0f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,19 @@ use coord::Coord; use crossterm::{ - event::{ self, KeyCode, KeyEventKind }, - terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen }, + event::{self, KeyCode, KeyEventKind}, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; -use game::{ Direction, Game, GameState }; +use game::{Direction, Game, GameState}; use letters::Word; use ratatui::{ - prelude::{ CrosstermBackend, Terminal }, + prelude::{CrosstermBackend, Terminal}, style::Color, symbols::Marker, - widgets::canvas::{ Canvas, Line, Rectangle }, + widgets::canvas::{Canvas, Line, Rectangle}, }; -use std::io::{ stdout, Result }; +use std::io::{stdout, Result}; mod coord; mod game; @@ -50,15 +50,14 @@ fn main() -> Result<()> { } // check if reached the borders - if - game.head_coord.x.abs() + 1.0 >= width / 2.0 || - game.head_coord.y.abs() + 1.0 == height + if game.head_coord.x.abs() + 1.0 >= width / 2.0 + || game.head_coord.y.abs() + 1.0 == height { game.game_over(); } // check if the snake has eaten the point - if let None = game.point_coord { + if game.point_coord.is_none() { game.generate_new_point(width, height); } else if let Some(point) = &game.point_coord { if get_collision(&game.head_coord, point) { @@ -79,10 +78,10 @@ fn main() -> Result<()> { &(Rectangle { x: -width / 2.0, y: -height, - width: width, + width, height: height * 2.0, color: Color::White, - }) + }), ); ctx.layer(); @@ -91,7 +90,7 @@ fn main() -> Result<()> { ctx.print( -width / 2.0 + 3.0, height - 4.0, - format!("Score: {}", game.score) + format!("Score: {}", game.score), ); } @@ -99,23 +98,20 @@ fn main() -> Result<()> { match game.state { GameState::Running | GameState::Paused => { - game.corners - .windows(2) - .into_iter() - .for_each(|arr| { - let start_coord = &arr[0]; - let end_coord = &arr[1]; - - ctx.draw( - &(Line { - x1: start_coord.x, - y1: start_coord.y, - x2: end_coord.x, - y2: end_coord.y, - color: Color::Blue, - }) - ); - }); + game.corners.windows(2).for_each(|arr| { + let start_coord = &arr[0]; + let end_coord = &arr[1]; + + ctx.draw( + &(Line { + x1: start_coord.x, + y1: start_coord.y, + x2: end_coord.x, + y2: end_coord.y, + color: Color::Blue, + }), + ); + }); ctx.draw( &(Line { @@ -124,7 +120,7 @@ fn main() -> Result<()> { x2: last_corner_coord.x, y2: last_corner_coord.y, color: Color::Blue, - }) + }), ); if let Some(point) = &game.point_coord { @@ -135,7 +131,7 @@ fn main() -> Result<()> { x2: point.x, y2: point.y, color: Color::Red, - }) + }), ); } } @@ -149,8 +145,7 @@ fn main() -> Result<()> { } } }), - - area + area, ) }); From 6647e84d4ada729a2a7f0bdb6d9ce1e85421d740 Mon Sep 17 00:00:00 2001 From: pythops Date: Wed, 12 Jun 2024 17:09:43 +0200 Subject: [PATCH 3/3] Add vim keybindings --- src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index e3de0f2..4879217 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,22 +166,22 @@ fn main() -> Result<()> { game.state = GameState::Paused; } } - KeyCode::Char('a') => { + KeyCode::Char('a') | KeyCode::Char('h') => { if game.direction != Direction::Right { game.change_direction(Direction::Left); } } - KeyCode::Char('d') => { + KeyCode::Char('d') | KeyCode::Char('l') => { if game.direction != Direction::Left { game.change_direction(Direction::Right); } } - KeyCode::Char('w') => { + KeyCode::Char('w') | KeyCode::Char('k') => { if game.direction != Direction::Down { game.change_direction(Direction::Up); } } - KeyCode::Char('s') => { + KeyCode::Char('s') | KeyCode::Char('j') => { if game.direction != Direction::Up { game.change_direction(Direction::Down); }