commit 22a9742102a7f9b8612cdf0919ee377c9b08fe09 Author: markichnich Date: Mon Aug 14 23:08:37 2023 +0200 get basic tui set up diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2642848 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,261 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.0", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "shdoku" +version = "0.1.0" +dependencies = [ + "crossterm", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..089a252 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "shdoku" +description = "a basic tui-sudoku game" +version = "0.1.0" +edition = "2021" +authors = ["markichnich"] +license = "GPL-3.0-or-later" +keywords = ["sudoku", "tui", "game", "crossterm"] +categories = ["games", "mathematics", "science", "algorithms"] + +[dependencies] +crossterm = "0.27.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d1fdfc1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,170 @@ +extern crate crossterm; +use crossterm::{ + cursor::{MoveTo, SetCursorStyle}, + event::{poll, read, Event::*, KeyCode::*}, + execute, queue, + terminal::{disable_raw_mode, enable_raw_mode, size, Clear, ClearType::All}, +}; + +use std::{ + io::{self, stdout, Stdout, Write}, + time::Duration, +}; + +fn main() -> io::Result<()> { + let (width, height) = size()?; + let (mut width, mut height) = (width as usize, height as usize); + + let mut screen = vec![String::with_capacity(width); height]; + + let modifiable = [ + [false, false, true, true, false, true, true, true, true], + [false, true, true, false, false, false, true, true, true], + [true, false, false, true, true, true, true, false, true], + [false, true, true, true, false, true, true, true, false], + [false, true, true, false, true, false, true, true, false], + [false, true, true, true, false, true, true, true, false], + [true, false, true, true, true, true, false, false, true], + [true, true, true, false, false, false, true, true, false], + [true, true, true, true, false, true, true, false, false], + ]; + + let mut board = [ + [5, 3, 0, 0, 7, 0, 0, 0, 0], + [6, 0, 0, 1, 9, 5, 0, 0, 0], + [0, 9, 8, 0, 0, 0, 0, 6, 0], + [8, 0, 0, 0, 6, 0, 0, 0, 3], + [4, 0, 0, 8, 0, 3, 0, 0, 1], + [7, 0, 0, 0, 2, 0, 0, 0, 6], + [0, 6, 0, 0, 0, 0, 2, 8, 0], + [0, 0, 0, 4, 1, 9, 0, 0, 5], + [0, 0, 0, 0, 8, 0, 0, 7, 9], + ]; + + let mut cur_row = 0; + let mut cur_col = 0; + + let mut stdout = stdout(); + enable_raw_mode()?; + + loop { + let (w, h) = size()?; + (width, height) = (w as usize, h as usize); + + if poll(Duration::from_millis(250))? { + if let Ok(Key(k)) = read() { + match k.code { + Char('q') => break, + Char('h') => cur_col = (cur_col + 8) % 9, + Char('j') => cur_row = (cur_row + 1) % 9, + Char('k') => cur_row = (cur_row + 8) % 9, + Char('l') => cur_col = (cur_col + 1) % 9, + Char('H') => cur_col = (cur_col + 6) % 9, + Char('J') => cur_row = (cur_row + 3) % 9, + Char('K') => cur_row = (cur_row + 6) % 9, + Char('L') => cur_col = (cur_col + 3) % 9, + Char('x') => board[cur_row][cur_col] = 0, + Char(num) if ('1'..='9').contains(&num) => { + if modifiable[cur_row][cur_col] { + board[cur_row][cur_col] = num as u8 - b'0' + } + } + _ => {} + } + } + } + + render_to_screen(&mut screen, width, height, board); + draw_screen(&mut stdout, &screen)?; + place_cursor(&mut stdout, cur_row, cur_col, width, height)?; + } + + disable_raw_mode()?; + + Ok(()) +} + +fn place_cursor( + stdout: &mut Stdout, + cur_row: usize, + cur_col: usize, + width: usize, + height: usize, +) -> io::Result<()> { + let (x, y) = ((width / 2 - 14) as u16, (height / 2 - 7) as u16); + let (x, y) = (x + 2, y + 1); + let (x, y) = (x + 2 * cur_col as u16, y + cur_row as u16); + let x = match cur_col { + 0..=2 => x, + 3..=5 => x + 3, + _ => x + 6, + }; + let y = match cur_row { + 0..=2 => y, + 3..=5 => y + 1, + _ => y + 2, + }; + execute!(stdout, MoveTo(x, y), SetCursorStyle::SteadyBlock) +} + +fn draw_screen(stdout: &mut Stdout, screen: &[String]) -> io::Result<()> { + queue!(stdout, Clear(All))?; + let s: String = screen.join("\r\n"); + write!(stdout, "{}", s)?; + stdout.flush()?; + Ok(()) +} + +fn render_to_screen(screen: &mut Vec, cols: usize, rows: usize, board: [[u8; 9]; 9]) { + let mut lines = Vec::new(); + lines.push("┌────────┬────────┬────────┐".to_string().chars().collect()); + + for (row, row_slice) in board.iter().enumerate() { + match row { + 3 | 6 => { + lines.push("├────────┼────────┼────────┤".to_string().chars().collect()); + } + _ => {} + } + let mut line = String::new(); + for (col, cur_field) in row_slice.iter().enumerate() { + match col { + 0 => { + line.push_str("│ "); + } + 3 | 6 => { + line.push_str(" │ "); + } + _ => {} + } + line.push(match cur_field { + 0 => ' ', + n => (b'0' + n) as char, + }); + line.push(' '); + } + line.push_str(" │"); + lines.push(line); + } + lines.push("└────────┴────────┴────────┘".to_string().chars().collect()); + + let pad_hori = cols / 2 + lines[0].chars().count() / 2; + let pad_vert = rows - lines.len(); + let pad_top = pad_vert / 2; + let pad_bot = pad_vert - pad_top; + + let mut new_screen = Vec::new(); + + for _ in 0..pad_top { + new_screen.push(String::new()); + } + for line in lines { + let padded = format!("{: >width$}", line, width = pad_hori); + new_screen.push(padded); + } + for _ in 0..pad_bot { + new_screen.push(String::new()); + } + + *screen = new_screen; +}