├── .gitignore ├── src ├── main.rs ├── numbers.rs ├── lib.rs ├── lrd.rs ├── game.rs ├── wasm.rs ├── backend.rs ├── score.rs ├── pause.rs ├── manual.rs ├── queue.rs ├── timer.rs ├── board.rs ├── block.rs ├── tetris.rs └── color_grid.rs ├── www ├── bootstrap.js ├── index.js ├── webpack.config.js ├── index.html ├── package.json └── LICENSE-MIT ├── LICENSE ├── Cargo.toml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /wasm_backend/ 3 | /www/node_modules/ 4 | *.DS_Store -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "wasm-backend"))] 2 | fn main() { 3 | retris::game::run(); 4 | } 5 | 6 | #[cfg(feature = "wasm-backend")] 7 | fn main() {} -------------------------------------------------------------------------------- /src/numbers.rs: -------------------------------------------------------------------------------- 1 | pub fn padding(num: usize, length: usize) -> String { 2 | let mut output = num.to_string(); 3 | while output.len() < length { 4 | output = format!("0{}", output); 5 | } 6 | output 7 | } 8 | -------------------------------------------------------------------------------- /www/bootstrap.js: -------------------------------------------------------------------------------- 1 | // A dependency graph that contains any wasm must all be imported 2 | // asynchronously. This `bootstrap.js` file does the single async import, so 3 | // that no one else needs to worry about it again. 4 | import("./index.js") 5 | .catch(e => console.error("Error importing `index.js`:", e)); 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod backend; 2 | pub mod block; 3 | pub mod board; 4 | pub mod game; 5 | pub mod color_grid; 6 | pub mod lrd; 7 | pub mod manual; 8 | pub mod numbers; 9 | pub mod pause; 10 | pub mod score; 11 | pub mod tetris; 12 | pub mod timer; 13 | pub mod queue; 14 | 15 | #[cfg(feature = "wasm-backend")] 16 | pub mod wasm; 17 | 18 | -------------------------------------------------------------------------------- /www/index.js: -------------------------------------------------------------------------------- 1 | import { CursiveWrapper } from "retris"; 2 | 3 | const canvas = document.getElementById("cursive-wasm-canvas"); 4 | canvas.style.display = "block"; 5 | canvas.setAttribute("width", "1000"); 6 | canvas.setAttribute("height", "1000"); 7 | const ctx = canvas.getContext("2d"); 8 | ctx.fillStyle = "green"; 9 | ctx.fillRect(0, 0, 1000, 1000); 10 | 11 | CursiveWrapper.retris(); -------------------------------------------------------------------------------- /www/webpack.config.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | entry: "./bootstrap.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "bootstrap.js", 9 | }, 10 | mode: "development", 11 | plugins: [ 12 | new CopyWebpackPlugin(['index.html']) 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/lrd.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub enum LR { 3 | Left, 4 | Right, 5 | } 6 | 7 | impl LR { 8 | pub fn to_lrd(self) -> LRD { 9 | LRD::LR(self) 10 | } 11 | } 12 | 13 | #[derive(Clone, Copy)] 14 | pub enum LRD { 15 | LR(LR), 16 | Down, 17 | } 18 | 19 | impl LRD { 20 | pub fn delta(&self) -> (i32, i32) { 21 | match self { 22 | LRD::LR(LR::Left)=> (-1, 0), 23 | LRD::LR(LR::Right) => (1, 0), 24 | LRD::Down => (0, 1), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/game.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(feature = "wasm-backend"))] 2 | use crate::backend::backend; 3 | use crate::tetris::Tetris; 4 | 5 | use cursive::{ 6 | Cursive, 7 | view::{Nameable, Selector}, 8 | }; 9 | 10 | pub fn run() { 11 | let mut siv: Cursive = Cursive::new(); 12 | 13 | let tetris = Tetris::new().with_name("tetris"); 14 | 15 | siv.add_layer(tetris); 16 | siv.focus(&Selector::Name("tetris")).unwrap(); 17 | 18 | siv.set_fps(1000); 19 | 20 | let siv: std::sync::Mutex = std::sync::Mutex::new(siv); 21 | siv.lock().unwrap().run_with(|| backend()); 22 | } -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | wretris 6 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "wasm-backend")] 2 | 3 | use cursive::{ 4 | Cursive, 5 | view::{Nameable, Selector}, 6 | }; 7 | use wasm_bindgen::prelude::*; 8 | use std::sync::Mutex; 9 | use crate::backend; 10 | 11 | 12 | #[wasm_bindgen] 13 | pub struct CursiveWrapper { 14 | backend: Mutex, 15 | } 16 | 17 | #[wasm_bindgen] 18 | impl CursiveWrapper { 19 | #[wasm_bindgen(js_name = "retris")] 20 | pub async fn retris() -> CursiveWrapper { 21 | let mut siv: Cursive = Cursive::new(); 22 | let tetris = crate::tetris::Tetris::new().with_name("retris"); 23 | siv.add_layer(tetris); 24 | siv.focus(&Selector::Name("retris")).unwrap(); 25 | siv.set_fps(1000); 26 | let siv: Mutex = std::sync::Mutex::new(siv); 27 | siv.lock().unwrap().run_with(|| backend::backend()).await; 28 | CursiveWrapper { backend: siv } 29 | } 30 | } -------------------------------------------------------------------------------- /src/backend.rs: -------------------------------------------------------------------------------- 1 | use cursive::backends; 2 | use cursive::backend::Backend; 3 | 4 | #[cfg(feature = "blt-backend")] 5 | pub fn backend() -> Box { 6 | backends::blt::Backend::init() 7 | } 8 | 9 | #[cfg(feature = "termion-backend")] 10 | pub fn backend() -> Box { 11 | backends::termion::Backend::init().unwrap() 12 | } 13 | 14 | #[cfg(feature = "crossterm-backend")] 15 | pub fn backend() -> Box { 16 | backends::crossterm::Backend::init().unwrap() 17 | } 18 | 19 | #[cfg(feature = "pancurses-backend")] 20 | pub fn backend() -> Box { 21 | backends::curses::pan::Backend::init().unwrap() 22 | } 23 | 24 | #[cfg(feature = "ncurses-backend")] 25 | pub fn backend() -> Box { 26 | backends::curses::n::Backend::init().unwrap() 27 | } 28 | 29 | #[cfg(feature = "wasm-backend")] 30 | pub fn backend() -> Box { 31 | backends::wasm::Backend::init().unwrap() 32 | } 33 | -------------------------------------------------------------------------------- /www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wretris", 3 | "version": "0.5.0", 4 | "description": "This is a wasm + rust implementation of tetris using the Cursive library", 5 | "main": "index.js", 6 | "bin": { 7 | "create-wasm-app": ".bin/create-wasm-app.js" 8 | }, 9 | "scripts": { 10 | "build": "webpack --config webpack.config.js", 11 | "start": "webpack-dev-server" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/genieCS/retris.git" 16 | }, 17 | "keywords": [ 18 | "webassembly", 19 | "wasm", 20 | "rust", 21 | "webpack" 22 | ], 23 | "author": "Hyejin Lee ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/users/genieCS/projects/2" 27 | }, 28 | "homepage": "https://github.com/users/genieCS/projects/2", 29 | "dependencies": { 30 | "retris": "file:../pkg" 31 | }, 32 | "devDependencies": { 33 | "hello-wasm-pack": "^0.1.0", 34 | "webpack": "^4.29.3", 35 | "webpack-cli": "^3.1.0", 36 | "webpack-dev-server": "^3.1.5", 37 | "copy-webpack-plugin": "^5.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 hyejin lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /www/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) [year] [name] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/score.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | View, 3 | Printer, 4 | }; 5 | use crate::numbers; 6 | 7 | pub struct Score { 8 | score: usize, 9 | perfect: usize, 10 | } 11 | 12 | impl Default for Score { 13 | fn default() -> Self { 14 | Self { 15 | score: 0, 16 | perfect: 40, 17 | } 18 | } 19 | } 20 | 21 | impl Score { 22 | pub fn new() -> Score{ 23 | Self::default() 24 | } 25 | 26 | pub fn add(&mut self, s: usize) { 27 | self.score += s 28 | } 29 | 30 | pub fn is_gameover(&self) -> bool { 31 | self.score >= self.perfect 32 | } 33 | 34 | pub fn renew(&mut self) { 35 | self.score = 0; 36 | } 37 | 38 | fn num2str(&self) -> String { 39 | format!("Lines: {} / {}", numbers::padding(self.score, 2), self.perfect) 40 | } 41 | } 42 | 43 | impl View for Score { 44 | fn draw(&self, printer: &Printer) { 45 | printer.print((0, 0), &self.num2str()); 46 | } 47 | 48 | fn required_size(&mut self, _constraint: cursive::Vec2) -> cursive::Vec2 { 49 | let line = self.num2str(); 50 | cursive::Vec2::new(line.len() + 3, 1) 51 | } 52 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "retris" 3 | version = "0.5.1" 4 | edition = "2021" 5 | authors = ["Hyejin Lee "] 6 | repository = "https://github.com/genieCS/retris" 7 | description = "This is a Rust implementation of tetris using the Cursive library" 8 | license = "MIT" 9 | readme = "README.md" 10 | keywords = ["tui", "cursive", "tetris", "wasm"] 11 | 12 | [lib] 13 | crate-type = ["cdylib", "rlib"] 14 | 15 | [features] 16 | default = ["wasm-backend"] 17 | crossterm-backend = ["cursive/crossterm-backend", "crossterm"] 18 | ncurses-backend = ["cursive/ncurses-backend"] 19 | pancurses-backend = ["cursive/pancurses-backend"] 20 | termion-backend = ["cursive/termion-backend"] 21 | blt-backend = ["cursive/blt-backend"] 22 | wasm-backend = ["cursive/wasm-backend", "wasm-bindgen", "wasm-bindgen-futures", "getrandom", "js-sys", "web-sys"] 23 | 24 | [dependencies] 25 | rand = "0.8.4" 26 | 27 | [dependencies.wasm-bindgen] 28 | optional = true 29 | version = "0.2.63" 30 | 31 | [dependencies.wasm-bindgen-futures] 32 | version = "0.4.18" 33 | optional = true 34 | 35 | [dependencies.getrandom] 36 | version = "0.2.11" 37 | features = ["js"] 38 | optional = true 39 | 40 | [dependencies.js-sys] 41 | version = "0.3.64" 42 | optional = true 43 | 44 | [dependencies.web-sys] 45 | version = "0.3.64" 46 | features = [ 47 | "HtmlCanvasElement", 48 | "console", 49 | ] 50 | optional = true 51 | 52 | [dependencies.cursive] 53 | git = "https://github.com/geniecs/cursive" 54 | default-features = false 55 | features = ["wasm-backend"] 56 | 57 | [dependencies.crossterm] 58 | version = "0.26.1" 59 | optional = true 60 | -------------------------------------------------------------------------------- /src/pause.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | event::{Callback, Event, EventResult}, 3 | View, Vec2, 4 | Printer, 5 | theme::{BaseColor, Color, ColorStyle}, 6 | }; 7 | use crate::tetris::Tetris; 8 | 9 | pub struct Pause {} 10 | 11 | impl Default for Pause { 12 | fn default() -> Self { 13 | Self::new() 14 | } 15 | } 16 | 17 | impl Pause { 18 | pub fn new() -> Self { 19 | Self { 20 | } 21 | } 22 | } 23 | 24 | impl View for Pause { 25 | fn draw(&self, printer: &Printer) { 26 | for y in 0..printer.size.y { 27 | for x in 0..printer.size.x { 28 | printer.with_color(ColorStyle::new(Color::Dark(BaseColor::Blue), Color::Dark(BaseColor::Blue)), |printer| { 29 | printer.print((x, y), " "); 30 | }); 31 | } 32 | } 33 | printer.with_color(ColorStyle::new(Color::Dark(BaseColor::White), Color::Dark(BaseColor::Blue)), |printer| { 34 | printer.print((10, 2), "paused, press m to resume"); 35 | }); 36 | } 37 | 38 | fn required_size(&mut self, _constraint: cursive::Vec2) -> cursive::Vec2 { 39 | Vec2::new(45, 5) 40 | } 41 | 42 | fn on_event(&mut self, event: Event) -> EventResult { 43 | if event != Event::Char('m') && event != Event::Char('M') { 44 | EventResult::Ignored 45 | } else { 46 | EventResult::Consumed(Some(Callback::from_fn(move |s| { 47 | s.pop_layer(); 48 | s.call_on_name("tetris", |t: &mut Tetris| t.on_event(Event::Char('m'))); 49 | }))) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/manual.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | event::{Event, EventResult}, 3 | Printer, View, Vec2, 4 | }; 5 | 6 | pub struct Manual { 7 | } 8 | 9 | impl Default for Manual { 10 | fn default() -> Self { 11 | Self::new() 12 | } 13 | } 14 | 15 | impl Manual { 16 | pub fn new() -> Manual { 17 | Manual {} 18 | } 19 | } 20 | 21 | impl View for Manual { 22 | fn draw(&self, printer: &Printer) { 23 | let y_padding = 6; 24 | printer.print((0, y_padding), &format!(" {:18} ", "Manual")); 25 | printer.print((0 ,y_padding + 1), &format!(" {:18} ", "↑,e: rotate clockwise")); 26 | printer.print((0 ,y_padding + 2), &format!(" {:18} ", "w: rotate counterclockwise")); 27 | printer.print((0 ,y_padding + 3), &format!(" {:18} ", "s: flip turn")); 28 | printer.print((0, y_padding + 4), &format!(" {:18} ", "↓: speed up")); 29 | printer.print((0, y_padding + 5), &format!(" {:18} ", "←: left")); 30 | printer.print((0, y_padding + 6), &format!(" {:18} ", "a: left most")); 31 | printer.print((0, y_padding + 7), &format!(" {:18} ", "→: right")); 32 | printer.print((0, y_padding + 8), &format!(" {:18} ", "d: right most")); 33 | printer.print((0, y_padding + 9), &format!(" {:18} ", "space: hard drop")); 34 | printer.print((0, y_padding + 10), &format!(" {:18} ", "m: stop and resume")); 35 | printer.print((0, y_padding + 11), &format!(" {:18} ", "n: new game")); 36 | } 37 | 38 | fn required_size(&mut self, _constraints: Vec2) -> Vec2 { 39 | Vec2::new(30, 16) 40 | } 41 | 42 | fn on_event(&mut self, _: Event) -> EventResult { 43 | EventResult::Ignored 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Retris 2 | This is a simple implementation of the classic tetris game in Rust using [Cursive](https://github.com/gyscos/cursive) library. 3 | 4 | [try game](https://geniecs.github.io) 5 | 6 | https://github.com/genieCS/retris/assets/35099832/010d9acb-c2f3-4fb2-aee5-656bc8d42225 7 | 8 | 9 | # Features 10 | This is a Tetris game implementation with additional keyboard shortcuts for easier block manipulation. In this game, you can move the blocks to the leftmost or rightmost position using the added shortcuts, and rotate them in the opposite direction and flipturn which is 180 degree rotation. This makes it easier to play the game with fewer keyboard inputs. 11 | 12 | # Installation 13 | To install and run the game, you'll need to have Rust and Cargo installed on your system. Once you have Rust and Cargo installed, you can clone the repository and run the game using the following commands: 14 | 15 | ``` 16 | git clone https://github.com/geniecs/retris.git 17 | cd retris 18 | cargo run 19 | ``` 20 | Or you can download the crate from crates.io with following command: 21 | ``` 22 | cargo install retris 23 | ``` 24 | 25 | # How to Play 26 | The goal of the game is to clear as many lines as possible by fitting the falling blocks together. Use the keyboard controls to move and rotate the blocks as they fall. The game ends when the blocks reach the top of the screen or 40 lines are cleared. 27 | 28 | # Controls 29 | * a: Move the block to the leftmost position 30 | * d: Move the block to the rightmost position 31 | * w: Rotate the block counter-clockwise 32 | * ↑ or e: Rotate the block clockwise 33 | * ↓: Speed up the block 34 | * space: Hard drop the block 35 | * m: Stop and resume the game 36 | * n: Start a new game 37 | 38 | # Acknowledgements 39 | This project was inspired by the classic Tetris game and Cursive library for Rust. 40 | 41 | # License 42 | This project is licensed under the MIT License. See the LICENSE file for details. 43 | -------------------------------------------------------------------------------- /src/queue.rs: -------------------------------------------------------------------------------- 1 | use crate::block::{ Block, Shape }; 2 | use cursive:: { 3 | Printer, 4 | View, 5 | theme::{Color, ColorStyle}, 6 | }; 7 | use std::collections::VecDeque; 8 | 9 | pub struct Queue { 10 | pub blocks: VecDeque, 11 | shapes: Vec, 12 | } 13 | 14 | impl Default for Queue { 15 | fn default() -> Self { 16 | Self::new() 17 | } 18 | } 19 | 20 | impl Queue { 21 | pub fn new() -> Self { 22 | let mut blocks = VecDeque::new(); 23 | for _ in 0..3 { 24 | blocks.push_back(Block::default()); 25 | } 26 | Self { 27 | blocks, 28 | shapes: Shape::all(), 29 | } 30 | } 31 | 32 | pub fn renew(&mut self) { 33 | *self = Self::new(); 34 | } 35 | 36 | pub fn pop_and_spawn_new_block(&mut self) -> Block { 37 | let block = self.blocks.pop_front().unwrap(); 38 | if self.shapes.is_empty() { 39 | self.shapes = Shape::all(); 40 | } 41 | self.blocks.push_back(Block::new(self.shapes.pop().unwrap())); 42 | block 43 | } 44 | 45 | fn draw_blocks(&self, printer: &Printer) { 46 | let x_padding = 10; 47 | let mut y_padding = 7; 48 | for block in &self.blocks { 49 | for vector in &block.cells() { 50 | printer.with_color(block.color(), |printer| { 51 | printer.print((2*vector.0 + x_padding , y_padding + vector.1), " "); 52 | }); 53 | } 54 | y_padding += 5; 55 | } 56 | } 57 | 58 | fn draw_container(&self, printer: &Printer) { 59 | let x_padding = 4; 60 | let y_padding = 4; 61 | let color_style = ColorStyle::new(Color::Rgb(255,255,255), Color::Rgb(0,0,0)); 62 | for j in 0..15 { 63 | printer.with_color(color_style, |printer| { 64 | printer.print((x_padding, y_padding + j), "| |"); 65 | }); 66 | } 67 | printer.with_color(color_style, |printer| { 68 | printer.print((x_padding, y_padding + 15), "|__________|"); 69 | }); 70 | } 71 | } 72 | 73 | impl View for Queue { 74 | fn draw(&self, printer: &Printer) { 75 | self.draw_container(printer); 76 | self.draw_blocks(printer); 77 | } 78 | 79 | fn required_size(&mut self, _constraint: cursive::Vec2) -> cursive::Vec2 { 80 | cursive::Vec2::new(15, 20) 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::numbers; 2 | use cursive::{ 3 | View, 4 | Printer, 5 | }; 6 | 7 | #[cfg(not(feature = "wasm-backend"))] 8 | use std::time::Instant; 9 | #[cfg(feature = "wasm-backend")] 10 | use js_sys::Date; 11 | 12 | pub struct Timer { 13 | #[cfg(not(feature = "wasm-backend"))] 14 | start: Instant, 15 | #[cfg(feature = "wasm-backend")] 16 | start: f64, 17 | is_paused: bool, 18 | #[cfg(not(feature = "wasm-backend"))] 19 | pause_start: Instant, 20 | #[cfg(feature = "wasm-backend")] 21 | pause_start: f64, 22 | 23 | } 24 | 25 | impl Default for Timer { 26 | fn default() -> Self { 27 | Self::new() 28 | } 29 | } 30 | 31 | impl Timer { 32 | pub fn new() -> Self { 33 | let now = Self::now(); 34 | Self { 35 | start: now, 36 | is_paused: false, 37 | pause_start: now, 38 | } 39 | } 40 | 41 | #[cfg(not(feature = "wasm-backend"))] 42 | fn now() -> Instant { 43 | Instant::now() 44 | } 45 | 46 | #[cfg(feature = "wasm-backend")] 47 | fn now() -> f64 { 48 | Date::now() 49 | } 50 | 51 | pub fn renew(&mut self) { 52 | let now = Self::now(); 53 | self.start = now; 54 | self.pause_start = now; 55 | self.is_paused = false; 56 | } 57 | 58 | pub fn toggle_pause(&mut self) { 59 | let now = Self::now(); 60 | if self.is_paused { 61 | self.start += now - self.pause_start; 62 | } else { 63 | self.pause_start = now; 64 | } 65 | self.is_paused = !self.is_paused; 66 | } 67 | 68 | pub fn reset(&mut self) { 69 | let now = Self::now(); 70 | self.start = now; 71 | self.pause_start = now; 72 | } 73 | 74 | pub fn time2str(&self) -> String { 75 | let (mins, secs, mills) = self.elapsed(); 76 | let mins = numbers::padding(mins as usize, 2); 77 | let secs = numbers::padding(secs as usize, 2); 78 | let mills = numbers::padding(mills as usize, 3); 79 | format!("Time {}:{}:{}", mins, secs, mills) 80 | } 81 | 82 | fn elapsed(&self) -> (u128, u128, u128) { 83 | let mills = self.elapsed_mills(); 84 | let mins = mills / 60000; 85 | let secs = (mills % 60000) / 1000; 86 | let mills = mills % 1000; 87 | (mins, secs, mills) 88 | } 89 | 90 | #[cfg(not(feature = "wasm-backend"))] 91 | fn elapsed_mills(&self) -> u128 { 92 | if self.is_paused { 93 | (self.pause_start - self.start).as_millis() 94 | } else { 95 | (Self::now() - self.pause_start).as_millis() 96 | } 97 | } 98 | 99 | #[cfg(feature = "wasm-backend")] 100 | fn elapsed_mills(&self) -> u128 { 101 | if self.is_paused { 102 | (self.pause_start - self.start) as u128 103 | } else { 104 | (Self::now() - self.pause_start) as u128 105 | } 106 | } 107 | } 108 | 109 | impl View for Timer { 110 | fn draw(&self, printer: &Printer) { 111 | printer.print((0, 0), &self.time2str()); 112 | } 113 | 114 | fn required_size(&mut self, _: cursive::Vec2) -> cursive::Vec2 { 115 | let line = self.time2str(); 116 | cursive::Vec2::new(line.len() + 3, 1) 117 | } 118 | } -------------------------------------------------------------------------------- /src/board.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | event::{Event, EventResult, Key}, 3 | View, 4 | Printer, 5 | theme::{Color, ColorStyle}, 6 | }; 7 | use crate::block::{ Block, BlockWithPos }; 8 | use crate::color_grid::ColorGrid; 9 | use crate::lrd::{ LR, LRD }; 10 | 11 | pub struct Board { 12 | colors: ColorGrid, 13 | } 14 | 15 | impl Board { 16 | pub fn new(background_color: (ColorStyle, ColorStyle), warning_color: ColorStyle, width: usize, height: usize) -> Self { 17 | Self { 18 | colors: ColorGrid::new( 19 | background_color, 20 | warning_color, 21 | width, 22 | height, 23 | ), 24 | } 25 | } 26 | 27 | pub fn renew(&mut self) { 28 | self.colors.renew(); 29 | } 30 | 31 | pub fn move_block_lrd(&self, block: &BlockWithPos, lrd: LRD) -> (Option, bool) { 32 | self.colors.move_block_lrd(block, lrd) 33 | } 34 | 35 | pub fn on_down(&mut self, is_drop: bool, is_begin: bool) -> (bool, bool) { 36 | self.colors.on_down(is_drop, is_begin) 37 | } 38 | 39 | pub fn merge_block(&mut self) -> usize { 40 | self.colors.merge_block() 41 | } 42 | 43 | pub fn insert(&mut self, block: Block) { 44 | self.colors.insert(block); 45 | } 46 | 47 | fn rotate(&mut self, hit_bottom: bool, clockwise: bool) -> bool { 48 | self.colors.rotate(hit_bottom, clockwise) 49 | } 50 | 51 | fn flip_turn(&mut self, hit_bottom: bool) -> bool { 52 | self.colors.flip_turn(hit_bottom) 53 | } 54 | 55 | fn handle_lr(&mut self, lr: LR, hit_bottom: bool, is_hard: bool) -> bool { 56 | self.colors.handle_lr(lr, hit_bottom, is_hard) 57 | } 58 | 59 | pub fn handle_event(&mut self, event: Event, hit_bottom: bool) -> bool { 60 | match event { 61 | Event::Key(Key::Left) => self.handle_lr(LR::Left, hit_bottom, false), 62 | Event::Key(Key::Right) => self.handle_lr(LR::Right, hit_bottom, false), 63 | Event::Key(Key::Up) | Event::Char('e') | Event::Char('E') => self.rotate(hit_bottom, true), 64 | Event::Char('s') | Event::Char('S') => self.flip_turn(hit_bottom), 65 | Event::Char('w') | Event::Char('W') => self.rotate(hit_bottom, false), 66 | Event::Char('a') | Event::Char('A') => self.handle_lr(LR::Left, hit_bottom, true), 67 | Event::Char('d') | Event::Char('D') => self.handle_lr(LR::Right, hit_bottom, true), 68 | _ => false, 69 | } 70 | } 71 | 72 | fn draw_background(&self, printer: &Printer) { 73 | let width = self.colors.width(); 74 | let height = self.colors.height(); 75 | for j in 0..height { 76 | for i in 0..width { 77 | printer.with_color(self.colors[self.colors.width() * j + i], |printer| { 78 | printer.print((2*i, j), " "); 79 | }); 80 | } 81 | } 82 | } 83 | 84 | fn draw_block(&self, printer: &Printer) { 85 | for cell in self.colors.block.cells() { 86 | printer.with_color(self.colors.block.color(), |printer| { 87 | printer.print((2*cell.x, cell.y), " "); 88 | }); 89 | } 90 | } 91 | 92 | fn draw_hint(&self, printer: &Printer) { 93 | #[cfg(not(feature = "wasm-backend"))] 94 | let color = ColorStyle::new(Color::Rgb(255,255,255), Color::Rgb(0,0,0)); 95 | #[cfg(feature = "wasm-backend")] 96 | let color = ColorStyle::new(Color::Rgb(255,255,255), Color::Rgb(255,255,255)); 97 | let hint = self.colors.hint(); 98 | for cell in hint.cells() { 99 | printer.with_color(color, |printer| { 100 | printer.print((2*cell.x, cell.y), "░░"); 101 | }); 102 | } 103 | } 104 | 105 | fn draw_dangerous(&self, printer: &Printer) { 106 | let width = self.colors.width(); 107 | let mut warning_stage = false; 108 | for j in 0..3 { 109 | for i in 0..width { 110 | if self.colors.is_occupied(i, j) { 111 | warning_stage = true; 112 | break; 113 | } 114 | } 115 | if warning_stage { 116 | break; 117 | } 118 | } 119 | if !warning_stage { 120 | return; 121 | } 122 | for j in 0..3 { 123 | for i in 0..width { 124 | if !self.colors.is_occupied(i, j) { 125 | printer.with_color(self.colors.warning_color, |printer| { 126 | printer.print((2*i, j), " "); 127 | }); 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | impl View for Board { 135 | fn draw(&self, printer: &Printer) { 136 | self.draw_background(printer); 137 | self.draw_dangerous(printer); 138 | self.draw_hint(printer); 139 | self.draw_block(printer) 140 | } 141 | 142 | fn required_size(&mut self, _constraint: cursive::Vec2) -> cursive::Vec2 { 143 | let width = self.colors.width(); 144 | let height = self.colors.height(); 145 | cursive::Vec2::new(2*width + 3, height + 10) 146 | } 147 | 148 | fn on_event(&mut self, event: Event) -> EventResult { 149 | self.handle_event(event, false); 150 | EventResult::Consumed(None) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/block.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | Vec2, 3 | theme::{Color, ColorStyle}, 4 | }; 5 | use rand::thread_rng; 6 | use rand::seq::SliceRandom; 7 | 8 | type Pos = (i32, i32); 9 | 10 | 11 | #[derive(Clone, Debug)] 12 | pub struct BlockWithPos { 13 | pub block: Block, 14 | pub pos: Vec2, 15 | } 16 | 17 | impl BlockWithPos { 18 | pub fn new(block: Block, pos: Vec2) -> Self { 19 | Self { 20 | block, 21 | pos, 22 | } 23 | } 24 | 25 | pub fn cells(&self) -> Vec { 26 | self.block.cells().into_iter().map(|cell| { 27 | let x = self.pos.x as i32 + cell.0; 28 | let y = self.pos.y as i32 + cell.1; 29 | Vec2::new(x as usize, y as usize) 30 | }).collect::>() 31 | } 32 | 33 | pub fn color(&self) -> ColorStyle { 34 | self.block.color() 35 | } 36 | } 37 | 38 | #[derive(Clone, Debug)] 39 | pub struct Block { 40 | shape: Shape, 41 | rotation: Rotation, 42 | } 43 | 44 | impl Default for Block { 45 | fn default() -> Self { 46 | Self { 47 | shape: Shape::random(), 48 | rotation: Rotation::R0, 49 | } 50 | } 51 | } 52 | 53 | impl Block { 54 | pub fn new(shape: Shape) -> Self { 55 | Self { 56 | shape, 57 | rotation: Rotation::R0, 58 | } 59 | } 60 | 61 | pub fn cells(&self) -> Vec { 62 | match self.rotation { 63 | Rotation::R0 => self.shape.cells(), 64 | Rotation::R90 => self.shape.cells().into_iter().map(|(x,y)| (-y,x)).collect(), 65 | Rotation::R180 => self.shape.cells().into_iter().map(|(x,y)| (-x,-y)).collect(), 66 | Rotation::R270 => self.shape.cells().into_iter().map(|(x,y)| (y,-x)).collect(), 67 | } 68 | } 69 | 70 | pub fn rotate(&self, clockwise: bool) -> Block { 71 | if clockwise { 72 | self.rotate_clockwise() 73 | } else { 74 | self.rotate_counter_clockwise() 75 | } 76 | } 77 | 78 | fn rotate_clockwise(&self) -> Block { 79 | match (&self.shape, &self.rotation) { 80 | (Shape::O, _) => self.clone(), 81 | (_,Rotation::R0) => Block { shape: self.shape.clone(), rotation: Rotation::R90 }, 82 | (_,Rotation::R90) => Block { shape: self.shape.clone(), rotation: Rotation::R180 }, 83 | (_,Rotation::R180) => Block { shape: self.shape.clone(), rotation: Rotation::R270 }, 84 | (_,Rotation::R270) => Block { shape: self.shape.clone(), rotation: Rotation::R0 }, 85 | } 86 | } 87 | 88 | fn rotate_counter_clockwise(&self) -> Block { 89 | match (&self.shape, &self.rotation) { 90 | (Shape::O, _) => self.clone(), 91 | (_,Rotation::R0) => Block { shape: self.shape.clone(), rotation: Rotation::R270 }, 92 | (_,Rotation::R90) => Block { shape: self.shape.clone(), rotation: Rotation::R0 }, 93 | (_,Rotation::R180) => Block { shape: self.shape.clone(), rotation: Rotation::R90 }, 94 | (_,Rotation::R270) => Block { shape: self.shape.clone(), rotation: Rotation::R180 }, 95 | } 96 | } 97 | 98 | pub fn flip_turn(&self) -> Block { 99 | match (&self.shape, &self.rotation) { 100 | (Shape::O, _) => self.clone(), 101 | (_,Rotation::R0) => Block { shape: self.shape.clone(), rotation: Rotation::R180 }, 102 | (_,Rotation::R90) => Block { shape: self.shape.clone(), rotation: Rotation::R270 }, 103 | (_,Rotation::R180) => Block { shape: self.shape.clone(), rotation: Rotation::R0 }, 104 | (_,Rotation::R270) => Block { shape: self.shape.clone(), rotation: Rotation::R90 }, 105 | } 106 | } 107 | 108 | #[cfg(not(feature = "wasm-backend"))] 109 | pub fn color(&self) -> ColorStyle { 110 | use cursive::theme::BaseColor; 111 | match self.shape { 112 | Shape::I => ColorStyle::new(Color::Dark(BaseColor::Blue), Color::Dark(BaseColor::Blue)), 113 | Shape::O => ColorStyle::new(Color::Dark(BaseColor::Yellow), Color::Dark(BaseColor::Yellow)), 114 | Shape::T => ColorStyle::new(Color::Dark(BaseColor::Magenta), Color::Dark(BaseColor::Magenta)), 115 | Shape::S => ColorStyle::new(Color::Dark(BaseColor::Green), Color::Dark(BaseColor::Green)), 116 | Shape::Z => ColorStyle::new(Color::Dark(BaseColor::Red), Color::Dark(BaseColor::Red)), 117 | Shape::J => ColorStyle::new(Color::Dark(BaseColor::Cyan), Color::Dark(BaseColor::Cyan)), 118 | Shape::L => ColorStyle::new(Color::Dark(BaseColor::White), Color::Dark(BaseColor::White)), 119 | } 120 | } 121 | 122 | #[cfg(feature = "wasm-backend")] 123 | pub fn color(&self) -> ColorStyle { 124 | match self.shape { 125 | Shape::I => ColorStyle::new(Color::Rgb(55, 210, 235), Color::Rgb(55, 210, 235)), 126 | Shape::O => ColorStyle::new(Color::Rgb(240, 205, 0), Color::Rgb(240, 205, 0)), 127 | Shape::T => ColorStyle::new(Color::Rgb(145, 80, 175), Color::Rgb(145, 80, 175)), 128 | Shape::S => ColorStyle::new(Color::Rgb(0, 175, 55), Color::Rgb(0, 175, 55)), 129 | Shape::Z => ColorStyle::new(Color::Rgb(220, 0, 20), Color::Rgb(220, 0, 20)), 130 | Shape::J => ColorStyle::new(Color::Rgb(0, 120, 195), Color::Rgb(0, 120, 195)), 131 | Shape::L => ColorStyle::new(Color::Rgb(220, 100, 0), Color::Rgb(220, 100, 0)), 132 | } 133 | } 134 | } 135 | 136 | // I O T S Z J L 137 | // 138 | // _ _ _ _ _ _ _ _ _ _ _ _ _ 139 | // |_|_|_|_| |_|_| _|_|_ _|_|_| |_|_|_ |_|_ _ _ _|_| 140 | // |_|_| |_|_|_| |_|_| |_|_| |_|_|_| |_|_|_| 141 | #[derive(Clone, Debug)] 142 | pub enum Shape { 143 | I, 144 | O, 145 | T, 146 | S, 147 | Z, 148 | J, 149 | L, 150 | } 151 | 152 | impl Shape { 153 | fn random() -> Self { 154 | Self::all().pop().unwrap() 155 | } 156 | 157 | pub fn all() -> Vec { 158 | let mut shapes = vec![Shape::I, Shape::O, Shape::T, Shape::S, Shape::Z, Shape::J, Shape::L]; 159 | shapes.shuffle(&mut thread_rng()); 160 | shapes 161 | } 162 | 163 | fn cells(&self) -> Vec { 164 | match self { 165 | Shape::I => vec![(0,0),(-2,0),(-1,0),(1,0)], 166 | Shape::O => vec![(0,0),(-1,-1),(0,-1),(-1,0)], 167 | Shape::T => vec![(0,0),(-1,0),(0,-1),(1,0)], 168 | Shape::S => vec![(0,0),(-1,0),(0,-1),(1,-1)], 169 | Shape::Z => vec![(0,0),(1,0),(0,-1),(-1,-1)], 170 | Shape::J => vec![(0,0),(-1,-1),(-1,0),(1,0)], 171 | Shape::L => vec![(0,0),(-1,0),(1,0),(1,-1)], 172 | } 173 | } 174 | } 175 | 176 | #[derive(Clone, Debug)] 177 | enum Rotation { 178 | R0, 179 | R90, 180 | R180, 181 | R270, 182 | } 183 | -------------------------------------------------------------------------------- /src/tetris.rs: -------------------------------------------------------------------------------- 1 | use crate::board::Board; 2 | use crate::manual::Manual; 3 | use crate::pause::Pause; 4 | use crate::score::Score; 5 | use crate::timer::Timer; 6 | use crate::queue::Queue; 7 | use cursive::{ 8 | event::{Callback, Event, EventResult, Key}, 9 | views::Dialog, 10 | Printer, Vec2, View, 11 | theme::{Color, ColorStyle}, 12 | }; 13 | 14 | #[cfg(not(feature = "wasm-backend"))] 15 | const SLOW_SPEED: usize = 30; 16 | #[cfg(feature = "wasm-backend")] 17 | const SLOW_SPEED: usize = 10; 18 | 19 | #[cfg(not(feature = "wasm-backend"))] 20 | const NORMAL_SPEED: usize = 10; 21 | #[cfg(feature = "wasm-backend")] 22 | const NORMAL_SPEED: usize = 5; 23 | 24 | const FAST_SPEED: usize = 1; 25 | pub struct Tetris { 26 | score: Score, 27 | timer: Timer, 28 | manual: Manual, 29 | board: Board, 30 | queue: Queue, 31 | score_size: Vec2, 32 | timer_size: Vec2, 33 | manual_size: Vec2, 34 | board_size: Vec2, 35 | is_paused: bool, 36 | hit_bottom: bool, 37 | frame_idx: usize, 38 | max_frame_idx: usize, 39 | gameover: bool, 40 | } 41 | 42 | impl Default for Tetris { 43 | fn default() -> Self { 44 | Self::new() 45 | } 46 | } 47 | 48 | impl Tetris { 49 | pub fn new() -> Tetris { 50 | let background_color = ( 51 | ColorStyle::new(Color::Rgb(0,0,0), Color::Rgb(0,0,0)), 52 | ColorStyle::new(Color::Rgb(0,0,0), Color::Rgb(30,30,30)), 53 | ); 54 | let warning_color = ColorStyle::new(Color::Rgb(0,0,0), Color::Rgb(200,200,0)); 55 | let mut score = Score::new(); 56 | let mut timer = Timer::new(); 57 | let mut manual = Manual::new(); 58 | let mut board = Board::new(background_color, warning_color, 10, 20); 59 | let score_size = score.required_size(Vec2::new(0,0)); 60 | let timer_size = timer.required_size(Vec2::new(0,0)); 61 | let manual_size = manual.required_size(Vec2::new(0,0)); 62 | let board_size = board.required_size(Vec2::new(0,0)); 63 | Tetris { 64 | score, 65 | timer, 66 | manual, 67 | board, 68 | queue: Queue::new(), 69 | score_size, 70 | timer_size, 71 | manual_size, 72 | board_size, 73 | is_paused: false, 74 | hit_bottom: false, 75 | frame_idx: 0, 76 | max_frame_idx: SLOW_SPEED, 77 | gameover: false, 78 | } 79 | } 80 | 81 | fn on_down(&mut self, is_drop: bool, is_begin: bool) -> EventResult { 82 | if self.is_paused { 83 | return EventResult::Consumed(None); 84 | } 85 | let (gameover, hit_bottom) = self.board.on_down(is_drop, is_begin); 86 | let gameover = gameover || self.score.is_gameover(); 87 | if gameover { 88 | self.gameover = true; 89 | self.toggle_pause(); 90 | return EventResult::Consumed(Some(Callback::from_fn(move |s| { 91 | s.add_layer(Dialog::info("Game Over!")); 92 | }))); 93 | } 94 | if hit_bottom { 95 | if is_drop { 96 | self.merge_block(); 97 | } else { 98 | self.hit_bottom = hit_bottom; 99 | self.frame_idx = 0; 100 | self.max_frame_idx = NORMAL_SPEED; 101 | } 102 | } 103 | EventResult::Consumed(None) 104 | } 105 | 106 | fn new_game(&mut self) -> EventResult { 107 | self.score.renew(); 108 | self.board.renew(); 109 | self.queue.renew(); 110 | self.timer.renew(); 111 | self.is_paused = false; 112 | self.hit_bottom = false; 113 | self.frame_idx = 0; 114 | self.max_frame_idx = SLOW_SPEED; 115 | self.gameover = false; 116 | EventResult::Consumed(None) 117 | } 118 | 119 | fn stop_and_resume(&mut self) -> EventResult { 120 | self.toggle_pause(); 121 | if self.is_paused { 122 | EventResult::Consumed(Some(Callback::from_fn(move |s| { 123 | s.add_layer(Pause::new()); 124 | }))) 125 | } else { 126 | EventResult::Consumed(None) 127 | } 128 | } 129 | 130 | fn toggle_pause(&mut self) { 131 | self.is_paused = !self.is_paused; 132 | self.timer.toggle_pause(); 133 | } 134 | 135 | fn handle_merge_and_pass(&mut self, event: Event) -> EventResult { 136 | if self.gameover && event != Event::Char('n') && event != Event::Char('N') { 137 | return EventResult::Consumed(None); 138 | } 139 | let is_begin = self.hit_bottom; 140 | if self.hit_bottom { 141 | self.merge_block(); 142 | } 143 | match event { 144 | Event::Key(Key::Down) => self.speed_up(), 145 | Event::Refresh => self.on_down(false, is_begin), 146 | Event::Char(' ') => self.on_down(true, is_begin), 147 | Event::Char('n') | Event::Char('N') => self.new_game(), 148 | Event::Char('m') | Event::Char('M') => self.stop_and_resume(), 149 | _ => EventResult::Ignored, 150 | } 151 | } 152 | 153 | fn merge_block(&mut self) { 154 | let score = self.board.merge_block(); 155 | self.score.add(score); 156 | let block = self.queue.pop_and_spawn_new_block(); 157 | self.board.insert(block); 158 | self.hit_bottom = false; 159 | self.max_frame_idx = SLOW_SPEED; 160 | self.frame_idx = 0; 161 | } 162 | 163 | fn speed_up(&mut self) -> EventResult { 164 | self.max_frame_idx = FAST_SPEED; 165 | self.frame_idx = 0; 166 | EventResult::Consumed(None) 167 | } 168 | 169 | fn pass_event_to_board(&mut self, event: Event) -> EventResult { 170 | if self.is_paused || self.gameover { 171 | return EventResult::Consumed(None) 172 | } 173 | let moved = self.board.handle_event(event, self.hit_bottom); 174 | if self.hit_bottom && moved { 175 | self.max_frame_idx = std::cmp::min(3 + self.max_frame_idx, 2 * NORMAL_SPEED); 176 | } 177 | EventResult::Consumed(None) 178 | } 179 | } 180 | 181 | impl View for Tetris { 182 | fn draw(&self, printer: &Printer) { 183 | let x_padding = 4; 184 | let y_padding = 4; 185 | let score_padding = Vec2::new(x_padding, y_padding); 186 | let timer_padding = Vec2::new(x_padding, y_padding + 1 + self.score_size.y); 187 | let manual_padding = Vec2::new(x_padding, y_padding + self.score_size.y + self.timer_size.y); 188 | let first_column_x_padding = std::cmp::max(std::cmp::max(self.manual_size.x, self.score_size.x), self.timer_size.x); 189 | let board_padding = Vec2::new(x_padding + first_column_x_padding + 2, y_padding); 190 | let queue_padding = Vec2::new(x_padding + first_column_x_padding + self.board_size.x, y_padding); 191 | 192 | let score_printer = printer.offset(score_padding); 193 | let timer_printer = printer.offset(timer_padding); 194 | let manual_printer = printer.offset(manual_padding); 195 | let board_printer = printer.offset(board_padding); 196 | let queue_printer = printer.offset(queue_padding); 197 | 198 | self.score.draw(&score_printer); 199 | self.timer.draw(&timer_printer); 200 | self.manual.draw(&manual_printer); 201 | self.board.draw(&board_printer); 202 | self.queue.draw(&queue_printer); 203 | } 204 | 205 | fn required_size(&mut self, constraints: Vec2) -> Vec2 { 206 | let score_size = self.score.required_size(constraints); 207 | let timer_size = self.timer.required_size(constraints); 208 | let manual_size = self.manual.required_size(constraints); 209 | let board_size = self.board.required_size(constraints); 210 | let queue_size = self.queue.required_size(constraints); 211 | Vec2::new(std::cmp::max(std::cmp::max(manual_size.x, score_size.x), timer_size.x) + board_size.x + queue_size.x + 10, board_size.y) 212 | } 213 | 214 | fn on_event(&mut self, event: Event) -> EventResult { 215 | if event == Event::Refresh { 216 | self.frame_idx += 1; 217 | if self.frame_idx == self.max_frame_idx { 218 | self.frame_idx = 0; 219 | } else { 220 | return EventResult::Ignored; 221 | } 222 | } 223 | 224 | match event { 225 | Event::Refresh | Event::Key(Key::Down) | Event::Char(' ') | Event::Char('n') | Event::Char('N') | Event::Char('m') | Event::Char('M') => self.handle_merge_and_pass(event), 226 | _ => self.pass_event_to_board(event), 227 | } 228 | } 229 | } -------------------------------------------------------------------------------- /src/color_grid.rs: -------------------------------------------------------------------------------- 1 | use cursive::{ 2 | theme::{ColorStyle}, 3 | Vec2, 4 | }; 5 | use std::ops::Index; 6 | use crate::block::{Block, BlockWithPos}; 7 | use crate::lrd::{ LR, LRD }; 8 | 9 | pub struct ColorGrid { 10 | pub block: BlockWithPos, 11 | colors: Vec, 12 | width: usize, 13 | height: usize, 14 | background_color: (ColorStyle, ColorStyle), 15 | pub warning_color: ColorStyle, 16 | } 17 | 18 | #[derive(Clone, Copy)] 19 | enum FlipRotate { 20 | FlipTurn, 21 | Rotate { 22 | clockwise: bool, 23 | } 24 | } 25 | 26 | impl ColorGrid { 27 | pub fn new(background_color: (ColorStyle, ColorStyle), warning_color: ColorStyle, width: usize, height: usize) -> Self { 28 | let mut colors = Vec::with_capacity(width * height); 29 | for h in 0..height { 30 | for w in 0..width { 31 | let color = if (w + h) % 2 == 0 { 32 | background_color.0 33 | } else { 34 | background_color.1 35 | }; 36 | colors.push(color); 37 | } 38 | } 39 | Self { 40 | block: Self::insert_random(width), 41 | colors, 42 | width, 43 | height, 44 | background_color, 45 | warning_color, 46 | } 47 | } 48 | 49 | pub fn move_block_lrd(&self, block: &BlockWithPos, lrd: LRD) -> (Option, bool) { 50 | let (can_move, stop) = self.can_move(block, &lrd); 51 | if !can_move { 52 | return (None, stop) 53 | } 54 | let delta = lrd.delta(); 55 | let x = block.pos.x as i32 + delta.0; 56 | let y = block.pos.y as i32 + delta.1; 57 | let bwp = BlockWithPos::new(block.block.clone(), Vec2::new(x as usize, y as usize)); 58 | (Some(bwp), stop) 59 | } 60 | 61 | fn can_move(&self, block: &BlockWithPos, lrd: &LRD) -> (bool, bool) { 62 | let delta = lrd.delta(); 63 | let mut moved = true; 64 | let mut stop = false; 65 | let board_width = self.width() as i32; 66 | let board_height = self.height() as i32; 67 | for cell in block.cells() { 68 | let next_x = cell.x as i32 + delta.0; 69 | let next_y = cell.y as i32 + delta.1; 70 | if next_x < 0 || next_x >= board_width || next_y < 0 || next_y >= board_height || self.is_occupied(next_x as usize, next_y as usize) 71 | { 72 | moved = false; 73 | stop = true; 74 | break; 75 | } 76 | if next_y + 1 == board_height || self.is_occupied(next_x as usize, next_y as usize + 1) 77 | { 78 | stop = true; 79 | } 80 | } 81 | (moved, stop) 82 | } 83 | 84 | pub fn hint(&self) -> BlockWithPos { 85 | let mut hint = self.block.clone(); 86 | let mut stopped = false; 87 | while !stopped { 88 | let (block, hit_bottom) = self.move_block_lrd(&hint, LRD::Down); 89 | stopped = hit_bottom || block.is_none(); 90 | hint = block.unwrap_or(hint); 91 | } 92 | hint 93 | } 94 | 95 | pub fn on_down(&mut self, is_drop: bool, is_begin: bool) -> (bool, bool) { // (gameover, hit_bottom) 96 | let mut stopped = false; 97 | let mut hit_bottom = is_drop; 98 | let mut current: Option; 99 | let gameover = false; 100 | while !stopped { 101 | (current, hit_bottom)= self.move_block_lrd(&self.block, LRD::Down); 102 | match current { 103 | Some(b) => self.block = b, 104 | None => return (is_begin, true), 105 | } 106 | stopped = hit_bottom || !is_drop; 107 | } 108 | (gameover, hit_bottom) 109 | } 110 | 111 | pub fn handle_lr(&mut self, lr: LR, hit_bottom: bool, is_hard: bool) -> bool { 112 | let lrd = lr.to_lrd(); 113 | let mut stopped = false; 114 | let mut moved = false; 115 | while !stopped { 116 | let (block, hit_wall) = self.move_block_lrd(&self.block, lrd); 117 | if block.is_some() { 118 | moved = true; 119 | self.block = block.unwrap(); 120 | } 121 | stopped = hit_wall || !is_hard; 122 | } 123 | if hit_bottom { 124 | self.on_down(true, false); 125 | } 126 | moved 127 | } 128 | 129 | pub fn flip_turn(&mut self, hit_bottom: bool) -> bool { 130 | self.flip_rotate(hit_bottom, FlipRotate::FlipTurn) 131 | } 132 | 133 | pub fn rotate(&mut self, hit_bottom: bool, clockwise: bool) -> bool { 134 | self.flip_rotate(hit_bottom, FlipRotate::Rotate { clockwise }) 135 | } 136 | 137 | fn flip_rotate(&mut self, hit_bottom: bool, flip_rotate: FlipRotate) -> bool { 138 | let axises: Vec = self.block.block.cells().into_iter() 139 | .map(|(x,y)| (self.block.pos.x as i32 + x, self.block.pos.y as i32 + y)) 140 | .filter(|(x,y)| 0 <= *x && *x < self.width() as i32 && 0 <= *y && *y < self.height() as i32) 141 | .map(|(x, y)| Vec2::new(x as usize, y as usize)).collect(); 142 | for axis in axises { 143 | let mut pos = axis; 144 | for _ in 0..10 { 145 | let mut possible = true; 146 | let next_block = match flip_rotate { 147 | FlipRotate::FlipTurn => self.block.block.flip_turn(), 148 | FlipRotate::Rotate { clockwise } => self.block.block.rotate(clockwise), 149 | }; 150 | 151 | for cell in next_block.cells() { 152 | let x = pos.x as i32 + cell.0; 153 | let y = pos.y as i32 + cell.1; 154 | if x < 0 { 155 | pos.x += 1; 156 | possible = false; 157 | break; 158 | } else if x >= self.width() as i32 { 159 | pos.x -= 1; 160 | possible = false; 161 | break; 162 | } else if y < 0 { 163 | pos.y += 1; 164 | possible = false; 165 | break; 166 | } else if y >= self.height() as i32 { 167 | pos.y -= 1; 168 | possible = false; 169 | break; 170 | } else if self.is_occupied(x as usize, y as usize) { 171 | possible = false; 172 | break; 173 | } 174 | } 175 | if possible { 176 | self.block = BlockWithPos::new(next_block, pos); 177 | if hit_bottom { 178 | self.on_down(true, false); 179 | } 180 | return true 181 | } 182 | } 183 | } 184 | false 185 | } 186 | 187 | pub fn width(&self) -> usize { 188 | self.width 189 | } 190 | 191 | pub fn height(&self) -> usize { 192 | self.height 193 | } 194 | 195 | fn set_background(&mut self, x: usize, y: usize) { 196 | let color = if (x + y) % 2 == 0 { 197 | self.background_color.0 198 | } else { 199 | self.background_color.1 200 | }; 201 | self.colors[self.width * y + x] = color; 202 | } 203 | 204 | pub fn renew(&mut self) { 205 | for x in 0..self.width() { 206 | for y in 0..self.height() { 207 | self.set_background(x, y); 208 | } 209 | } 210 | self.block = ColorGrid::insert_random(self.width()) 211 | } 212 | 213 | pub fn insert_random(width: usize) -> BlockWithPos { 214 | BlockWithPos::new(Block::default(), Vec2::new(width / 2, 1)) 215 | } 216 | 217 | pub fn insert(&mut self, block: Block) { 218 | self.block = BlockWithPos::new(block, Vec2::new(self.width() / 2, 1)); 219 | } 220 | 221 | pub fn is_occupied(&self, x: usize, y: usize) -> bool { 222 | self.colors[self.width * y + x] != self.background_color.0 && 223 | self.colors[self.width * y + x] != self.background_color.1 && 224 | self.colors[self.width * y + x] != self.warning_color 225 | } 226 | 227 | pub fn merge_block(&mut self) -> usize { 228 | self.fill_board_with_block(); 229 | self.remove_rows_if_possible() 230 | } 231 | 232 | fn fill_board_with_block(&mut self) { 233 | for cell in self.block.cells() { 234 | self.colors[self.width * cell.y + cell.x] = self.block.color(); 235 | } 236 | } 237 | 238 | fn remove_rows_if_possible(&mut self) -> usize { 239 | let mut rows_to_remove = Vec::new(); 240 | for _y in 0..self.height() { 241 | let y = self.height() - _y - 1; 242 | let mut remove = true; 243 | for x in 0..self.width() { 244 | if !self.is_occupied(x, y) { 245 | remove = false; 246 | break; 247 | } 248 | } 249 | if remove { 250 | rows_to_remove.push(y); 251 | } 252 | } 253 | let score = rows_to_remove.len(); 254 | self.remove_rows(rows_to_remove); 255 | score 256 | } 257 | 258 | fn remove_rows(&mut self, rows_to_remove: Vec) { 259 | if rows_to_remove.is_empty() { 260 | return; 261 | } 262 | let mut fill_y = self.height - 1; 263 | let mut check_y = self.height - 1; 264 | for row in rows_to_remove { 265 | while check_y > row { 266 | if fill_y != check_y { 267 | self.set_background_row(check_y, fill_y); 268 | } 269 | fill_y -= 1; 270 | check_y -= 1; 271 | } 272 | check_y = row - 1; 273 | } 274 | while check_y > 0 { 275 | self.set_background_row(check_y, fill_y); 276 | fill_y -= 1; 277 | check_y -= 1; 278 | } 279 | while fill_y > 0 { 280 | for x in 0..self.width { 281 | self.set_background(x, fill_y); 282 | } 283 | fill_y -= 1; 284 | } 285 | } 286 | 287 | fn set_background_row(&mut self, from: usize, to: usize) { 288 | for w in 0..self.width { 289 | if self.is_occupied(w, from) { 290 | self.colors[self.width * to + w] = self.colors[self.width * from + w]; 291 | continue 292 | } 293 | let color = if (w + to) % 2 == 0 { 294 | self.background_color.0 295 | } else { 296 | self.background_color.1 297 | }; 298 | self.colors[self.width * to + w] = color; 299 | } 300 | } 301 | } 302 | 303 | impl Index for ColorGrid { 304 | type Output = ColorStyle; 305 | 306 | fn index(&self, index: usize) -> &Self::Output { 307 | &self.colors[index] 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 10 | dependencies = [ 11 | "cfg-if", 12 | "getrandom", 13 | "once_cell", 14 | "version_check", 15 | ] 16 | 17 | [[package]] 18 | name = "autocfg" 19 | version = "1.1.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 22 | 23 | [[package]] 24 | name = "bear-lib-terminal" 25 | version = "2.0.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "315549f695a3fef8ad1e1ebad093a7c38d135fab85e10067841744901f907116" 28 | dependencies = [ 29 | "bear-lib-terminal-sys", 30 | ] 31 | 32 | [[package]] 33 | name = "bear-lib-terminal-sys" 34 | version = "1.3.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3a04c83b2c02f07128177fa98ea2d241f85d351d376dcee70694fcb6960a279f" 37 | dependencies = [ 38 | "libc", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 46 | 47 | [[package]] 48 | name = "bumpalo" 49 | version = "3.13.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 52 | 53 | [[package]] 54 | name = "cc" 55 | version = "1.0.79" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 58 | 59 | [[package]] 60 | name = "cfg-if" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 64 | 65 | [[package]] 66 | name = "crossbeam-channel" 67 | version = "0.5.8" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 70 | dependencies = [ 71 | "cfg-if", 72 | "crossbeam-utils", 73 | ] 74 | 75 | [[package]] 76 | name = "crossbeam-utils" 77 | version = "0.8.15" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" 80 | dependencies = [ 81 | "cfg-if", 82 | ] 83 | 84 | [[package]] 85 | name = "crossterm" 86 | version = "0.26.1" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" 89 | dependencies = [ 90 | "bitflags", 91 | "crossterm_winapi", 92 | "libc", 93 | "mio", 94 | "parking_lot", 95 | "signal-hook", 96 | "signal-hook-mio", 97 | "winapi", 98 | ] 99 | 100 | [[package]] 101 | name = "crossterm_winapi" 102 | version = "0.9.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 105 | dependencies = [ 106 | "winapi", 107 | ] 108 | 109 | [[package]] 110 | name = "cursive" 111 | version = "0.20.0" 112 | source = "git+https://github.com/geniecs/cursive#379227a05f242b18656481b24ed937956a3b4f29" 113 | dependencies = [ 114 | "ahash", 115 | "bear-lib-terminal", 116 | "cfg-if", 117 | "crossbeam-channel", 118 | "crossterm", 119 | "cursive_core", 120 | "lazy_static", 121 | "libc", 122 | "log", 123 | "maplit", 124 | "ncurses", 125 | "pancurses", 126 | "signal-hook", 127 | "termion", 128 | "unicode-segmentation", 129 | "unicode-width", 130 | "wasm-bindgen", 131 | "web-sys", 132 | ] 133 | 134 | [[package]] 135 | name = "cursive-macros" 136 | version = "0.1.0" 137 | source = "git+https://github.com/geniecs/cursive#379227a05f242b18656481b24ed937956a3b4f29" 138 | dependencies = [ 139 | "proc-macro2", 140 | "quote", 141 | "syn 1.0.109", 142 | ] 143 | 144 | [[package]] 145 | name = "cursive_core" 146 | version = "0.3.7" 147 | source = "git+https://github.com/geniecs/cursive#379227a05f242b18656481b24ed937956a3b4f29" 148 | dependencies = [ 149 | "ahash", 150 | "crossbeam-channel", 151 | "cursive-macros", 152 | "enum-map", 153 | "enumset", 154 | "futures", 155 | "js-sys", 156 | "lazy_static", 157 | "log", 158 | "num", 159 | "owning_ref", 160 | "serde_json", 161 | "serde_yaml", 162 | "time", 163 | "unicode-segmentation", 164 | "unicode-width", 165 | "wasm-bindgen", 166 | "wasm-bindgen-futures", 167 | "web-sys", 168 | "xi-unicode", 169 | ] 170 | 171 | [[package]] 172 | name = "darling" 173 | version = "0.14.4" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" 176 | dependencies = [ 177 | "darling_core", 178 | "darling_macro", 179 | ] 180 | 181 | [[package]] 182 | name = "darling_core" 183 | version = "0.14.4" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" 186 | dependencies = [ 187 | "fnv", 188 | "ident_case", 189 | "proc-macro2", 190 | "quote", 191 | "syn 1.0.109", 192 | ] 193 | 194 | [[package]] 195 | name = "darling_macro" 196 | version = "0.14.4" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" 199 | dependencies = [ 200 | "darling_core", 201 | "quote", 202 | "syn 1.0.109", 203 | ] 204 | 205 | [[package]] 206 | name = "enum-map" 207 | version = "2.5.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "988f0d17a0fa38291e5f41f71ea8d46a5d5497b9054d5a759fae2cbb819f2356" 210 | dependencies = [ 211 | "enum-map-derive", 212 | ] 213 | 214 | [[package]] 215 | name = "enum-map-derive" 216 | version = "0.11.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" 219 | dependencies = [ 220 | "proc-macro2", 221 | "quote", 222 | "syn 1.0.109", 223 | ] 224 | 225 | [[package]] 226 | name = "enumset" 227 | version = "1.0.12" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" 230 | dependencies = [ 231 | "enumset_derive", 232 | ] 233 | 234 | [[package]] 235 | name = "enumset_derive" 236 | version = "0.6.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" 239 | dependencies = [ 240 | "darling", 241 | "proc-macro2", 242 | "quote", 243 | "syn 1.0.109", 244 | ] 245 | 246 | [[package]] 247 | name = "equivalent" 248 | version = "1.0.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 251 | 252 | [[package]] 253 | name = "fnv" 254 | version = "1.0.7" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 257 | 258 | [[package]] 259 | name = "futures" 260 | version = "0.3.28" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" 263 | dependencies = [ 264 | "futures-channel", 265 | "futures-core", 266 | "futures-executor", 267 | "futures-io", 268 | "futures-sink", 269 | "futures-task", 270 | "futures-util", 271 | ] 272 | 273 | [[package]] 274 | name = "futures-channel" 275 | version = "0.3.28" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 278 | dependencies = [ 279 | "futures-core", 280 | "futures-sink", 281 | ] 282 | 283 | [[package]] 284 | name = "futures-core" 285 | version = "0.3.28" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 288 | 289 | [[package]] 290 | name = "futures-executor" 291 | version = "0.3.28" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" 294 | dependencies = [ 295 | "futures-core", 296 | "futures-task", 297 | "futures-util", 298 | ] 299 | 300 | [[package]] 301 | name = "futures-io" 302 | version = "0.3.28" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" 305 | 306 | [[package]] 307 | name = "futures-macro" 308 | version = "0.3.28" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" 311 | dependencies = [ 312 | "proc-macro2", 313 | "quote", 314 | "syn 2.0.16", 315 | ] 316 | 317 | [[package]] 318 | name = "futures-sink" 319 | version = "0.3.28" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 322 | 323 | [[package]] 324 | name = "futures-task" 325 | version = "0.3.28" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 328 | 329 | [[package]] 330 | name = "futures-util" 331 | version = "0.3.28" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 334 | dependencies = [ 335 | "futures-channel", 336 | "futures-core", 337 | "futures-io", 338 | "futures-macro", 339 | "futures-sink", 340 | "futures-task", 341 | "memchr", 342 | "pin-project-lite", 343 | "pin-utils", 344 | "slab", 345 | ] 346 | 347 | [[package]] 348 | name = "getrandom" 349 | version = "0.2.10" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 352 | dependencies = [ 353 | "cfg-if", 354 | "js-sys", 355 | "libc", 356 | "wasi", 357 | "wasm-bindgen", 358 | ] 359 | 360 | [[package]] 361 | name = "hashbrown" 362 | version = "0.14.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" 365 | 366 | [[package]] 367 | name = "ident_case" 368 | version = "1.0.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 371 | 372 | [[package]] 373 | name = "indexmap" 374 | version = "2.0.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" 377 | dependencies = [ 378 | "equivalent", 379 | "hashbrown", 380 | ] 381 | 382 | [[package]] 383 | name = "itoa" 384 | version = "1.0.6" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 387 | 388 | [[package]] 389 | name = "js-sys" 390 | version = "0.3.64" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 393 | dependencies = [ 394 | "wasm-bindgen", 395 | ] 396 | 397 | [[package]] 398 | name = "lazy_static" 399 | version = "1.4.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 402 | 403 | [[package]] 404 | name = "libc" 405 | version = "0.2.147" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 408 | 409 | [[package]] 410 | name = "lock_api" 411 | version = "0.4.10" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 414 | dependencies = [ 415 | "autocfg", 416 | "scopeguard", 417 | ] 418 | 419 | [[package]] 420 | name = "log" 421 | version = "0.4.17" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 424 | dependencies = [ 425 | "cfg-if", 426 | ] 427 | 428 | [[package]] 429 | name = "maplit" 430 | version = "1.0.2" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 433 | 434 | [[package]] 435 | name = "memchr" 436 | version = "2.5.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 439 | 440 | [[package]] 441 | name = "mio" 442 | version = "0.8.8" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" 445 | dependencies = [ 446 | "libc", 447 | "log", 448 | "wasi", 449 | "windows-sys", 450 | ] 451 | 452 | [[package]] 453 | name = "ncurses" 454 | version = "5.101.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032" 457 | dependencies = [ 458 | "cc", 459 | "libc", 460 | "pkg-config", 461 | ] 462 | 463 | [[package]] 464 | name = "num" 465 | version = "0.4.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" 468 | dependencies = [ 469 | "num-complex", 470 | "num-integer", 471 | "num-iter", 472 | "num-rational", 473 | "num-traits", 474 | ] 475 | 476 | [[package]] 477 | name = "num-complex" 478 | version = "0.4.3" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" 481 | dependencies = [ 482 | "num-traits", 483 | ] 484 | 485 | [[package]] 486 | name = "num-integer" 487 | version = "0.1.45" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 490 | dependencies = [ 491 | "autocfg", 492 | "num-traits", 493 | ] 494 | 495 | [[package]] 496 | name = "num-iter" 497 | version = "0.1.43" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 500 | dependencies = [ 501 | "autocfg", 502 | "num-integer", 503 | "num-traits", 504 | ] 505 | 506 | [[package]] 507 | name = "num-rational" 508 | version = "0.4.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 511 | dependencies = [ 512 | "autocfg", 513 | "num-integer", 514 | "num-traits", 515 | ] 516 | 517 | [[package]] 518 | name = "num-traits" 519 | version = "0.2.15" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 522 | dependencies = [ 523 | "autocfg", 524 | ] 525 | 526 | [[package]] 527 | name = "num_threads" 528 | version = "0.1.6" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 531 | dependencies = [ 532 | "libc", 533 | ] 534 | 535 | [[package]] 536 | name = "numtoa" 537 | version = "0.1.0" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 540 | 541 | [[package]] 542 | name = "once_cell" 543 | version = "1.17.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 546 | 547 | [[package]] 548 | name = "owning_ref" 549 | version = "0.4.1" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 552 | dependencies = [ 553 | "stable_deref_trait", 554 | ] 555 | 556 | [[package]] 557 | name = "pancurses" 558 | version = "0.17.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "0352975c36cbacb9ee99bfb709b9db818bed43af57751797f8633649759d13db" 561 | dependencies = [ 562 | "libc", 563 | "log", 564 | "ncurses", 565 | "pdcurses-sys", 566 | "winreg", 567 | ] 568 | 569 | [[package]] 570 | name = "parking_lot" 571 | version = "0.12.1" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 574 | dependencies = [ 575 | "lock_api", 576 | "parking_lot_core", 577 | ] 578 | 579 | [[package]] 580 | name = "parking_lot_core" 581 | version = "0.9.8" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 584 | dependencies = [ 585 | "cfg-if", 586 | "libc", 587 | "redox_syscall 0.3.5", 588 | "smallvec", 589 | "windows-targets", 590 | ] 591 | 592 | [[package]] 593 | name = "pdcurses-sys" 594 | version = "0.7.1" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b" 597 | dependencies = [ 598 | "cc", 599 | "libc", 600 | ] 601 | 602 | [[package]] 603 | name = "pin-project-lite" 604 | version = "0.2.10" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" 607 | 608 | [[package]] 609 | name = "pin-utils" 610 | version = "0.1.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 613 | 614 | [[package]] 615 | name = "pkg-config" 616 | version = "0.3.26" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 619 | 620 | [[package]] 621 | name = "ppv-lite86" 622 | version = "0.2.17" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 625 | 626 | [[package]] 627 | name = "proc-macro2" 628 | version = "1.0.56" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 631 | dependencies = [ 632 | "unicode-ident", 633 | ] 634 | 635 | [[package]] 636 | name = "quote" 637 | version = "1.0.26" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 640 | dependencies = [ 641 | "proc-macro2", 642 | ] 643 | 644 | [[package]] 645 | name = "rand" 646 | version = "0.8.5" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 649 | dependencies = [ 650 | "libc", 651 | "rand_chacha", 652 | "rand_core", 653 | ] 654 | 655 | [[package]] 656 | name = "rand_chacha" 657 | version = "0.3.1" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 660 | dependencies = [ 661 | "ppv-lite86", 662 | "rand_core", 663 | ] 664 | 665 | [[package]] 666 | name = "rand_core" 667 | version = "0.6.4" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 670 | dependencies = [ 671 | "getrandom", 672 | ] 673 | 674 | [[package]] 675 | name = "redox_syscall" 676 | version = "0.2.16" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 679 | dependencies = [ 680 | "bitflags", 681 | ] 682 | 683 | [[package]] 684 | name = "redox_syscall" 685 | version = "0.3.5" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 688 | dependencies = [ 689 | "bitflags", 690 | ] 691 | 692 | [[package]] 693 | name = "redox_termios" 694 | version = "0.1.2" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" 697 | dependencies = [ 698 | "redox_syscall 0.2.16", 699 | ] 700 | 701 | [[package]] 702 | name = "retris" 703 | version = "0.5.1" 704 | dependencies = [ 705 | "crossterm", 706 | "cursive", 707 | "getrandom", 708 | "js-sys", 709 | "rand", 710 | "wasm-bindgen", 711 | "wasm-bindgen-futures", 712 | "web-sys", 713 | ] 714 | 715 | [[package]] 716 | name = "ryu" 717 | version = "1.0.15" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 720 | 721 | [[package]] 722 | name = "scopeguard" 723 | version = "1.2.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 726 | 727 | [[package]] 728 | name = "serde" 729 | version = "1.0.160" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" 732 | 733 | [[package]] 734 | name = "serde_json" 735 | version = "1.0.99" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" 738 | dependencies = [ 739 | "itoa", 740 | "ryu", 741 | "serde", 742 | ] 743 | 744 | [[package]] 745 | name = "serde_yaml" 746 | version = "0.9.25" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" 749 | dependencies = [ 750 | "indexmap", 751 | "itoa", 752 | "ryu", 753 | "serde", 754 | "unsafe-libyaml", 755 | ] 756 | 757 | [[package]] 758 | name = "signal-hook" 759 | version = "0.3.15" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" 762 | dependencies = [ 763 | "libc", 764 | "signal-hook-registry", 765 | ] 766 | 767 | [[package]] 768 | name = "signal-hook-mio" 769 | version = "0.2.3" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 772 | dependencies = [ 773 | "libc", 774 | "mio", 775 | "signal-hook", 776 | ] 777 | 778 | [[package]] 779 | name = "signal-hook-registry" 780 | version = "1.4.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 783 | dependencies = [ 784 | "libc", 785 | ] 786 | 787 | [[package]] 788 | name = "slab" 789 | version = "0.4.8" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 792 | dependencies = [ 793 | "autocfg", 794 | ] 795 | 796 | [[package]] 797 | name = "smallvec" 798 | version = "1.11.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" 801 | 802 | [[package]] 803 | name = "stable_deref_trait" 804 | version = "1.2.0" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 807 | 808 | [[package]] 809 | name = "syn" 810 | version = "1.0.109" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 813 | dependencies = [ 814 | "proc-macro2", 815 | "quote", 816 | "unicode-ident", 817 | ] 818 | 819 | [[package]] 820 | name = "syn" 821 | version = "2.0.16" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" 824 | dependencies = [ 825 | "proc-macro2", 826 | "quote", 827 | "unicode-ident", 828 | ] 829 | 830 | [[package]] 831 | name = "termion" 832 | version = "2.0.1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" 835 | dependencies = [ 836 | "libc", 837 | "numtoa", 838 | "redox_syscall 0.2.16", 839 | "redox_termios", 840 | ] 841 | 842 | [[package]] 843 | name = "time" 844 | version = "0.3.20" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" 847 | dependencies = [ 848 | "itoa", 849 | "libc", 850 | "num_threads", 851 | "serde", 852 | "time-core", 853 | "time-macros", 854 | ] 855 | 856 | [[package]] 857 | name = "time-core" 858 | version = "0.1.0" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" 861 | 862 | [[package]] 863 | name = "time-macros" 864 | version = "0.2.8" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" 867 | dependencies = [ 868 | "time-core", 869 | ] 870 | 871 | [[package]] 872 | name = "unicode-ident" 873 | version = "1.0.8" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 876 | 877 | [[package]] 878 | name = "unicode-segmentation" 879 | version = "1.10.1" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 882 | 883 | [[package]] 884 | name = "unicode-width" 885 | version = "0.1.10" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 888 | 889 | [[package]] 890 | name = "unsafe-libyaml" 891 | version = "0.2.9" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" 894 | 895 | [[package]] 896 | name = "version_check" 897 | version = "0.9.4" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 900 | 901 | [[package]] 902 | name = "wasi" 903 | version = "0.11.0+wasi-snapshot-preview1" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 906 | 907 | [[package]] 908 | name = "wasm-bindgen" 909 | version = "0.2.87" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 912 | dependencies = [ 913 | "cfg-if", 914 | "wasm-bindgen-macro", 915 | ] 916 | 917 | [[package]] 918 | name = "wasm-bindgen-backend" 919 | version = "0.2.87" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 922 | dependencies = [ 923 | "bumpalo", 924 | "log", 925 | "once_cell", 926 | "proc-macro2", 927 | "quote", 928 | "syn 2.0.16", 929 | "wasm-bindgen-shared", 930 | ] 931 | 932 | [[package]] 933 | name = "wasm-bindgen-futures" 934 | version = "0.4.37" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" 937 | dependencies = [ 938 | "cfg-if", 939 | "js-sys", 940 | "wasm-bindgen", 941 | "web-sys", 942 | ] 943 | 944 | [[package]] 945 | name = "wasm-bindgen-macro" 946 | version = "0.2.87" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 949 | dependencies = [ 950 | "quote", 951 | "wasm-bindgen-macro-support", 952 | ] 953 | 954 | [[package]] 955 | name = "wasm-bindgen-macro-support" 956 | version = "0.2.87" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 959 | dependencies = [ 960 | "proc-macro2", 961 | "quote", 962 | "syn 2.0.16", 963 | "wasm-bindgen-backend", 964 | "wasm-bindgen-shared", 965 | ] 966 | 967 | [[package]] 968 | name = "wasm-bindgen-shared" 969 | version = "0.2.87" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 972 | 973 | [[package]] 974 | name = "web-sys" 975 | version = "0.3.64" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 978 | dependencies = [ 979 | "js-sys", 980 | "wasm-bindgen", 981 | ] 982 | 983 | [[package]] 984 | name = "winapi" 985 | version = "0.3.9" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 988 | dependencies = [ 989 | "winapi-i686-pc-windows-gnu", 990 | "winapi-x86_64-pc-windows-gnu", 991 | ] 992 | 993 | [[package]] 994 | name = "winapi-i686-pc-windows-gnu" 995 | version = "0.4.0" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 998 | 999 | [[package]] 1000 | name = "winapi-x86_64-pc-windows-gnu" 1001 | version = "0.4.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1004 | 1005 | [[package]] 1006 | name = "windows-sys" 1007 | version = "0.48.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1010 | dependencies = [ 1011 | "windows-targets", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "windows-targets" 1016 | version = "0.48.1" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" 1019 | dependencies = [ 1020 | "windows_aarch64_gnullvm", 1021 | "windows_aarch64_msvc", 1022 | "windows_i686_gnu", 1023 | "windows_i686_msvc", 1024 | "windows_x86_64_gnu", 1025 | "windows_x86_64_gnullvm", 1026 | "windows_x86_64_msvc", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "windows_aarch64_gnullvm" 1031 | version = "0.48.0" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 1034 | 1035 | [[package]] 1036 | name = "windows_aarch64_msvc" 1037 | version = "0.48.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 1040 | 1041 | [[package]] 1042 | name = "windows_i686_gnu" 1043 | version = "0.48.0" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 1046 | 1047 | [[package]] 1048 | name = "windows_i686_msvc" 1049 | version = "0.48.0" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 1052 | 1053 | [[package]] 1054 | name = "windows_x86_64_gnu" 1055 | version = "0.48.0" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 1058 | 1059 | [[package]] 1060 | name = "windows_x86_64_gnullvm" 1061 | version = "0.48.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 1064 | 1065 | [[package]] 1066 | name = "windows_x86_64_msvc" 1067 | version = "0.48.0" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 1070 | 1071 | [[package]] 1072 | name = "winreg" 1073 | version = "0.5.1" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" 1076 | dependencies = [ 1077 | "winapi", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "xi-unicode" 1082 | version = "0.3.0" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" 1085 | --------------------------------------------------------------------------------