
【ゲーム】JavaScript:34 2048ゲーム
2048(トゥエンティフォーティ)は、2014年3月にイタリア人のガブリエレ・チルリ氏によって公開されたシンプルかつ中毒性の高いパズルゲームです。4×4 のグリッド上で同じ数字のタイルをスライド&マージし、最終的に「2048」のタイルを作成することを目指します。
遊び方と操作方法
- 矢印キー(↑↓←→)でグリッド上のすべてのタイルを同じ方向にスライド
- タイル移動後、空きマスに「2」または「4」の新規タイルがランダムに出現
- 同じ数字のタイル同士が接触すると合体し、タイルの数字が2倍になる
ルール
- 4×4 のグリッドでプレイ
- 空きマスに新しいタイル(2または4)が必ず1つ出現
- 同じ数字のタイル同士がスライド中に重なると合体
- 合体したタイルは合計値(例:2+2→4)となり、その分スコアが加算される
- すべてのマスが埋まっていて、かつどの方向にも移動・合体できなくなるとゲームオーバー
- 「2048」タイルの誕生でクリア(続けて上位タイルを作ることも可能)
- 理論上は「131072」までのタイルを作成可能
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
34 2048ゲーム
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
2048game_title.png | 2048game_bg.png |
---|---|
![]() | ![]() |
ゲーム画面イメージ

プログラム全文(2048game.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>🧩 2048ゲーム 🧩</title>
<style>
/* 全体リセット */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
body {
/* 背景画像を全面に敷く */
background: url('2048game_bg.png') no-repeat center center fixed;
background-size: cover;
font-family: 'Yu Gothic','Segoe UI',sans-serif;
position: relative;
overflow: hidden;
}
/* 画面(タイトル/ゲーム)共通 */
#title-screen, #game-screen {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* タイトル画面 */
#rules {
background: rgba(255,255,255,0.8);
padding: 20px;
border-radius: 8px;
color: #333;
text-align: left;
width: 60%;
max-width: 600px;
margin-bottom: 20px;
line-height: 1.5;
}
#title-img {
max-width: 80%;
margin-bottom: 20px;
}
#start-btn {
padding: 10px 20px;
font-size: 1em;
border: none;
border-radius: 4px;
cursor: pointer;
background: #8f7a66;
color: #f9f6f2;
}
/* ゲーム画面 */
#back-btn, #back-btn2 {
position: absolute;
top: 10px;
right: 10px;
padding: 8px 16px;
font-size: 0.9em;
border: none;
border-radius: 4px;
cursor: pointer;
background: #8f7a66;
color: #f9f6f2;
}
#back-btn2 {
top: auto;
bottom: 10px;
}
/* タイトルとスコアの読みやすさを強化 */
#game-screen h1 {
margin: 10px 0;
color: #776e65;
background: rgba(255,255,255,0.8);
padding: 8px 12px;
border-radius: 4px;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
#score {
margin-bottom: 10px;
font-size: 1.2em;
color: #776e65;
background: rgba(255,255,255,0.8);
padding: 6px 10px;
border-radius: 4px;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
/* グリッドコンテナ */
#grid-container {
display: grid;
grid-template-columns: repeat(4, 100px);
grid-template-rows: repeat(4, 100px);
gap: 10px;
background: #bbada0;
padding: 10px;
border-radius: 6px;
}
/* 各タイル */
.tile {
width: 100px;
height: 100px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2em;
font-weight: bold;
color: #776e65;
background: rgba(238, 228, 218, 0.35);
transition: background 0.2s;
}
.tile-2 { background: #eee4da; }
.tile-4 { background: #ede0c8; }
.tile-8 { background: #f2b179; color: #f9f6f2; }
.tile-16 { background: #f59563; color: #f9f6f2; }
.tile-32 { background: #f67c5f; color: #f9f6f2; }
.tile-64 { background: #f65e3b; color: #f9f6f2; }
.tile-128 { background: #edcf72; color: #f9f6f2; }
.tile-256 { background: #edcc61; color: #f9f6f2; }
.tile-512 { background: #edc850; color: #f9f6f2; }
.tile-1024 { background: #edc53f; color: #f9f6f2; }
.tile-2048 { background: #edc22e; color: #f9f6f2; }
/* ゲームオーバー表示 */
#game-over {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.7);
padding: 30px;
border-radius: 8px;
color: #f9f6f2;
text-align: center;
display: none;
}
#game-over p {
margin-bottom: 20px;
font-size: 1.5em;
}
#game-over button {
padding: 8px 16px;
font-size: 1em;
border: none;
border-radius: 4px;
cursor: pointer;
background: #8f7a66;
color: #f9f6f2;
}
</style>
</head>
<body>
<!-- ========== タイトル画面 ========== -->
<div id="title-screen">
<!-- タイトル画像 -->
<img id="title-img" src="2048game_title.png" alt="2048 タイトル">
<!-- ルール説明(左寄せ) -->
<div id="rules">
<p>🕹️ <strong>2048ゲーム の遊び方</strong></p>
<ul>
<li>矢印キーで全てのタイルを上下左右にスライドさせます。</li>
<li>移動後、空いているマスに「2」または「4」のタイルが出現します。</li>
<li>同じ数字のタイル同士がぶつかると合体し、数字が2倍になります。</li>
<li>全てのマスが埋まる前に「2048」のタイルを作るとクリア!</li>
<li>作れる最大のタイルは「131072」です。</li>
</ul>
</div>
<!-- スタートボタン -->
<button id="start-btn">▶️ スタート</button>
</div>
<!-- ========== ゲーム画面 ========== -->
<div id="game-screen" style="display:none;">
<!-- タイトル画面に戻るボタン -->
<button id="back-btn">🏠 タイトル画面に戻る</button>
<!-- ゲームタイトル -->
<h1>🧩 2048ゲーム 🧩</h1>
<!-- スコア表示 -->
<div id="score">スコア: 0</div>
<!-- グリッド -->
<div id="grid-container"></div>
<!-- ゲームオーバーオーバーレイ -->
<div id="game-over">
<p>😢 ゲームオーバー!😢</p>
<button id="back-btn2">🏠 タイトル画面に戻る</button>
</div>
</div>
<script>
// ====== 画面切り替え ======
const titleScreen = document.getElementById('title-screen');
const gameScreen = document.getElementById('game-screen');
const startBtn = document.getElementById('start-btn');
const backBtn = document.getElementById('back-btn');
const backBtn2 = document.getElementById('back-btn2');
// タイトル画面を表示
function showTitleScreen() {
gameActive = false; // ゲーム停止フラグ
titleScreen.style.display = 'flex';
gameScreen .style.display = 'none';
// 初期化しておく
init();
hideGameOver();
}
// ゲーム画面を表示
function showGameScreen() {
titleScreen.style.display = 'none';
gameScreen .style.display = 'flex';
gameActive = true; // ゲーム開始フラグ
updateView();
}
// ボタンイベント
startBtn.addEventListener('click', showGameScreen);
backBtn .addEventListener('click', showTitleScreen);
backBtn2.addEventListener('click', showTitleScreen);
// ====== 2048ゲーム本体 ======
// グリッドサイズ
const SIZE = 4; // 4×4
let grid = []; // ゲーム盤配列
let score = 0; // スコア
let gameActive = false; // ゲーム中かどうか
// DOM要素
const container = document.getElementById('grid-container');
const scoreEl = document.getElementById('score');
const gameOverEl= document.getElementById('game-over');
// キー入力管理
const keys = {};
window.addEventListener('keydown', e => {
keys[e.key] = true;
if (!gameActive) return; // ゲーム中のみ反応
let moved = false;
switch (e.key) {
case 'ArrowUp': moved = moveUp(); break;
case 'ArrowDown': moved = moveDown(); break;
case 'ArrowLeft': moved = moveLeft(); break;
case 'ArrowRight': moved = moveRight(); break;
}
if (moved) {
addRandomTile();
updateView();
if (isGameOver()) {
showGameOver();
}
}
});
window.addEventListener('keyup', e => { delete keys[e.key]; });
// 初期化:グリッド配列とタイル要素を生成
function init() {
score = 0;
grid = Array.from({length: SIZE}, () => Array(SIZE).fill(0));
container.innerHTML = '';
for (let y = 0; y < SIZE; y++) {
for (let x = 0; x < SIZE; x++) {
const tile = document.createElement('div');
tile.classList.add('tile');
container.appendChild(tile);
}
}
// 最初の2枚を配置
addRandomTile();
addRandomTile();
updateView();
}
// ランダムに「2」か「4」のタイルを空きマスに追加
function addRandomTile() {
const empty = [];
for (let y = 0; y < SIZE; y++) {
for (let x = 0; x < SIZE; x++) {
if (grid[y][x] === 0) empty.push({x, y});
}
}
if (empty.length === 0) return;
const {x, y} = empty[Math.floor(Math.random() * empty.length)];
grid[y][x] = (Math.random() < 0.9) ? 2 : 4;
}
// 画面更新:タイルとスコアを再描画
function updateView() {
const tiles = container.children;
let idx = 0;
for (let y = 0; y < SIZE; y++) {
for (let x = 0; x < SIZE; x++) {
const val = grid[y][x];
const tile= tiles[idx++];
tile.textContent = val === 0 ? '' : val;
tile.className = 'tile';
if (val > 0) tile.classList.add(`tile-${val}`);
}
}
scoreEl.textContent = `スコア: ${score}`;
}
// スライド&マージ共通処理
function slideAndMerge(arr) {
let row = arr.filter(v => v !== 0);
for (let i = 0; i < row.length - 1; i++) {
if (row[i] === row[i+1]) {
row[i] *= 2;
score += row[i];
row[i+1] = 0;
}
}
row = row.filter(v => v !== 0);
while (row.length < SIZE) row.push(0);
return row;
}
// 各方向の移動処理
function moveLeft() {
let moved = false;
for (let y = 0; y < SIZE; y++) {
const old = [...grid[y]];
const merged = slideAndMerge(old);
grid[y] = merged;
if (merged.some((v,i) => v !== old[i])) moved = true;
}
return moved;
}
function moveRight() {
let moved = false;
for (let y = 0; y < SIZE; y++) {
const old = [...grid[y]].reverse();
const merged = slideAndMerge(old).reverse();
if (merged.some((v,i) => v !== grid[y][i])) moved = true;
grid[y] = merged;
}
return moved;
}
function moveUp() {
let moved = false;
for (let x = 0; x < SIZE; x++) {
const col = [];
for (let y = 0; y < SIZE; y++) col.push(grid[y][x]);
const merged = slideAndMerge(col);
for (let y = 0; y < SIZE; y++) {
if (grid[y][x] !== merged[y]) moved = true;
grid[y][x] = merged[y];
}
}
return moved;
}
function moveDown() {
let moved = false;
for (let x = 0; x < SIZE; x++) {
const col = [];
for (let y = 0; y < SIZE; y++) col.push(grid[y][x]);
const merged = slideAndMerge(col.reverse()).reverse();
for (let y = 0; y < SIZE; y++) {
if (grid[y][x] !== merged[y]) moved = true;
grid[y][x] = merged[y];
}
}
return moved;
}
// ゲームオーバー判定
function isGameOver() {
// 空きセルがあれば継続
for (let y = 0; y < SIZE; y++)
for (let x = 0; x < SIZE; x++)
if (grid[y][x] === 0) return false;
// 隣接セルに同じ値があれば継続
for (let y = 0; y < SIZE; y++) {
for (let x = 0; x < SIZE; x++) {
const v = grid[y][x];
if ((x < SIZE-1 && grid[y][x+1] === v) ||
(y < SIZE-1 && grid[y+1][x] === v)) {
return false;
}
}
}
return true;
}
// ゲームオーバー時の表示
function showGameOver() {
gameActive = false;
gameOverEl.style.display = 'block';
}
function hideGameOver() {
gameOverEl.style.display = 'none';
}
// 初期表示はタイトル画面
showTitleScreen();
</script>
</body>
</html>
アルゴリズムの流れ
手順 | 内容 | 関数/処理 |
---|---|---|
1. 初期化 | 4×4 グリッドをゼロ埋め、最初のタイルを 2 枚出現 | init() → addRandomTile() ×2 |
2. 描画 | DOM に tile 要素を配置し、数字と色を更新 | updateView() |
3. ユーザ操作 | 矢印キーで方向を検出し、スライド&マージ処理を実行 | moveUp() / moveDown() / moveLeft() / moveRight() |
4. 合体処理 | 同じ数字の連続するタイルを合体しスコアを加算 | slideAndMerge() |
5. 新規生成 | 移動があった場合、空きマスにランダムで「2」または「4」を追加 | addRandomTile() |
6. ゲームオーバー/継続判定 | 空きマスなし&合体可能位置なしなら終了、そうでなければ継続 | isGameOver() → showGameOver() |
関数の詳細
関数名 | 役割 |
---|---|
init() | グリッド配列を初期化し、DOM 上に tile 要素を 16 個生成 |
addRandomTile() | 空きマスをリスト化し、1 つを選んで「2」か「4」を配置 |
updateView() | 各セル要素に数値と背景色クラスを反映し、スコア表示を更新 |
slideAndMerge(arr) | 1 行/1 列を左詰め→同値合体→再左詰めし、新配列を返す |
moveLeft() /moveRight() /moveUp() /moveDown() | 各方向のスライド&マージをグリッドに適用し、動きがあれば true を返す |
isGameOver() | 空きセルの有無と隣接セルの同値有無をチェックし、終了判定を返す |
showGameOver() | ゲームを停止し、オーバーレイで「ゲームオーバー」を表示 |
改造のポイント
- サイズ変更・カスタムグリッド
SIZE
を変更し、5×5 や 6×6 のバリエーションを追加可能。大きいグリッドは難易度アップにつながります。 - タイルアニメーション
合体時のエフェクトやスライドの動きに CSS アニメーションを追加し、視覚的に楽しく演出しましょう。 - UNDO(取り消し)機能
各操作前のグリッドとスコアをスタックに保存し、UNDO ボタンで一手戻せるようにすると上級者向けに便利です。 - 目標値変更
2048 以外に「4096」「8192」を目標としたモードを実装し、さらなる高得点を目指せるようにできます。 - スコアランキング
ローカルストレージにハイスコアを保存し、プレイごとの最高記録を表示するとリプレイ性が高まります。
最後に
2048 は「シンプルなルール × 奥深い戦略」が魅力です。プログラム構造も分かりやすく、カスタマイズや拡張にぴったりな教材になります。ぜひ上記のポイントをもとに、ご自身だけのオリジナルバージョンにチャレンジしてみてください!