Vanilla JS Robot

pure Javascript version

by Allen N.

HTML

<button id="turnLeftButton">Turn left</button>
<button id="moveForwardButton">Move forward</button>
<button id="turnRightButton">Turn right</button>
<button id="flipDirectionButton">Flip direction</button>

<div class="grid">
  <div class="column">
    <div class="cell"><span class="robot">U</span></div>
    <div class="cell"><span class="obstacle">X</span></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
  <div class="column">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"><span class="obstacle">X</span></div>
    <div class="cell"></div>
  </div>
  <div class="column">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"><span class="obstacle">X</span></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
  <div class="column">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"><span class="obstacle">X</span></div>
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
  <div class="column">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"><span class="obstacle">X</span></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div...

SCSS

.grid {
  border: 2px solid black;
  height: 400px;
  width: 400px;
  display: flex;
  flex-direction: row;

  .column {
    display: flex;
    flex-direction: column;
    flex: 1;

    .cell {
      position: relative;
      flex: 1;
      border: 1px solid black;
      text-align: center;
      font-size: 2em;
      
      span {
        position: absolute;
        left: 50%;
        top: 50%;
        
        &.robot {
          transform: translate(-50%, -48%) scale(1.2);
        }
        &.obstacle {
          transform: translate(-50%, -50%) scale(1.9);
        }
      }
    }
  }
}

JavaScript

class Robot {
  constructor() {
    this.x = 0; // x position in the grid
    this.y = 0; // y position in the grid
    this.direction = 1; // direction index (0: up, 1: right, 2: down, 3: left)
  }

  turnRight() {
    this.direction = (this.direction + 1) % 4; // cycle through direction indices
  }

  turnLeft() {
    this.direction = (this.direction + 3) % 4; // cycle through direction indices in reverse order
  }

  moveForward() {
    const movements = [
      [-1, 0], // up
      [0, 1], // right
      [1, 0], // down
      [0, -1], // left
    ];

    const [dx, dy] = movements[this.direction];
    const newY = this.y + dy;
    const newX = this.x + dx;

    const isWithinGrid = (x, y) => x >= 0 && x < 10 && y >= 0 && y < 10;
    const isNotObstacle = (x, y) => !grid[y * 10 + x].innerHTML.includes("obstacle");

    if (isWithinGrid(newX, newY) && isNotObstacle(newX, newY)) {
      this.x = newX;
      this.y = newY;
    } else {
      // Temporarily turn right and check the next position in that direction
      this.turnRight();
      const [nextDx, nextDy] = movements[this.direction];
      const nextNewY = this.y + nextDy;
      const nextNewX = this.x + nextDx;

      // If there's a wall or obstacle in the next position after turning right, turn left instead
      if (!isWithinGrid(nextNewX, nextNewY) || !isNotObstacle(nextNewX, nextNewY)) {
        this.turnLeft();
        this.turnLeft();
      }
    }
  }

  flipDirection() {
    this.direction = (this.direction + 2) % 4;
  }
}

const robot = new Robot();
const grid = document.querySelectorAll(".grid .column .cell");

function updateGrid() {
  // clear robot icon in all cells except for obstacles
  grid.forEach((cell) => {
    if (!cell.innerHTML.includes("obstacle")) {
      cell.innerHTML = "";
    }
  });

  // place robot icon in the current cell with the correct arrow icon
  const robotCell = grid[robot.y * 10 + robot.x];
  const arrowIcons = ["🔼", "▶️", "🔽", "◀️"];
  robotCell.innerHTML =...