<div id="lockers"></div>

<div id="status">
  <button onClick="startReplay()">Start Replay</button>
  <button onClick="stopReplay()">Stop Replay</button>
  <button onClick="previousReplay()">
    < Previous Student</button>
      <button onClick="nextReplay()">Next Student ></button>

      <div id="studentStatus"></div>
      <div id="openLockers"></div>
</div>
#lockers, #status {
  width: 75%;
  margin: 0 auto;
  margin-top: 1em;
  text-align: center;
}

#lockers .fa-door-open, #lockers .fa-door-closed {
  padding-bottom: .4em;
  font-size: 2em;
  opacity: .5;
}

#lockers .fa-door-open {
  color: green;
}

#lockers .fa-door-closed {
  color: red;
}

button {
  margin-bottom: 10px;
}
function initLockerUI(n) {
  // render the lockers to the DOM
  for (var i = 0; i < n; i++) {
    var tag = document.createElement("i");
    tag.classList.add("fa");
    tag.classList.add("fa-door-closed");
    var element = document.getElementById("lockers");
    element.appendChild(tag);
  }
}

const lockerCount = 100;
const studentCount = lockerCount;

// create UI for bank of lockers
initLockerUI(lockerCount);

// set up bank of lockers, initialized to false (closed)
let lockers = Array(lockerCount).fill(false);
let lockerStates = [];
let mischievousStudents = 0;

while (mischievousStudents < studentCount) {
  
  // add one more mischievous student to the ranks!
  mischievousStudents++;
  
  switch(mischievousStudents) {
    case 1:
      // the first mischievous student will open all the lockers
      lockers = lockers.map(function(state, i) {
        return true; // true = open, false = closed  
      });
      break;
    case 2:
      // mischievous student 2 will close every other locker, starting at 2
      lockers = lockers.map(function(state, i) {
        return (i+1) % 2 === 0 ? false : state;
      });
      break;
    default:
      // student n will *invert* every nth locker
      lockers = lockers.map(function(state, i) {
        return (i+1) % this.student === 0 ? !state : state;
      }, {
        student: mischievousStudents
      });
  }
  // we'll save the current locker state for replay later
  lockerStates.push(lockers);
} // end student traversal

// replay code
let replayIntervalTimer;
let currentLockerReplay = 0;
let replaying = false;

function getOpenLockers(lockers) {
  let openLockers = [];
  lockers.forEach(function(x, index) {
    x && openLockers.push(index + 1);
  });
  return openLockers.join(", ");
}

function incrementOrStop() {
  if (currentLockerReplay < lockerCount - 1) {
    currentLockerReplay++;
  } else {
    currentLockerReplay = 0;
    clearInterval(replayIntervalTimer);
  }
}

function updateLockerUIStates() {
  lockers = lockerStates[currentLockerReplay];
  const lockerDoors = document.querySelectorAll(".fa-w-20");
  for (var i = 0; i < lockerDoors.length; i++) {
    lockerDoors[i].classList.remove("fa-door-closed");
    lockerDoors[i].classList.remove("fa-door-open");
    lockerDoors[i].classList.add(lockers[i] ? "fa-door-open" : "fa-door-closed");
  }
  
  // output statistics
  document.getElementById("studentStatus").innerHTML = "Student: " + (currentLockerReplay + 1) + " of " + studentCount;
  
  document.getElementById("openLockers").innerHTML = "Open Lockers: " + getOpenLockers(lockers);

  // increment or stop
  incrementOrStop();
}

function startReplay() {
  if (!replaying) {
    replayIntervalTimer = setInterval(updateLockerUIStates, 100);  
    replaying = true;
  }
}

function stopReplay() {
  clearInterval(replayIntervalTimer);
  replaying = false;
}

function nextReplay() {
  stopReplay();
  updateLockerUIStates();
}

function previousReplay() {
  stopReplay();
  currentLockerReplay = currentLockerReplay > 1 ? 
    currentLockerReplay - 2 : 
    0;
  updateLockerUIStates();
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js