Wordleとは?ゲームのルールと魅力を理解しよう

Wordleの基本ルールと遊び方
Wordleは2021年に大ブームを起こした5文字の英単語当てゲームです。プレイヤーは6回の推測チャンスで正解の単語を当てることが目標となります。
| 色 | 意味 | 例 |
|---|---|---|
| 緑(Green) | 正しい文字が正しい位置 | 答えが「CRANE」で「C」を1文字目に入力 |
| 黄(Yellow) | 正しい文字だが位置が違う | 答えが「CRANE」で「R」を1文字目に入力 |
| 灰(Gray) | 答えに含まれない文字 | 答えが「CRANE」で「B」を入力 |
なぜWordleが学習題材として優秀なのか
Wordleは初心者プログラマーにとって理想的な学習プロジェクトです。以下の理由で多くのプログラミングスクールでも採用されています:
- ルールがシンプルで理解しやすい
- HTML/CSS/JavaScriptの基本を全て学べる
- 段階的に機能を追加できる
- ポートフォリオ作品として見栄えが良い
- ユーザーインターフェースの設計経験が積める
必要な技術スタックと学習できるスキル
| 技術 | 使用場面 | 学習できるスキル |
|---|---|---|
| HTML | ゲーム画面の構造作成 | セマンティックマークアップ、アクセシビリティ |
| CSS | 見た目とレイアウト | Grid/Flexbox、レスポンシブデザイン、アニメーション |
| JavaScript | ゲームロジック | DOM操作、イベント処理、データ構造、アルゴリズム |
| Local Storage | 進捗保存 | ブラウザストレージAPI、データ永続化 |
開発環境の準備とプロジェクト構成
必要なファイル構成とディレクトリ設計
Wordleゲームは単純な構成で開始できます。以下のファイル構成を推奨します:
wordle-game/
├── index.html # メインのHTMLファイル
├── style.css # スタイルシート
├── script.js # JavaScript(ゲームロジック)
├── words.js # 単語リスト
└── README.md # プロジェクト説明
開発に必要なツールとエディタ設定
効率的な開発のために以下のツールを準備しましょう:
| ツール | 用途 | 推奨設定 |
|---|---|---|
| Visual Studio Code | コードエディタ | Live Server拡張機能をインストール |
| Chrome DevTools | デバッグとテスト | Consoleタブを常時開いてエラー確認 |
| Git | バージョン管理 | コミットは機能単位で小分けに |
| GitHub Pages | 無料ホスティング | 完成後の公開用 |
HTMLの基本構造とメタ情報の設定
まずは基本となるHTMLファイルを作成します。レスポンシブ対応とSEO対策も考慮した構造にしましょう:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="JavaScript で作成した Wordle ゲーム">
<title>Wordle Game - 5文字単語当てゲーム</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Wordle</h1>
<p>6回のチャンスで5文字の単語を当てよう!</p>
</header>
<main>
<div id="game-container">
<!-- ゲーム要素はJavaScriptで動的に生成 -->
</div>
</main>
<script src="words.js"></script>
<script src="script.js"></script>
</body>
</html>
ゲーム画面のUI設計とHTML構造
ワイヤーフレームから始める画面設計
Wordleの画面は主に3つの要素で構成されています。ユーザビリティを考慮した配置が重要です:
- ゲームボード:6行5列のマス目(推測入力エリア)
- 仮想キーボード:画面タップでの文字入力
- ステータス表示:ゲーム結果や操作ガイド
セマンティックなHTML構造の実装
アクセシビリティと保守性を考慮したHTML構造を作成します。適切な要素選択とARIA属性の使用がポイントです:
<main id="game-container">
<!-- ゲームボード -->
<section id="game-board" aria-label="単語推測ボード">
<!-- JavaScript で動的に生成される 6x5 のグリッド -->
</section>
<!-- メッセージ表示エリア -->
<div id="message" role="status" aria-live="polite"></div>
<!-- 仮想キーボード -->
<section id="keyboard" aria-label="仮想キーボード">
<div class="keyboard-row">
<button class="key" data-key="Q">Q</button>
<button class="key" data-key="W">W</button>
<!-- 他のキー... -->
</div>
<div class="keyboard-row">
<button class="key special" data-key="ENTER">ENTER</button>
<button class="key special" data-key="BACKSPACE">⌫</button>
</div>
</section>
</main>
アクセシビリティを考慮したマークアップ
| 要素 | アクセシビリティ対応 | 実装方法 |
|---|---|---|
| ゲームボード | スクリーンリーダー対応 | aria-label、role属性の使用 |
| キーボード | フォーカス管理 | tabindex、keyboard navigationの実装 |
| 状態変化 | 動的な情報通知 | aria-live=”polite”の活用 |
| 色による表現 | 色覚異常への配慮 | パターンやアイコンの併用 |
CSSでゲームらしい見た目を作成
Wordleらしいデザインの実装方法
オリジナルのWordleに近い見た目を再現しつつ、独自性も加えた設計にします。重要なのは視認性とユーザビリティです:
/* 基本設定とCSS変数 */
:root {
--color-correct: #6aaa64; /* 緑:正解位置 */
--color-present: #c9b458; /* 黄:文字は正解だが位置違い */
--color-absent: #787c7e; /* 灰:不正解文字 */
--color-empty: #ffffff; /* 白:未入力 */
--color-border: #d3d6da; /* ボーダー色 */
--font-family: 'Arial', sans-serif;
}
body {
margin: 0;
padding: 20px;
font-family: var(--font-family);
background-color: #f7f7f7;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
header {
text-align: center;
margin-bottom: 2rem;
}
header h1 {
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin: 0;
letter-spacing: 0.05em;
}
CSS Grid・Flexboxを活用したレイアウト
ゲームボードは CSS Grid を使用して実装します。レスポンシブ対応も考慮した設計にしましょう:
/* ゲームボードのグリッドレイアウト */
#game-board {
display: grid;
grid-template-rows: repeat(6, 1fr);
gap: 5px;
margin-bottom: 2rem;
padding: 10px;
}
.board-row {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 5px;
}
.tile {
width: 62px;
height: 62px;
border: 2px solid var(--color-border);
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: bold;
color: #333;
background-color: var(--color-empty);
text-transform: uppercase;
transition: all 0.2s ease;
}
/* 状態別のタイルスタイル */
.tile.correct {
background-color: var(--color-correct);
border-color: var(--color-correct);
color: white;
}
.tile.present {
background-color: var(--color-present);
border-color: var(--color-present);
color: white;
}
.tile.absent {
background-color: var(--color-absent);
border-color: var(--color-absent);
color: white;
}
レスポンシブ対応とモバイルファースト
モバイルデバイスでも快適にプレイできるよう、レスポンシブデザインを実装します:
/* キーボードのレスポンシブデザイン */
#keyboard {
max-width: 500px;
width: 100%;
}
.keyboard-row {
display: flex;
justify-content: center;
margin-bottom: 8px;
gap: 6px;
}
.key {
min-width: 44px; /* タッチしやすい最小サイズ */
height: 58px;
border: none;
border-radius: 4px;
background-color: #d3d6da;
color: #333;
font-weight: bold;
cursor: pointer;
transition: background-color 0.1s ease;
font-size: 0.9rem;
}
.key:hover {
background-color: #bbb;
}
.key.special {
min-width: 65px;
font-size: 0.8rem;
}
/* タブレット・モバイル対応 */
@media (max-width: 768px) {
.tile {
width: 50px;
height: 50px;
font-size: 1.5rem;
}
.key {
min-width: 36px;
height: 48px;
font-size: 0.8rem;
}
header h1 {
font-size: 2rem;
}
}
@media (max-width: 480px) {
.tile {
width: 45px;
height: 45px;
font-size: 1.2rem;
}
.key {
min-width: 32px;
height: 42px;
font-size: 0.7rem;
}
}
JavaScriptでゲームロジックを実装
ゲーム状態管理とデータ構造の設計
効率的なゲーム状態管理のため、適切なデータ構造を設計しましょう。オブジェクト指向の考え方を取り入れます:
// ゲーム状態を管理するクラス
class WordleGame {
constructor() {
this.targetWord = ''; // 正解の単語
this.currentRow = 0; // 現在の行(0-5)
this.currentCol = 0; // 現在の列(0-4)
this.gameState = 'playing'; // playing, won, lost
this.guesses = []; // 推測の履歴
this.board = []; // ゲームボードの状態
this.keyboardState = {}; // キーボードの色状態
this.initializeBoard();
this.setTargetWord();
}
// ボードの初期化
initializeBoard() {
this.board = Array(6).fill(null).map(() =>
Array(5).fill({ letter: '', state: 'empty' })
);
}
// ランダムな単語を選択
setTargetWord() {
const words = WORD_LIST; // words.js で定義
this.targetWord = words[Math.floor(Math.random() * words.length)].toUpperCase();
console.log('Target word:', this.targetWord); // 開発用
}
// 文字入力の処理
handleKeyPress(key) {
if (this.gameState !== 'playing') return;
if (key === 'ENTER') {
this.submitGuess();
} else if (key === 'BACKSPACE') {
this.deleteLetter();
} else if (key.length === 1 && /[A-Z]/.test(key)) {
this.addLetter(key);
}
}
}
単語判定システムの構築
Wordleの核となる単語判定ロジックを実装します。正確な判定のため、文字の重複も考慮する必要があります:
// 推測単語の判定ロジック
evaluateGuess(guess) {
const result = Array(5).fill('absent');
const targetLetters = this.targetWord.split('');
const guessLetters = guess.split('');
// 第1段階:正確な位置の文字をチェック(緑)
for (let i = 0; i < 5; i++) {
if (guessLetters[i] === targetLetters[i]) {
result[i] = 'correct';
targetLetters[i] = null; // マーク済み
guessLetters[i] = null; // マーク済み
}
}
// 第2段階:存在するが位置が違う文字をチェック(黄)
for (let i = 0; i < 5; i++) {
if (guessLetters[i] !== null) {
const targetIndex = targetLetters.indexOf(guessLetters[i]);
if (targetIndex !== -1) {
result[i] = 'present';
targetLetters[targetIndex] = null; // 使用済みにマーク
}
}
}
return result;
}
// 推測の送信処理
submitGuess() {
if (this.currentCol !== 5) {
this.showMessage('5文字入力してください');
return;
}
const currentGuess = this.getCurrentGuess();
// 辞書チェック(オプション)
if (!this.isValidWord(currentGuess)) {
this.showMessage('辞書にない単語です');
this.shakeRow(this.currentRow);
return;
}
// 判定実行
const evaluation = this.evaluateGuess(currentGuess);
this.updateBoard(this.currentRow, currentGuess, evaluation);
this.updateKeyboard(currentGuess, evaluation);
// ゲーム終了判定
if (currentGuess === this.targetWord) {
this.gameState = 'won';
this.showMessage('正解です!');
this.celebrateWin();
} else if (this.currentRow === 5) {
this.gameState = 'lost';
this.showMessage(`正解は ${this.targetWord} でした`);
} else {
this.currentRow++;
this.currentCol = 0;
}
}
キーボード入力とバリデーション処理
物理キーボードと仮想キーボードの両方に対応した入力処理を実装します:
// イベントリスナーの設定
setupEventListeners() {
// 物理キーボード対応
document.addEventListener('keydown', (e) => {
const key = e.key.toUpperCase();
if (key === 'ENTER' || key === 'BACKSPACE') {
this.handleKeyPress(key);
} else if (/^[A-Z]$/.test(key)) {
this.handleKeyPress(key);
}
e.preventDefault();
});
// 仮想キーボード対応
document.querySelectorAll('.key').forEach(button => {
button.addEventListener('click', (e) => {
const key = e.target.dataset.key;
this.handleKeyPress(key);
});
});
}
// 文字の追加
addLetter(letter) {
if (this.currentCol < 5) {
this.board[this.currentRow][this.currentCol] = {
letter: letter,
state: 'filled'
};
this.updateTile(this.currentRow, this.currentCol, letter);
this.currentCol++;
}
}
// 文字の削除
deleteLetter() {
if (this.currentCol > 0) {
this.currentCol--;
this.board[this.currentRow][this.currentCol] = {
letter: '',
state: 'empty'
};
this.updateTile(this.currentRow, this.currentCol, '');
}
}
// 現在の推測を取得
getCurrentGuess() {
return this.board[this.currentRow]
.map(cell => cell.letter)
.join('');
}
ユーザビリティとアニメーション効果
スムーズなアニメーション実装
ユーザー体験を向上させるため、適切なアニメーション効果を追加します。タイルの反転アニメーションが特に重要です:
/* タイル反転アニメーション */
@keyframes flipTile {
0% {
transform: rotateX(0);
}
50% {
transform: rotateX(90deg);
}
100% {
transform: rotateX(0);
}
}
.tile.flip {
animation: flipTile 0.6s ease-in-out;
}
/* 文字入力時のポップアニメーション */
@keyframes popTile {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.tile.pop {
animation: popTile 0.1s ease;
}
/* 不正解時の揺れアニメーション */
@keyframes shakeRow {
0%, 100% {
transform: translateX(0);
}
20%, 60% {
transform: translateX(-10px);
}
40%, 80% {
transform: translateX(10px);
}
}
.board-row.shake {
animation: shakeRow 0.5s ease;
}
フィードバック機能とエラーハンドリング
ユーザーの操作に対する適切なフィードバックを実装します:
// メッセージ表示システム
showMessage(text, duration = 2000) {
const messageElement = document.getElementById('message');
messageElement.textContent = text;
messageElement.classList.add('show');
setTimeout(() => {
messageElement.classList.remove('show');
}, duration);
}
// 行の揺れアニメーション
shakeRow(rowIndex) {
const row = document.querySelector(`[data-row="${rowIndex}"]`);
row.classList.add('shake');
setTimeout(() => {
row.classList.remove('shake');
}, 500);
}
// タイルの反転アニメーション(判定結果表示)
animateRowReveal(rowIndex, evaluation) {
const tiles = document.querySelectorAll(`[data-row="${rowIndex}"] .tile`);
tiles.forEach((tile, index) => {
setTimeout(() => {
tile.classList.add('flip');
// アニメーション中間で色変更
setTimeout(() => {
tile.classList.remove('filled');
tile.classList.add(evaluation[index]);
}, 300);
// アニメーション終了後にクラス削除
setTimeout(() => {
tile.classList.remove('flip');
}, 600);
}, index * 100); // 順次実行
});
}
// 勝利時のお祝いアニメーション
celebrateWin() {
const tiles = document.querySelectorAll(`[data-row="${this.currentRow}"] .tile`);
tiles.forEach((tile, index) => {
setTimeout(() => {
tile.classList.add('celebrate');
}, index * 100);
});
}
ローカルストレージを使った進捗保存
ユーザーの進捗を保存し、ページをリロードしても継続できる機能を実装します:
// ゲーム状態の保存
saveGameState() {
const gameData = {
targetWord: this.targetWord,
currentRow: this.currentRow,
currentCol: this.currentCol,
gameState: this.gameState,
board: this.board,
keyboardState: this.keyboardState,
timestamp: Date.now()
};
localStorage.setItem('wordleGameState', JSON.stringify(gameData));
}
// ゲーム状態の読み込み
loadGameState() {
const savedData = localStorage.getItem('wordleGameState');
if (!savedData) return false;
try {
const gameData = JSON.parse(savedData);
// 24時間以内のデータのみ復元
const oneDay = 24 * 60 * 60 * 1000;
if (Date.now() - gameData.timestamp > oneDay) {
localStorage.removeItem('wordleGameState');
return false;
}
// 状態復元
this.targetWord = gameData.targetWord;
this.currentRow = gameData.currentRow;
this.currentCol = gameData.currentCol;
this.gameState = gameData.gameState;
this.board = gameData.board;
this.keyboardState = gameData.keyboardState;
this.restoreUI();
return true;
} catch (error) {
console.error('Failed to load game state:', error);
localStorage.removeItem('wordleGameState');
return false;
}
}
// 統計データの管理
updateStats(won) {
const stats = this.getStats();
stats.gamesPlayed++;
if (won) {
stats.gamesWon++;
stats.currentStreak++;
stats.maxStreak = Math.max(stats.maxStreak, stats.currentStreak);
stats.guessDistribution[this.currentRow]++;
} else {
stats.currentStreak = 0;
}
localStorage.setItem('wordleStats', JSON.stringify(stats));
}
パフォーマンス最適化とデプロイ準備
コードの最適化とベストプラクティス
パフォーマンスと保守性を向上させるための最適化を行います:
| 最適化項目 | 実装方法 | 効果 |
|---|---|---|
| DOM操作の最小化 | DocumentFragmentの使用、バッチ更新 | レンダリング性能向上 |
| イベントデリゲーション | 親要素での一括イベント処理 | メモリ使用量削減 |
| debounce/throttle | 頻繁なイベント処理の制御 | CPU負荷軽減 |
| CSS transition | GPUアクセラレーション活用 | アニメーション性能向上 |
// 効率的なDOM操作の例
updateBoard(rowIndex, guess, evaluation) {
const fragment = document.createDocumentFragment();
const tiles = document.querySelectorAll(`[data-row="${rowIndex}"] .tile`);
// バッチでスタイル更新
requestAnimationFrame(() => {
tiles.forEach((tile, index) => {
tile.textContent = guess[index];
tile.dataset.state = evaluation[index];
});
});
}
// メモリリークを防ぐイベントリスナー管理
class EventManager {
constructor() {
this.listeners = [];
}
addEventListener(element, event, handler) {
element.addEventListener(event, handler);
this.listeners.push({ element, event, handler });
}
removeAllListeners() {
this.listeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler);
});
this.listeners = [];
}
}
デバッグ手法とテスト方法
効率的なデバッグとテストのためのアプローチを実装します:
// デバッグ用のヘルパー関数
class DebugHelper {
static enableDebugMode() {
window.wordleDebug = {
revealAnswer: () => console.log('Answer:', game.targetWord),
setAnswer: (word) => { game.targetWord = word.toUpperCase(); },
skipToRow: (row) => { game.currentRow = row; },
showGameState: () => console.log('Game State:', game)
};
console.log('Debug mode enabled. Use window.wordleDebug');
}
static logGameState(game) {
console.group('Game State');
console.log('Target:', game.targetWord);
console.log('Current Row:', game.currentRow);
console.log('Current Col:', game.currentCol);
console.log('Game Status:', game.gameState);
console.groupEnd();
}
}
// 簡単なユニットテスト
function runTests() {
const game = new WordleGame();
// 判定ロジックのテスト
const testCases = [
{ target: 'CRANE', guess: 'CRANE', expected: ['correct', 'correct', 'correct', 'correct', 'correct'] },
{ target: 'CRANE', guess: 'CLEAR', expected: ['correct', 'absent', 'present', 'absent', 'correct'] },
{ target: 'SPEED', guess: 'ERASE', expected: ['present', 'absent', 'absent', 'absent', 'present'] }
];
testCases.forEach(({ target, guess, expected }, index) => {
game.targetWord = target;
const result = game.evaluateGuess(guess);
const passed = JSON.stringify(result) === JSON.stringify(expected);
console.log(`Test ${index + 1}: ${passed ? 'PASS' : 'FAIL'}`, { target, guess, result, expected });
});
}
無料ホスティングサービスでの公開手順
完成したゲームを無料で公開する方法を解説します。GitHub Pages が最も簡単でおすすめです:
| ステップ | 作業内容 | 注意点 |
|---|---|---|
| 1. リポジトリ作成 | GitHubで新しいリポジトリを作成 | Public設定にする |
| 2. ファイルアップロード | 全てのファイルをpush | index.htmlが必須 |
| 3. Pages設定 | Settings > Pages で main branch を選択 | 数分で反映される |
| 4. 動作確認 | 公開URLでテストプレイ | モバイルでも確認 |
# Git での公開手順
git init
git add .
git commit -m "Initial commit: Wordle game implementation"
git branch -M main
git remote add origin https://github.com/yourusername/wordle-game.git
git push -u origin main
# GitHub Pages URL例
# https://yourusername.github.io/wordle-game/
完成後のチェックリスト
| カテゴリ | チェック項目 | 確認方法 |
|---|---|---|
| 機能性 | 正しい単語判定ができる | 様々なパターンでテストプレイ |
| ユーザビリティ | 直感的な操作が可能 | 初心者にプレイしてもらう |
| レスポンシブ | モバイルで正常動作 | 実機での操作確認 |
| アクセシビリティ | キーボード操作対応 | Tabキーのみでプレイ |
| パフォーマンス | スムーズなアニメーション | 低性能デバイスでの確認 |
| データ永続化 | 進捗が保存される | ページリロード後の状態確認 |
Wordleゲームの実装を通じて、現代的なウェブ開発の基礎を学ぶことができます。HTML/CSS/JavaScriptの連携、ユーザビリティの考慮、パフォーマンス最適化など、実際のプロジェクトで必要となるスキルを総合的に身につけられる優秀な学習題材です。
完成したゲームは、あなたのポートフォリオの重要な作品となるでしょう。ユーザーフィードバックを集めて、さらなる改良を続けてください。
