ミニゲーム
遊びたいゲームを選ぶと、そのゲームだけが表示されます。
初級
使うのは「ランダム」と「if(条件分岐)」だけ。まずここから。
🔢 数当てゲーム
初級コンピュータが 1〜100 の中から決めた数字を当てる、練習問題の定番です。「もっと大きい/小さい」のヒントが頼りです。
仕組みとコードの説明(はじめての人向け)
const answer = Math.floor(Math.random() * 100) + 1;
if (guess === answer) {
// 当たり
} else if (guess < answer) {
// もっと大きい
} else {
// もっと小さい
}
どうやって動いているか
- ゲームが始まると、コンピュータはまず「答え」をこっそり決めます。1〜100の中から、サイコロを振るように適当に1つ選んで、名前つきの箱にしまっておきます。この「名前つきの箱」のことを、プログラムでは変数と呼びます。答えはあなたには見えません。
- あなたが数字を入れてボタンを押すと、コンピュータは「箱の中の答え」と「あなたの数字」を比べます。
- 同じなら当たり。あなたの数字の方が小さければ「もっと大きい」、大きければ「もっと小さい」と教えてくれます。
- 当たるまで、これをくり返します。「挑戦回数」は、別の箱に1ずつ足して数えています。
コードを、ふだんの言葉で読むと
- 1行目は、答えを決める行です。「0〜99のサイコロ」を作って1を足すので、答えは1〜100になります。
ifは「もし〜なら」という意味で、「答えとまったく同じなら」という条件です。else ifは「それ以外で、もし〜なら」。ここでは「答えより小さいとき」です。elseは「それ以外は全部」。残りは「大きい」しかないので、ここで受け止めます。
なぜ、こう書くのか
- 答えをランダム(適当な数字)にする理由:いつも同じ数字だと、2回目から答えが分かってしまい、ゲームにならないからです。
- 条件を3つに分ける理由:比べた結果は「同じ・小さい・大きい」の3通りしかありません。起こる場合を全部、漏れなく分けるのがプログラムの基本の書き方です。
- 答えではなく方向だけ教える理由:答えそのものは教えず、方向だけ教えるからゲームになります。ヒントのたびに候補がしぼれるので、真ん中の数字から聞いていくのがコツです。
✊ じゃんけん
初級手を選ぶと、コンピュータがランダムで手を出します。勝ち・負け・あいこの数を数えます。
仕組みとコードの説明(はじめての人向け)
// 手を数字で表す:0=グー、1=チョキ、2=パー
const cpu = Math.floor(Math.random() * 3);
const diff = (cpu - player + 3) % 3;
if (diff === 0) { /* あいこ */ }
else if (diff === 1) { /* 勝ち */ }
else { /* 負け */ }
どうやって動いているか
- あなたがボタンを押すと、コンピュータもその場で手を決めます。決め方はサイコロと同じで、0・1・2のどれか1つを適当に選びます。
- 実はコンピュータの中では、グー・チョキ・パーを言葉ではなく数字で覚えています(0=グー、1=チョキ、2=パー)。
- 2つの数字を引き算して、その結果で「勝ち・負け・あいこ」を判定します。
- 結果を表示して、勝ち・負け・あいこの箱に、それぞれ数を1つ足します。
コードを、ふだんの言葉で読むと
cpuの行は、コンピュータの手を0〜2のサイコロで決めています。diffの行は、「コンピュータの数字 − あなたの数字」をして、% 3(3で割った余り)で結果を0・1・2のどれかに直しています。- 最後の3行は、
diffが0ならあいこ、1なら勝ち、それ以外(2)なら負け、という決め方です。
なぜ、こう書くのか
- 手を数字にする理由:コンピュータは「グーはチョキに勝つ」という言葉のルールを、そのままでは扱えません。数字に置き換えると、引き算という計算だけで勝敗が決められるようになります。
- この並び(0→1→2)にする理由:グー(0)はチョキ(1)に勝ち、チョキ(1)はパー(2)に勝ち、パー(2)はグー(0)に勝つ、と「次の数字に勝つ」並びになっているからです。だから引き算で判定できます。
%(余り)を使う理由:パー(2)とグー(0)のように、並びが一周して戻る場合があるからです。時計の針が12の次に1へ戻るのと同じで、余りを使うと一周した場合も同じ計算で処理できます。+3しているのは、引き算がマイナスにならないようにするためです。
🗺️ 宝探し
初級5×5のマスの中に、宝物が1つだけ隠れています。マスを開くと、宝物までの距離(縦の差+横の差)が数字で出ます。数字が小さいほど近くです。
仕組みとコードの説明(はじめての人向け)
const SIZE = 5;
const treasureX = random(SIZE);
const treasureY = random(SIZE);
function random(n) {
return Math.floor(Math.random() * n);
}
function distance(x, y) {
return Math.abs(x - treasureX)
+ Math.abs(y - treasureY);
}
if (distance(x, y) === 0) {
// 宝物を見つけた
} else {
// 距離をヒントとして表示する
}
どうやって動いているか
- 最初に、コンピュータは宝物の「住所」を決めます。住所は「横に何番目か」と「縦に何番目か」の2つの数字です。サイコロで決めて、隠しておきます。
- あなたがマスを開くと、コンピュータは「開けたマスの住所」と「宝物の住所」を比べます。
- 横に何マス離れているか、縦に何マス離れているかを別々に数えて、足したものが「距離」です。たとえば横に2、縦に1離れていれば、距離は3です。
- 距離が0、つまり住所がまったく同じなら当たりです。それ以外なら、距離の数字だけを見せます(場所そのものは教えません)。
コードを、ふだんの言葉で読むと
treasureXとtreasureYは、宝物の住所(横の番号と縦の番号)をしまう箱です。distanceの部分は、横の差と縦の差を足しています。Math.absは「マイナスをなくす」という命令で、左に3つ離れていても右に3つ離れていても、離れ具合は同じ「3」として数えるためです。if (distance === 0)は、「距離が0、つまり同じマスなら見つけた」という判定です。
なぜ、こう書くのか
- 場所を2つの数字にする理由:マス目は、横と縦の番号があれば1マスを特定できるからです。将棋盤や地図と同じ考え方です。
- 距離を「横の差+縦の差」にする理由:マス目の世界では、ななめには進めず、横と縦にしか動けないからです。実際に歩く歩数とだいたい同じになります。
- マスごとに違う処理を書かない理由:25個のマス全部に、「開けたら距離を計算する」という同じ決まりを使い回しています。決まりが1つなら、直すときも1か所で済みます。
🎰 スロットマシン
初級「回す」を押すと数字がぐるぐる回り、左から順番に止まります。3つ同じ数字がそろえば大当たりです。
仕組みとコードの説明(はじめての人向け)
const reels = [0, 0, 0];
for (let i = 0; i < 3; i++) {
reels[i] = Math.floor(Math.random() * 10);
}
// 回っている間は、60ミリ秒ごとに適当な数字を表示します
setInterval(showRandom, 60);
// 左から順に止めます(600 → 1100 → 1600ミリ秒後)
setTimeout(stopReel, 600); // 1つ目
setTimeout(stopReel, 1100); // 2つ目
setTimeout(stopReel, 1600); // 3つ目
if (reels[0] === reels[1] && reels[1] === reels[2]) {
// 3つそろった(大当たり)
}
どうやって動いているか
- 「回す」を押すと、コンピュータはまず「止まったときに出る数字」を0〜9から3つ決めて、箱の並び(配列)にしまいます。
- 画面の数字は、目まぐるしく切り替わりながら回ります。左から順に、少しずつ間を空けて止まっていき、止まる瞬間に、決めておいた数字に置き換わります。
- 全部止まったら、3つの数字を比べて、全部同じなら大当たり、2つだけ同じなら「おしい」です。
- 回した回数と大当たりの回数は、別の箱で数えています。
コードを、ふだんの言葉で読むと
reelsは、数字を入れる箱3つ分です。reels[0]が1つ目、reels[1]が2つ目、reels[2]が3つ目です。番号が0から始まるのが配列の約束です。forは「くり返し」の命令です。「i を0、1、2と変えながら、同じ処理を3回する」という意味です。&&は「〜で、かつ〜」という意味です。「1つ目と2つ目が同じで、かつ2つ目と3つ目が同じ」なら、3つ全部が同じだと言えます。setIntervalは「○ミリ秒ごとにくり返す」setTimeoutは「○ミリ秒後に1回だけ実行する」という、時間の予約の命令です。止まる時間を左から少しずつ遅らせる(600→1100→1600)ことで、順番に止まって見えます。ミリ秒は1秒の1000分の1の単位です。
なぜ、こう書くのか
- 箱を3つ並べる(配列にする)理由:数字が3つあるものを、バラバラの変数3つで持つより、並びで持つ方が「i 番目」と数えて処理できて、くり返し(for)と相性がいいからです。
- 大当たりがなかなか出ない理由:3つそろう確率は 10×10×10 の組み合わせのうち10通り、つまり100回に1回くらいです。お店のスロットがなかなか当たらないのと同じ仕組みです。
➕ 計算ドリル
初級足し算・引き算・掛け算の問題が10問出ます。数字と記号は毎回ランダムです。
仕組みとコードの説明(はじめての人向け)
let a = Math.floor(Math.random() * 9) + 1;
let b = Math.floor(Math.random() * 9) + 1;
let op = ['+', '−', '×'][Math.floor(Math.random() * 3)];
let answer;
if (op === '+') {
answer = a + b;
} else if (op === '−') {
answer = a - b;
} else {
answer = a * b;
}
どうやって動いているか
- コンピュータは、2つの数字と、計算の記号(+・−・×)をランダムに決めて、問題を表示します。
- 答えは、問題を作った時点でコンピュータが先に計算して、箱にしまってあります。
- あなたが答えを入れると、箱の中の答えと比べて、正解かどうかを教えます。
- 10問終わると、正解の数を表示します。
コードを、ふだんの言葉で読むと
- 1〜2行目は「1〜9の数字を適当に決める」行です。数当てゲームの「答えを決める」と同じ書き方です。
- 3行目は、記号の並び
['+', '−', '×']の中から、ランダムに1つ選んでいます。並びの中から1つ選ぶのも、配列の使い方です。 if / else if / elseは数当てゲームと同じ「3つの場合分け」です。記号によって、計算のしかたを変えています。
なぜ、こう書くのか
- 問題をランダムにする理由:問題を全部書いておく必要がなく、数字の組み合わせの数だけ問題が作れるからです。
- 引き算で大きい方を前にする理由:「3 − 7」のような答えがマイナスになる問題を避けるためです。a より b が大きければ、2つの数字を入れ替えてから問題にします。
- 答えを先に計算しておく理由:あなたの答えと比べるには、正しい答えを持っている必要があるからです。問題を作る側が先に答えを用意するのが、ドリルやテストの基本の形です。
中級
配列や文字列、くり返しを使って、判定の工夫が増えます。考え方の型は上級にもつながります。
⭕ 三目並べ(○×ゲーム)
中級あなたは ○、コンピュータは × です。縦・横・斜めのどれかで先に3つ並べた方が勝ちです。
仕組みとコードの説明(はじめての人向け)
// 勝ちになる並びは8通りだけ。先に全部書いておきます
const LINES = [
[0,1,2],[3,4,5],[6,7,8], // 横
[0,3,6],[1,4,7],[2,5,8], // 縦
[0,4,8],[2,4,6] // 斜め
];
function win(board, p) {
return LINES.some(line =>
line.every(i => board[i] === p));
}
どうやって動いているか
- 盤は、9個の箱が一列に並んだものとして管理しています。左上から0〜8の番号がついていて、真ん中は4番です。あなたが置くと、その番号の箱に「○」が入ります。
- 置くたびに、コンピュータは「勝ちの並び」になっていないか調べます。
- コンピュータの番では、決まった順番でマスを探します。まず自分が勝てるマス、次に、あなたが次で勝ちそうなマスをふさぎ、それでもなければ真ん中、最後は空いているマスから適当に選びます。
- 勝ち負けが決まったら盤を止めて、それ以上置けないようにします。
コードを、ふだんの言葉で読むと
LINESは、勝ちになる並び8通り(横3つ、縦3つ、斜め2つ)を、最初に全部書き並べたものです。winの部分は、「8通りのうち、どれか1つでも、3マス全部が同じ記号で埋まっているか」を調べています。someは「どれか1つでも」、everyは「全部」という意味の命令です。
なぜ、こう書くのか
- 勝ちの並びを先に全部書く理由:三目並べの勝ち方は8通りしかありません。「どう調べるか」を工夫するより、答えを全部書いて照らし合わせる方が、簡単で間違いがありません。
- 盤を一列の箱にする理由:3×3の盤でも、番号を0〜8と一列にすれば十分に表せるからです。管理が簡単になります(五目並べのように大きくなければ、この方法で足ります)。
- コンピュータに「探す順番」を決めておく理由:コンピュータは自分で考えているわけではありません。「勝てるマス→止めるマス→真ん中」という順番を人が先に決めておくと、その通りに動くだけで、考えているように見えます。
🔢 ヒット&ブロー
中級コンピュータが決めた、重ならない3つの数字を当てます。ヒット=数字も場所も合っている、ブロー=数字は合っているが場所が違う、というヒントが出ます。
仕組みとコードの説明(はじめての人向け)
function judge(guess, secret) {
let hit = 0, blow = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (guess[i] === secret[j]) {
if (i === j) hit++;
else blow++;
}
}
}
return [hit, blow];
}
どうやって動いているか
- コンピュータは、重ならない数字を3つ選んで、箱の並び(配列)にしまいます。
- あなたが3つの数字を入れると、答えと1つずつ、全部の組み合わせで比べます。
- 数字が合っていて、場所(何番目か)も同じならヒット。数字は合っているのに場所が違うならブローです。
- ヒントと今までの予想は一覧に残るので、それを見ながら候補をしぼっていきます。
コードを、ふだんの言葉で読むと
guess[i]は「あなたの予想の i 番目」secret[j]は「答えの j 番目」という意味です。forの中にforが入っています(二重のくり返し)。あなたの数字1つに対して、答えの数字全部と比べるので、比べる回数は 3×3=9回です。i === jは「場所も同じ」という意味です。数字が同じで場所も同じならヒット、場所が違えばブローと分けています。
なぜ、こう書くのか
- 二重のくり返しを使う理由:「片方の全部」と「もう片方の全部」を比べるときの、決まった書き方だからです。成績表と名簿を照らし合わせるような作業は、だいたいこの形になります。
- 数字を重ねない理由:同じ数字が2つあると、「どっちと合ったのか」があいまいになり、ヒントが分かりにくくなるからです。
🃏 ブラックジャック
中級カードの合計を21に近づけるゲームです。21を超えると負け(バースト)。A(エース)は11にも1にもなります。相手は合計が17以上になるまで引き続けます。
仕組みとコードの説明(はじめての人向け)
function total(hand) {
let sum = 0; // 合計
let aces = 0; // エースの数
for (const card of hand) {
sum += (card === 1) ? 11 : card;
if (card === 1) aces++;
}
while (sum > 21 && aces > 0) {
sum -= 10; // エースを 11 ではなく 1 として数え直す
aces--;
}
return sum;
}
// 相手は、合計が17未満なら引き続けます
while (total(dealer) < 17) {
dealer.push(drawCard());
}
どうやって動いているか
- ゲームが始まると、あなたと相手にカードが2枚ずつ配られます。相手の2枚目は裏向きで、合計も見えません。
- 「ヒット」を押すとカードが1枚増えます。21を超えたら、その場で負けです。
- 「スタンド」で止めると、相手の番になります。相手は「合計が17以上になるまで引く」という決まりだけで動きます。
- 両方の合計を比べて、21に近い方の勝ちです。
コードを、ふだんの言葉で読むと
totalは「カードの並びを受け取って、合計を返す」処理です。エースはまず11として足します。whileは「〜の間、くり返す」という命令です。「合計が21を超えていて、まだエースが残っている間」は、合計から10を引いて、エースを1として数え直します。- 相手の動きも
whileです。「合計が17より小さい間、カードを引く」という1つの決まりで書けます。
なぜ、こう書くのか
- 合計の計算を1つの処理にまとめる理由:あなたの合計、相手の合計、エースの数え直しなど、同じ計算が何度も出てくるからです。1か所にまとめると、間違えたときも直すのは1か所で済みます。
- 相手に「17」という決まりだけ持たせる理由:コンピュータは考えていません。「17未満なら引く」という単純な決まりでも、相手らしく動いて見えます。三目並べの「探す順番」と同じ発想です。
🔤 ハングマン
中級隠れた英単語(IT・パソコンの言葉)の文字を、1つずつ当てていくゲームです。ミスは6回までです。
仕組みとコードの説明(はじめての人向け)
const word = 'JAVA';
let guessed = {}; // 選んだ文字を入れておく
let disp = '';
for (let i = 0; i < word.length; i++) {
const ch = word.charAt(i); // i 番目の文字
disp += guessed[ch] ? ch : '_';
}
どうやって動いているか
- コンピュータは、単語の一覧から1つをランダムに選びます。最初は全部の文字が「_」で隠れています。
- あなたが文字のボタンを押すと、その文字が単語の中にあるか調べます。
- あれば、その文字の場所全部が開きます。なければ、ミスの残り回数が1つ減ります。
- 全部開けば勝ち、ミスが6回になると負けで、答えの単語が表示されます。
コードを、ふだんの言葉で読むと
word.lengthは「文字の数」です。JAVA なら 4 になります。word.charAt(i)は「i 番目の文字を取り出す」という意味です。番号は0から始まるので、charAt(0) は 'J' です。guessed[ch] ? ch : '_'は「その文字を選んだことがあれば文字を、まだなら _ を」という意味の短い場合分けです。- 表示は、単語の文字を1つずつ見て、「開くか隠すか」を決めながら組み立てています。
なぜ、こう書くのか
- 選んだ文字を記録しておく理由:画面の表示は、この記録から毎回作り直せます。表示と記録を分けておくと、「新しい単語」でも同じ作り方が使えます。
- 文字列も「並び」として扱う理由:文字列は、文字が並んだものと考えることができます。配列で学んだ「i 番目を取り出す」考え方が、そのまま使えます。
上級
二次元配列と「方向を数える」判定、コンピュータの読みが入ります。迷路は二次元配列の地図を、ボタンで移動します。
⚫ 五目並べ
上級あなたは黒石です。空いているマスをタップすると石を置けます。コンピュータが白石を置くので、先に縦・横・斜めのどれかで5つ並べた方が勝ちです。
仕組みとコードの説明(はじめての人向け)
// 0=空き、1=黒、2=白。盤は二次元配列です
board[y][x] = 1;
// 縦・横・斜めの4方向をまとめて表します
const dirs = [
[1, 0], [0, 1], [1, 1], [1, -1]
];
function isWin(x, y, player) {
return dirs.some(function (d) {
return countLine(x, y, d[0], d[1], player) >= 5;
});
}
どうやって動いているか
- 盤は10×10で大きいので、場所は「縦の番号」と「横の番号」の2つで管理します。マンションの部屋を「○階の○号室」で表すのと同じです。この「番号が2つある箱の並び」のことを、プログラムでは二次元配列と呼びます。
- あなたが石を置くと、コンピュータは、その石を中心に、縦・横・斜め(2つ)の、合わせて4つの向きを調べます。
- それぞれの向きに、同じ色の石が何個続いているか、両側に向かって1つずつ数えます。どれかの向きで5個以上なら勝ちです。
- コンピュータの番では、三目並べと同じ順番(勝てる場所→勝ちを止める場所)に加えて、「石の近く」や「真ん中の方」のように良さそうな場所に点数を付けて、一番点の高い場所に置きます。
コードを、ふだんの言葉で読むと
board[y][x]は、「縦y番目・横x番目のマス」という意味です。ここに0(空き)・1(黒)・2(白)の数字を入れて、盤の様子を表しています。dirsは、4つの向きを「横に+1、縦に+0」のような数字の組で書いたものです。[1, 0]は横向き、[0, 1]は縦向き、[1, 1]と[1, -1]は斜めです。isWinの部分は、「置いた石から両側へ同じ色を数えて、合計が5以上なら勝ち」という判定です。
なぜ、こう書くのか
- 置いた石の周りだけ調べる理由:勝ち負けが変わるのは、今置いた石の並びだけだからです。盤全体を毎回調べ直す必要はなく、周りだけ見れば十分で、処理も簡単になります。
- 向きを数字の組で書く理由:4つの向きに、同じ「数える処理」を使い回せるからです。向きごとに別々のプログラムを書くと4倍の量になり、間違いも増えます。
- コンピュータが点数制な理由:五目並べはマスが100個もあって、「良い場所」が1つに決まらないからです。候補に点数を付けて一番高い場所を選ぶ、という決め方にしています(本当に強いAIではなく、簡単なルールです)。
🧭 迷路ゲーム
上級🚶 があなた、🏁 がゴールです。下の矢印ボタンで動きます。グレーのマスは壁で、通れません。迷路は2種類からランダムに出ます。
仕組みとコードの説明(はじめての人向け)
// 地図は文字列の並びです(1=壁、0=道)
const map = [
'1111',
'1001',
'1011',
'1111'
];
// 進みたい場所(nx, ny)が壁でなければ進めます
if (map[ny].charAt(nx) !== '1') {
px = nx;
py = ny;
}
// ゴールの場所に着いたらクリア
if (px === gx && py === gy) {
// ゴール!
}
どうやって動いているか
- 迷路は、「壁(1)」と「道(0)」を並べた地図で表しています。あなたの場所は、横の番号 x と縦の番号 y の2つで表します(五目並べの盤と同じ考え方です)。
- 矢印ボタンを押すと、まず「進みたい場所」を計算します。例えば右ボタンなら、横の番号が1つ増えた場所です。
- その場所が壁なら進めず、そのまま。道なら、あなたの場所を新しい場所に書き換えます。
- ゴールの場所とあなたの場所が同じになったら、クリアです。
コードを、ふだんの言葉で読むと
map[ny]は「地図の ny 行目」という意味で、charAt(nx)は「その行の nx 番目の文字」です。ハングマンで使った charAt と同じです。!== '1'は「壁ではない」という意味です。「壁なら止まる」ではなく「壁でなければ進む」と書くことで、進む処理を1か所にまとめています。- 場所を2つの数字(x と y)で持つおかげで、上下左右の移動は「どちらの数字を±1するか」だけで表せます。
なぜ、こう書くのか
- 迷路を文字の地図にする理由:見た目そのままの形で書けるからです。地図を書き換えるだけで、別の迷路が作れます。
- 「進めるかどうか」を先に調べる理由:プログラムでは、動く前に行き先をチェックするのが基本です。壁の中に入ってから戻すより、先に止める方が簡単で間違いがありません。