Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
8
Task/Matrix-digital-rain/Rust/matrix-digital-rain-1.rust
Normal file
8
Task/Matrix-digital-rain/Rust/matrix-digital-rain-1.rust
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "matrix-digital-rain"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
termion = "1.5.6"
|
||||
rand = "0.8.5"
|
||||
229
Task/Matrix-digital-rain/Rust/matrix-digital-rain-2.rust
Normal file
229
Task/Matrix-digital-rain/Rust/matrix-digital-rain-2.rust
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
#![warn(clippy::pedantic)] // make sure that clippy is even more annoying
|
||||
|
||||
use rand::prelude::{ThreadRng, SliceRandom};
|
||||
use rand::{thread_rng, Rng};
|
||||
use termion::{color::Rgb};
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::sync::mpsc::{channel, TryRecvError};
|
||||
use std::thread;
|
||||
use std::{io::{Write, stdout, stdin}, iter::repeat, time::Duration};
|
||||
|
||||
/// Character pool to pick from
|
||||
/// If your terminal is struggling with that choice, replace them by ordinary latin characters.
|
||||
/// There's currently no nicer way to initialize constant array in Rust
|
||||
const CHARS: [char; 322] = [
|
||||
'M', 'Ї', 'Љ', 'Њ', 'Ћ', 'Ќ', 'Ѝ', 'Ў', 'Џ', 'Б', 'Г', 'Д', 'Ж', 'И', 'Й', 'Л', 'П', 'Ф', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ',
|
||||
'Ы', 'Э', 'Ю', 'Я', 'в', 'д', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'п', 'т', 'ф', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы',
|
||||
'ь', 'э', 'ю', 'я', 'ѐ', 'ё', 'ђ', 'ѓ', 'є', 'ї', 'љ', 'њ', 'ћ', 'ќ', 'ѝ', 'ў', 'џ', 'Ѣ', 'ѣ', 'ѧ', 'Ѯ', 'ѱ', 'Ѳ',
|
||||
'ѳ', 'ҋ', 'Ҍ', 'ҍ', 'Ҏ', 'ҏ', 'Ґ', 'ґ', 'Ғ', 'ғ', 'Ҕ', 'ҕ', 'Җ', 'җ', 'Ҙ', 'ҙ', 'Қ', 'қ', 'ҝ', 'ҟ', 'ҡ', 'Ң', 'ң',
|
||||
'Ҥ', 'ҥ', 'ҩ', 'Ҫ', 'ҫ', 'Ҭ', 'ҭ', 'Ұ', 'ұ', 'Ҳ', 'ҳ', 'ҵ', 'ҷ', 'ҹ', 'Һ', 'ҿ', 'Ӂ', 'ӂ', 'Ӄ', 'ӄ', 'ӆ', 'Ӈ', 'ӈ',
|
||||
'ӊ', 'Ӌ', 'ӌ', 'ӎ', 'Ӑ', 'ӑ', 'Ӓ', 'ӓ', 'Ӕ', 'ӕ', 'Ӗ', 'ӗ', 'Ә', 'ә', 'Ӛ', 'ӛ', 'Ӝ', 'ӝ', 'Ӟ', 'ӟ', 'ӡ', 'Ӣ', 'ӣ',
|
||||
'Ӥ', 'ӥ', 'Ӧ', 'ӧ', 'Ө', 'ө', 'Ӫ', 'ӫ', 'Ӭ', 'ӭ', 'Ӯ', 'ӯ', 'Ӱ', 'ӱ', 'Ӳ', 'ӳ', 'Ӵ', 'ӵ', 'Ӷ', 'ӷ', 'Ӹ', 'ӹ', 'Ӻ',
|
||||
'ӽ', 'ӿ', 'Ԁ', 'ԍ', 'ԏ', 'Ԑ', 'ԑ', 'ԓ', 'Ԛ', 'ԟ', 'Ԧ', 'ԧ', 'Ϥ', 'ϥ', 'ϫ', 'ϭ', 'ゥ', 'ェ', 'ォ', 'ャ', 'ュ', 'ョ', 'ッ',
|
||||
'ー', 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', 'サ', 'シ', 'ス', 'セ', 'ソ', 'タ', 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ',
|
||||
'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ヘ', 'ホ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ', 'ワ', 'ン',
|
||||
'ⲁ', 'Ⲃ', 'ⲃ', 'Ⲅ', 'Γ', 'Δ', 'Θ', 'Λ', 'Ξ', 'Π', 'Ѐ', 'Ё', 'Ђ', 'Ѓ', 'Є', 'ⲉ', 'Ⲋ', 'ⲋ', 'Ⲍ', 'ⲍ', 'ⲏ', 'ⲑ', 'ⲓ',
|
||||
'ⲕ', 'ⲗ', 'ⲙ', 'ⲛ', 'Ⲝ', 'ⲝ', 'ⲡ', 'ⲧ', 'ⲩ', 'ⲫ', 'ⲭ', 'ⲯ', 'ⳁ', 'Ⳉ', 'ⳉ', 'ⳋ', 'ⳤ', '⳥', '⳦', '⳨', '⳩', '∀', '∁',
|
||||
'∂', '∃', '∄', '∅', '∆', '∇', '∈', '∉', '∊', '∋', '∌', '∍', '∎', '∏', '∐', '∑', '∓', 'ℇ', 'ℏ', '℥', 'Ⅎ', 'ℷ', '⩫',
|
||||
'⨀', '⨅', '⨆', '⨉', '⨍', '⨎', '⨏', '⨐', '⨑', '⨒', '⨓', '⨔', '⨕', '⨖', '⨗', '⨘', '⨙', '⨚', '⨛', '⨜', '⨝', '⨿', '⩪',
|
||||
];
|
||||
|
||||
/// convert a brightness value to a green-ish gradient color
|
||||
fn color(brightness: u8) -> Rgb {
|
||||
let v = f32::from(brightness) / 255.0;
|
||||
let r = v.powi(7);
|
||||
let g = v.powi(1);
|
||||
let b = v.powi(4);
|
||||
// r, g, b will be in 0.0..=1.0 so there's no risk of exceeding the u8's range
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
Rgb ((r * 255.0).round() as u8, (g * 255.0).round() as u8, (b * 255.0).round() as u8)
|
||||
}
|
||||
|
||||
/// A single character on the screen with its current brightness
|
||||
#[derive(Clone, Copy)]
|
||||
struct Symbol {
|
||||
char: char,
|
||||
brightness: u8,
|
||||
}
|
||||
|
||||
/// Start with a black space by default
|
||||
impl Default for Symbol {
|
||||
fn default() -> Self {
|
||||
Self { char: ' ', brightness: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
/// output the colored symbol at the current cursor position
|
||||
fn print<W: Write>(self, out: &mut W) {
|
||||
write!(out, "{}{}", termion::color::Fg(color(self.brightness)), self.char).unwrap();
|
||||
}
|
||||
|
||||
/// reduce the brightness of the symbol by a certain amount and make sure the value doesn't underrun
|
||||
fn darken(&mut self) {
|
||||
self.brightness = self.brightness.saturating_sub(10);
|
||||
}
|
||||
|
||||
/// replace the character for this symbol and bring it to full brightness
|
||||
fn set(&mut self, char: char) {
|
||||
self.char = char;
|
||||
self.brightness = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/// a single column of symbols
|
||||
#[derive(Clone)]
|
||||
struct Column {
|
||||
symbols: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
/// create a new column with a given height
|
||||
fn new(height: usize) -> Self {
|
||||
Self {
|
||||
symbols: vec![Symbol::default(); height],
|
||||
}
|
||||
}
|
||||
|
||||
/// print out a single colored symbol of this column
|
||||
fn print_symbol<W: Write>(&self, out: &mut W, row: usize) {
|
||||
self.symbols[row].print(out);
|
||||
}
|
||||
|
||||
/// reduce the brightness of the entire column
|
||||
fn darken(&mut self) {
|
||||
self.symbols.iter_mut().for_each(Symbol::darken);
|
||||
}
|
||||
|
||||
fn set(&mut self, row: usize, char: char) {
|
||||
self.symbols[row].set(char);
|
||||
}
|
||||
}
|
||||
|
||||
/// Current position of a _falling symbol_
|
||||
struct Droplet {
|
||||
/// For the start of the animation we want to be able to place the symbol _above_ the screen,
|
||||
/// that's we need negative row values as well.
|
||||
row: isize,
|
||||
col: usize,
|
||||
}
|
||||
|
||||
impl Droplet {
|
||||
/// create a new Droplet at a random location somewhere above the actual screen
|
||||
fn new_random(rng: &mut ThreadRng, width: usize, height: usize) -> Self {
|
||||
// the height of the terminal is expected lie within a sane range of this type
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
Self {
|
||||
row: -(rng.gen_range(0..height) as isize),
|
||||
col: rng.gen_range(0..width),
|
||||
}
|
||||
}
|
||||
|
||||
/// move the droplet down by one row
|
||||
/// if it hits the bottom row, move it back up to a random column
|
||||
fn update(&mut self, width: usize, height: usize) {
|
||||
self.row += 1;
|
||||
// the height of the terminal is expected lie within a sane range of this type
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
if self.row >= height as isize {
|
||||
let mut rng = thread_rng();
|
||||
self.col = rng.gen_range(0..width);
|
||||
self.row = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The entire screen filled with colored symbols
|
||||
struct Screen {
|
||||
width: usize,
|
||||
height: usize,
|
||||
columns: Vec<Column>,
|
||||
droplets: Vec<Droplet>,
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
|
||||
/// create a new empty screen with the given dimensions
|
||||
fn new(width: usize, height: usize) -> Self {
|
||||
let mut rng = thread_rng();
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
columns: repeat(Column::new(height)).take(width).collect(),
|
||||
droplets: (0..width).map(|_| Droplet::new_random(&mut rng, width, height)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// print the entire screen to the terminal
|
||||
fn print<W: Write>(&self, out: &mut W) {
|
||||
for row in 0..self.height {
|
||||
for column in &self.columns {
|
||||
column.print_symbol(out, row);
|
||||
}
|
||||
write!(out, "\r\n").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// make all droplets fall down by one row
|
||||
fn update_droplets(&mut self) {
|
||||
let mut rng = thread_rng();
|
||||
for droplet in &mut self.droplets {
|
||||
droplet.update(self.width, self.height);
|
||||
if let Ok(row) = droplet.row.try_into() {
|
||||
let ch = CHARS.choose(&mut rng).copied().unwrap_or(' ');
|
||||
self.columns[droplet.col].set(row, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reduce the brightness of all symbols in this screen
|
||||
fn darken(&mut self) {
|
||||
self.columns.iter_mut().for_each(Column::darken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// create the screen with the terminal's dimensions (omit the last row to prevent auto-scrolling)
|
||||
let (width, height) = termion::terminal_size().unwrap();
|
||||
let mut screen = Screen::new(width as usize, height as usize - 1);
|
||||
|
||||
// create a channel which allows to send stuff between thread boundaries
|
||||
let (tx, rx) = channel();
|
||||
|
||||
// spawn a new thread which will blockingly wait for a key to be pressed
|
||||
thread::spawn(move || {
|
||||
stdin().keys().next();
|
||||
// send something down the channel to notify the main thread that a key has been pressed
|
||||
tx.send(()).expect("Could not send signal on channel.");
|
||||
});
|
||||
|
||||
// get write access to the terminal
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
// clear the screen and hide the cursor
|
||||
write!(stdout, "{}{}", termion::clear::All, termion::cursor::Hide).unwrap();
|
||||
|
||||
// continue while no key has been pressed (i.e. the notification channel is empty)
|
||||
while rx.try_recv() == Err(TryRecvError::Empty) {
|
||||
// move cursor to the top left and set background color to black
|
||||
write!(stdout, "{}{}", termion::cursor::Goto(1, 1), termion::color::Bg(termion::color::Rgb(0, 0, 0))).unwrap();
|
||||
|
||||
// screen update
|
||||
screen.print(&mut stdout);
|
||||
screen.darken();
|
||||
screen.update_droplets();
|
||||
// make sure the terminal updates _now_
|
||||
stdout.flush().unwrap();
|
||||
|
||||
// slow down animation
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
|
||||
// reset Terminal back to normal
|
||||
write!(stdout, "{}", termion::style::Reset).unwrap();
|
||||
write!(stdout, "{}", termion::clear::All).unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue