
【ゲーム】JavaScript:29 マスターマインドゲーム
「🎯 マスターマインドゲーム 🎯」は、コンピューターが4色のピンを隠し持ち、その配列をプレイヤーが推理するクラシックなボードゲームです。プレイヤーは色の組み合わせをスロットにセットし、「ヒット(赤ピン)」と「ブロー(白ピン)」のヒントをもとに正解のコードを当てます。
遊び方・操作方法
- タイトル画面の「スタート 🚀」ボタンをクリック
- スロット(4つの丸枠)をクリックして選択
- 下のカラーパレットから色を選ぶと、選択中のスロットに色が入る
- 4つすべて埋めたら「判定 ✔️」ボタンをクリック
- ヒット数(色と位置が一致)とブロー数(色のみ一致)が赤・白の小さなピンで表示される
- ヒントをもとにスロットを塗り直し、正解を目指す
- 赤ヒットが4になるまで繰り返し
- クリア後に試行回数と評価コメントが表示される
ルール
- コード長:4ピン
- 使用色:6色(赤・緑・青・黄・紫・オレンジ)
- 試行回数制限:なし(何回でも挑戦可能)
- ヒント:
・赤ピン…位置も色も正解
・白ピン…色は正解だが位置が異なる - 勝利条件:ヒットが4になるとクリア
- 終了後:試行回数に応じて「天才ですね!」などの評価コメント
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
29 マスターマインドゲーム
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
mastermind_title.png | mastermind_bg.png |
---|---|
![]() | ![]() |
ゲーム画面イメージ

プログラム全文(mastermind.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>🎯 マスターマインドゲーム 🎯</title>
<style>
/* ===== 全体背景設定 ===== */
body {
margin: 0; /* 余白リセット */
padding: 0;
font-family: 'Arial', sans-serif; /* フォント指定 */
/* 背景画像を固定表示 */
background: url('mastermind_bg.png') no-repeat center center fixed;
background-size: cover; /* 背景を全画面に合わせる */
}
/* ===== 中央オーバーレイ共通 ===== */
.overlay {
position: absolute; /* 絶対配置 */
top: 50%; left: 50%; /* 画面中央 */
transform: translate(-50%, -50%); /* 真ん中に調整 */
background-color: rgba(255,255,255,0.9); /* 半透明白 */
padding: 20px; /* 内側余白 */
border-radius: 10px; /* 角丸 */
text-align: center; /* 中央寄せ */
width: 90%; max-width: 600px; /* 幅設定 */
}
/* 非表示クラス */
.hidden { display: none; }
/* タイトル画像サイズ調整 */
.title-image {
display: block;
margin: 0 auto 10px;
max-width: 80%; height: auto;
}
/* 汎用ボタンスタイル */
.btn {
display: inline-block;
padding: 12px 24px;
font-size: 18px;
font-weight: bold;
margin: 10px 5px;
cursor: pointer;
border: 2px solid #28a745;
border-radius: 8px;
background-color: #28a745;
color: #fff;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
transition: background-color 0.2s, transform 0.1s;
}
.btn:hover { background-color: #218838; transform: translateY(-2px); }
.btn:active { transform: translateY(0); box-shadow: 0 2px 4px rgba(0,0,0,0.2); }
/* パレットとスロット */
#palette { display: flex; justify-content: center; gap: 10px; margin: 10px 0; }
.color-btn { width: 40px; height: 40px; border-radius: 50%; cursor: pointer; border: 2px solid #333; }
/* カラーオプション */
.red { background-color: #e74c3c; }
.green { background-color: #2ecc71; }
.blue { background-color: #3498db; }
.yellow { background-color: #f1c40f; }
.purple { background-color: #9b59b6; }
.orange { background-color: #e67e22; }
/* 推測スロット */
#slots { display: flex; justify-content: center; gap: 10px; margin: 10px 0; }
.slot { width: 40px; height: 40px; border: 2px dashed #333; border-radius: 50%; }
.slot.filled { border-style: solid; }
/* 履歴表示 */
#history { margin-top: 15px; max-height: 200px; overflow-y: auto; }
.row { display: flex; align-items: center; justify-content: center; margin-bottom: 8px; }
.guess { display: flex; gap: 5px; margin-right: 15px; }
.peg { width: 15px; height: 15px; display: inline-block; border-radius: 50%; margin: 2px; }
.hit { background-color: #e74c3c; } /* 赤ヒント */
.blow { background-color: #fff; border:1px solid #333; } /* 白ヒント */
/* メッセージ */
.message {
margin-top: 10px;
padding: 8px;
background-color: rgba(0,0,0,0.7);
color: #fff;
border-radius: 5px;
min-height: 1.5em;
}
</style>
</head>
<body>
<!-- タイトル画面 -->
<div id="title-screen" class="overlay">
<img src="mastermind_title.png" alt="マスターマインドタイトル" class="title-image">
<h1>🎯 マスターマインドゲーム 🎯</h1>
<!-- ゲーム説明 -->
<p>色の組み合わせを推理して、4本のピンの配置を当てましょう。</p>
<p>【ヒット】位置も色も正しいピンには赤いヒントピンが立ちます。</p>
<p>【ブロー】色は正しいが位置が違うピンには白いヒントピンが立ちます。</p>
<p>赤いヒントピンが4本立つまで挑戦!</p>
<!-- ゲーム開始ボタン -->
<button id="start-btn" class="btn">スタート 🚀</button>
</div>
<!-- ゲーム画面 -->
<div id="game-screen" class="overlay hidden">
<!-- 試行回数表示 -->
<h2>試行回数: <span id="attempts">0</span></h2>
<!-- スロット領域 -->
<div id="slots"></div>
<!-- カラーパレット -->
<div id="palette"></div>
<!-- クリア&判定ボタン -->
<button id="clear-btn" class="btn">クリア</button>
<button id="guess-btn" class="btn">判定 ✔️</button>
<!-- 履歴表示 -->
<div id="history"></div>
<!-- 状態メッセージ -->
<div id="message" class="message">ピンを選んでスロットを埋めてください</div>
</div>
<!-- 終了画面 -->
<div id="end-screen" class="overlay hidden">
<h2 id="end-title">結果発表</h2>
<p id="eval-message"></p>
<button id="restart-btn" class="btn">タイトルに戻る 🔄</button>
</div>
<script>
// ===== 設定 =====
const COLORS = ['red','green','blue','yellow','purple','orange']; // 選択可能な色リスト
const CODE_LENGTH = 4; // コード長
// ===== ゲーム状態 =====
let secretCode = []; // コンピュータが生成する秘密コード配列
let attempts = 0; // 試行回数カウント
let currentGuess = []; // プレイヤーの現在の予想配列
let selectedSlot = null;// 選択中のスロット番号
// ===== DOM要素 =====
const titleScreen = document.getElementById('title-screen');
const gameScreen = document.getElementById('game-screen');
const endScreen = document.getElementById('end-screen');
const startBtn = document.getElementById('start-btn');
const clearBtn = document.getElementById('clear-btn');
const guessBtn = document.getElementById('guess-btn');
const restartBtn = document.getElementById('restart-btn');
const slotsDiv = document.getElementById('slots');
const paletteDiv = document.getElementById('palette');
const historyDiv = document.getElementById('history');
const messageDiv = document.getElementById('message');
const attemptsSpan = document.getElementById('attempts');
const endTitle = document.getElementById('end-title');
const evalMessage = document.getElementById('eval-message');
/**
* タイトル画面を表示
*/
function showTitle() {
titleScreen.classList.remove('hidden');
gameScreen.classList.add('hidden');
endScreen.classList.add('hidden');
}
/**
* ゲーム開始処理
*/
function startGame() {
// 秘密コード生成
secretCode = generateCode();
// 状態初期化
attempts = 0;
attemptsSpan.textContent = attempts;
historyDiv.innerHTML = '';
messageDiv.textContent = 'ピンを選んでスロットを埋めてください';
// UI初期化
buildSlots();
buildPalette();
// 画面切り替え
titleScreen.classList.add('hidden');
endScreen.classList.add('hidden');
gameScreen.classList.remove('hidden');
}
// DOM読み込み後にイベント登録
document.addEventListener('DOMContentLoaded', () => {
startBtn.addEventListener('click', startGame); // スタート
clearBtn.addEventListener('click', () => {
buildSlots(); messageDiv.textContent = 'クリアしました';
});
guessBtn.addEventListener('click', handleGuess); // 判定
restartBtn.addEventListener('click', showTitle); // リスタート
showTitle(); // 初期表示はタイトル
});
/**
* 秘密コード(ランダム)を生成
*/
function generateCode() {
const arr = [];
for (let i = 0; i < CODE_LENGTH; i++) {
const idx = Math.floor(Math.random() * COLORS.length);
arr.push(COLORS[idx]);
}
console.log('Secret Code:', arr); // デバッグ用ログ
return arr;
}
/**
* ブランクスロットを生成
*/
function buildSlots() {
slotsDiv.innerHTML = '';
currentGuess = [];
selectedSlot = null;
for (let i = 0; i < CODE_LENGTH; i++) {
const slot = document.createElement('div');
slot.className = 'slot';
slot.dataset.index = i;
slot.addEventListener('click', () => selectSlot(i));
slotsDiv.append(slot);
}
}
/**
* カラーパレットを生成
*/
function buildPalette() {
paletteDiv.innerHTML = '';
COLORS.forEach(color => {
const btn = document.createElement('div');
btn.className = `color-btn ${color}`;
btn.dataset.color = color;
btn.addEventListener('click', () => chooseColor(color));
paletteDiv.append(btn);
});
}
/**
* スロットを選択状態にする
*/
function selectSlot(index) {
selectedSlot = index;
// 枠線で選択中を可視化
document.querySelectorAll('.slot').forEach(s => s.style.borderColor = '#333');
document.querySelector(`.slot[data-index='${index}']`).style.borderColor = '#007BFF';
}
/**
* 選択したスロットに色を配置
*/
function chooseColor(color) {
if (selectedSlot === null) {
messageDiv.textContent = 'まずスロットを選択してください';
return;
}
const slot = document.querySelector(`.slot[data-index='${selectedSlot}']`);
slot.style.backgroundColor = color;
slot.classList.add('filled');
currentGuess[selectedSlot] = color;
// 次のスロット選択へ
selectSlot((selectedSlot + 1) % CODE_LENGTH);
}
/**
* プレイヤーの予想を判定
*/
function handleGuess() {
// 未配置チェック
if (currentGuess.includes(undefined)) {
messageDiv.textContent = 'すべてのスロットに色を配置してください';
return;
}
attempts++; // 試行回数加算
attemptsSpan.textContent = attempts;
// ヒット&ブロー判定
let codeCopy = secretCode.slice();
let guessCopy = currentGuess.slice();
let hit = 0, blow = 0;
// ヒット判定(位置&色)
guessCopy.forEach((g,i) => {
if (g === codeCopy[i]) {
hit++;
codeCopy[i] = guessCopy[i] = null; // 二重カウント防止
}
});
// ブロー判定(色のみ)
guessCopy.forEach(g => {
if (g && codeCopy.includes(g)) {
blow++;
codeCopy[codeCopy.indexOf(g)] = null;
}
});
// 判定結果を履歴に表示
const row = document.createElement('div'); row.className = 'row';
const guessDiv = document.createElement('div'); guessDiv.className = 'guess';
currentGuess.forEach(c => {
const d = document.createElement('div');
d.className = `color-btn ${c}`;
d.style.width = d.style.height = '30px';
d.style.border = '1px solid #333';
guessDiv.append(d);
}); row.append(guessDiv);
for (let i = 0; i < hit; i++) { const p = document.createElement('div'); p.className = 'peg hit'; row.append(p); }
for (let i = 0; i < blow; i++) { const p = document.createElement('div'); p.className = 'peg blow'; row.append(p); }
historyDiv.prepend(row);
// ゲーム終了判定
if (hit === CODE_LENGTH) {
endGame();
} else {
messageDiv.textContent = `ヒット: ${hit}、ブロー: ${blow}`;
}
}
/**
* ゲーム終了処理
*/
function endGame() {
gameScreen.classList.add('hidden');
endScreen.classList.remove('hidden');
endTitle.textContent = '🎉 正解! 🎉';
// 評価コメント
let comment = '';
if (attempts <= 4) comment = '天才ですね!';
else if (attempts <= 8) comment = '素晴らしいです!';
else if (attempts <= 12) comment = '良い成績です!';
else comment = 'もっと練習しましょう!';
evalMessage.textContent = `${attempts} 回で正解しました。${comment}`;
}
</script>
</body>
</html>
アルゴリズムの流れ
ステップ | 処理内容 | 主な関数/命令 |
---|---|---|
1. タイトル表示 | タイトル画面だけ表示、他画面を隠す | showTitle() |
2. ゲーム開始 | 秘密コード生成、状態変数初期化、UIクリア、ゲーム画面表示 | startGame() |
3. 秘密コード生成 | COLORS からランダムに 4 色を選択(重複可) | generateCode() |
4. スロット/パレット構築 | HTML要素を動的生成してクリックイベントを設定 | buildSlots() , buildPalette() |
5. スロット選択・色配置 | スロットクリックで selectedSlot を設定→パレットで色を選ぶとスロット背景色に反映 | selectSlot() , chooseColor() |
6. 推測判定 | currentGuess と secretCode をコピー→ヒット判定→ブロー判定→結果を履歴へ表示 | handleGuess() |
7. 終了判定 | ヒット数が 4 でクリア → endGame() → 試行回数に応じた評価コメントを表示 | handleGuess() → endGame() |
関数の詳しい解説
関数名 | 説明 |
---|---|
showTitle() | タイトル画面表示、他画面を非表示 |
startGame() | ゲーム状態の初期化(秘密コード・試行回数・履歴など)、UIクリア、buildSlots() /buildPalette() |
generateCode() | ランダムに 4 色選ぶ秘密コードを配列で返却 |
buildSlots() | 4 つの空スロットを動的に生成、クリックで選択可能に |
buildPalette() | カラーパレットを生成し、色選択時にスロットへ反映 |
selectSlot(i) | スロットを選択中としてハイライト、selectedSlot に保存 |
chooseColor(c) | 選択中スロットに色を設定し、次のスロットを暗黙選択 |
handleGuess() | ヒット/ブロー判定ロジック、履歴行の生成・挿入、終了判定 |
endGame() | ゲーム画面を隠し、結果画面に切り替え、評価メッセージを生成 |
改造のポイント
- 色数・コード長変更
COLORS
配列の長さやCODE_LENGTH
を変えるだけで、難易度を自在に調整可能です。 - 重複禁止ルール
秘密コード生成時に同じ色を使わないようにsplice
で配列から削除すれば、本家ルールに近づきます。 - ビジュアル演出強化
ヒントピンをリアルに配置するミニボードを追加したり、優勝時にアニメーションを入れると演出UP。 - 試行回数制限
試行回数に上限(例:10 回)を設け、超過時は敗北とする仕組みも実装可能です。 - ヒント機能
「残り何色正解」「特定の色は含まれていない」など補助ヒントを追加して、学習モードを作るのも面白いです。 - ランキング機能
localStorage
で最少試行回数やクリア時間を保存し、ハイスコアを表示する仕組みを入れるとリプレイ性が高まります。
アドバイス:秘密コードを伏せる代わりに「🔒」アイコンを使ったり、スロットをドラッグ&ドロップで埋めるインタラクションを加えると、より直感的で楽しい UI になります。また、敗北時に正解コードを一気にアニメーションで見せるなど、リッチな演出を検討してみてください!