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

19 KiB
Raw Blame History

rand — krok za krokom

Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.

Cargo.toml:

[dependencies]
rand = "0.8"
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():

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?

let rng = rand::thread_rng();
let cislo: i32 = rng.gen();

Kapitola 2: gen_range — číslo v rozsahu

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.

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

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 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

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> (kategória → slová). Napíš funkciu:

fn nahodne_slovo(slovnik: &HashMap<String, Vec<String>>, kategoria: &str) -> Option<&String>

3c. Máš Vec. 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

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

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). Rozdaj 7 kariet — vyber 7 náhodných.


Kapitola 5: shuffle — zamiešanie

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í

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. Zamieša poradie. Vypíš otázky v novom poradí.

5c. Čo je zle?

let cisla = vec![1, 2, 3, 4, 5];
cisla.shuffle(&mut rand::thread_rng());

Kapitola 6: Generovanie náhodných znakov a reťazcov

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

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. Vyber 5 náhodných.


Kapitola 7: gen_bool a bernoulli — pravdepodobnosť

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

// Š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

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

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

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

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 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

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

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.