miya3333 commited on
Commit
ed2ca9d
·
verified ·
1 Parent(s): 114b88b

Upload 3 files

Browse files

Implemented a battle mode against enemy AI.

Files changed (2) hide show
  1. index.html +12 -0
  2. script.js +131 -1
index.html CHANGED
@@ -7,10 +7,22 @@
7
  </head>
8
  <body>
9
  <div class="container">
 
 
 
 
 
 
 
 
 
10
  <div id="gameInfo">
11
  探す数字: <span id="target">--</span>
12
  </div>
13
  <button id="startBtn">ゲーム開始</button>
 
 
 
14
  <canvas id="gameCanvas" width="800" height="600"></canvas>
15
  <div id="message"></div>
16
  </div>
 
7
  </head>
8
  <body>
9
  <div class="container">
10
+ <div id="modeSelect" style="margin-bottom:1rem;">
11
+ <label><input type="radio" name="mode" value="single" checked> シングルプレイ</label>
12
+ <label style="margin-left:1rem;"><input type="radio" name="mode" value="vsAI"> VS AI</label>
13
+ </div>
14
+ <div id="difficultySelect" style="display:none; margin-bottom:1rem;">
15
+ 難易度:
16
+ <label><input type="radio" name="difficulty" value="easy" checked> 簡単</label>
17
+ <label style="margin-left:1rem;"><input type="radio" name="difficulty" value="hard"> 難しい</label>
18
+ </div>
19
  <div id="gameInfo">
20
  探す数字: <span id="target">--</span>
21
  </div>
22
  <button id="startBtn">ゲーム開始</button>
23
+ <div id="scores" style="display:none; margin-bottom:1rem;">
24
+ プレイヤー: <span id="playerScore">0</span> AI: <span id="aiScore">0</span>
25
+ </div>
26
  <canvas id="gameCanvas" width="800" height="600"></canvas>
27
  <div id="message"></div>
28
  </div>
script.js CHANGED
@@ -4,6 +4,30 @@
4
  const targetSpan = document.getElementById('target');
5
  const startBtn = document.getElementById('startBtn');
6
  const messageDiv = document.getElementById('message');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  let items = [];
8
  let remaining = [];
9
  let currentTarget = null;
@@ -26,6 +50,22 @@
26
  messageDiv.textContent = '';
27
  messageDiv.className = '';
28
  targetSpan.textContent = '--';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  for (let i = 1; i <= count; i++) {
30
  const text = i.toString();
31
  const metrics = ctx.measureText(text);
@@ -65,12 +105,27 @@
65
  currentTarget = null;
66
  targetSpan.textContent = '--';
67
  messageDiv.className = 'clear';
68
- messageDiv.textContent = 'ゲームクリア!';
 
 
 
 
 
 
 
 
 
 
 
 
69
  return;
70
  }
71
  const idx = Math.floor(Math.random() * remaining.length);
72
  currentTarget = remaining[idx].num;
73
  targetSpan.textContent = currentTarget;
 
 
 
74
  }
75
 
76
  function repositionItems() {
@@ -137,6 +192,73 @@
137
  ctx.lineTo(x2, y1);
138
  ctx.stroke();
139
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  canvas.addEventListener('click', e => {
142
  if (currentTarget === null) return;
@@ -148,6 +270,14 @@
148
  if (clickX >= item.x && clickX <= item.x + item.width &&
149
  clickY <= item.y && clickY >= item.y - item.height) {
150
  if (item.num === currentTarget) {
 
 
 
 
 
 
 
 
151
  messageDiv.className = 'correct';
152
  messageDiv.textContent = '正解!';
153
  drawAll();
 
4
  const targetSpan = document.getElementById('target');
5
  const startBtn = document.getElementById('startBtn');
6
  const messageDiv = document.getElementById('message');
7
+ const modeRadios = document.querySelectorAll('input[name="mode"]');
8
+ const difficultyDiv = document.getElementById('difficultySelect');
9
+ const difficultyRadios = document.querySelectorAll('input[name="difficulty"]');
10
+ modeRadios.forEach(radio => {
11
+ radio.addEventListener('change', () => {
12
+ if (document.querySelector('input[name="mode"]:checked').value === 'vsAI') {
13
+ difficultyDiv.style.display = 'block';
14
+ } else {
15
+ difficultyDiv.style.display = 'none';
16
+ }
17
+ });
18
+ });
19
+ const scoresDiv = document.getElementById('scores');
20
+ const playerScoreSpan = document.getElementById('playerScore');
21
+ const aiScoreSpan = document.getElementById('aiScore');
22
+ let gameMode = 'single';
23
+ let playerScore = 0;
24
+ let aiScore = 0;
25
+ let aiTimeout = null;
26
+ let aiAccuracy;
27
+ const AI_EASY_ACCURACY = 0.3;
28
+ const AI_HARD_ACCURACY = 0.7;
29
+ const aiMinDelay = 500;
30
+ const aiMaxDelay = 2000;
31
  let items = [];
32
  let remaining = [];
33
  let currentTarget = null;
 
50
  messageDiv.textContent = '';
51
  messageDiv.className = '';
52
  targetSpan.textContent = '--';
53
+ gameMode = document.querySelector('input[name="mode"]:checked').value;
54
+ if (gameMode === 'vsAI') {
55
+ const difficulty = document.querySelector('input[name="difficulty"]:checked').value;
56
+ aiAccuracy = difficulty === 'easy' ? AI_EASY_ACCURACY : AI_HARD_ACCURACY;
57
+ playerScore = 0;
58
+ aiScore = 0;
59
+ playerScoreSpan.textContent = playerScore;
60
+ aiScoreSpan.textContent = aiScore;
61
+ scoresDiv.style.display = 'block';
62
+ } else {
63
+ scoresDiv.style.display = 'none';
64
+ }
65
+ if (aiTimeout) {
66
+ clearTimeout(aiTimeout);
67
+ aiTimeout = null;
68
+ }
69
  for (let i = 1; i <= count; i++) {
70
  const text = i.toString();
71
  const metrics = ctx.measureText(text);
 
105
  currentTarget = null;
106
  targetSpan.textContent = '--';
107
  messageDiv.className = 'clear';
108
+ if (gameMode === 'vsAI') {
109
+ if (aiTimeout) {
110
+ clearTimeout(aiTimeout);
111
+ aiTimeout = null;
112
+ }
113
+ let resultText = '';
114
+ if (playerScore > aiScore) resultText = 'あなたの勝ち!';
115
+ else if (playerScore < aiScore) resultText = 'AIの勝ち!';
116
+ else resultText = '引き分け!';
117
+ messageDiv.textContent = 'ゲームクリア!結果: あなた ' + playerScore + ' - AI ' + aiScore + ' ' + resultText;
118
+ } else {
119
+ messageDiv.textContent = 'ゲームクリア!';
120
+ }
121
  return;
122
  }
123
  const idx = Math.floor(Math.random() * remaining.length);
124
  currentTarget = remaining[idx].num;
125
  targetSpan.textContent = currentTarget;
126
+ if (gameMode === 'vsAI') {
127
+ scheduleAIAttempt();
128
+ }
129
  }
130
 
131
  function repositionItems() {
 
192
  ctx.lineTo(x2, y1);
193
  ctx.stroke();
194
  }
195
+ function drawBlueCircle(item) {
196
+ const cx = item.x + item.width / 2;
197
+ const cy = item.y - item.height / 2;
198
+ const r = Math.max(item.width, item.height) / 2 + 5;
199
+ ctx.strokeStyle = 'blue';
200
+ ctx.lineWidth = 3;
201
+ ctx.beginPath();
202
+ ctx.arc(cx, cy, r, 0, 2 * Math.PI);
203
+ ctx.stroke();
204
+ }
205
+ function drawBlueCross(item) {
206
+ const x1 = item.x;
207
+ const y1 = item.y - item.height;
208
+ const x2 = item.x + item.width;
209
+ const y2 = item.y;
210
+ ctx.strokeStyle = 'blue';
211
+ ctx.lineWidth = 3;
212
+ ctx.beginPath();
213
+ ctx.moveTo(x1, y1);
214
+ ctx.lineTo(x2, y2);
215
+ ctx.moveTo(x1, y2);
216
+ ctx.lineTo(x2, y1);
217
+ ctx.stroke();
218
+ }
219
+
220
+ function scheduleAIAttempt() {
221
+ if (aiTimeout) clearTimeout(aiTimeout);
222
+ const delay = aiMinDelay + Math.random() * (aiMaxDelay - aiMinDelay);
223
+ aiTimeout = setTimeout(doAIAttempt, delay);
224
+ }
225
+
226
+ function doAIAttempt() {
227
+ aiTimeout = null;
228
+ if (gameMode !== 'vsAI' || currentTarget === null) return;
229
+ const correct = Math.random() < aiAccuracy;
230
+ if (correct) {
231
+ const idx = remaining.findIndex(item => item.num === currentTarget);
232
+ if (idx === -1) return;
233
+ const item = remaining[idx];
234
+ aiScore++;
235
+ aiScoreSpan.textContent = aiScore;
236
+ messageDiv.className = 'correct';
237
+ messageDiv.textContent = 'AIが正解!';
238
+ drawAll();
239
+ drawBlueCircle(item);
240
+ setTimeout(() => {
241
+ remaining.splice(idx, 1);
242
+ const oldItems = remaining.map(it => ({ ...it }));
243
+ const newItems = repositionItems();
244
+ animateReposition(oldItems, newItems, 1000, () => {
245
+ remaining = newItems;
246
+ pickNext();
247
+ });
248
+ }, 500);
249
+ } else {
250
+ if (remaining.length > 1) {
251
+ const wrongItems = remaining.filter(item => item.num !== currentTarget);
252
+ const wrongItem = wrongItems[Math.floor(Math.random() * wrongItems.length)];
253
+ messageDiv.className = 'wrong';
254
+ messageDiv.textContent = 'AIが間違えた!';
255
+ drawAll();
256
+ drawBlueCross(wrongItem);
257
+ setTimeout(drawAll, 500);
258
+ }
259
+ scheduleAIAttempt();
260
+ }
261
+ }
262
 
263
  canvas.addEventListener('click', e => {
264
  if (currentTarget === null) return;
 
270
  if (clickX >= item.x && clickX <= item.x + item.width &&
271
  clickY <= item.y && clickY >= item.y - item.height) {
272
  if (item.num === currentTarget) {
273
+ if (gameMode === 'vsAI') {
274
+ if (aiTimeout) {
275
+ clearTimeout(aiTimeout);
276
+ aiTimeout = null;
277
+ }
278
+ playerScore++;
279
+ playerScoreSpan.textContent = playerScore;
280
+ }
281
  messageDiv.className = 'correct';
282
  messageDiv.textContent = '正解!';
283
  drawAll();