Raven7 commited on
Commit
7255ba2
·
verified ·
1 Parent(s): 94f9cce

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +271 -145
index.html CHANGED
@@ -1,5 +1,5 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -12,30 +12,34 @@
12
  }
13
 
14
  body {
 
 
 
15
  display: flex;
16
- flex-direction: column;
17
  align-items: center;
18
- min-height: 100vh;
19
- background: #f0f0f0;
20
- font-family: Arial, sans-serif;
21
  padding: 20px;
22
  }
23
 
24
  .game-container {
25
- background: #DEB887;
26
- padding: 20px;
27
- border-radius: 10px;
28
- box-shadow: 0 0 20px rgba(0,0,0,0.1);
 
29
  }
30
 
31
  .board {
32
  display: grid;
33
  grid-template-columns: repeat(19, 30px);
34
  grid-template-rows: repeat(19, 30px);
35
- gap: 0px;
36
- background: #DEB887;
37
- border: 2px solid #000;
38
- position: relative;
 
 
 
39
  }
40
 
41
  .intersection {
@@ -43,108 +47,202 @@
43
  height: 30px;
44
  position: relative;
45
  cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
  .intersection::before {
49
  content: '';
50
  position: absolute;
51
- top: 50%;
52
  left: 0;
 
53
  width: 100%;
54
  height: 1px;
55
- background: #000;
 
56
  }
57
 
58
  .intersection::after {
59
  content: '';
60
  position: absolute;
61
- left: 50%;
62
  top: 0;
 
63
  height: 100%;
64
  width: 1px;
65
- background: #000;
 
66
  }
67
 
68
- .stone {
 
69
  position: absolute;
70
- width: 26px;
71
- height: 26px;
 
72
  border-radius: 50%;
73
- top: 2px;
74
- left: 2px;
75
- z-index: 1;
76
- transition: all 0.2s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
79
  .black {
80
- background: #000;
81
- box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
82
  }
83
 
84
  .white {
85
- background: #fff;
86
- box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
87
  }
88
 
89
  .controls {
90
- margin-top: 20px;
91
  display: flex;
92
- gap: 10px;
 
 
93
  }
94
 
95
  button {
96
  padding: 10px 20px;
97
  font-size: 16px;
98
  border: none;
99
- border-radius: 5px;
100
- cursor: pointer;
101
- background: #4CAF50;
102
  color: white;
103
- transition: background 0.3s;
 
 
104
  }
105
 
106
  button:hover {
107
- background: #45a049;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
 
110
- .score {
111
- margin-top: 20px;
112
  font-size: 18px;
113
- display: flex;
114
- gap: 20px;
 
 
 
 
115
  }
116
 
117
  .mode-select {
 
 
 
118
  margin-bottom: 20px;
119
  }
120
 
121
- select {
122
- padding: 8px;
123
- font-size: 16px;
124
- border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  }
126
  </style>
127
  </head>
128
  <body>
129
- <div class="mode-select">
130
- <select id="gameMode">
131
- <option value="pvp">Player vs Player</option>
132
- <option value="ai">Player vs AI</option>
133
- </select>
134
- </div>
135
-
136
  <div class="game-container">
137
- <div class="board" id="board"></div>
138
- </div>
139
-
140
- <div class="score">
141
- <div>Black Score: <span id="blackScore">0</span></div>
142
- <div>White Score: <span id="whiteScore">0</span></div>
143
- </div>
144
-
145
- <div class="controls">
146
- <button id="passBtn">Pass</button>
147
- <button id="resetBtn">Reset</button>
 
 
 
 
 
 
 
 
 
 
 
 
148
  </div>
149
 
150
  <script>
@@ -153,25 +251,28 @@
153
  this.size = 19;
154
  this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
155
  this.currentPlayer = 'black';
 
156
  this.lastMove = null;
157
- this.passes = 0;
158
  this.gameMode = 'pvp';
159
- this.score = { black: 0, white: 0 };
160
- this.capturedStones = { black: 0, white: 0 };
161
-
162
  this.initialize();
163
  }
164
 
165
  initialize() {
166
  const boardElement = document.getElementById('board');
167
  boardElement.innerHTML = '';
168
-
169
  for(let i = 0; i < this.size; i++) {
170
  for(let j = 0; j < this.size; j++) {
171
  const intersection = document.createElement('div');
172
  intersection.className = 'intersection';
173
  intersection.dataset.row = i;
174
  intersection.dataset.col = j;
 
 
 
 
 
175
  intersection.addEventListener('click', (e) => this.handleMove(e));
176
  boardElement.appendChild(intersection);
177
  }
@@ -183,6 +284,20 @@
183
  this.gameMode = e.target.value;
184
  this.reset();
185
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
 
188
  handleMove(e) {
@@ -201,26 +316,25 @@
201
  isValidMove(row, col) {
202
  if(this.board[row][col] !== null) return false;
203
 
204
- // Place stone temporarily to check if it would be immediately captured
205
  this.board[row][col] = this.currentPlayer;
206
- const group = this.getGroup(row, col);
207
- const liberties = this.countLiberties(group);
208
 
209
- // Revert the temporary stone
210
  this.board[row][col] = null;
211
 
212
- return liberties > 0;
213
  }
214
 
215
  placeStone(row, col) {
216
- if(this.board[row][col] === null) {
217
- this.board[row][col] = this.currentPlayer;
218
- this.renderStone(row, col);
219
- this.captures();
220
- this.passes = 0;
221
- this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
222
- this.updateScore();
223
- }
 
224
  }
225
 
226
  renderStone(row, col) {
@@ -229,20 +343,47 @@
229
  stone.className = `stone ${this.currentPlayer}`;
230
  intersection.appendChild(stone);
231
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
  getGroup(row, col) {
234
  const color = this.board[row][col];
235
- const group = new Set();
 
236
  const stack = [[row, col]];
237
 
238
  while(stack.length > 0) {
239
  const [r, c] = stack.pop();
240
  const key = `${r},${c}`;
241
 
242
- if(!group.has(key)) {
243
- group.add(key);
244
- const neighbors = this.getNeighbors(r, c);
245
 
 
246
  for(const [nr, nc] of neighbors) {
247
  if(this.board[nr][nc] === color) {
248
  stack.push([nr, nc]);
@@ -254,32 +395,26 @@
254
  return group;
255
  }
256
 
257
- countLiberties(group) {
258
- const liberties = new Set();
259
-
260
- for(const pos of group) {
261
- const [row, col] = pos.split(',').map(Number);
262
  const neighbors = this.getNeighbors(row, col);
263
-
264
- for(const [nr, nc] of neighbors) {
265
- if(this.board[nr][nc] === null) {
266
- liberties.add(`${nr},${nc}`);
267
- }
268
  }
269
  }
270
-
271
- return liberties.size;
272
  }
273
 
274
  getNeighbors(row, col) {
275
  const neighbors = [];
276
  const directions = [[-1,0], [1,0], [0,-1], [0,1]];
277
 
278
- for(const [dr, dc] of directions) {
279
- const newRow = row + dr;
280
- const newCol = col + dc;
281
 
282
- if(newRow >= 0 && newRow < this.size && newCol >= 0 && newCol < this.size) {
 
283
  neighbors.push([newRow, newCol]);
284
  }
285
  }
@@ -287,27 +422,44 @@
287
  return neighbors;
288
  }
289
 
290
- captures() {
291
- for(let i = 0; i < this.size; i++) {
292
- for(let j = 0; j < this.size; j++) {
293
- if(this.board[i][j] !== null) {
294
- const group = this.getGroup(i, j);
295
- if(this.countLiberties(group) === 0) {
296
- for(const pos of group) {
297
- const [row, col] = pos.split(',').map(Number);
298
- const capturedColor = this.board[row][col];
299
- this.board[row][col] = null;
300
- const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
301
- intersection.innerHTML = '';
302
- this.capturedStones[this.currentPlayer]++;
303
- }
304
- }
305
- }
306
- }
307
  }
308
  }
309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  makeAIMove() {
 
 
 
 
 
 
 
 
311
  const validMoves = [];
312
  for(let i = 0; i < this.size; i++) {
313
  for(let j = 0; j < this.size; j++) {
@@ -325,38 +477,12 @@
325
  }
326
  }
327
 
328
- pass() {
329
- this.passes++;
330
- if(this.passes === 2) {
331
- alert('Game Over!');
332
- return;
333
- }
334
- this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
335
- if(this.gameMode === 'ai' && this.currentPlayer === 'white') {
336
- setTimeout(() => this.makeAIMove(), 500);
337
- }
338
- }
339
-
340
- reset() {
341
- this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
342
- this.currentPlayer = 'black';
343
- this.passes = 0;
344
- this.score = { black: 0, white: 0 };
345
- this.capturedStones = { black: 0, white: 0 };
346
- const intersections = document.querySelectorAll('.intersection');
347
- intersections.forEach(intersection => intersection.innerHTML = '');
348
- this.updateScore();
349
- }
350
-
351
- updateScore() {
352
- document.getElementById('blackScore').textContent =
353
- this.score.black + this.capturedStones.black;
354
- document.getElementById('whiteScore').textContent =
355
- this.score.white + this.capturedStones.white;
356
  }
357
  }
358
 
359
  const game = new GoGame();
360
  </script>
361
  </body>
362
- </html>
 
1
  <!DOCTYPE html>
2
+ <html>
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
12
  }
13
 
14
  body {
15
+ font-family: 'Segoe UI', system-ui, sans-serif;
16
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
17
+ min-height: 100vh;
18
  display: flex;
19
+ justify-content: center;
20
  align-items: center;
 
 
 
21
  padding: 20px;
22
  }
23
 
24
  .game-container {
25
+ max-width: 800px;
26
+ background: rgba(255, 255, 255, 0.95);
27
+ border-radius: 20px;
28
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
29
+ padding: 30px;
30
  }
31
 
32
  .board {
33
  display: grid;
34
  grid-template-columns: repeat(19, 30px);
35
  grid-template-rows: repeat(19, 30px);
36
+ gap: 1px;
37
+ background: #dcb35c;
38
+ border: 2px solid #2c3e50;
39
+ border-radius: 4px;
40
+ margin: 20px auto;
41
+ padding: 10px;
42
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
43
  }
44
 
45
  .intersection {
 
47
  height: 30px;
48
  position: relative;
49
  cursor: pointer;
50
+ transition: background 0.2s;
51
+ }
52
+
53
+ .intersection:hover::before {
54
+ content: '';
55
+ position: absolute;
56
+ width: 28px;
57
+ height: 28px;
58
+ border-radius: 50%;
59
+ background: rgba(0, 0, 0, 0.1);
60
+ top: 1px;
61
+ left: 1px;
62
+ z-index: 2;
63
  }
64
 
65
  .intersection::before {
66
  content: '';
67
  position: absolute;
 
68
  left: 0;
69
+ top: 50%;
70
  width: 100%;
71
  height: 1px;
72
+ background: rgba(0, 0, 0, 0.7);
73
+ z-index: 1;
74
  }
75
 
76
  .intersection::after {
77
  content: '';
78
  position: absolute;
 
79
  top: 0;
80
+ left: 50%;
81
  height: 100%;
82
  width: 1px;
83
+ background: rgba(0, 0, 0, 0.7);
84
+ z-index: 1;
85
  }
86
 
87
+ .star-point::after {
88
+ content: '';
89
  position: absolute;
90
+ width: 8px;
91
+ height: 8px;
92
+ background: #2c3e50;
93
  border-radius: 50%;
94
+ top: 50%;
95
+ left: 50%;
96
+ transform: translate(-50%, -50%);
97
+ z-index: 2;
98
+ }
99
+
100
+ .stone {
101
+ width: 28px;
102
+ height: 28px;
103
+ border-radius: 50%;
104
+ position: absolute;
105
+ top: 1px;
106
+ left: 1px;
107
+ z-index: 3;
108
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
109
+ animation: placeStone 0.3s ease-out;
110
+ }
111
+
112
+ @keyframes placeStone {
113
+ from {
114
+ transform: scale(1.2);
115
+ opacity: 0;
116
+ }
117
+ to {
118
+ transform: scale(1);
119
+ opacity: 1;
120
+ }
121
  }
122
 
123
  .black {
124
+ background: radial-gradient(circle at 35% 35%, #4a4a4a 0%, #000000 100%);
125
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
126
  }
127
 
128
  .white {
129
+ background: radial-gradient(circle at 35% 35%, #ffffff 0%, #e0e0e0 100%);
130
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
131
  }
132
 
133
  .controls {
 
134
  display: flex;
135
+ justify-content: center;
136
+ gap: 15px;
137
+ margin: 20px 0;
138
  }
139
 
140
  button {
141
  padding: 10px 20px;
142
  font-size: 16px;
143
  border: none;
144
+ border-radius: 8px;
145
+ background: #3498db;
 
146
  color: white;
147
+ cursor: pointer;
148
+ transition: all 0.3s;
149
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
150
  }
151
 
152
  button:hover {
153
+ background: #2980b9;
154
+ transform: translateY(-2px);
155
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
156
+ }
157
+
158
+ select {
159
+ padding: 8px 16px;
160
+ font-size: 16px;
161
+ border: 1px solid #ddd;
162
+ border-radius: 8px;
163
+ background: white;
164
+ cursor: pointer;
165
+ transition: all 0.3s;
166
+ }
167
+
168
+ select:hover {
169
+ border-color: #3498db;
170
+ }
171
+
172
+ .game-info {
173
+ text-align: center;
174
+ margin: 20px 0;
175
+ padding: 15px;
176
+ background: #f8f9fa;
177
+ border-radius: 10px;
178
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
179
  }
180
 
181
+ .game-info div {
182
+ margin: 10px 0;
183
  font-size: 18px;
184
+ color: #2c3e50;
185
+ }
186
+
187
+ #currentPlayer {
188
+ font-weight: bold;
189
+ color: #3498db;
190
  }
191
 
192
  .mode-select {
193
+ display: flex;
194
+ justify-content: center;
195
+ gap: 15px;
196
  margin-bottom: 20px;
197
  }
198
 
199
+ @media (max-width: 768px) {
200
+ .game-container {
201
+ padding: 15px;
202
+ }
203
+
204
+ .board {
205
+ transform: scale(0.8);
206
+ transform-origin: center;
207
+ }
208
+
209
+ .controls {
210
+ flex-direction: column;
211
+ align-items: center;
212
+ }
213
+
214
+ button {
215
+ width: 100%;
216
+ max-width: 200px;
217
+ }
218
  }
219
  </style>
220
  </head>
221
  <body>
 
 
 
 
 
 
 
222
  <div class="game-container">
223
+ <div class="mode-select">
224
+ <select id="gameMode">
225
+ <option value="pvp">Player vs Player</option>
226
+ <option value="ai">Player vs AI</option>
227
+ </select>
228
+ <select id="difficulty">
229
+ <option value="easy">Easy</option>
230
+ <option value="hard">Hard</option>
231
+ </select>
232
+ </div>
233
+
234
+ <div id="board" class="board"></div>
235
+
236
+ <div class="game-info">
237
+ <div>Current Player: <span id="currentPlayer">Black</span></div>
238
+ <div>Black Captures: <span id="blackCaptures">0</span></div>
239
+ <div>White Captures: <span id="whiteCaptures">0</span></div>
240
+ </div>
241
+
242
+ <div class="controls">
243
+ <button id="passBtn">Pass</button>
244
+ <button id="resetBtn">Reset</button>
245
+ </div>
246
  </div>
247
 
248
  <script>
 
251
  this.size = 19;
252
  this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
253
  this.currentPlayer = 'black';
254
+ this.captures = { black: 0, white: 0 };
255
  this.lastMove = null;
 
256
  this.gameMode = 'pvp';
257
+ this.difficulty = 'easy';
 
 
258
  this.initialize();
259
  }
260
 
261
  initialize() {
262
  const boardElement = document.getElementById('board');
263
  boardElement.innerHTML = '';
264
+
265
  for(let i = 0; i < this.size; i++) {
266
  for(let j = 0; j < this.size; j++) {
267
  const intersection = document.createElement('div');
268
  intersection.className = 'intersection';
269
  intersection.dataset.row = i;
270
  intersection.dataset.col = j;
271
+
272
+ if(this.isStarPoint(i, j)) {
273
+ intersection.classList.add('star-point');
274
+ }
275
+
276
  intersection.addEventListener('click', (e) => this.handleMove(e));
277
  boardElement.appendChild(intersection);
278
  }
 
284
  this.gameMode = e.target.value;
285
  this.reset();
286
  });
287
+ document.getElementById('difficulty').addEventListener('change', (e) => {
288
+ this.difficulty = e.target.value;
289
+ });
290
+
291
+ this.updateDisplay();
292
+ }
293
+
294
+ isStarPoint(row, col) {
295
+ const starPoints = [
296
+ [3,3], [3,9], [3,15],
297
+ [9,3], [9,9], [9,15],
298
+ [15,3], [15,9], [15,15]
299
+ ];
300
+ return starPoints.some(point => point[0] === row && point[1] === col);
301
  }
302
 
303
  handleMove(e) {
 
316
  isValidMove(row, col) {
317
  if(this.board[row][col] !== null) return false;
318
 
 
319
  this.board[row][col] = this.currentPlayer;
320
+ const captures = this.checkCaptures(row, col);
321
+ const suicide = this.isSuicideMove(row, col);
322
 
 
323
  this.board[row][col] = null;
324
 
325
+ return !suicide || captures.length > 0;
326
  }
327
 
328
  placeStone(row, col) {
329
+ this.board[row][col] = this.currentPlayer;
330
+ this.renderStone(row, col);
331
+
332
+ const captures = this.checkCaptures(row, col);
333
+ this.removeCaptures(captures);
334
+
335
+ this.lastMove = [row, col];
336
+ this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
337
+ this.updateDisplay();
338
  }
339
 
340
  renderStone(row, col) {
 
343
  stone.className = `stone ${this.currentPlayer}`;
344
  intersection.appendChild(stone);
345
  }
346
+ checkCaptures(row, col) {
347
+ const captures = [];
348
+ const opponent = this.currentPlayer === 'black' ? 'white' : 'black';
349
+ const neighbors = this.getNeighbors(row, col);
350
+
351
+ for(const [nRow, nCol] of neighbors) {
352
+ if(this.board[nRow][nCol] === opponent) {
353
+ const group = this.getGroup(nRow, nCol);
354
+ if(this.isGroupCaptured(group)) {
355
+ captures.push(...group);
356
+ }
357
+ }
358
+ }
359
+
360
+ return captures;
361
+ }
362
+
363
+ removeCaptures(captures) {
364
+ for(const [row, col] of captures) {
365
+ this.board[row][col] = null;
366
+ const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
367
+ intersection.innerHTML = '';
368
+ this.captures[this.currentPlayer]++;
369
+ }
370
+ }
371
 
372
  getGroup(row, col) {
373
  const color = this.board[row][col];
374
+ const group = [];
375
+ const visited = new Set();
376
  const stack = [[row, col]];
377
 
378
  while(stack.length > 0) {
379
  const [r, c] = stack.pop();
380
  const key = `${r},${c}`;
381
 
382
+ if(!visited.has(key)) {
383
+ visited.add(key);
384
+ group.push([r, c]);
385
 
386
+ const neighbors = this.getNeighbors(r, c);
387
  for(const [nr, nc] of neighbors) {
388
  if(this.board[nr][nc] === color) {
389
  stack.push([nr, nc]);
 
395
  return group;
396
  }
397
 
398
+ isGroupCaptured(group) {
399
+ for(const [row, col] of group) {
 
 
 
400
  const neighbors = this.getNeighbors(row, col);
401
+ for(const [nRow, nCol] of neighbors) {
402
+ if(this.board[nRow][nCol] === null) return false;
 
 
 
403
  }
404
  }
405
+ return true;
 
406
  }
407
 
408
  getNeighbors(row, col) {
409
  const neighbors = [];
410
  const directions = [[-1,0], [1,0], [0,-1], [0,1]];
411
 
412
+ for(const [dRow, dCol] of directions) {
413
+ const newRow = row + dRow;
414
+ const newCol = col + dCol;
415
 
416
+ if(newRow >= 0 && newRow < this.size &&
417
+ newCol >= 0 && newCol < this.size) {
418
  neighbors.push([newRow, newCol]);
419
  }
420
  }
 
422
  return neighbors;
423
  }
424
 
425
+ isSuicideMove(row, col) {
426
+ const group = this.getGroup(row, col);
427
+ return this.isGroupCaptured(group);
428
+ }
429
+
430
+ pass() {
431
+ this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black';
432
+ this.updateDisplay();
433
+
434
+ if(this.gameMode === 'ai' && this.currentPlayer === 'white') {
435
+ setTimeout(() => this.makeAIMove(), 500);
 
 
 
 
 
 
436
  }
437
  }
438
 
439
+ reset() {
440
+ this.board = Array(this.size).fill().map(() => Array(this.size).fill(null));
441
+ this.currentPlayer = 'black';
442
+ this.captures = { black: 0, white: 0 };
443
+ this.lastMove = null;
444
+ this.initialize();
445
+ }
446
+
447
+ updateDisplay() {
448
+ document.getElementById('currentPlayer').textContent =
449
+ this.currentPlayer.charAt(0).toUpperCase() + this.currentPlayer.slice(1);
450
+ document.getElementById('blackCaptures').textContent = this.captures.black;
451
+ document.getElementById('whiteCaptures').textContent = this.captures.white;
452
+ }
453
+
454
  makeAIMove() {
455
+ if(this.difficulty === 'easy') {
456
+ this.makeRandomMove();
457
+ } else {
458
+ this.makeStrategicMove();
459
+ }
460
+ }
461
+
462
+ makeRandomMove() {
463
  const validMoves = [];
464
  for(let i = 0; i < this.size; i++) {
465
  for(let j = 0; j < this.size; j++) {
 
477
  }
478
  }
479
 
480
+ makeStrategicMove() {
481
+ this.makeRandomMove();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  }
483
  }
484
 
485
  const game = new GoGame();
486
  </script>
487
  </body>
488
+ </html>