Files
JR-priprava-na-skusku/priprava/rust_priprava14.md
2026-03-06 08:02:25 +01:00

675 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# rand — krok za krokom (aktuálna verzia)
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
`Cargo.toml`:
```toml
[dependencies]
rand = "0.10"
```
Najjednoduchší import — dáva ti všetko bežné:
```rust
use rand::prelude::*;
```
---
## Kapitola 1: Generátor — rand::rng()
```rust
use rand::Rng;
fn main() {
let mut rng = rand::rng();
// Náhodné celé číslo (celý rozsah typu)
let cislo: i32 = rng.random();
println!("{}", cislo);
// Náhodné f64 medzi 0.0 a 1.0
let desatinne: f64 = rng.random();
println!("{}", desatinne);
// Náhodné bool
let minca: bool = rng.random();
println!("{}", minca);
}
```
`mut rng` — generátor mení svoj stav pri každom volaní.
### Úlohy 1
**1a.** Vygeneruj 5 náhodných i32 čísel. Vypíš ich.
**1b.** Vygeneruj 10 náhodných bool hodnôt. Spočítaj koľko bolo true.
**1c.** Čo je zle?
```rust
let rng = rand::rng();
let cislo: i32 = rng.random();
```
---
## Kapitola 2: random_range — číslo v rozsahu
```rust
use rand::Rng;
let mut rng = rand::rng();
// Od 1 do 10 (vrátane oboch)
let cislo: i32 = rng.random_range(1..=10);
// Od 1 do 9 (10 NIE JE zahrnuté)
let cislo: i32 = rng.random_range(1..10);
// Float od 0.0 do 1.0 (1.0 nie je zahrnuté)
let cislo: f64 = rng.random_range(0.0..1.0);
// Náhodný index do vektora
let vektor = vec!["a", "b", "c", "d", "e"];
let index: usize = rng.random_range(0..vektor.len());
let nahodny_prvok = &vektor[index];
```
| Zápis | Rozsah | Príklad |
|-------|--------|---------|
| `1..10` | 1 až 9 | bez konca |
| `1..=10` | 1 až 10 | vrátane konca |
| `0..vektor.len()` | 0 až posledný index | náhodný index |
### Úlohy 2
**2a.** Simuluj hod kockou (16). Hoď 100-krát, spočítaj výskyty každého čísla (HashMap).
**2b.** Vygeneruj náhodné heslo — 8 znakov a-z. Hint: `rng.random_range(b'a'..=b'z') as char`.
**2c.** Napíš funkciu `nahodne_cislo(od: i32, do_: i32) -> i32`.
---
## Kapitola 3: choose — náhodný výber z kolekcie
Toto je **najdôležitejšie** na skúške.
```rust
use rand::seq::IndexedRandom; // TOTO TREBA!
let mut rng = rand::rng();
let ovocie = vec!["jablko", "hruška", "banán", "pomaranč"];
// choose — vráti Option (None ak prázdny Vec)
let nahodne = ovocie.choose(&mut rng);
// Some(&"hruška")
match nahodne {
Some(o) => println!("Vybrané: {}", o),
None => println!("Prázdny zoznam"),
}
```
**Import:** `use rand::seq::IndexedRandom;` — bez tohto `choose` nefunguje!
Alebo použi `use rand::prelude::*;` — ten to zahŕňa.
### choose s Vec<String>
```rust
use rand::seq::IndexedRandom;
let slova = vec!["pes".to_string(), "mačka".to_string(), "kôň".to_string()];
let mut rng = rand::rng();
let nahodne: Option<&String> = slova.choose(&mut rng);
// Some(&"mačka")
```
### choose na skúške — SpravcaHry.vytvor_novu_hru
```rust
use rand::seq::IndexedRandom;
use std::collections::HashMap;
struct SpravcaHry {
slovnik: HashMap<String, Vec<String>>,
}
impl SpravcaHry {
fn vytvor_novu_hru(&self, kategoria: &str) -> Option<Hra> {
// 1. Nájdi kategóriu
let slova = self.slovnik.get(kategoria)?;
// 2. Prázdna → None
if slova.is_empty() {
return None;
}
// 3. Vyber náhodné slovo
let mut rng = rand::rng();
let slovo = slova.choose(&mut rng)?;
// 4. Vytvor hru
Some(Hra::new(slovo))
}
}
```
Krok po kroku:
```
slovnik = {"zvierata": ["pes", "mačka", "kôň"]}
kategoria = "zvierata"
→ slovnik.get("zvierata") → Some(&["pes", "mačka", "kôň"])
→ slova.choose(&mut rng) → Some(&"mačka")
→ Hra::new("mačka")
kategoria = "neexistuje"
→ slovnik.get("neexistuje") → None
→ ? → return None
```
### Úlohy 3
**3a.** Máš Vec mien. Vyber náhodné a vypíš.
**3b.** Máš HashMap<String, Vec<String>> (kategória → slová). Napíš funkciu:
```rust
fn nahodne_slovo(slovnik: &HashMap<String, Vec<String>>, kategoria: &str) -> Option<&String>
```
**3c.** Čo sa stane ak zavoláš `.choose()` na prázdnom vektore?
---
## Kapitola 4: sample — viac náhodných prvkov
V novej verzii rand sa `choose_multiple` nahradil metódou `sample`:
```rust
use rand::seq::IndexedRandom;
let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::rng();
// Vyber 3 náhodné prvky (BEZ opakovania)
let vybrane: Vec<&i32> = cisla.sample(&mut rng, 3).collect();
// napr. [&7, &2, &9]
// Ak chceš viac ako je prvkov, vráti len toľko koľko je
let vsetky: Vec<&i32> = cisla.sample(&mut rng, 100).collect();
```
### Na skúške — výber N otázok
```rust
struct Kviz {
otazky: Vec<Otazka>,
}
impl Kviz {
fn nahodnych_n(&self, n: usize) -> Vec<&Otazka> {
let mut rng = rand::rng();
self.otazky.sample(&mut rng, n).collect()
}
}
```
### Úlohy 4
**4a.** Máš 20 študentov. Vyber 5 náhodných na skúšanie.
**4b.** Máš balíček kariet (Vec<String>). Rozdaj 7 kariet.
---
## Kapitola 5: shuffle — zamiešanie
```rust
use rand::seq::SliceRandom; // shuffle je v SliceRandom!
let mut cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::rng();
// shuffle — zamieša NA MIESTE (mení Vec)
cisla.shuffle(&mut rng);
// napr. [7, 2, 9, 1, 5, 3, 10, 4, 8, 6]
```
**Import:** `use rand::seq::SliceRandom;` — shuffle je v SliceRandom, nie IndexedRandom!
### Kedy čo
| Chcem... | Použi | Import |
|----------|-------|--------|
| 1 náhodný prvok | `.choose()` | `IndexedRandom` |
| N náhodných (bez zmeny originálu) | `.sample()` | `IndexedRandom` |
| Zamiešať celý zoznam | `.shuffle()` | `SliceRandom` |
### Na skúške — zamiešanie otázok
```rust
use rand::seq::SliceRandom;
impl Kviz {
fn hraj(&mut self) {
let mut rng = rand::rng();
self.otazky.shuffle(&mut rng);
for otazka in &self.otazky {
println!("{}", otazka.text);
}
}
}
```
### Úlohy 5
**5a.** Máš vektor kariet. Zamiešaj. Vypíš prvých 5.
**5b.** Čo je zle?
```rust
let cisla = vec![1, 2, 3, 4, 5];
cisla.shuffle(&mut rand::rng());
```
---
## Kapitola 6: Náhodné znaky a reťazce
```rust
use rand::Rng;
use rand::distr::Alphanumeric; // distr, nie distributions!
let mut rng = rand::rng();
// Náhodný ASCII znak a-z
let znak: char = rng.random_range(b'a'..=b'z') as char;
// Náhodný veľký znak A-Z
let znak: char = rng.random_range(b'A'..=b'Z') as char;
// Alfanumerický reťazec (a-z, A-Z, 0-9)
let heslo: String = (0..12)
.map(|_| rng.sample(Alphanumeric) as char)
.collect();
// napr. "a7Bk9xQ2mF1p"
```
### Výber náhodného znaku z reťazca
```rust
use rand::seq::IndexedRandom;
let abeceda: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
let mut rng = rand::rng();
let nahodny_znak = abeceda.choose(&mut rng);
// Some(&'m')
```
### Úlohy 6
**6a.** Vygeneruj heslo: 10 znakov, len malé písmená a-z.
**6b.** Vygeneruj náhodné meno: veľké prvé + 4 malé písmená.
---
## Kapitola 7: random_bool — pravdepodobnosť
```rust
use rand::Rng;
let mut rng = rand::rng();
// 50% šanca na true
let minca: bool = rng.random_bool(0.5);
// 70% šanca na true
let dost_casto: bool = rng.random_bool(0.7);
// 10% šanca na true
let zriedka: bool = rng.random_bool(0.1);
```
### Úlohy 7
**7a.** Simuluj 1000 hodov mincou. Koľkokrát padol orol (true)?
**7b.** Funkcia vráti "Výhra!" s 15% pravdepodobnosťou, inak "Skús znova".
---
## Kapitola 8: Kombinácia s IO — interaktívne hry
### Hádaj číslo
```rust
use rand::Rng;
use std::io::{self, Write};
fn prompt(text: &str) -> String {
print!("{}", text);
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).unwrap();
vstup.trim().to_string()
}
fn main() {
let mut rng = rand::rng();
let tajne: i32 = rng.random_range(1..=100);
let mut pokusy = 0;
println!("Hádaj číslo od 1 do 100!");
loop {
let vstup = prompt("Tvoj tip: ");
let tip: i32 = match vstup.parse() {
Ok(n) => n,
Err(_) => {
println!("Zadaj číslo!");
continue;
}
};
pokusy += 1;
if tip < tajne {
println!("Viac!");
} else if tip > tajne {
println!("Menej!");
} else {
println!("Správne! Uhádol si na {} pokusov.", pokusy);
break;
}
}
}
```
### Obesenec s náhodným slovom
```rust
use rand::seq::IndexedRandom;
use std::collections::{HashSet, HashMap};
use std::io::{self, Write};
fn nacitaj_znak() -> Option<char> {
print!("Zadaj písmeno: ");
io::stdout().flush().unwrap();
let mut vstup = String::new();
io::stdin().read_line(&mut vstup).ok()?;
vstup.trim().to_lowercase().chars().next()
}
fn main() {
let slovnik: HashMap<&str, Vec<&str>> = HashMap::from([
("zvierata", vec!["pes", "macka", "kon", "krava", "zajac"]),
("jedlo", vec!["pizza", "burger", "salat", "rizoto"]),
]);
// Vyber náhodnú kategóriu a slovo
let mut rng = rand::rng();
let kategorie: Vec<&&str> = slovnik.keys().collect();
let kategoria = **kategorie.choose(&mut rng).unwrap();
let slova = &slovnik[kategoria];
let slovo = *slova.choose(&mut rng).unwrap();
println!("Kategória: {}", kategoria);
let mut uhadnute: HashSet<char> = HashSet::new();
let mut skusane: HashSet<char> = HashSet::new();
let mut zivoty: u8 = 6;
loop {
let zobrazene: String = slovo.chars().map(|c| {
if uhadnute.contains(&c) { c } else { '_' }
}).collect();
println!("\n{} (životov: {})", zobrazene, zivoty);
if slovo.chars().all(|c| uhadnute.contains(&c)) {
println!("Vyhral si! Slovo bolo: {}", slovo);
break;
}
if zivoty == 0 {
println!("Prehral si! Slovo bolo: {}", slovo);
break;
}
let Some(pismeno) = nacitaj_znak() else {
println!("Neplatný vstup");
continue;
};
if !skusane.insert(pismeno) {
println!("Už si skúšal!");
continue;
}
if slovo.contains(pismeno) {
uhadnute.insert(pismeno);
println!("Správne!");
} else {
zivoty -= 1;
println!("Zle!");
}
}
}
```
### Úlohy 8
**8a.** Napíš "hádaj číslo" s obtiažnosťou: ľahká (110), stredná (150), ťažká (1100).
**8b.** Napíš kvízovú hru: 5 otázok z náhodne zamiešaného poolu, hráč zadáva číslo odpovede.
---
## Kapitola 9: Náhodné z HashMap a HashSet
Pre iterátory (nie slice) treba `IteratorRandom`:
```rust
use rand::seq::IteratorRandom; // pre .choose() na iterátore
let mut mapa = HashMap::new();
mapa.insert("a", 1);
mapa.insert("b", 2);
mapa.insert("c", 3);
let mut rng = rand::rng();
// Náhodný kľúč
let kluc = mapa.keys().choose(&mut rng);
// Some(&"b")
// Náhodný pár
let par = mapa.iter().choose(&mut rng);
// Some((&"a", &1))
```
```rust
// HashSet — rovnako
use rand::seq::IteratorRandom;
let mnozina: HashSet<String> = vec!["pes", "mačka", "kôň"]
.into_iter().map(String::from).collect();
let mut rng = rand::rng();
let nahodny = mnozina.iter().choose(&mut rng);
// Some(&"mačka")
```
### Úlohy 9
**9a.** Máš HashMap kategórií. Vyber náhodnú kategóriu, potom náhodné slovo z nej.
**9b.** Máš HashSet<char> skúšaných písmen. Vyber písmeno z abecedy, ktoré ešte NIE JE v sete.
---
## Kapitola 10: Kompletný vzor — SpravcaHry zo skúšky
```rust
use rand::seq::IndexedRandom;
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Default)]
pub struct SpravcaHry {
pub slovnik: HashMap<String, Vec<String>>,
}
impl SpravcaHry {
pub fn nacitaj_zo_suboru(cesta: &PathBuf) -> Option<SpravcaHry> {
let raw = std::fs::read_to_string(cesta).ok()?;
serde_json::from_str(&raw).ok()
}
pub fn uloz_do_suboru(&self, cesta: &PathBuf) -> bool {
let Ok(json) = serde_json::to_string_pretty(&self) else {
return false;
};
std::fs::write(cesta, json).is_ok()
}
pub fn vytvor_novu_hru(&self, kategoria: &str) -> Option<Hra> {
let slova = self.slovnik.get(kategoria)?;
if slova.is_empty() {
return None;
}
let mut rng = rand::rng();
let slovo = slova.choose(&mut rng)?;
Some(Hra::new(slovo))
}
pub fn pridaj_kategoriu(&mut self, kategoria: &str) -> Result<(), ()> {
if self.slovnik.contains_key(kategoria) {
return Err(());
}
self.slovnik.insert(kategoria.to_string(), Vec::new());
Ok(())
}
pub fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> {
let slova = self.slovnik.get_mut(kategoria).ok_or(())?;
slova.push(slovo.to_string());
Ok(())
}
}
```
### main.rs s clapom
```rust
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
struct Args {
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
PridajKategoriu {
cesta: PathBuf,
kategoria: String,
},
PridajSlovo {
cesta: PathBuf,
kategoria: String,
slovo: String,
},
Hraj {
cesta: PathBuf,
kategoria: String,
},
}
fn main() {
let args = Args::parse();
match args.cmd {
Cmd::PridajKategoriu { cesta, kategoria } => {
let mut spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.pridaj_kategoriu(&kategoria) {
Ok(()) => {
spravca.uloz_do_suboru(&cesta);
println!("Kategória pridaná.");
}
Err(()) => println!("Kategória už existuje."),
}
}
Cmd::PridajSlovo { cesta, kategoria, slovo } => {
let mut spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.pridaj_slovo(&kategoria, &slovo) {
Ok(()) => {
spravca.uloz_do_suboru(&cesta);
println!("Slovo pridané.");
}
Err(()) => println!("Kategória neexistuje."),
}
}
Cmd::Hraj { cesta, kategoria } => {
let spravca = SpravcaHry::nacitaj_zo_suboru(&cesta)
.unwrap_or_default();
match spravca.vytvor_novu_hru(&kategoria) {
Some(mut hra) => {
hra.hraj(ConsoleIOManager);
}
None => println!("Kategória neexistuje alebo je prázdna."),
}
}
}
}
```
### Úlohy 10
**10a.** Napíš celý SpravcaHry od nuly bez pozerania.
**10b.** Napíš main.rs s clapom bez pozerania.
**10c.** Rozšír o metódu `nahodna_kategoria(&self) -> Option<&String>`. Použi `IteratorRandom`.
---
## Prehľad: čo importovať a kedy
| Chcem... | Import | Metóda |
|----------|--------|--------|
| Náhodné číslo | `use rand::Rng;` | `rng.random()` |
| Číslo v rozsahu | `use rand::Rng;` | `rng.random_range(1..=6)` |
| Náhodný bool | `use rand::Rng;` | `rng.random_bool(0.5)` |
| Náhodný prvok z Vec | `use rand::seq::IndexedRandom;` | `vec.choose(&mut rng)` |
| N náhodných prvkov | `use rand::seq::IndexedRandom;` | `vec.sample(&mut rng, n)` |
| Zamiešať Vec | `use rand::seq::SliceRandom;` | `vec.shuffle(&mut rng)` |
| Náhodný z iterátora | `use rand::seq::IteratorRandom;` | `iter.choose(&mut rng)` |
| Alfanumerické znaky | `use rand::distr::Alphanumeric;` | `rng.sample(Alphanumeric) as char` |
| Všetko naraz | `use rand::prelude::*;` | zahŕňa Rng, IndexedRandom, SliceRandom |
**Vždy:** `let mut rng = rand::rng();`
## Zmeny oproti starým verziám (0.8)
Ak niekde vidíš starý kód, tu je preklad:
| Staré (rand 0.8) | Nové (rand 0.9+) |
|-------------------|-------------------|
| `rand::thread_rng()` | `rand::rng()` |
| `rng.gen()` | `rng.random()` |
| `rng.gen_range(1..=6)` | `rng.random_range(1..=6)` |
| `rng.gen_bool(0.5)` | `rng.random_bool(0.5)` |
| `use rand::distributions::` | `use rand::distr::` |
| `vec.choose_multiple(&mut rng, n)` | `vec.sample(&mut rng, n)` |
| `use rand::seq::SliceRandom;` (pre choose) | `use rand::seq::IndexedRandom;` |