作ったもの
Memory Game — https://sen.ltd/portfolio/memory-game/
- 4 難易度(12 / 16 / 36 / 64 枚)
- 5 テーマ(絵文字 / 数字 / アルファベット / 図形 / ひらがな)
- CSS 3D フリップアニメーション
- 手数カウンタ + タイマー
- 難易度別ベストスコア(localStorage)
- クリア時に紙吹雪
vanilla JS、ゼロ依存、ビルド不要。node --test で 38 ケース。
イミュータブル状態
{
cards: [{ id, value, flipped, matched }],
firstCard: null | cardId,
moves, matches, started, startedAt
}
firstCard が「1 枚目めくって 2 枚目待ち」の状態を表す。全関数が新しい state を返す純関数。
クリック中の多重フリップを防ぐ
if (card.flipped || card.matched) return state;
このガードがあれば「判定中にクリックしても何も起きない」が自然に実現できる。個別にタイマー状態を持つより安全。
CSS 3D フリップ
.card-inner {
transform-style: preserve-3d;
transition: transform 0.4s;
}
.card.flipped .card-inner {
transform: rotateY(180deg);
}
.card-front, .card-back {
backface-visibility: hidden;
}
.card-back { transform: rotateY(180deg); }
backface-visibility: hidden が鍵。これがないと裏面と表面が重なって見える。正面を向いている方だけ描画される。
Fisher-Yates シャッフル
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
全順列について一様。sort(() => Math.random() - 0.5) は偏るので絶対使わない。
シリーズ
100+ 公開ポートフォリオ シリーズの #99 — 目標まであと 1 つ。
- 📦 リポジトリ: https://github.com/sen-ltd/memory-game
- 🌐 デモ: https://sen.ltd/portfolio/memory-game/
- 🏢 会社: https://sen.ltd/
