Files

13 KiB
Raw Permalink Blame History

id, title, challengeType, forumTopicId, dashedName
id title challengeType forumTopicId dashedName
5900f3c11000cf542c50fed3 Завдання 84: Шанси Монополії 5 302198 problem-84-monopoly-odds

--description--

У грі, Монополія, стандартна плата встановлюється наступним чином:

ВПЕРЕД А1 CC1 А2 Т1 Р1 В1 CH1 В2 B3 ТЮРМА
H2   С1
Т2   U1
H1   C2
CH3   C3
R4   R2
G3   D1
CC3   CC2
G2   D2
G1   D3
G2J F3 U2 F2 F1 R3 E3 E2 CH2 E1 FP

Гравець стартує зі стартової площадки і додає очки з 6-бічних кубиків для визначення кількості квадратів, за якими просувається за годинниковою стрілкою. Без будь-яких подальших правил ми очікуємо відвідати кожен квадрат з рівною ймовірністю: 2,5%. Однак, кроки на G2J (прямуйте до тюрми), CC (скриня спільноти), і CH (шанс) змінює цей розподіл.

На додачу до G2J, є ще одна карта з CC і CH, що наказує гравцю іти безпосередньо до в'язниці, якщо гравець викидає три парні поспіль, то вони не підвищує результат свого третього кидка. Але вони потрапляють безпосередньо до тюрми.

На початку гри CC та CH картки змішані. Коли гравець опиняється на CC або CH він бере карту зверху відповідної купи і, після виконання інструкцій, повертає картку донизу купи. У кожній стопці по шістнадцять карт, але для цілей цієї задачі ми матимемо справу лише з картками, які вказують рух; будь-яка інструкція, яка не стосується руху буде проігнорована, і гравець залишиться на полі CC/CH.

  • Скриня спільноти (2/16 карт):
    1. Перейти на початок
    2. Перейти до ТЮРМИ
  • Шанс (10/16 карт):
    1. Перейти на початок
    2. Перейти до ТЮРМИ
    3. Перейти до C1
    4. Перейти до E3
    5. Перейти до H2
    6. Перейти до C1
    7. Перейти до наступної R (залізнична компанія)
    8. Перейти до наступної R
    9. Перейти до наступного U (комунальна компанія)
    10. Назад на 3 ходи.

Корінь цієї проблеми стосується ймовірності відвідання певного квадрату. Тобто ймовірність закінчити на цьому квадраті після виконання ходу. З цієї причини слід розуміти, що, за винятком G2J, при якому ймовірність фінішу на цьому кроці дорівнює нулю, квадрати CH матимуть найнижчі ймовірності, як 5/8 запит руху на інший квадрат, а ми зацікавленні саме на фінальному квадраті, де гравець закінчує після кожного кидку. Ми не будемо розрізняти "Просто відвідування" та відправлення до ТЮРЬМИ, також ми ігноруватимемо правило щодо вимагання подвійної плати щоб "вийти з в'язниці", припустивши, що гравці платять за те, щоб вийти на свій наступний хід.

Починаючи з кроку GO та послідовно нумеруючи квадрати з 00 до 39, ми можемо об'єднати ці двоцифрові числа для отримання рядків, що відповідають наборам квадратів.

Статистично можна показати, що три найпопулярніші квадрати, по порядку, це ТЮРМА (6.24%) = Квадрат 10, Е3 (3.18%) = Квадрат 24, і GO (3.09%) = Квадрат 00. Отож, ці три найпопулярніші квадрати можна перелічити шестизначним модальним рядком 102400.

Якщо замість 6-гранного кубика використовується двоn-гранний кубик, знайдіть шестизначний модальний рядок.

--hints--

monopolyOdds(8) має вивести рядок.

assert(typeof monopolyOdds(8) === 'string');

monopolyOdds(8) має вивести рядок 102400.

assert.strictEqual(monopolyOdds(8), '102400');

monopolyOdds(10) має вивести рядок 100024.

assert.strictEqual(monopolyOdds(10), '100024');

monopolyOdds(20) має вивести рядок 100005.

assert.strictEqual(monopolyOdds(20), '100005');

monopolyOdds(4) має вивести рядок 101524.

assert.strictEqual(monopolyOdds(4), '101524');

--seed--

--seed-contents--

function monopolyOdds(n) {

  return true;
}

monopolyOdds(8);

--solutions--

function monopolyOdds(n) {
  function chanceCard(position, chanceCardPosition) {
    chanceCardPosition = (chanceCardPosition + 1) % 16;
    if (chanceCardPosition < 6) {
      position = chanceCardsMoves[chanceCardPosition];
    } else if (chanceCardPosition === 6 || chanceCardPosition === 7) {
      position = nextMovesFromR[position];
    } else if (chanceCardPosition === 8) {
      position = nextMovesFromU[position];
    } else if (chanceCardPosition === 9) {
      position -= 3;
    }
    return [position, chanceCardPosition];
  }

  function chestCard(position, chestPosition) {
    chestPosition = (chestPosition + 1) % 16;
    if (chestPosition < 2) {
      position = chestCardsMoves[chestPosition];
    }
    return [position, chestPosition];
  }

  function isChest(position) {
    return position === 2 || position === 17 || position === 33;
  }

  function isChance(position) {
    return position === 7 || position === 22 || position === 36;
  }

  function isJail(position) {
    return position === 30;
  }

  function roll(dice) {
    return Math.floor(Math.random() * dice) + 1;
  }

  function getTopThree(board) {
    return sortByVisits(board)
      .slice(0, 3)
      .map(elem => elem[0].toString().padStart(2, '0'))
      .join('');
  }

  function sortByVisits(board) {
    return board
      .map((element, index) => [index, element])
      .sort((a, b) => a[1] - b[1])
      .reverse();
  }

  const rounds = 2000000;
  const chestCardsMoves = [0, 10];
  const chanceCardsMoves = [0, 10, 11, 24, 39, 5];
  const nextMovesFromR = { 7: 15, 22: 25, 36: 5 };
  const nextMovesFromU = { 7: 12, 36: 12, 22: 28 };

  const board = new Array(40).fill(0);
  let doubleCount = 0;
  let curPosition = 0;
  let curChestCard = 0;
  let curChanceCard = 0;

  for (let i = 0; i < rounds; i++) {
    const dice1 = roll(n);
    const dice2 = roll(n);

    if (dice1 === dice2) {
      doubleCount++;
    } else {
      doubleCount = 0;
    }

    if (doubleCount > 2) {
      curPosition = 10;
      doubleCount = 0;
    } else {
      curPosition = (curPosition + dice1 + dice2) % 40;

      if (isChance(curPosition)) {
        [curPosition, curChanceCard] = chanceCard(curPosition, curChanceCard);
      } else if (isChest(curPosition)) {
        [curPosition, curChestCard] = chestCard(curPosition, curChestCard);
      } else if (isJail(curPosition)) {
        curPosition = 10;
      }
    }
    board[curPosition]++;
  }
  return getTopThree(board);
}