├── .gitignore ├── img └── example.png ├── src ├── main.rs ├── lib.rs ├── game.rs ├── sudoku.rs └── board.rs ├── justfile ├── Cargo.toml ├── CHANGELOG.md ├── LICENSE ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /img/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TianyiShi2001/sudoku-tui/HEAD/img/example.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Tianyi Shi 2 | // 3 | // This software is released under the MIT License. 4 | // https://opensource.org/licenses/MIT 5 | 6 | use sudoku_tui::game::run; 7 | 8 | fn main() { 9 | run(); 10 | } 11 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | build-win: 2 | cargo build --release 3 | mv ./target/release/sudoku.exe ./target/release/sudoku-x86_64-pc-windows-msvc.exe 4 | 5 | build-mac: 6 | cargo build --release 7 | mv ./target/release/sudoku ./target/release/sudoku-x86_64-apple-darwin 8 | 9 | build-linux: 10 | cargo build --release 11 | mv ./target/release/sudoku ./target/release/sudoku-x86_64-unknown-linux-gnu -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Tianyi Shi 2 | // 3 | // This software is released under the MIT License. 4 | // https://opensource.org/licenses/MIT 5 | 6 | //! # sudoku-tui 7 | //! 8 | //! Play sudoku on the command line. 9 | //! 10 | //! For for information, please go to [the GitHub repository](https://github.com/TianyiShi2001/sudoku-tui) 11 | 12 | pub mod board; 13 | pub mod game; 14 | pub mod sudoku; 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sudoku-tui" 3 | version = "0.2.0" 4 | authors = ["Tianyi "] 5 | edition = "2018" 6 | description = "Play sudoku on the command line. (TUI interface)" 7 | license = "MIT" 8 | repository = "https://github.com/TianyiShi2001/sudoku-tui" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | sudoku = "0.7" 14 | cursive = {version = "0.15", default-features = false } 15 | rand = "0.7" 16 | clock-core = "0.0" 17 | hhmmss = "0.1" 18 | 19 | [features] 20 | default = ["cursive/crossterm-backend"] 21 | 22 | [[bin]] 23 | name = "sudoku" 24 | path = "src/main.rs" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 2020-10-10 2 | 3 | ### New Features 4 | 5 | - Info panel 6 | - Timing 7 | 8 | ### Bugfix 9 | 10 | - corrected undo & redo behaviour 11 | - undo & redo should be counted as moves 12 | 13 | ## 0.1.0 2020-10-09 14 | 15 | ### New Features 16 | 17 | - use `crossterm` to provide windows support 18 | 19 | ## 0.0.4 2020-10-09 20 | 21 | ### New Features 22 | 23 | - Config view and Finish views (not quite elaborated at the moment, though) 24 | 25 | ### Bugfix 26 | 27 | - Now entering `0` into an empty cell will be ignored. 28 | 29 | ## 0.0.3 2020-10-08 30 | 31 | ### New Features 32 | 33 | - Undo/Redo 34 | 35 | ### Bugfix 36 | 37 | - Fix the issue that adding a hint may lead to invalid board 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tianyi Shi 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sudoku-tui 2 | 3 | [![crates.io](https://img.shields.io/crates/d/sudoku-tui.svg)](https://crates.io/crates/sudoku-tui) 4 | [![crates.io](https://img.shields.io/crates/v/sudoku-tui.svg)](https://crates.io/crates/sudoku-tui) 5 | [![crates.io](https://img.shields.io/crates/l/sudoku-tui.svg)](https://crates.io/crates/sudoku-tui) 6 | 7 | Play sudoku on the command line. 8 | 9 | ![example.png](https://github.com/TianyiShi2001/sudoku-tui/raw/main/img/example.png) 10 | 11 | # Installation 12 | 13 | `cargo install sudoku-tui` 14 | 15 | # Usage 16 | 17 | Run `sudoku` to start game. 18 | 19 | Use arrow keys/mouse wheel/mouse click to navigate. Enter the number 0-9 to fill in. Click `` or press `` to obtain a hint. `Ctrl/Cmd + Z` to undo (unfortunately, due to [technical limitations](https://github.com/gyscos/cursive/issues/516), `Ctrl/Cmd + Shift + Z` is not able to map to "redo", but there's a button for it). 20 | 21 | # Compatibility 22 | 23 | Works on Linux, MacOS and Windows, although you may be unable to use mouse actions on Windows. 24 | 25 | # Roadmap 26 | 27 | - [X] Basic logic 28 | - [X] Display `You win` 29 | - [X] Undo/Redo (`Ctrl + Shift + Z` binding not yet) 30 | - [ ] Limit number of steps? 31 | - [ ] Score calculation & difficulty selection 32 | - [ ] history? 33 | - [ ] time taken 34 | - [ ] Hex -------------------------------------------------------------------------------- /src/game.rs: -------------------------------------------------------------------------------- 1 | use crate::board::SudokuBoard; 2 | use cursive::{ 3 | traits::*, 4 | views::{Button, Dialog, DummyView, LinearLayout}, 5 | Cursive, 6 | }; 7 | 8 | pub fn run() { 9 | let mut siv = cursive::default(); 10 | 11 | siv.add_global_callback('r', restart); 12 | siv.add_global_callback('h', hint); 13 | siv.add_global_callback('q', Cursive::quit); 14 | 15 | siv.set_fps(2); 16 | 17 | let board = SudokuBoard::new(); 18 | 19 | let buttons1 = LinearLayout::horizontal() 20 | .child(Button::new("Restart", restart)) 21 | .child(Button::new("Hint", hint)) 22 | .child(Button::new("Undo", undo)) 23 | .child(Button::new("Redo", redo)); 24 | 25 | let buttons2 = LinearLayout::horizontal() 26 | .child(DummyView) 27 | .child(DummyView) 28 | .child(DummyView) 29 | .child(DummyView) 30 | .child(DummyView) 31 | .child(DummyView) 32 | .child(DummyView) 33 | .child(DummyView) 34 | .child(DummyView) 35 | .child(DummyView) 36 | .child(DummyView) 37 | .child(DummyView) 38 | .child(DummyView) 39 | .child(DummyView) 40 | .child(DummyView) 41 | .child(Button::new("Help", help)) 42 | .child(Button::new("Quit", Cursive::quit)); 43 | 44 | let view = Dialog::around( 45 | LinearLayout::vertical() 46 | .child(board.with_name("board")) 47 | .child(buttons1) 48 | .child(buttons2), 49 | ) 50 | .title("SUDOKU"); 51 | 52 | siv.add_layer(view); 53 | 54 | siv.run(); 55 | } 56 | 57 | fn restart(s: &mut Cursive) { 58 | s.call_on_name("board", |board: &mut SudokuBoard| { 59 | board.restart(); 60 | }); 61 | } 62 | 63 | fn hint(s: &mut Cursive) { 64 | s.call_on_name("board", |board: &mut SudokuBoard| { 65 | board.hint(); 66 | }); 67 | } 68 | 69 | fn undo(s: &mut Cursive) { 70 | s.call_on_name("board", |board: &mut SudokuBoard| { 71 | board.undo(); 72 | }); 73 | } 74 | 75 | fn redo(s: &mut Cursive) { 76 | s.call_on_name("board", |board: &mut SudokuBoard| { 77 | board.redo(); 78 | }); 79 | } 80 | 81 | fn help(s: &mut Cursive) { 82 | s.add_layer(Dialog::info("Use arrow keys/TAB/Shift+TAB/mouse wheel/mouse click to navigate.\nEnter number 0-9 to fill in.\nClick or press to obtain a hint.\nGood luck.")) 83 | } 84 | -------------------------------------------------------------------------------- /src/sudoku.rs: -------------------------------------------------------------------------------- 1 | pub type SudokuMatrix = [[u8; 9]; 9]; 2 | type Coord = [usize; 2]; 3 | 4 | #[derive(Debug)] 5 | pub struct Sudoku { 6 | matrix: SudokuMatrix, 7 | pub available: [[bool; 9]; 9], 8 | } 9 | 10 | impl Sudoku { 11 | // fn new(matrix: SudokuMatrix) -> Self { 12 | // Self { 13 | // matrix, 14 | // coords: Self::find_empty_coords(&matrix), 15 | // } 16 | // } 17 | 18 | fn find_availability(sudoku: &SudokuMatrix) -> [[bool; 9]; 9] { 19 | let mut available = [[false; 9]; 9]; 20 | for i in 0..9 { 21 | for j in 0..9 { 22 | available[i][j] = sudoku[i][j] == 0; 23 | } 24 | } 25 | available 26 | } 27 | 28 | /// Convert from a `[[u8; 9]; 9]` matrix (array-of-array) to a `Sudoku` 29 | pub fn from_matrix(matrix: SudokuMatrix) -> Self { 30 | matrix.into() 31 | } 32 | 33 | pub fn finished(&self) -> bool { 34 | for i in 0..9 { 35 | for j in 0..9 { 36 | if self[[i, j]] == 0 { 37 | return false; 38 | } 39 | } 40 | } 41 | true 42 | } 43 | 44 | // /// Check whether the sudoku is still valid 45 | pub fn conflict(&self, v: u8, coord: Coord) -> Option<[usize; 2]> { 46 | if let Some(coord) = self.conflict_row(v, coord) { 47 | Some(coord) 48 | } else if let Some(coord) = self.conflict_col(v, coord) { 49 | Some(coord) 50 | } else if let Some(coord) = self.conflict_box(v, coord) { 51 | Some(coord) 52 | } else { 53 | None 54 | } 55 | } 56 | 57 | fn conflict_row(&self, v: u8, coord: Coord) -> Option<[usize; 2]> { 58 | let [i, _] = coord; 59 | for j in 0..9 { 60 | if self[[i, j]] == v { 61 | return Some([i, j]); 62 | } 63 | } 64 | None 65 | } 66 | fn conflict_col(&self, v: u8, coord: Coord) -> Option<[usize; 2]> { 67 | let [_, j] = coord; 68 | for i in 0..9 { 69 | if self[[i, j]] == v { 70 | return Some([i, j]); 71 | } 72 | } 73 | None 74 | } 75 | fn conflict_box(&self, v: u8, coord: Coord) -> Option<[usize; 2]> { 76 | let [i_, j_] = coord; 77 | let [i_, j_] = [i_ / 3, j_ / 3]; 78 | for i in 3 * i_..3 * i_ + 3 { 79 | // "inner" i and j; indexes of individual cells 80 | for j in 3 * j_..3 * j_ + 3 { 81 | if v == self[[i, j]] { 82 | return Some([i, j]); 83 | } 84 | } 85 | } 86 | None 87 | } 88 | } 89 | 90 | impl std::convert::From for Sudoku { 91 | fn from(matrix: SudokuMatrix) -> Self { 92 | Self { 93 | matrix, 94 | available: Self::find_availability(&matrix), 95 | } 96 | } 97 | } 98 | 99 | impl std::ops::Index for Sudoku { 100 | type Output = u8; 101 | 102 | fn index(&self, coords: Coord) -> &Self::Output { 103 | &self.matrix[coords[0]][coords[1]] 104 | } 105 | } 106 | 107 | impl std::ops::IndexMut for Sudoku { 108 | fn index_mut(&mut self, coords: Coord) -> &mut u8 { 109 | &mut self.matrix[coords[0]][coords[1]] 110 | } 111 | } 112 | 113 | use std::fmt; 114 | 115 | impl fmt::Display for Sudoku { 116 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 117 | let mut s = String::with_capacity(180); 118 | for i in 0..9 { 119 | for j in 0..9 { 120 | s.push_str(&format!("{} ", self[[i, j]])) 121 | } 122 | s.push('\n') 123 | } 124 | write!(f, "{}", s) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/board.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Tianyi Shi 2 | // 3 | // This software is released under the MIT License. 4 | // https://opensource.org/licenses/MIT 5 | 6 | use crate::sudoku::Sudoku; 7 | use clock_core::stopwatch::Stopwatch; 8 | use cursive::{ 9 | event::{Event, EventResult, Key, MouseEvent}, 10 | theme::ColorStyle, 11 | view::View, 12 | Printer, Vec2, 13 | }; 14 | use hhmmss::Hhmmss; 15 | use rand::prelude::*; 16 | // type SudokuMatrix = [u8; 81]; 17 | type SudokuMatrix = [[u8; 9]; 9]; 18 | 19 | #[derive(Debug)] 20 | enum BoardState { 21 | Config, 22 | Playing, 23 | Finish, 24 | } 25 | 26 | #[derive(Debug)] 27 | pub struct SudokuBoard { 28 | ans: SudokuMatrix, 29 | sudoku: Sudoku, 30 | focus: [usize; 2], 31 | history: Vec<([usize; 2], u8)>, 32 | redo: Vec<([usize; 2], u8)>, 33 | undos: usize, 34 | moves: usize, 35 | hints: usize, 36 | conflict: Option<[usize; 2]>, 37 | state: BoardState, 38 | stopwatch: Stopwatch, 39 | } 40 | 41 | impl SudokuBoard { 42 | pub fn new() -> Self { 43 | let ans_ = sudoku::Sudoku::generate_filled().to_bytes(); 44 | let sudoku_ = 45 | sudoku::Sudoku::generate_unique_from(sudoku::Sudoku::from_bytes(ans_.clone()).unwrap()) 46 | .to_bytes(); 47 | let mut ans = [[0u8; 9]; 9]; 48 | for i in 0..9 { 49 | for j in 0..9 { 50 | ans[i][j] = ans_[i * 9 + j]; 51 | } 52 | } 53 | let mut sudoku = [[0u8; 9]; 9]; 54 | for i in 0..9 { 55 | for j in 0..9 { 56 | sudoku[i][j] = sudoku_[i * 9 + j]; 57 | } 58 | } 59 | let i = sudoku_.iter().position(|&x| x == 0).unwrap(); 60 | Self { 61 | ans, 62 | sudoku: sudoku.into(), 63 | focus: [i / 9, i % 9], 64 | moves: 0, 65 | undos: 0, 66 | hints: 0, 67 | history: Vec::new(), 68 | redo: Vec::new(), 69 | conflict: None, 70 | state: BoardState::Config, 71 | stopwatch: Stopwatch::new(), 72 | } 73 | } 74 | 75 | fn draw_config(&self, printer: &Printer) { 76 | printer.print((2, 6), "Press to Start!"); 77 | } 78 | 79 | fn draw_finish(&self, printer: &Printer) { 80 | printer.print((5, 2), "Congratulations!"); 81 | printer.print((5, 4), &format!(" Steps: {}", self.moves)); 82 | printer.print((5, 5), &format!(" Redos: {}", self.undos)); 83 | printer.print((5, 6), &format!(" Hints: {}", self.hints)); 84 | printer.print((1, 8), "Press to continue"); 85 | } 86 | 87 | fn draw_playing(&self, printer: &Printer) { 88 | printer.print((0, 0), "┏━━━┯━━━┯━━━┓"); 89 | for (i, i_) in (1..4) 90 | .into_iter() 91 | .chain((5..8).into_iter()) 92 | .chain((9..12).into_iter()) 93 | .enumerate() 94 | { 95 | printer.print((0, i_), "┃"); 96 | printer.print((12, i_), "┃"); 97 | for (j, j_) in (1..4) 98 | .into_iter() 99 | .chain((5..8).into_iter()) 100 | .chain((9..12).into_iter()) 101 | .enumerate() 102 | { 103 | let n = self.sudoku[[i, j]]; 104 | if self.sudoku.available[i][j] { 105 | if n > 0 { 106 | printer.with_style(ColorStyle::secondary(), |p| { 107 | p.print((j_, i_), &format!("{}", n)); 108 | }); 109 | } 110 | } else { 111 | // printer.with_effect(Effect::Bold, |p|{p.print((j_, i_), &format!("{}", n));}) 112 | printer.print((j_, i_), &format!("{}", n)); 113 | } 114 | } 115 | } 116 | for i in [4usize, 8, 12].iter() { 117 | printer.print((0, *i), "┠"); 118 | printer.print((12, *i), "┨"); 119 | for j in (1..4) 120 | .into_iter() 121 | .chain((5..8).into_iter()) 122 | .chain((9..12).into_iter()) 123 | { 124 | printer.print((j, *i), "─"); 125 | } 126 | } 127 | for j in [4usize, 8].iter() { 128 | for i in (1..4) 129 | .into_iter() 130 | .chain((5..8).into_iter()) 131 | .chain((9..12).into_iter()) 132 | { 133 | printer.print((*j, i), "│"); 134 | } 135 | } 136 | for i in [4usize, 8].iter() { 137 | for j in [4usize, 8].iter() { 138 | printer.print((*j, *i), "┼"); 139 | } 140 | } 141 | // for i in (1..18).step_by(2) { 142 | // printer.print((0, i), "┃"); 143 | // printer.print((18, i), "┃"); 144 | // } 145 | // printer.print((0, 0), "┏━━━━━━━━━━━━━━━━━━┓"); 146 | printer.print((0, 12), "┗━━━┷━━━┷━━━┛"); 147 | 148 | // draw selected 149 | let focus = self.sudoku[self.focus]; 150 | let txt = if focus == 0 { 151 | " ".to_owned() 152 | } else { 153 | format!("{}", focus) 154 | }; 155 | printer.with_color(ColorStyle::highlight(), |printer| { 156 | printer.print(self.focus_xy(), &txt); 157 | }); 158 | 159 | // draw conflicted 160 | if let Some(coord) = self.conflict { 161 | printer.with_color(ColorStyle::highlight_inactive(), |p| { 162 | p.print(Self::coord_to_xy(coord), &format!("{}", self.sudoku[coord])); 163 | }); 164 | } 165 | 166 | // draw info 167 | printer.print((14, 0), "Time Elapsed"); 168 | printer.print((16, 1), &self.stopwatch.read().hhmmss()); 169 | 170 | printer.print((18, 3), "Moves"); 171 | printer.print((20, 4), &format!("{}", self.moves)); 172 | 173 | printer.print((18, 6), "Hint"); 174 | printer.print((18, 7), &format!("{}/Inf", self.hints)); 175 | } 176 | 177 | fn focus_xy(&self) -> (usize, usize) { 178 | Self::coord_to_xy(self.focus) 179 | } 180 | 181 | fn coord_to_xy(coord: [usize; 2]) -> (usize, usize) { 182 | const C: [usize; 9] = [1, 2, 3, 5, 6, 7, 9, 10, 11]; 183 | (C[coord[1]], C[coord[0]]) 184 | } 185 | 186 | fn xy_to_coord(xy: (usize, usize)) -> Option<[usize; 2]> { 187 | const C: [usize; 13] = [ 188 | usize::MAX, 189 | 0, 190 | 1, 191 | 2, 192 | usize::MAX, 193 | 3, 194 | 4, 195 | 5, 196 | usize::MAX, 197 | 6, 198 | 7, 199 | 8, 200 | usize::MAX, 201 | ]; 202 | let x = C[xy.0]; 203 | let y = C[xy.1]; 204 | if x != usize::MAX && y != usize::MAX { 205 | Some([x, y]) 206 | } else { 207 | None 208 | } 209 | } 210 | 211 | fn set_sodoku_value_and_check_finish(&mut self, coord: [usize; 2], v: u8) { 212 | self.sudoku[coord] = v; 213 | if self.sudoku.finished() { 214 | self.state = BoardState::Finish; 215 | self.stopwatch.pause(); 216 | } 217 | } 218 | 219 | fn fill(&mut self, v: u8) { 220 | self.redo.clear(); 221 | self.moves += 1; 222 | match self.sudoku.conflict(v, self.focus) { 223 | None => { 224 | self.conflict = None; 225 | self.history.push((self.focus, self.sudoku[self.focus])); 226 | self.set_sodoku_value_and_check_finish(self.focus, v); 227 | } 228 | Some(coord) => { 229 | self.conflict = Some(coord); 230 | } 231 | } 232 | } 233 | 234 | pub fn hint(&mut self) { 235 | let mut avail = Vec::new(); 236 | for i in 0..9 { 237 | for j in 0..9 { 238 | if self.sudoku.available[i][j] { 239 | avail.push([i, j]); 240 | } 241 | } 242 | } 243 | 244 | if avail.len() > 0 { 245 | self.hints += 1; 246 | let coord = avail[rand::random::() % avail.len()]; 247 | let [i, j] = coord; 248 | self.set_sodoku_value_and_check_finish(coord, self.ans[i][j]); 249 | self.sudoku.available[i][j] = false; 250 | } 251 | } 252 | 253 | pub fn undo(&mut self) { 254 | self.undos += 1; 255 | self.moves += 1; 256 | if let Some((coord, v)) = self.history.pop() { 257 | self.redo.push((self.focus, self.sudoku[self.focus])); 258 | self.sudoku[coord] = v; 259 | } 260 | } 261 | 262 | pub fn redo(&mut self) { 263 | self.moves += 1; 264 | if let Some((coord, v)) = self.redo.pop() { 265 | self.history.push((coord, v)); 266 | self.sudoku[coord] = v; 267 | } 268 | } 269 | 270 | pub fn restart(&mut self) { 271 | *self = SudokuBoard::new(); 272 | } 273 | 274 | fn move_focus_right(&mut self) { 275 | let [i, j] = self.focus; 276 | for k in 1..9 { 277 | let j_ = (j + k) % 9; 278 | if self.sudoku.available[i][j_] { 279 | self.focus = [i, j_]; 280 | return; 281 | } 282 | } 283 | } 284 | fn move_focus_left(&mut self) { 285 | let [i, j] = self.focus; 286 | for k in 1..9 { 287 | let j_ = (9 + j - k) % 9; 288 | if self.sudoku.available[i][j_] { 289 | self.focus = [i, j_]; 290 | return; 291 | } 292 | } 293 | } 294 | fn move_focus_down(&mut self) { 295 | let [i, j] = self.focus; 296 | for k in 1..9 { 297 | let i_ = (i + k) % 9; 298 | if self.sudoku.available[i_][j] { 299 | self.focus = [i_, j]; 300 | return; 301 | } 302 | } 303 | } 304 | fn move_focus_up(&mut self) { 305 | let [i, j] = self.focus; 306 | for k in 1..9 { 307 | let i_ = (9 + i - k) % 9; 308 | if self.sudoku.available[i_][j] { 309 | self.focus = [i_, j]; 310 | return; 311 | } 312 | } 313 | } 314 | 315 | fn move_focus_next(&mut self) { 316 | let [mut i, mut j] = self.focus; 317 | let mut x = 9 * i + j; 318 | for _ in 1..81 { 319 | x = (x + 1) % 81; 320 | 321 | i = x / 9; 322 | j = x % 9; 323 | if self.sudoku.available[i][j] { 324 | self.focus = [i, j]; 325 | return; 326 | } 327 | } 328 | } 329 | 330 | fn move_focus_prev(&mut self) { 331 | let [mut i, mut j] = self.focus; 332 | let mut x = 9 * i + j; 333 | for _ in 1..81 { 334 | x = (81 + x - 1) % 81; 335 | 336 | i = x / 9; 337 | j = x % 9; 338 | if self.sudoku.available[i][j] { 339 | self.focus = [i, j]; 340 | return; 341 | } 342 | } 343 | } 344 | } 345 | 346 | impl View for SudokuBoard { 347 | fn draw(&self, printer: &Printer) { 348 | match self.state { 349 | BoardState::Config => self.draw_config(printer), 350 | BoardState::Playing => self.draw_playing(printer), 351 | BoardState::Finish => self.draw_finish(printer), 352 | } 353 | } 354 | fn required_size(&mut self, _: Vec2) -> Vec2 { 355 | // Vec2::new(19, 19) 356 | Vec2::new(16, 13) 357 | } 358 | 359 | fn on_event(&mut self, event: Event) -> EventResult { 360 | match self.state { 361 | BoardState::Config => { 362 | match event { 363 | Event::Key(Key::Enter) => { 364 | self.restart(); 365 | self.state = BoardState::Playing; 366 | self.stopwatch = Stopwatch::new(); 367 | self.stopwatch.resume(); 368 | } 369 | _ => return EventResult::Ignored, 370 | } 371 | EventResult::Consumed(None) 372 | } 373 | BoardState::Playing => { 374 | match event { 375 | Event::Char(c) => { 376 | if c.is_numeric() { 377 | let n = c.to_digit(10).unwrap() as u8; 378 | if n > 0 { 379 | self.fill(n); 380 | } 381 | } else { 382 | match c { 383 | 'h' => self.hint(), 384 | _ => return EventResult::Ignored, 385 | } 386 | } 387 | return EventResult::Consumed(None); 388 | } 389 | Event::Key(Key::Right) => { 390 | self.move_focus_right(); 391 | } 392 | Event::Key(Key::Left) => self.move_focus_left(), 393 | Event::Key(Key::Down) => self.move_focus_down(), 394 | Event::Key(Key::Up) => self.move_focus_up(), 395 | Event::Key(Key::Tab) => self.move_focus_next(), 396 | Event::Shift(Key::Tab) => self.move_focus_prev(), 397 | Event::Mouse { 398 | offset, 399 | position, 400 | event, 401 | } => { 402 | match event { 403 | MouseEvent::WheelDown => self.move_focus_next(), 404 | MouseEvent::WheelUp => self.move_focus_prev(), 405 | MouseEvent::Press(_) 406 | if position > offset 407 | && position - offset < cursive::XY::new(12, 12) => 408 | { 409 | if let Some(coord) = Self::xy_to_coord(( 410 | position.y - offset.y, 411 | position.x - offset.x, 412 | )) { 413 | if self.sudoku.available[coord[0]][coord[1]] { 414 | self.focus = coord; 415 | } 416 | } 417 | } 418 | _ => return EventResult::Ignored, 419 | } 420 | return EventResult::Consumed(None); 421 | } 422 | Event::CtrlChar('z') => self.undo(), 423 | // Event::CtrlChar('Z') => self.redo(), // doesn't work 424 | // Event::CtrlShift(Key::???) => self.redo(), // Key::Char? 425 | 426 | // Event::Key(Key::Enter) => { 427 | // self.start(); 428 | // } 429 | // Event::Mouse { 430 | // offset, 431 | // position, 432 | // event, 433 | // } => match event { 434 | // MouseEvent::WheelDown => self.set_selection(self.get_selection() - 1), 435 | // MouseEvent::WheelUp => self.set_selection(self.get_selection() + 1), 436 | // }, 437 | _ => return EventResult::Ignored, 438 | } 439 | EventResult::Consumed(None) 440 | } 441 | BoardState::Finish => { 442 | match event { 443 | Event::Key(Key::Enter) => { 444 | self.state = BoardState::Config; 445 | } 446 | _ => return EventResult::Ignored, 447 | } 448 | EventResult::Consumed(None) 449 | } 450 | } 451 | } 452 | 453 | fn take_focus(&mut self, _: cursive::direction::Direction) -> bool { 454 | true 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.3.8" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" 8 | dependencies = [ 9 | "const-random", 10 | ] 11 | 12 | [[package]] 13 | name = "ahash" 14 | version = "0.4.5" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "0adac150c2dd5a9c864d054e07bda5e6bc010cd10036ea5f17e82a2f5867f735" 17 | dependencies = [ 18 | "const-random", 19 | ] 20 | 21 | [[package]] 22 | name = "arc-swap" 23 | version = "0.4.7" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" 26 | 27 | [[package]] 28 | name = "array-macro" 29 | version = "1.0.5" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "06e97b4e522f9e55523001238ac59d13a8603af57f69980de5d8de4bbbe8ada6" 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "1.0.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 38 | 39 | [[package]] 40 | name = "base-x" 41 | version = "0.2.6" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1" 44 | 45 | [[package]] 46 | name = "bitflags" 47 | version = "1.2.1" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 50 | 51 | [[package]] 52 | name = "bumpalo" 53 | version = "3.4.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 56 | 57 | [[package]] 58 | name = "cfg-if" 59 | version = "0.1.10" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 62 | 63 | [[package]] 64 | name = "chrono" 65 | version = "0.4.19" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 68 | dependencies = [ 69 | "libc", 70 | "num-integer", 71 | "num-traits", 72 | "time 0.1.44", 73 | "winapi", 74 | ] 75 | 76 | [[package]] 77 | name = "clock-core" 78 | version = "0.0.7" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "5113b069881ad79bf9ff2d265aa15467feabeace9846d482d8e5ff408c456496" 81 | dependencies = [ 82 | "chrono", 83 | ] 84 | 85 | [[package]] 86 | name = "cloudabi" 87 | version = "0.0.3" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 90 | dependencies = [ 91 | "bitflags", 92 | ] 93 | 94 | [[package]] 95 | name = "const-random" 96 | version = "0.1.11" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "02dc82c12dc2ee6e1ded861cf7d582b46f66f796d1b6c93fa28b911ead95da02" 99 | dependencies = [ 100 | "const-random-macro", 101 | "proc-macro-hack", 102 | ] 103 | 104 | [[package]] 105 | name = "const-random-macro" 106 | version = "0.1.11" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "fc757bbb9544aa296c2ae00c679e81f886b37e28e59097defe0cf524306f6685" 109 | dependencies = [ 110 | "getrandom 0.2.0", 111 | "proc-macro-hack", 112 | ] 113 | 114 | [[package]] 115 | name = "const_fn" 116 | version = "0.4.2" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" 119 | 120 | [[package]] 121 | name = "crossbeam-channel" 122 | version = "0.4.4" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" 125 | dependencies = [ 126 | "crossbeam-utils", 127 | "maybe-uninit", 128 | ] 129 | 130 | [[package]] 131 | name = "crossbeam-utils" 132 | version = "0.7.2" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 135 | dependencies = [ 136 | "autocfg", 137 | "cfg-if", 138 | "lazy_static", 139 | ] 140 | 141 | [[package]] 142 | name = "crossterm" 143 | version = "0.17.7" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7" 146 | dependencies = [ 147 | "bitflags", 148 | "crossterm_winapi", 149 | "lazy_static", 150 | "libc", 151 | "mio", 152 | "parking_lot", 153 | "signal-hook", 154 | "winapi", 155 | ] 156 | 157 | [[package]] 158 | name = "crossterm_winapi" 159 | version = "0.6.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c" 162 | dependencies = [ 163 | "winapi", 164 | ] 165 | 166 | [[package]] 167 | name = "crunchy" 168 | version = "0.2.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 171 | 172 | [[package]] 173 | name = "cursive" 174 | version = "0.15.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "7a9f12332ab2bca26979ef00cfef9a1c2e287db03b787a83d892ad9961f81374" 177 | dependencies = [ 178 | "ahash 0.3.8", 179 | "cfg-if", 180 | "crossbeam-channel", 181 | "crossterm", 182 | "cursive_core", 183 | "enumset", 184 | "lazy_static", 185 | "libc", 186 | "log", 187 | "signal-hook", 188 | "unicode-segmentation", 189 | "unicode-width", 190 | ] 191 | 192 | [[package]] 193 | name = "cursive_core" 194 | version = "0.1.1" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "85fc5b6a8ba2f1bc743892068bde466438f78d6247197e2dc094bfd53fdea4b7" 197 | dependencies = [ 198 | "ahash 0.4.5", 199 | "chrono", 200 | "crossbeam-channel", 201 | "enum-map", 202 | "enumset", 203 | "lazy_static", 204 | "libc", 205 | "log", 206 | "num", 207 | "owning_ref", 208 | "signal-hook", 209 | "unicode-segmentation", 210 | "unicode-width", 211 | "xi-unicode", 212 | ] 213 | 214 | [[package]] 215 | name = "darling" 216 | version = "0.10.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 219 | dependencies = [ 220 | "darling_core", 221 | "darling_macro", 222 | ] 223 | 224 | [[package]] 225 | name = "darling_core" 226 | version = "0.10.2" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 229 | dependencies = [ 230 | "fnv", 231 | "ident_case", 232 | "proc-macro2", 233 | "quote", 234 | "strsim", 235 | "syn", 236 | ] 237 | 238 | [[package]] 239 | name = "darling_macro" 240 | version = "0.10.2" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 243 | dependencies = [ 244 | "darling_core", 245 | "quote", 246 | "syn", 247 | ] 248 | 249 | [[package]] 250 | name = "discard" 251 | version = "1.0.4" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 254 | 255 | [[package]] 256 | name = "enum-map" 257 | version = "0.6.3" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "2f81f09b9cb18af2ea1da2688a1d6b1762b4f938d7495bb034bce48d4c608043" 260 | dependencies = [ 261 | "array-macro", 262 | "enum-map-derive", 263 | ] 264 | 265 | [[package]] 266 | name = "enum-map-derive" 267 | version = "0.4.3" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "e57001dfb2532f5a103ff869656887fae9a8defa7d236f3e39d2ee86ed629ad7" 270 | dependencies = [ 271 | "proc-macro2", 272 | "quote", 273 | "syn", 274 | ] 275 | 276 | [[package]] 277 | name = "enumset" 278 | version = "1.0.1" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "959a80a2062fedd66ed41d99736212de987b3a8c83a4c2cef243968075256bd1" 281 | dependencies = [ 282 | "enumset_derive", 283 | "num-traits", 284 | ] 285 | 286 | [[package]] 287 | name = "enumset_derive" 288 | version = "0.5.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "74bef436ac71820c5cf768d7af9ba33121246b09a00e09a55d94ef8095a875ac" 291 | dependencies = [ 292 | "darling", 293 | "proc-macro2", 294 | "quote", 295 | "syn", 296 | ] 297 | 298 | [[package]] 299 | name = "fnv" 300 | version = "1.0.7" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 303 | 304 | [[package]] 305 | name = "fuchsia-cprng" 306 | version = "0.1.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 309 | 310 | [[package]] 311 | name = "getrandom" 312 | version = "0.1.15" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 315 | dependencies = [ 316 | "cfg-if", 317 | "libc", 318 | "wasi 0.9.0+wasi-snapshot-preview1", 319 | ] 320 | 321 | [[package]] 322 | name = "getrandom" 323 | version = "0.2.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" 326 | dependencies = [ 327 | "cfg-if", 328 | "libc", 329 | "wasi 0.9.0+wasi-snapshot-preview1", 330 | ] 331 | 332 | [[package]] 333 | name = "hhmmss" 334 | version = "0.1.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "11a3a7d0916cb01ef108a66108640419767991ea31d11a1c851bed37686a6062" 337 | dependencies = [ 338 | "chrono", 339 | "time 0.2.22", 340 | ] 341 | 342 | [[package]] 343 | name = "ident_case" 344 | version = "1.0.1" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 347 | 348 | [[package]] 349 | name = "itoa" 350 | version = "0.4.6" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 353 | 354 | [[package]] 355 | name = "lazy_static" 356 | version = "1.4.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 359 | 360 | [[package]] 361 | name = "libc" 362 | version = "0.2.79" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" 365 | 366 | [[package]] 367 | name = "lock_api" 368 | version = "0.3.4" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 371 | dependencies = [ 372 | "scopeguard", 373 | ] 374 | 375 | [[package]] 376 | name = "log" 377 | version = "0.4.11" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 380 | dependencies = [ 381 | "cfg-if", 382 | ] 383 | 384 | [[package]] 385 | name = "maybe-uninit" 386 | version = "2.0.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 389 | 390 | [[package]] 391 | name = "mio" 392 | version = "0.7.3" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "e53a6ea5f38c0a48ca42159868c6d8e1bd56c0451238856cc08d58563643bdc3" 395 | dependencies = [ 396 | "libc", 397 | "log", 398 | "miow", 399 | "ntapi", 400 | "winapi", 401 | ] 402 | 403 | [[package]] 404 | name = "miow" 405 | version = "0.3.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" 408 | dependencies = [ 409 | "socket2", 410 | "winapi", 411 | ] 412 | 413 | [[package]] 414 | name = "ntapi" 415 | version = "0.3.4" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" 418 | dependencies = [ 419 | "winapi", 420 | ] 421 | 422 | [[package]] 423 | name = "num" 424 | version = "0.3.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "ab3e176191bc4faad357e3122c4747aa098ac880e88b168f106386128736cf4a" 427 | dependencies = [ 428 | "num-complex", 429 | "num-integer", 430 | "num-iter", 431 | "num-rational", 432 | "num-traits", 433 | ] 434 | 435 | [[package]] 436 | name = "num-complex" 437 | version = "0.3.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "b05ad05bd8977050b171b3f6b48175fea6e0565b7981059b486075e1026a9fb5" 440 | dependencies = [ 441 | "num-traits", 442 | ] 443 | 444 | [[package]] 445 | name = "num-integer" 446 | version = "0.1.43" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 449 | dependencies = [ 450 | "autocfg", 451 | "num-traits", 452 | ] 453 | 454 | [[package]] 455 | name = "num-iter" 456 | version = "0.1.41" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" 459 | dependencies = [ 460 | "autocfg", 461 | "num-integer", 462 | "num-traits", 463 | ] 464 | 465 | [[package]] 466 | name = "num-rational" 467 | version = "0.3.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" 470 | dependencies = [ 471 | "autocfg", 472 | "num-integer", 473 | "num-traits", 474 | ] 475 | 476 | [[package]] 477 | name = "num-traits" 478 | version = "0.2.12" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 481 | dependencies = [ 482 | "autocfg", 483 | ] 484 | 485 | [[package]] 486 | name = "owning_ref" 487 | version = "0.4.1" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 490 | dependencies = [ 491 | "stable_deref_trait", 492 | ] 493 | 494 | [[package]] 495 | name = "parking_lot" 496 | version = "0.10.2" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" 499 | dependencies = [ 500 | "lock_api", 501 | "parking_lot_core", 502 | ] 503 | 504 | [[package]] 505 | name = "parking_lot_core" 506 | version = "0.7.2" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" 509 | dependencies = [ 510 | "cfg-if", 511 | "cloudabi", 512 | "libc", 513 | "redox_syscall", 514 | "smallvec", 515 | "winapi", 516 | ] 517 | 518 | [[package]] 519 | name = "ppv-lite86" 520 | version = "0.2.9" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 523 | 524 | [[package]] 525 | name = "proc-macro-hack" 526 | version = "0.5.18" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" 529 | 530 | [[package]] 531 | name = "proc-macro2" 532 | version = "1.0.24" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 535 | dependencies = [ 536 | "unicode-xid", 537 | ] 538 | 539 | [[package]] 540 | name = "quote" 541 | version = "1.0.7" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 544 | dependencies = [ 545 | "proc-macro2", 546 | ] 547 | 548 | [[package]] 549 | name = "rand" 550 | version = "0.5.6" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" 553 | dependencies = [ 554 | "cloudabi", 555 | "fuchsia-cprng", 556 | "libc", 557 | "rand_core 0.3.1", 558 | "winapi", 559 | ] 560 | 561 | [[package]] 562 | name = "rand" 563 | version = "0.7.3" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 566 | dependencies = [ 567 | "getrandom 0.1.15", 568 | "libc", 569 | "rand_chacha", 570 | "rand_core 0.5.1", 571 | "rand_hc", 572 | ] 573 | 574 | [[package]] 575 | name = "rand_chacha" 576 | version = "0.2.2" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 579 | dependencies = [ 580 | "ppv-lite86", 581 | "rand_core 0.5.1", 582 | ] 583 | 584 | [[package]] 585 | name = "rand_core" 586 | version = "0.3.1" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 589 | dependencies = [ 590 | "rand_core 0.4.2", 591 | ] 592 | 593 | [[package]] 594 | name = "rand_core" 595 | version = "0.4.2" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 598 | 599 | [[package]] 600 | name = "rand_core" 601 | version = "0.5.1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 604 | dependencies = [ 605 | "getrandom 0.1.15", 606 | ] 607 | 608 | [[package]] 609 | name = "rand_hc" 610 | version = "0.2.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 613 | dependencies = [ 614 | "rand_core 0.5.1", 615 | ] 616 | 617 | [[package]] 618 | name = "redox_syscall" 619 | version = "0.1.57" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 622 | 623 | [[package]] 624 | name = "rustc_version" 625 | version = "0.2.3" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 628 | dependencies = [ 629 | "semver", 630 | ] 631 | 632 | [[package]] 633 | name = "ryu" 634 | version = "1.0.5" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 637 | 638 | [[package]] 639 | name = "scopeguard" 640 | version = "1.1.0" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 643 | 644 | [[package]] 645 | name = "semver" 646 | version = "0.9.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 649 | dependencies = [ 650 | "semver-parser", 651 | ] 652 | 653 | [[package]] 654 | name = "semver-parser" 655 | version = "0.7.0" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 658 | 659 | [[package]] 660 | name = "serde" 661 | version = "1.0.116" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" 664 | 665 | [[package]] 666 | name = "serde_derive" 667 | version = "1.0.116" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" 670 | dependencies = [ 671 | "proc-macro2", 672 | "quote", 673 | "syn", 674 | ] 675 | 676 | [[package]] 677 | name = "serde_json" 678 | version = "1.0.58" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" 681 | dependencies = [ 682 | "itoa", 683 | "ryu", 684 | "serde", 685 | ] 686 | 687 | [[package]] 688 | name = "sha1" 689 | version = "0.6.0" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 692 | 693 | [[package]] 694 | name = "signal-hook" 695 | version = "0.1.16" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" 698 | dependencies = [ 699 | "libc", 700 | "mio", 701 | "signal-hook-registry", 702 | ] 703 | 704 | [[package]] 705 | name = "signal-hook-registry" 706 | version = "1.2.1" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" 709 | dependencies = [ 710 | "arc-swap", 711 | "libc", 712 | ] 713 | 714 | [[package]] 715 | name = "smallvec" 716 | version = "1.4.2" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 719 | 720 | [[package]] 721 | name = "socket2" 722 | version = "0.3.15" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" 725 | dependencies = [ 726 | "cfg-if", 727 | "libc", 728 | "redox_syscall", 729 | "winapi", 730 | ] 731 | 732 | [[package]] 733 | name = "stable_deref_trait" 734 | version = "1.2.0" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 737 | 738 | [[package]] 739 | name = "standback" 740 | version = "0.2.11" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "f4e0831040d2cf2bdfd51b844be71885783d489898a192f254ae25d57cce725c" 743 | dependencies = [ 744 | "version_check", 745 | ] 746 | 747 | [[package]] 748 | name = "stdweb" 749 | version = "0.4.20" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 752 | dependencies = [ 753 | "discard", 754 | "rustc_version", 755 | "stdweb-derive", 756 | "stdweb-internal-macros", 757 | "stdweb-internal-runtime", 758 | "wasm-bindgen", 759 | ] 760 | 761 | [[package]] 762 | name = "stdweb-derive" 763 | version = "0.5.3" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 766 | dependencies = [ 767 | "proc-macro2", 768 | "quote", 769 | "serde", 770 | "serde_derive", 771 | "syn", 772 | ] 773 | 774 | [[package]] 775 | name = "stdweb-internal-macros" 776 | version = "0.2.9" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 779 | dependencies = [ 780 | "base-x", 781 | "proc-macro2", 782 | "quote", 783 | "serde", 784 | "serde_derive", 785 | "serde_json", 786 | "sha1", 787 | "syn", 788 | ] 789 | 790 | [[package]] 791 | name = "stdweb-internal-runtime" 792 | version = "0.1.5" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 795 | 796 | [[package]] 797 | name = "strsim" 798 | version = "0.9.3" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 801 | 802 | [[package]] 803 | name = "sudoku" 804 | version = "0.7.0" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "1e65a7b35b086b2cd433f08c5f970a1a4950f0ab99ff751ea0d6ea060c53c0e4" 807 | dependencies = [ 808 | "crunchy", 809 | "rand 0.5.6", 810 | ] 811 | 812 | [[package]] 813 | name = "sudoku-tui" 814 | version = "0.2.0" 815 | dependencies = [ 816 | "clock-core", 817 | "cursive", 818 | "hhmmss", 819 | "rand 0.7.3", 820 | "sudoku", 821 | ] 822 | 823 | [[package]] 824 | name = "syn" 825 | version = "1.0.42" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" 828 | dependencies = [ 829 | "proc-macro2", 830 | "quote", 831 | "unicode-xid", 832 | ] 833 | 834 | [[package]] 835 | name = "time" 836 | version = "0.1.44" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 839 | dependencies = [ 840 | "libc", 841 | "wasi 0.10.0+wasi-snapshot-preview1", 842 | "winapi", 843 | ] 844 | 845 | [[package]] 846 | name = "time" 847 | version = "0.2.22" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "55b7151c9065e80917fbf285d9a5d1432f60db41d170ccafc749a136b41a93af" 850 | dependencies = [ 851 | "const_fn", 852 | "libc", 853 | "standback", 854 | "stdweb", 855 | "time-macros", 856 | "version_check", 857 | "winapi", 858 | ] 859 | 860 | [[package]] 861 | name = "time-macros" 862 | version = "0.1.1" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 865 | dependencies = [ 866 | "proc-macro-hack", 867 | "time-macros-impl", 868 | ] 869 | 870 | [[package]] 871 | name = "time-macros-impl" 872 | version = "0.1.1" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" 875 | dependencies = [ 876 | "proc-macro-hack", 877 | "proc-macro2", 878 | "quote", 879 | "standback", 880 | "syn", 881 | ] 882 | 883 | [[package]] 884 | name = "unicode-segmentation" 885 | version = "1.6.0" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 888 | 889 | [[package]] 890 | name = "unicode-width" 891 | version = "0.1.8" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 894 | 895 | [[package]] 896 | name = "unicode-xid" 897 | version = "0.2.1" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 900 | 901 | [[package]] 902 | name = "version_check" 903 | version = "0.9.2" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 906 | 907 | [[package]] 908 | name = "wasi" 909 | version = "0.9.0+wasi-snapshot-preview1" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 912 | 913 | [[package]] 914 | name = "wasi" 915 | version = "0.10.0+wasi-snapshot-preview1" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 918 | 919 | [[package]] 920 | name = "wasm-bindgen" 921 | version = "0.2.68" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" 924 | dependencies = [ 925 | "cfg-if", 926 | "wasm-bindgen-macro", 927 | ] 928 | 929 | [[package]] 930 | name = "wasm-bindgen-backend" 931 | version = "0.2.68" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" 934 | dependencies = [ 935 | "bumpalo", 936 | "lazy_static", 937 | "log", 938 | "proc-macro2", 939 | "quote", 940 | "syn", 941 | "wasm-bindgen-shared", 942 | ] 943 | 944 | [[package]] 945 | name = "wasm-bindgen-macro" 946 | version = "0.2.68" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" 949 | dependencies = [ 950 | "quote", 951 | "wasm-bindgen-macro-support", 952 | ] 953 | 954 | [[package]] 955 | name = "wasm-bindgen-macro-support" 956 | version = "0.2.68" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" 959 | dependencies = [ 960 | "proc-macro2", 961 | "quote", 962 | "syn", 963 | "wasm-bindgen-backend", 964 | "wasm-bindgen-shared", 965 | ] 966 | 967 | [[package]] 968 | name = "wasm-bindgen-shared" 969 | version = "0.2.68" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" 972 | 973 | [[package]] 974 | name = "winapi" 975 | version = "0.3.9" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 978 | dependencies = [ 979 | "winapi-i686-pc-windows-gnu", 980 | "winapi-x86_64-pc-windows-gnu", 981 | ] 982 | 983 | [[package]] 984 | name = "winapi-i686-pc-windows-gnu" 985 | version = "0.4.0" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 988 | 989 | [[package]] 990 | name = "winapi-x86_64-pc-windows-gnu" 991 | version = "0.4.0" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 994 | 995 | [[package]] 996 | name = "xi-unicode" 997 | version = "0.2.1" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "e71b85d8b1b8bfaf4b5c834187554d201a8cd621c2bbfa33efd41a3ecabd48b2" 1000 | --------------------------------------------------------------------------------