Graphically Plot a Random Walk

Pixels take a hike!

Part 1 - Part 2 - Part 3

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.