Files
2022-01-23 00:08:20 +09:00

6.9 KiB

id, title, challengeType, forumTopicId, dashedName
id title challengeType forumTopicId dashedName
59694356a6e7011f7f1c5f4e フリーセルのカードを配る 5 302246 deal-cards-for-freecell

--description--

フリーセル は、ポール・アルフィレ (Paul Alfille) が1978年にPLATOシステムに導入したソリティアというカードゲームです。 Microsoftのジム・ホーン (Jim Horne) は、ゲーム名をフリーセルに変更し、DOS向け、そしてWindows向けに、ゲームを再実装しました。 このバージョンでは、32000のゲーム番号が導入されました。

ゲームが普及し、ジム・ホーンがアルゴリズムを開示したため、他のフリーセル実装でもMicrosoftと同様のアルゴリズムでカードが配られるようになりました。 カードの配り方には1から32000まで番号が付けられています。 Microsoftの新しいバージョンでは、1から1000000までの番号が付けられた100万のゲーム番号があります。一部の実装では、この範囲外の番号も使用されています。

Microsoft Cにおける [線形合同法](https://rosettacode.org/wiki/linear congruential generator "linear congruential generator") をアルゴリズムに使用しています。

  • $state_{n + 1} \equiv 214013 \times state_n + 2531011 \pmod{2^{31}}$
  • $rand_n = state_n \div 2^{16}$
  • $rand_n$ は 0から32767の範囲です。

アルゴリズムは以下のとおりです。

  1. RNGにゲーム番号を与えます。
  2. 52枚のカードの 配列 を作成します。クラブのエース、ダイヤモンドのエース、ハートのエース、スペードのエース、クラブの2、ダイヤモンドの2、と続きます。エース、2、3、4、5、6、7、8、9、10、ジャック、クイーン、キングの順番です。 配列インデックスは0から51で、クラブのエースは0、スペードのキングは51です。
  3. 配列が空になるまでです。
  4. インデックスでランダムなカードを選択します≡ 次の乱数(Mod配列の長さ)。
    • このランダムなカードを配列の最後のカードと交換します。
    • このランダムなカードを配列から削除します (配列の数が1つ減る)。
    • このランダムなカードを配ります。
  5. 52枚すべてのカードを、表を上にして8列に配ります。 最初の8枚のカードが8列に配られ、その上に次の8枚のカードが配られ、と続いていきます。

例:

カードを配る順番

 1  2  3  4  5  6  7  8
 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48
49 50 51 52

ゲーム#1

[
  ['JD', '2D', '9H', 'JC', '5D', '7H', '7C', '5H'],
  ['KD', 'KC', '9S', '5S', 'AD', 'QC', 'KH', '3H'],
  ['2S', 'KS', '9D', 'QD', 'JS', 'AS', 'AH', '3C'],
  ['4C', '5C', 'TS', 'QH', '4H', 'AC', '4D', '7S'],
  ['3S', 'TD', '4S', 'TH', '8H', '2C', 'JH', '7D'],
  ['6D', '8S', '8D', 'QS', '6C', '3D', '8C', 'TC'],
  ['6S', '9C', '2H', '6H']
]

ゲーム#617

[
  ['7D', 'AD', '5C', '3S', '5S', '8C', '2D', 'AH'],
  ['TD', '7S', 'QD', 'AC', '6D', '8H', 'AS', 'KH'],
  ['TH', 'QC', '3H', '9D', '6S', '8D', '3D', 'TC'],
  ['KD', '5H', '9S', '3C', '8S', '7H', '4D', 'JS'],
  ['4C', 'QS', '9C', '9H', '7C', '6H', '2C', '2S'],
  ['4S', 'TS', '2H', '5D', 'JC', '6C', 'JH', 'QH'],
  ['JD', 'KS', 'KC', '4H']
]

--instructions--

ゲーム番号を受け取り、このアルゴリズムと同じ順序でカードを配る関数を作成します。 この関数はフリーセルボードを表す2次元配列を返します。

ゲーム番号は、1000000ゲームのフリーセルソリューション に照らし合わせて確認することもできます (ビデオソリューションを呼び出し、最初のゲーム番号を表示する)。

--hints--

dealFreeCell という関数です。

assert(typeof dealFreeCell === 'function');

dealFreeCell(seed) はオブジェクトを返します。

assert(typeof dealFreeCell(1) === 'object');

dealFreeCell(seed) は長さ7の配列を返します。

assert(dealFreeCell(1).length === 7);

dealFreeCell(1) は例「Game #1」と同一の配列を返します。

assert.deepEqual(dealFreeCell(1), game1);

dealFreeCell(617) は「Game #617」と同一の配列を返します。

assert.deepEqual(dealFreeCell(617), game617);

--seed--

--after-user-code--

const replaceThis = 3;
const game1 = [
  ['JD', '2D', '9H', 'JC', '5D', '7H', '7C', '5H'],
  ['KD', 'KC', '9S', '5S', 'AD', 'QC', 'KH', '3H'],
  ['2S', 'KS', '9D', 'QD', 'JS', 'AS', 'AH', '3C'],
  ['4C', '5C', 'TS', 'QH', '4H', 'AC', '4D', '7S'],
  ['3S', 'TD', '4S', 'TH', '8H', '2C', 'JH', '7D'],
  ['6D', '8S', '8D', 'QS', '6C', '3D', '8C', 'TC'],
  ['6S', '9C', '2H', '6H']
];
const game617 = [
  ['7D', 'AD', '5C', '3S', '5S', '8C', '2D', 'AH'],
  ['TD', '7S', 'QD', 'AC', '6D', '8H', 'AS', 'KH'],
  ['TH', 'QC', '3H', '9D', '6S', '8D', '3D', 'TC'],
  ['KD', '5H', '9S', '3C', '8S', '7H', '4D', 'JS'],
  ['4C', 'QS', '9C', '9H', '7C', '6H', '2C', '2S'],
  ['4S', 'TS', '2H', '5D', 'JC', '6C', 'JH', 'QH'],
  ['JD', 'KS', 'KC', '4H']
];

--seed-contents--

function dealFreeCell(seed) {

  return true;
}

--solutions--

// RNG
function FreeCellRNG(seed) {
  return {
    lastNum: seed,
    next() {
      this.lastNum = ((214013 * this.lastNum) + 2531011) % (Math.pow(2, 31));
      return this.lastNum >> 16;
    }
  };
}
// Get cards
function getDeck() {
  const ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K'];
  const suits = ['C', 'D', 'H', 'S'];
  const cards = [];
  for (let i = 0; i < ranks.length; i += 1) {
    for (let j = 0; j < suits.length; j += 1) {
      cards.push(`${ranks[i]}${suits[j]}`);
    }
  }
  return cards;
}
function dealFreeCell(seed) {
  const rng = FreeCellRNG(seed);
  const deck = getDeck();

  const deltCards = [[], [], [], [], [], [], []];
  let currentColumn = 0;
  let currentRow = 0;

  let rand;
  let temp;
  let card;
  while (deck.length > 0) {
    // Choose a random card
    rand = rng.next() % deck.length;

    // Swap this random card with the last card in the array
    temp = deck[deck.length - 1];
    deck[deck.length - 1] = deck[rand];
    deck[rand] = temp;

    // Remove this card from the array
    card = deck.pop();

    // Deal this card
    deltCards[currentRow].push(card);
    currentColumn += 1;
    if (currentColumn === 8) {
      currentColumn = 0;
      currentRow += 1;
    }
  }

  return deltCards;
}