Files
JR-priprava-na-skusku/priprava/rust_priprava14.md
2026-03-06 01:36:58 +01:00

750 lines
19 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
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.
`Cargo.toml`:
```toml
[dependencies]
rand = "0.8"
```
```rust
use rand::Rng; // toto treba skoro vždy
```
---
## Kapitola 1: Generátor náhodných čísel — thread_rng
Všetko začína generátorom. V Ruste sa volá `thread_rng()`:
```rust
use rand::Rng;
fn main() {
// Vytvor generátor
let mut rng = rand::thread_rng();
// Náhodné i32 (celý rozsah)
let cislo: i32 = rng.gen();
println!("{}", cislo); // napr. -1847294628
// Náhodné f64 medzi 0.0 a 1.0
let desatinne: f64 = rng.gen();
println!("{}", desatinne); // napr. 0.7382...
// Náhodné bool
let minca: bool = rng.gen();
println!("{}", minca); // true alebo false
}
```
**`mut rng`** — generátor sa mení pri každom volaní (posúva sa jeho vnútorný stav), preto musí byť `mut`.
### Ú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 a koľko false.
**1c.** Čo je zle?
```rust
let rng = rand::thread_rng();
let cislo: i32 = rng.gen();
```
---
## Kapitola 2: gen_range — číslo v rozsahu
```rust
use rand::Rng;
let mut rng = rand::thread_rng();
// Celé číslo od 1 do 10 (vrátane oboch)
let cislo: i32 = rng.gen_range(1..=10);
// Celé číslo od 1 do 9 (10 NIE JE zahrnuté)
let cislo: i32 = rng.gen_range(1..10);
// Float od 0.0 do 1.0 (1.0 nie je zahrnuté)
let cislo: f64 = rng.gen_range(0.0..1.0);
// Index do vektora
let vektor = vec!["a", "b", "c", "d", "e"];
let index: usize = rng.gen_range(0..vektor.len());
let nahodny_prvok = &vektor[index];
```
**`..=`** — inclusive (vrátane konca). `..` — exclusive (bez konca).
| Zápis | Rozsah | Príklad |
|-------|--------|---------|
| `1..10` | 1 až 9 | kocka bez 10 |
| `1..=10` | 1 až 10 | kocka s 10 |
| `0..vektor.len()` | 0 až posledný index | náhodný index |
### Úlohy 2
**2a.** Simuluj hod kockou (16). Hoď 100-krát a spočítaj koľkokrát padlo každé číslo (použi HashMap).
**2b.** Vygeneruj náhodné heslo — 8 náhodných znakov z abecedy a-z. Hint: vygeneruj číslo 0..26, pripočítaj k `b'a'`, preveď na char.
**2c.** Napíš funkciu `nahodne_cislo_v_rozsahu(od: i32, do_: i32) -> i32`. Použi `gen_range`.
---
## Kapitola 3: choose — náhodný výber z kolekcie
Toto je **najdôležitejšie** na skúške. `choose` vyberie náhodný prvok z vektora alebo slice.
```rust
use rand::seq::SliceRandom; // TOTO TREBA IMPORTOVAŤ!
let mut rng = rand::thread_rng();
let ovocie = vec!["jablko", "hruška", "banán", "pomaranč"];
// choose — vráti Option<&&str> (Option lebo Vec môže byť prázdny)
let nahodne = ovocie.choose(&mut rng);
// Some(&"hruška") — náhodný prvok
match nahodne {
Some(o) => println!("Vybrané: {}", o),
None => println!("Prázdny zoznam"),
}
```
**Import:** `use rand::seq::SliceRandom;` — bez tohto `choose` nefunguje!
### choose s Vec<String>
```rust
use rand::seq::SliceRandom;
let slova = vec!["pes".to_string(), "mačka".to_string(), "kôň".to_string()];
let mut rng = rand::thread_rng();
let nahodne: Option<&String> = slova.choose(&mut rng);
// Some(&"mačka")
```
### choose s Vec<Struct>
```rust
struct Otazka {
text: String,
odpoved: String,
}
let otazky = vec![
Otazka { text: "2+2?".into(), odpoved: "4".into() },
Otazka { text: "Hlavné mesto SR?".into(), odpoved: "Bratislava".into() },
];
let nahodna: Option<&Otazka> = otazky.choose(&mut rng);
if let Some(otazka) = nahodna {
println!("{}", otazka.text);
}
```
### choose na skúške — SpravcaHry.vytvor_novu_hru
```rust
use rand::seq::SliceRandom;
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 v slovníku
let slova = self.slovnik.get(kategoria)?;
// 2. Skontroluj či nie je prázdna
if slova.is_empty() {
return None;
}
// 3. Vyber náhodné slovo
let mut rng = rand::thread_rng();
let slovo = slova.choose(&mut rng)?;
// 4. Vytvor hru s tým slovom
Some(Hra::new(slovo))
}
}
```
Krok po kroku:
```
slovnik = {"zvierata": ["pes", "mačka", "kôň"], "jedlo": ["pizza", "burger"]}
kategoria = "zvierata"
→ slovnik.get("zvierata") → Some(&["pes", "mačka", "kôň"])
→ slova = &["pes", "mačka", "kôň"]
→ slova.choose(&mut rng) → Some(&"mačka")
→ Hra::new("mačka")
→ Some(Hra { hladane_slovo: "mačka", ... })
kategoria = "neexistuje"
→ slovnik.get("neexistuje") → None
→ ? vráti None z funkcie
```
### Úlohy 3
**3a.** Máš Vec mien. Vyber náhodné meno a vypíš ho.
**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.** Máš Vec<Otazka>. Vyber 5 náhodných otázok (bez opakovania). Hint: pozri kapitolu 5 (shuffle).
**3d.** Čo sa stane ak zavoláš `.choose()` na prázdnom vektore?
---
## Kapitola 4: choose_multiple — viac náhodných prvkov
```rust
use rand::seq::SliceRandom;
let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::thread_rng();
// Vyber 3 náhodné prvky (BEZ opakovania)
let vybrane: Vec<&i32> = cisla.choose_multiple(&mut rng, 3).collect();
// napr. [&7, &2, &9]
// Ak chceš viac ako je prvkov, vráti všetky (v náhodnom poradí)
let vsetky: Vec<&i32> = cisla.choose_multiple(&mut rng, 100).collect();
// všetkých 10 prvkov v náhodnom poradí
```
### 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::thread_rng();
self.otazky.choose_multiple(&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 — vyber 7 náhodných.
---
## Kapitola 5: shuffle — zamiešanie
```rust
use rand::seq::SliceRandom;
let mut cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut rng = rand::thread_rng();
// shuffle — zamieša NA MIESTE (mení Vec)
cisla.shuffle(&mut rng);
// cisla je teraz napr. [7, 2, 9, 1, 5, 3, 10, 4, 8, 6]
```
**`&mut self`** — shuffle mení vektor, preto `let mut cisla` a `&mut rng`.
### Kedy shuffle vs choose_multiple
| Chcem... | Použi |
|----------|-------|
| 1 náhodný prvok | `choose` |
| N náhodných (bez zmeny originálu) | `choose_multiple` |
| Zamiešať celý zoznam | `shuffle` |
| Prvých N po zamiešaní | `shuffle` + `.iter().take(n)` |
### Na skúške — zamiešanie otázok/odpovedí
```rust
impl Kviz {
fn hraj(&mut self) {
let mut rng = rand::thread_rng();
// Zamieša poradie otázok
self.otazky.shuffle(&mut rng);
for otazka in &self.otazky {
println!("{}", otazka.text);
// ...
}
}
}
// Alebo zamiešaj odpovede ku každej otázke
impl Otazka {
fn zamiesaj_odpovede(&mut self) {
let mut rng = rand::thread_rng();
self.odpovede.shuffle(&mut rng);
}
}
```
### Úlohy 5
**5a.** Máš vektor kariet. Zamieša ho. Vypíš prvých 5 (to je "ruka" hráča).
**5b.** Máš Vec<Otazka>. Zamieša poradie. Vypíš otázky v novom poradí.
**5c.** Čo je zle?
```rust
let cisla = vec![1, 2, 3, 4, 5];
cisla.shuffle(&mut rand::thread_rng());
```
---
## Kapitola 6: Generovanie náhodných znakov a reťazcov
```rust
use rand::Rng;
use rand::distributions::Alphanumeric;
let mut rng = rand::thread_rng();
// Náhodný ASCII znak a-z
let znak: char = rng.gen_range(b'a'..=b'z') as char;
// Náhodný veľký znak A-Z
let znak: char = rng.gen_range(b'A'..=b'Z') as char;
// Náhodný 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::SliceRandom;
let abeceda: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
let mut rng = rand::thread_rng();
let nahodny_znak = abeceda.choose(&mut rng);
// Some(&'m')
```
### Úlohy 6
**6a.** Vygeneruj náhodné heslo dlhé 10 znakov, len z malých písmen a-z.
**6b.** Vygeneruj náhodné meno: veľké prvé písmeno + 4 malé písmená.
**6c.** Máš abecedu slovenských znakov (vrátane diakritiky) ako Vec<char>. Vyber 5 náhodných.
---
## Kapitola 7: gen_bool a bernoulli — pravdepodobnosť
```rust
use rand::Rng;
let mut rng = rand::thread_rng();
// 50% šanca na true
let minca: bool = rng.gen_bool(0.5);
// 70% šanca na true
let dost_casto: bool = rng.gen_bool(0.7);
// 10% šanca na true
let zriedka: bool = rng.gen_bool(0.1);
// 100% true
let vzdy: bool = rng.gen_bool(1.0);
// 0% true
let nikdy: bool = rng.gen_bool(0.0);
```
### Praktické použitie
```rust
// Šanca na kritický zásah v hre
fn je_kriticky_zasah(sila: f64) -> bool {
let mut rng = rand::thread_rng();
rng.gen_bool(sila / 100.0) // sila 25 → 25% šanca
}
// Náhodná udalosť
fn nahodna_udalost() {
let mut rng = rand::thread_rng();
if rng.gen_bool(0.3) {
println!("Nastala špeciálna udalosť! (30% šanca)");
}
}
```
### Úlohy 7
**7a.** Simuluj 1000 hodov mincou. Koľkokrát padol orol (true)?
**7b.** Napíš funkciu, ktorá vráti "Výhra!" s pravdepodobnosťou 15% a "Skús znova" inak.
---
## 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::thread_rng();
let tajne: i32 = rng.gen_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::SliceRandom;
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() {
// Slovník
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::thread_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);
// Stav hry
let mut uhadnute: HashSet<char> = HashSet::new();
let mut skusane: HashSet<char> = HashSet::new();
let mut zivoty: u8 = 6;
loop {
// Zobraz slovo
let zobrazene: String = slovo.chars().map(|c| {
if uhadnute.contains(&c) { c } else { '_' }
}).collect();
println!("\n{} (životov: {})", zobrazene, zivoty);
// Výhra?
if slovo.chars().all(|c| uhadnute.contains(&c)) {
println!("Vyhral si! Slovo bolo: {}", slovo);
break;
}
// Prehra?
if zivoty == 0 {
println!("Prehral si! Slovo bolo: {}", slovo);
break;
}
// Vstup
let Some(pismeno) = nacitaj_znak() else {
println!("Neplatný vstup");
continue;
};
if !skusane.insert(pismeno) {
println!("Toto písmeno si už skúšal!");
continue;
}
if slovo.contains(pismeno) {
uhadnute.insert(pismeno);
println!("Správne!");
} else {
zivoty -= 1;
println!("Zle!");
}
}
}
```
### Úlohy 8
**8a.** Napíš hru "hádaj číslo" kde rozsah závisí od obtiažnosti:
- Ľahká: 110
- Stredná: 150
- Ťažká: 1100
Použi prompt na výber obtiažnosti.
**8b.** Napíš jednoduchú kvízovú hru: 5 otázok z náhodne zamiešaného poolu. Každá otázka má 4 odpovede (zamiešané). Hráč zadáva číslo odpovede.
---
## Kapitola 9: Náhodné s kolekciami
### Náhodný kľúč z HashMap
```rust
use rand::seq::IteratorRandom; // TOTO 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::thread_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))
```
**Import:** `use rand::seq::IteratorRandom;` — toto umožní `.choose()` na akomkoľvek iterátore, nielen na slice.
### Náhodný prvok z HashSet
```rust
use rand::seq::IteratorRandom;
let mnozina: HashSet<String> = vec!["pes", "mačka", "kôň"]
.into_iter().map(String::from).collect();
let mut rng = rand::thread_rng();
let nahodny = mnozina.iter().choose(&mut rng);
// Some(&"mačka")
```
### Úlohy 9
**9a.** Máš HashMap kategórií. Vyber náhodnú kategóriu (kľúč), potom náhodné slovo z nej.
**9b.** Máš HashSet<char> skúšaných písmen. Vyber náhodné písmeno z abecedy, ktoré ešte NIE JE v sete.
---
## Kapitola 10: Kompletný vzor — SpravcaHry zo skúšky
```rust
use rand::seq::SliceRandom;
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()
}
// NAJDÔLEŽITEJŠIA METÓDA — náhodné slovo z kategórie
pub fn vytvor_novu_hru(&self, kategoria: &str) -> Option<Hra> {
// 1. Nájdi kategóriu
let slova = self.slovnik.get(kategoria)?;
// 2. Prázdna kategória → None
if slova.is_empty() {
return None;
}
// 3. Náhodný výber
let mut rng = rand::thread_rng();
let slovo = slova.choose(&mut rng)?;
// 4. Vytvor hru
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. Musíš mať:
- `slovnik: HashMap<String, Vec<String>>`
- `nacitaj_zo_suboru`, `uloz_do_suboru`
- `pridaj_kategoriu`, `pridaj_slovo`
- `vytvor_novu_hru` s `choose`
**10b.** Napíš main.rs s clapom bez pozerania. Subcommands:
- `pridaj-kategoriu <cesta> <kategoria>`
- `pridaj-slovo <cesta> <kategoria> <slovo>`
- `hraj <cesta> <kategoria>`
**10c.** Rozšír SpravcaHry o metódu `nahodna_kategoria(&self) -> Option<&String>` — vyber náhodnú kategóriu. Použi `IteratorRandom`.
---
## Prehľad: čo importovať a kedy
| Chcem... | Import | Metóda |
|----------|--------|--------|
| Náhodné číslo | `use rand::Rng;` | `rng.gen()`, `rng.gen_range(1..=6)` |
| Náhodný bool | `use rand::Rng;` | `rng.gen_bool(0.5)` |
| Náhodný prvok z Vec/slice | `use rand::seq::SliceRandom;` | `vec.choose(&mut rng)` |
| Viac náhodných prvkov | `use rand::seq::SliceRandom;` | `vec.choose_multiple(&mut rng, n)` |
| Zamiešať Vec | `use rand::seq::SliceRandom;` | `vec.shuffle(&mut rng)` |
| Náhodný z iterátora (HashMap, HashSet) | `use rand::seq::IteratorRandom;` | `iter.choose(&mut rng)` |
| Alfanumerické znaky | `use rand::distributions::Alphanumeric;` | `rng.sample(Alphanumeric)` |
**Vždy:** `let mut rng = rand::thread_rng();` — vytvor generátor.
**Vždy:** `&mut rng` — predávaj ako meniteľnú referenciu.