数学パズルをJavaScriptで解いてみたよ3

問題

1から100までの番号が書かれた100枚のカードが順番に並べられています。

最初、すべてのカードは裏返しの状態で置かれています。

ある人が2番目のカードから、1枚おきにカードを裏返していきます。

すると、2,4,6,…100のカードが表を向くようになります。

次に、別の人が、3番目のカードから2枚おきにカードを裏返していきます。

また、別の人が、4番目のカードから3枚おきにカードを裏返していきます。

このような作業を繰り返し、どのカードの向きも変わらなくなるまで続けたとします。

カードの向きがかわらなくなったとき、裏向きになっているカードの番号をすべて求めてください。

調べたこと

  • Array.from で配列を作れた記憶があるが、どうやるかわからなかったので調べた

実装

const doReverse = (arr, step) => {
  return arr.map((card, i) => {
    const doReverse = (i + 1) % step === 0

    if (doReverse) {
      return {
        ...card,
        isReverse: !card.isReverse,
      }
    }
    return {
      ...card,
    }
  })
}

const getIsReverseCards = (cards) =>
  cards.filter((card) => card.isReverse === true)

const main = () => {
  const end = 100
  let step = 2

  let cards = Array.from({ length: end }, (_, i) => ({
    number: i + 1,
    isReverse: true,
  }))

  while (step <= end) {
    cards = doReverse(cards, step)
    step++
  }

  return getIsReverseCards(cards)
}

console.log(main())

単純にループさせただけです。

もっと短い書き方もできると思うのですが、個人的には誰がみてもわかるコードの方が良いと思っています。(自分が奇抜な書き方ができないだけかもしれないですが)

回答では数学的な解決がされていますが、これを実際の現場で書いたらレビューで怒られそうだなと。。。

実際の仕事では、トリッキーなコードを書くより、問題(仕様)が分からなくてもある程度何をしているかわかるコードの方がありがたがられます。

上の例でいうと、main関数だけ読んでも、なんとなく何をしているかがわかるように命名や関数抽出をしたつもりです。