Table of Contents

Random Walk

Introduction

Welcome to the Random Walk demonstration page! This interactive web application showcases a visual representation of a random walk algorithm, allowing you to observe how a path evolves as it traverses through a grid.

Features

  • Real-time Control: Use the play/pause button to start or stop the random walk at any time.
  • Customizable Preferences: Toggle options to prefer unwalked cells or end the walk once all cells are visited.
  • Adjustable Speed: Use the slider to control the speed of the random walk, from slow to fast.

Demonstration

Below, you'll find the grid where the random walk occurs. On the right, you'll see controls to interact with the algorithm. Adjust settings to see how different preferences affect the path.

Feel free to explore the code sections to understand the underlying HTML, CSS, and JavaScript that power this demonstration.

Enjoy experimenting with the Random Walk and observe the fascinating patterns that emerge!

Control
Setting

Code

HTML

<section>
  <h2>Demonstration</h2>

  <div class="row row--dark row--font--lg">
    <div id="control" class="col-default-12 col-phone-12 col-tablet-6 col-desktop-small-6">
      <div id="TableContainer">
        <table id="Target_RandomWalk"></table>
      </div>
    </div>
    <div id="control" class="col-default-12 col-phone-12 col-tablet-6 col-desktop-small-6">
      <fieldset>
        <legend>Control</legend>
        <div>
          <!-- <button id="buttonPlayPause" class="button--small">Pause</button> -->
          <button id="buttonPlayPause">Pause</button>
        </div>
      </fieldset>
    </div>
    <div id="setting" class="col-default-12 col-phone-12 col-tablet-6 col-desktop-small-6">
      <fieldset>
        <legend>Setting</legend>
        <div>
          <label for="togglePreference">
            <input type="checkbox" id="togglePreference" checked>
            Prefer Unwalked Cells
          </label>
        </div>
        <div>
          <label for="togglePauseOnComplete">
            <input type="checkbox" id="togglePauseOnComplete" checked>
            Pause On All Visited
          </label>
        </div>
        <div>
          <label for="speedSlider">
            Speed:
            <input type="range" id="speedSlider" min="1" max="12" value="12">
          </label>
        </div>
      </fieldset>
    </div>
  </div>
</section>

CSS

#TableContainer {
  max-width: 70lvh;
  max-height: 70lvh;
  margin-left: auto;
  margin-right: auto;
}
#Target_RandomWalk {
  width: 100%;
  border-collapse: collapse;
}
table td {
  background-color: #333;
  height: 0;
  padding: 0;
  margin: 0;
  border: 1px solid #f0f0f022;

}
#control, #setting {
  text-align: center;
}

JS

document.addEventListener('DOMContentLoaded', function () {
  const target = document.getElementById('Target_RandomWalk');
  const togglePreference = document.getElementById('togglePreference');
  const togglePauseOnComplete = document.getElementById('togglePauseOnComplete');
  const speedSlider = document.getElementById('speedSlider');
  const buttonPlayPause = document.getElementById('buttonPlayPause');

  let preferUnwalked = true;
  let pauseOnComplete = true;
  let walking = false;
  let hue = 0;
  let walkTimeout;

  function calcSpeed(value) {
    return Math.pow(2, 13 - value)
  }

  let speed = calcSpeed(speedSlider.value);

  function updatePlayPause() {
    buttonPlayPause.textContent = walking ? 'Pause' : 'Play';
  }
  
  togglePreference.addEventListener('change', () => {
    preferUnwalked = togglePreference.checked;
  });

  togglePauseOnComplete.addEventListener('change', () => {
    pauseOnComplete = togglePauseOnComplete.checked;
  });

  speedSlider.addEventListener('input', () => {
    speed = calcSpeed(speedSlider.value);
    if (walking) {
      clearTimeout(walkTimeout);
      randomWalk();
    }
  });

  buttonPlayPause.addEventListener('click', () => {
    walking = !walking;
    if (walking) {
      randomWalk();
    } else {
      clearTimeout(walkTimeout);
    }
    updatePlayPause();
  });

  const columns = 64;
  const rows = columns;
  target.style.setProperty('--columns', columns);

  const cells = [];

  for (let row = 0; row < rows; row++) {
    const tr = document.createElement('tr');
    for (let col = 0; col < columns; col++) {
      const td = document.createElement('td');
      tr.appendChild(td);
      cells.push({ element: td, visited: false });
    }
    target.appendChild(tr);
  }

  let currentIndex = (Math.floor(rows / 2) * columns) + Math.floor(columns / 2);
  let lastDirection = { x: 0, y: 0 };
  cells[currentIndex].visited = true;
  cells[currentIndex].element.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;
  
  function updateCellSize() {
    const cellWidth = cells[0].element.clientWidth;
    console.log("cellWidth = " + cellWidth)
    cells.forEach(cell => {
      cell.element.style.height = `${cellWidth}px`;
    });
  }
  
  function randomWalk() {
    walking = true;

    let directions = [{ x: 0, y: 1 }, { x: 1, y: 0 }, { x: 0, y: -1 }, { x: -1, y: 0 }];
    let attempts = directions.map(dir => ({
      x: currentIndex % columns + dir.x,
      y: Math.floor(currentIndex / columns) + dir.y
    })).filter(pos => pos.x >= 0 && pos.x < columns && pos.y >= 0 && pos.y < rows);

    if (preferUnwalked) {
      const unwalked = attempts.filter(pos => !cells[pos.y * columns + pos.x].visited);
      attempts = unwalked.length > 0 ? unwalked : attempts;
    }

    if (attempts.length > 0) {
      const choice = attempts[Math.floor(Math.random() * attempts.length)];
      currentIndex = choice.y * columns + choice.x;
      cells[currentIndex].visited = true;
      hue = (hue + 0.1) % 360; // Increment the hue and wrap around at 360
      cells[currentIndex].element.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;
      lastDirection = { x: choice.x - currentIndex % columns, y: choice.y - Math.floor(currentIndex / columns) };
    }

    if (pauseOnComplete && cells.every(cell => cell.visited)) {
      walking = false;
      return;
    }

    walkTimeout = setTimeout(randomWalk, speed);
  }

  updateCellSize(); // Initial size update
  window.addEventListener('resize', updateCellSize); // Update on resize
  
  randomWalk(); // Start the random walk

  updatePlayPause();
});