Random Walk - Part 2
In this second part we want to add a fade effect so that the trail from the walker fades to the background colour.
This will be implemented in a brute force way. Yes I know I could make a list of locations the walk had been and then only update those buffer locations, but we will just do a simple iteration through the buffer checking each pixel to see if it is white (0xFFFFFF) if it is not white then do a +=0x010101.
The other part of this code is to add a delay in so that you can decide if you want to slow the fade down. To do this we set a constant FADE_SPEED to a value, then only update the fade very "FADE_SPEED" iterations.
The updated Rust code
// 8 way random walk with minifb and a 5px dot.
// Now with added fade!
// written for maths.earth 20250101
use minifb::{Key, Window, WindowOptions};
use rand::Rng;
// Define the size of the window.
const WIDTH: usize = 800;
const HEIGHT: usize = 600;
// Define the size of the dot as an odd number so the "centre" aligns with the walker's (x, y).
const DOT_SIZE: usize = 5;
// Define fade speed.
const FADE_SPEED: u16 = 15;
// Define struct to store the walker's position.
struct Walker {
x: usize,
y: usize,
}
// Define methods for the Walker.
impl Walker {
fn new() -> Self {
Walker {
x: WIDTH / 2,
y: HEIGHT / 2,
}
}
fn step(&mut self) {
// 8 directions: 0..8
let mut rng = rand::thread_rng();
match rng.gen_range(0..8) {
0 => { // Right
if self.x < WIDTH - 1 {
self.x += 1;
}
}
1 => { // Left
if self.x > 0 {
self.x -= 1;
}
}
2 => { // Down
if self.y < HEIGHT - 1 {
self.y += 1;
}
}
3 => { // Up
if self.y > 0 {
self.y -= 1;
}
}
4 => { // Diagonal down-right
if self.x < WIDTH - 1 {
self.x += 1;
}
if self.y < HEIGHT - 1 {
self.y += 1;
}
}
5 => { // Diagonal up-right
if self.x < WIDTH - 1 {
self.x += 1;
}
if self.y > 0 {
self.y -= 1;
}
}
6 => { // Diagonal down-left
if self.x > 0 {
self.x -= 1;
}
if self.y < HEIGHT - 1 {
self.y += 1;
}
}
7 => { // Diagonal up-left
if self.x > 0 {
self.x -= 1;
}
if self.y > 0 {
self.y -= 1;
}
}
_ => {}
}
}
fn show(&self, buffer: &mut [u32]) {
// Plot a small DOT_SIZE x DOT_SIZE block around (x, y).
// Make sure we stay within bounds.
let half = (DOT_SIZE / 2) as isize;
for dy in -half..=half {
for dx in -half..=half {
let px = self.x as isize + dx;
let py = self.y as isize + dy;
if px >= 0 && px < WIDTH as isize && py >= 0 && py < HEIGHT as isize {
buffer[py as usize * WIDTH + px as usize] = 0x000000; // black
}
}
}
}
}
fn main() {
// Create a window with default options.
let mut window = Window::new(
"Random Walk (8-way movement)",
WIDTH,
HEIGHT,
WindowOptions::default(),
)
.unwrap_or_else(|e| {
panic!("Failed to create window: {}", e);
});
let mut temp_pixel: u32;
let mut fade_speed: u16 = FADE_SPEED;
// Create a buffer for the entire screen, initialised to white.
let mut buffer = vec![0xFFFFFF; WIDTH * HEIGHT];
// Create our Walker.
let mut walker = Walker::new();
// Main event loop.
while window.is_open() && !window.is_key_down(Key::Escape) {
if fade_speed == 0 {
// Fade the buffer to white.
for pixel in buffer.iter_mut() {
// if pixel is not white incremete by 1 for each channel.
if *pixel != 0xFFFFFF {
temp_pixel = *pixel;
temp_pixel += 0x010101;
if temp_pixel > 0xFFFFFF {
*pixel = 0xFFFFFF;
} else {
*pixel = temp_pixel;
}
}
}
fade_speed = FADE_SPEED;
} else {
fade_speed -= 1;
}
// Update the walker's position.
walker.step();
// Draw the new position with a larger dot.
walker.show(&mut buffer);
// Render the updated buffer.
window
.update_with_buffer(&buffer, WIDTH, HEIGHT)
.unwrap();
}
}
Cargo.toml
[package] name = "randomwalk" version = "0.1.0" edition = "2021" [dependencies] minifb = "0.27.0" rand = "0.8"
The image produced:
Mission Accomplished
And there we have it, our walk train fades to white at a preset variable fade rate.