8.8. Демонстрация - Симуляция игры

Шаг 1

Идея решения этой задачи довольно проста: представим наших игроков как очередь. Чтобы «исключить» очередного игрока, сначала отсчитаем от начала очереди нужное количество «невылетающих» игроков, по мере отсчёта перекладывая их в конец очереди. А затем просто исключим первого игрока в очереди за ними.

import {eliminate} from "./logger";

const players = ['GottaSaiyan', 'Mountaintrid', 'Rectionom', 'JoshChase', 'DreamLess', 'BlondiePlanet', 'Breakingbing', 'Goldenelox'];

function play(players) {
  return [];
}

eliminate(players, play(players));

Шаг 2

Чтобы убедиться в правильности нашей логики исключения, сначала прогоним наш алгоритм на фиксированном шаге. Например, зафиксируем его в двойке. Как мы видим, из-за того, что в нашей функции мы модифицируем массив игроков, оставляя там только победителя, список до демонстрации не долетает. Будьте осторожнее с мутированием данных внутри своей функции. Лучше хотя бы сохранять их локальную копию, что мы и сделаем.


import {eliminate} from "./logger";

const players = ['GottaSaiyan', 'Mountaintrid', 'Rectionom', 'JoshChase', 'DreamLess', 'BlondiePlanet', 'Breakingbing', 'Goldenelox'];

function play(players) {
  const step = 2
  const eliminated = [];

  // пока у нас нет победителя
  while (players.length !== 1) {
    let remove = step;

    // пока не дошли до вылетающего человека
    while (remove) {
      // берём человека, первого в очереди на удаление
      const first = players.shift();

      // и кладём его в конец ожидающих удаления...
      players.push(first);

      remove--;
    }

    // как только дошли до удаляемого — удаляем его из очереди и кладём в наш массив результатов игры
    const eliminatedLogin = players.shift();

    eliminated.push({
      login: eliminatedLogin,
      step,
    })
  }

  return eliminated;
}

eliminate(players, play(players));

Шаг 3

Теперь всё работает как надо! Допишем небольшую функцию, которая будет возвращать случайный шаг для передвижения по очереди.

import {eliminate} from "./logger";

const players = ['GottaSaiyan', 'Mountaintrid', 'Rectionom', 'JoshChase', 'DreamLess', 'BlondiePlanet', 'Breakingbing', 'Goldenelox'];

function play(players) {
  players = [...players];

  const step = 2
  const eliminated = [];

  // пока у нас нет победителя
  while (players.length !== 1) {
    let remove = step;

    // пока не дошли до вылетающего человека
    while (remove) {
      // берём человека, первого в очереди на удаление
      const first = players.shift();

      // и кладём его в конец ожидающих удаления...
      players.push(first);

      remove--;
    }

    // как только дошли до удаляемого — удаляем его из очереди и кладём в наш массив результатов игры
    const eliminatedLogin = players.shift();

    eliminated.push({
      login: eliminatedLogin,
      step,
    })
  }

  return eliminated;
}

eliminate(players, play(players));

Шаг 4

Несмотря на не совсем простую логику, с помощью очереди мы смогли описать поведение нашей игры. Теперь можете делать ставки на игроков и переживать за них, кликая по кнопке над демонстрацией.

import {eliminate} from "./logger";

const MAX_STEP = 3;
const players = ['GottaSaiyan', 'Mountaintrid', 'Rectionom', 'JoshChase', 'DreamLess', 'BlondiePlanet', 'Breakingbing', 'Goldenelox'];

function randomStep() {
  return Math.floor(Math.random() * MAX_STEP + 1);
}

function play(players) {
  players = [...players];

  const eliminated = [];

  // пока у нас нет победителя
  while (players.length !== 1) {
    const step = randomStep();
    let remove = step;

    // пока не дошли до вылетающего человека
    while (remove) {
      // берём человека, первого в очереди на удаление
      const first = players.shift();

      // и кладём его в конец ожидающих удаления...
      players.push(first);

      remove--;
    }

    // как только дошли до удаляемого — удаляем его из очереди и кладём в наш массив результатов игры
    const eliminatedLogin = players.shift();

    eliminated.push({
      login: eliminatedLogin,
      step,
    })
  }

  return eliminated;
}

eliminate(players, play(players));

const playButton = document.getElementById('play-button');

playButton.addEventListener('click', () => eliminate(players, play(players)));

File logger.js

const playersList = document.getElementById('players');

export function eliminate(players, eliminated) {
  playersList.innerHTML = '';

  const playersElements = players.map(login => {
    const element = document.createElement('li');

    element.innerText = login;

    return element;
  });

  playersElements.forEach(element => {
    playersList.appendChild(element);
  });

  eliminated.forEach(({login}, index) => {
    const eliminationTiming = (index + 1) * 1000;

    const eliminatedPlayerIndex = players.findIndex(playerLogin => playerLogin === login);
    const eliminatedElement = playersElements[eliminatedPlayerIndex];

    setTimeout(() => {
      eliminatedElement.style.textDecoration = 'line-through';
      eliminatedElement.style.color = 'gray';
    }, eliminationTiming)
  })
}

}