This commit is contained in:
2026-05-29 21:28:16 +02:00
commit 654d0ed46a
4 changed files with 734 additions and 0 deletions

152
src/main.rs Normal file
View File

@@ -0,0 +1,152 @@
use std::io::{self, Write};
use std::time::Duration;
use rand::prelude::*;
use tokio::time::sleep;
static CHARSET: [&str; 221] = [
"!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3",
"4", "5", "6", "7", "8", "9", ",", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
"Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
"m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "-", "}", "~", "Ç",
"ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å", "É", "æ", "Æ", "ô",
"ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£", "¥", "", "ƒ", "á", "í", "ó", "ú", "ñ", "Ñ", "ª",
"º", "¿", "", "¬", "½", "¼", "¡", "«", "»", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"ʱ", "ß", "γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "", "φ", "ε", "", "", "±", "",
"", "", "", "÷", "", "°", "", "·", "", "", "²", "",
];
struct Cipher {
text: String,
line_count: usize,
mask: Vec<bool>,
}
impl Cipher {
async fn load(delay: u64) -> Self {
let mut text = String::new();
let stdin = io::stdin();
let mut line_count = 0;
for line in stdin.lines() {
if let Ok(line) = line {
text += &line;
text += "\n";
Self::print_line(line.as_str(), delay).await;
line_count += 1;
}
}
let mask = Self::get_mask(text.as_str());
Cipher {
text,
line_count,
mask,
}
}
fn get_mask(lines: &str) -> Vec<bool> {
let mut mask: Vec<bool> = Default::default();
for c in lines.chars() {
match c {
c if c.is_whitespace() => mask.push(true),
_ => mask.push(false),
}
}
mask
}
async fn print_line(line: &str, delay: u64) {
for c in line.chars() {
match c {
c if c.is_whitespace() => print!("{c}"),
_ => {
let idx = rand::random_range(0..CHARSET.len());
print!("{}", CHARSET[idx]);
}
}
let _ = std::io::stdout().flush();
if delay > 0 {
sleep(Duration::from_millis(delay)).await;
}
}
println!();
}
fn reset(&self) {
if self.line_count > 0 {
print!("\x1b[{}F", self.line_count);
}
}
fn display(&self, update_prob: u32) {
for (c, m) in self.text.chars().zip(self.mask.iter()) {
match m {
true => print!("{c}"),
false => {
if rand::random_range(0..100)<update_prob{
let idx = rand::random_range(0..CHARSET.len());
print!("{}", CHARSET[idx]);
}
else{
print!("\x1b[1C");
}
}
}
}
let _ = std::io::stdout().flush();
}
fn random_unmask(&mut self, num: usize) -> bool {
let mut rng = rand::rng();
let mut idx: Vec<usize> = self
.mask
.iter()
.enumerate()
.filter_map(|(i, m)| if *m { None } else { Some(i) })
.collect();
for offset in 0..num {
let selected: usize = rand::random_range(offset..idx.len());
self.mask[idx[selected]] = true;
idx[selected] = idx[offset];
if offset == (idx.len()-1){
return true;
}
}
false
}
}
#[tokio::main]
async fn main() {
let mut cipher = Cipher::load(10).await;
sleep(Duration::from_millis(1000)).await;
for _ in 0..20 {
cipher.reset();
cipher.display(100);
sleep(Duration::from_millis(50)).await;
}
loop {
cipher.reset();
cipher.display(20);
sleep(Duration::from_millis(100)).await;
if cipher.random_unmask(10){
break
}
}
cipher.reset();
cipher.display(100);
}