Source: movement_controls.js

// Part responsible for capturing user input

let action_queue = []

let can_apply_move_instantly = true

let current_event_listener = undefined


// The process used by two below function smoothes movement a little bit
// Player cannot zoom through the board or cut corners

NEXT_ACTION_TIMEOUT = 100 // Time (in miliseconds) between two consecutive actions
MAX_ACTIONS_IN_QUEUE = 3 // How many actions can be in queue, if queue is full events are ignored

/** Queues action to be handled and handles it as soon as it's possible
 * 
 * @param {Function} handler 
 * @param {any} action 
 */
function apply_action(handler, action) {
  if (action_queue.length < MAX_ACTIONS_IN_QUEUE)
    action_queue.push(action)
  if (can_apply_move_instantly) { next_queued_action(handler) }
}

/** Processes actions.
 *  When there are no waiting actions, the next one can be applied immediately.
 *  Otherwise try to apply every action until the first success
 * 
 *  @param {Function} handler 
 */
function next_queued_action(handler) {
  if (action_queue.length === 0) {
    can_apply_move_instantly = true
    return
  }
  can_apply_move_instantly = false
  while (action_queue.length > 0 && !handler(action_queue.shift()));
  setTimeout(() => next_queued_action(handler), NEXT_ACTION_TIMEOUT)
}

/**
 * Removes previously set binding of keyboard events
 */
function unlink_controls() {
  document.removeEventListener("keydown", current_event_listener)
}

/** Binds keyboard events to game actions, and calls provided handler on action 
 * 
 * @param {function} handler 
 */
function link_controls(handler) {
  unlink_controls()
  action_queue = []
  current_event_listener = e => {
    if (e.code === "KeyA" || e.code === "KeyH" || e.code === "ArrowLeft")
      apply_action(handler, Actions.MOVE_LEFT)
    else if (e.code === "KeyD" || e.code === "KeyL" || e.code === "ArrowRight")
      apply_action(handler, Actions.MOVE_RIGHT)
    else if (e.code === "KeyW" || e.code === "KeyK" || e.code === "ArrowUp")
      apply_action(handler, Actions.MOVE_UP)
    else if (e.code === "KeyS" || e.code === "KeyJ" || e.code === "ArrowDown")
      apply_action(handler, Actions.MOVE_DOWN)
    else if (e.code == "KeyR")
      apply_action(handler, Actions.RESTART)
  }
  document.addEventListener("keydown", current_event_listener)
}