Уроки · 76
Новая случайность при каждом запуске
Запусти программу с rand::gen_range несколько раз подряд, и случайные
числа каждый раз окажутся одними и теми же, в том же порядке. Например,
кружок из урока «Случайная точка» при каждом запуске
прыгает по одним и тем же местам. Давай разберёмся, почему так бывает и
как это поменять.
Откуда берутся случайные числа
Заголовок раздела «Откуда берутся случайные числа»Компьютер не бросает кубик. Он считает числа по формуле, а формуле нужно число, с которого начать. Это стартовое число называют seed, по-русски «зерно»: с него всё начинается, и из него вырастает весь набор чисел. Пока зерно одно и то же, и числа получаются одни и те же.
Иногда это полезно. Представь, что игра сама создаёт мир: расставляет деревья, горы и озёра, и руками мы их не рисуем. Если взять то же зерно, получится тот же самый мир. С другим зерном получится другой. Так устроены миры в Майнкрафте: зерно можно задать самому.
rand::srand
Заголовок раздела «rand::srand»Зерно задаёт функция rand::srand:
// Задаём зерно. Дальше все числа из gen_range считаются от него.rand::srand(42);
// Это число всегда будет одним и тем же при зерне 42.let x = rand::gen_range(0.0, 10.0);Вызывать её нужно один раз, в самом начале программы, до первого
gen_range.
Новое зерно при каждом запуске
Заголовок раздела «Новое зерно при каждом запуске»Чтобы каждый запуск отличался от прошлого, нужно зерно, которое каждый раз новое. Подходит текущее время: оно никогда не повторяется.
// Текущее время в секундах — каждый запуск оно другое. Берём его как зерно.rand::srand(macroquad::miniquad::date::now() as u64);date::now() отдаёт текущее время в секундах. as u64 делает из него
целое число, потому что srand ждёт именно целое, как в уроке
«Целые числа». u64 — ещё один тип целых чисел, он
рассчитан на большие значения.
В предыдущих уроках мы такую строку не писали, поэтому там случайный расклад при каждом запуске одинаковый.
Попробуй сам
Заголовок раздела «Попробуй сам»Меняй seed и смотри, как меняется расклад. Верни 42 — получишь тот же расклад, что был сначала. Кнопка берёт в качестве зерна текущее время:
use macroquad::prelude::*;
// Зерно (seed): с него начинается набор случайных чисел.const SEED: u64 = 42;// Сколько кружков нарисовать.const COUNT: usize = 20;
#[macroquad::main("Новая случайность")]async fn main() { // Задаём зерно один раз, в самом начале, до первого gen_range. rand::srand(SEED);
// Список случайных точек — сначала пустой. let mut points: Vec<(f32, f32)> = Vec::new(); // Заполняем его один раз, при старте программы. for _ in 0..COUNT { // Случайная точка: x от 0 до ширины экрана, y от 0 до высоты. let x = rand::gen_range(0.0, screen_width()); let y = rand::gen_range(0.0, screen_height()); // Кладём точку в список. points.push((x, y)); }
loop { clear_background(BLACK);
// Рисуем кружок в каждой точке списка. for &(x, y) in &points { draw_circle(x, y, 30.0, YELLOW); }
next_frame().await; }}Что здесь происходит
Заголовок раздела «Что здесь происходит»- Случайные числа считаются по формуле. Начинается она с зерна, по-английски seed.
rand::srand(SEED)задаёт зерно. Его вызывают один раз, в начале программы, до первогоgen_range.- Одно и то же зерно даёт один и тот же расклад кружков. Другое зерно даёт другой.
date::now() as u64берёт в качестве зерна текущее время, и каждый запуск получается новым. Кнопка «Из времени» делает то же самое.