kimhyunwoo commited on
Commit
64bc0b8
verified
1 Parent(s): 80d213e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +64 -88
index.html CHANGED
@@ -15,16 +15,16 @@
15
  justify-content: center;
16
  min-height: 100vh;
17
  margin: 0;
18
- background-color: #f0f0f0; /* Light Gray Background */
19
- overflow: hidden; /* Prevents scrollbars if content overflows */
20
  }
21
 
22
  #game-container {
23
- display: flex;
24
- flex-direction: column;
25
- align-items: center;
26
- width: 100%;
27
- max-width: 600px; /* Limit width on larger screens */
28
  }
29
 
30
  #game-title {
@@ -32,59 +32,58 @@
32
  font-weight: bold;
33
  margin-bottom: 0.5em;
34
  text-align: center;
35
- color: #333; /* Darker Text */
36
  user-select: none;
37
  }
38
 
39
  #game-instructions {
40
  text-align: center;
41
  margin-bottom: 1em;
42
- color: #555; /* Slightly Lighter Text */
43
  user-select: none;
44
  }
45
 
46
-
47
  #game-board {
48
- border-radius: 10px; /* Rounded Corners for the board */
49
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Subtle Shadow */
50
- border: 2px solid #a97a04;
51
- background-color: #e6b434; /* A warmer, more inviting yellow */
52
- touch-action: none; /* Prevents default touch behaviors like panning */
53
- /* Remove fixed width and height, use aspect ratio */
54
  }
55
 
56
-
57
  #ai-first {
58
  margin-top: 1em;
59
  padding: 10px 20px;
60
  font-size: 1em;
61
- border: none;
62
  border-radius: 5px;
63
- background-color: #4CAF50; /* Green Button */
64
- color: white;
65
  cursor: pointer;
66
- transition: background-color 0.3s ease;
67
  user-select: none;
68
  }
69
 
70
  #ai-first:hover {
71
- background-color: #367c39; /* Darker Green on Hover */
 
 
72
  }
73
  #ai-first:active {
74
- transform: translateY(1px); /* Slight press effect */
 
75
  }
76
 
77
-
78
- /* Loading indicator (spinner) */
79
  #loading-indicator {
80
- display: none; /* Hidden by default */
81
  margin-top: 20px;
82
- border: 6px solid #f3f3f3; /* Light grey */
83
- border-top: 6px solid #3498db; /* Blue */
84
  border-radius: 50%;
85
  width: 50px;
86
  height: 50px;
87
- animation: spin 2s linear infinite;
88
  }
89
 
90
  @keyframes spin {
@@ -92,17 +91,14 @@
92
  100% { transform: rotate(360deg); }
93
  }
94
 
95
-
96
- /* Mobile-first responsiveness */
97
  @media (max-width: 600px) {
98
- #game-title {
99
- font-size: 1.5em; /* Smaller title on mobile */
100
- }
101
-
102
- #game-instructions {
103
- font-size: 0.9em;
104
- }
105
- /* Adjust padding/margins as needed */
106
  }
107
  </style>
108
  </head>
@@ -116,28 +112,28 @@
116
  <div id="loading-indicator"></div>
117
  </div>
118
  <script>
 
 
119
  function BoardGame(agent, num_rows, num_cols) {
120
  this.agent = agent;
121
- this.audio = new Audio('/stone.ogg');
122
  this.num_cols = num_cols;
123
  this.num_rows = num_rows;
124
  var this_ = this;
125
  this.canvas_ctx = document.getElementById("game-board").getContext("2d");
126
- this.board_scale = 1; // Initial scale, will be calculated.
127
 
128
- // Calculate the board scale dynamically
129
  this.calculateBoardScale = function() {
130
  const containerWidth = document.getElementById("game-container").offsetWidth;
131
  const containerHeight = window.innerHeight - document.getElementById("game-instructions").offsetHeight
132
- - document.getElementById("ai-first").offsetHeight - 80; //padding
133
 
134
 
135
- const canvasWidth = containerWidth * 0.95; // Use 95% of container width, max 600 (handled in CSS)
136
  const canvasHeight = canvasWidth * (num_rows + 1) / (num_cols + 1);
137
 
138
- //If the calculated height is too big for the container, scale by height
139
  if(canvasHeight > containerHeight) {
140
- const adjustedCanvasHeight = containerHeight * 0.9; //prevent overflow
141
  const adjustedCanvasWidth = adjustedCanvasHeight * (num_cols+1) / (num_rows+1);
142
  this.board_scale = adjustedCanvasHeight / (num_rows + 1);
143
  this_.canvas_ctx.canvas.width = adjustedCanvasWidth;
@@ -148,12 +144,11 @@
148
  this_.canvas_ctx.canvas.width = canvasWidth;
149
  this_.canvas_ctx.canvas.height = canvasHeight;
150
  }
151
-
152
  this_.canvas_ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset transform
153
  this_.canvas_ctx.scale(this.board_scale, this.board_scale);
154
  this_.canvas_ctx.translate(0.5, 0.5);
155
  }
156
- this.calculateBoardScale(); //Initial calculation
157
 
158
 
159
  this.reset = function () {
@@ -170,11 +165,9 @@
170
  return this.board[this.num_cols * row + col];
171
  }
172
  this.is_terminated = function () {
173
- // check if the game is terminated
174
  if (this.board.some((x) => x == 0) == false) return true;
175
  for (let i = 0; i < this.num_rows; i++) {
176
  for (let j = 0; j < this.num_cols; j++) {
177
- // check winner at cell i, j
178
  var p = this.get(i, j);
179
  if (p == 0) continue;
180
  for (let [dx, dy] of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
@@ -194,15 +187,14 @@
194
  return false;
195
  };
196
  this.submit_board = async function () {
197
-
198
- document.getElementById("loading-indicator").style.display = "block"; // Show loader
199
  await new Promise(r => setTimeout(r, 1000));
200
  if (this_.is_terminated()) return { "terminated": true, "action": -1 };
201
  const obs = tf.tensor(this_.board, [this_.num_rows, this_.num_cols], 'float32');
202
  const normalized_obs = tf.mul(obs, this_.ai_player);
203
  const [action_logits, value] = this_.agent.execute(normalized_obs, ["output_0", "output_1"]);
204
  const action = await tf.argMax(action_logits).array();
205
- document.getElementById("loading-indicator").style.display = "none"; // Hide loader
206
  return {
207
  "terminated": false,
208
  "action": action,
@@ -215,7 +207,6 @@
215
  this.ai_play = function () {
216
  this_.submit_board().then(
217
  function (info) {
218
-
219
  let x = info["action"];
220
  if (x != -1) {
221
  let [_, y] = this_.get_candidate(x);
@@ -231,7 +222,7 @@
231
  }
232
  ).catch(function (e) {
233
  console.error("AI play error:", e);
234
- });
235
  };
236
  document.getElementById("ai-first").onclick = function () {
237
  this_.reset();
@@ -239,7 +230,6 @@
239
  this_.ai_play();
240
  };
241
 
242
- // Unified event handler for touch and mouse
243
  this.handleClick = function(x, y) {
244
  var loc_x = Math.floor(x / this_.board_scale - 0.5);
245
  var loc_y = Math.floor(y / this_.board_scale - 0.5);
@@ -264,9 +254,8 @@
264
  }
265
  }
266
 
267
- // Touch event handler
268
  document.getElementById("game-board").addEventListener('touchstart', function(e) {
269
- e.preventDefault(); // Prevents additional mouse events
270
  var rect = this.getBoundingClientRect();
271
  var touch = e.touches[0];
272
  var x = touch.clientX - rect.left;
@@ -292,7 +281,7 @@
292
  ctx.fillStyle = color;
293
  ctx.fill();
294
  ctx.lineWidth = 0.02;
295
- ctx.strokeStyle = "black"; // Keep a subtle border
296
  ctx.stroke();
297
  };
298
  this.get_candidate = function (x) {
@@ -304,19 +293,16 @@
304
  };
305
  this.render = function () {
306
  let ctx = this.canvas_ctx;
307
- ctx.clearRect(-1, -1, num_cols + 1, num_rows + 1); // Clear entire canvas
308
-
309
- // Draw the board (wood-like)
310
- ctx.fillStyle = "#e6b434"; // Lighter wood color
311
  ctx.fillRect(-0.5, -0.5, num_cols, num_rows);
312
 
313
- // Draw grid lines
314
  ctx.lineWidth = 0.1 / 2;
315
- ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"; // More subtle lines
316
  for (let i = 0; i < this.num_cols; i++) {
317
  ctx.beginPath();
318
  ctx.moveTo(i, 0);
319
- ctx.lineTo(i, this.num_rows -1);
320
  ctx.stroke();
321
  }
322
  for (let i = 0; i < this.num_rows; i++) {
@@ -326,14 +312,14 @@
326
  ctx.stroke();
327
  }
328
 
329
-
330
  for (let i = 0; i < this.board.length; i++) {
331
  let x = i % this.num_cols;
332
  let y = Math.floor(i / this.num_cols);
333
  if (this.board[i] == 0) continue;
334
- let color = (this.board[i] == 1) ? "#c0392b" : "#f1c40f"; // Red and Yellow
335
  this.draw_stone(x, y, color);
336
  }
 
337
  if (
338
  this.mouse_x >= 0 &&
339
  this.mouse_y >= 0 &&
@@ -344,15 +330,11 @@
344
  if (x == -1) return;
345
  this.mouse_x = x;
346
  this.mouse_y = y;
347
-
348
- // Preview stone
349
- let previewColor = (this.who_play == -1) ? "rgba(241, 196, 15, 0.5)" : "rgba(192, 57, 43, 0.5)"; // Semi-transparent
350
  this.draw_stone(x, y, previewColor);
351
-
352
  }
353
  };
354
 
355
- // Unified move handler
356
  this.handleMove = function(e) {
357
  let rect = this.canvas_ctx.canvas.getBoundingClientRect();
358
  let x, y;
@@ -362,43 +344,38 @@
362
  y = e.clientY - rect.top;
363
  } else if (e.type === 'touchmove') {
364
  e.preventDefault();
365
- if (e.touches.length > 0) { // Check if there are any touches
366
  x = e.touches[0].clientX - rect.left;
367
  y = e.touches[0].clientY - rect.top;
368
  } else {
369
- return; // No touches, exit
370
  }
371
  } else {
372
- return; // Unsupported event type
373
  }
374
 
375
  var loc_x = Math.floor(x / this_.board_scale - 0.5);
376
  var loc_y = Math.floor(y / this_.board_scale - 0.5);
377
  this_.mouse_x = loc_x;
378
- this_.mouse_y = loc_y; //Don't get candidate here. Only on click/tap
379
  this_.render();
380
  }
381
 
382
-
383
  document.getElementById("game-board").onmousemove = function (e) {
384
- this_.handleMove(e);
385
  };
386
 
387
- document.getElementById("game-board").addEventListener('touchmove', function(e) {
388
  this_.handleMove(e);
389
- }, false);
390
-
391
 
392
 
393
- //Resize listener
394
  window.addEventListener('resize', function() {
395
  this_.calculateBoardScale();
396
  this_.render();
397
  });
398
-
399
-
400
  };
401
- const modelUrl = '/model.json'; // Make sure path is correct
402
 
403
  const init_fn = async function () {
404
  await tf.setBackend('wasm');
@@ -412,9 +389,8 @@
412
 
413
  }).catch(error => {
414
  console.error("Error loading model:", error);
415
- // Handle the error, e.g., display a message to the user.
416
  document.getElementById("game-instructions").textContent = "Failed to load the AI. Please try refreshing the page.";
417
- document.getElementById("ai-first").disabled = true; // Disable the button.
418
  });
419
  });
420
  </script>
 
15
  justify-content: center;
16
  min-height: 100vh;
17
  margin: 0;
18
+ background-color: #f5f5f5; /* Very Light Gray */
19
+ overflow: hidden;
20
  }
21
 
22
  #game-container {
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ width: 100%;
27
+ max-width: 600px;
28
  }
29
 
30
  #game-title {
 
32
  font-weight: bold;
33
  margin-bottom: 0.5em;
34
  text-align: center;
35
+ color: #444; /* Darker Gray */
36
  user-select: none;
37
  }
38
 
39
  #game-instructions {
40
  text-align: center;
41
  margin-bottom: 1em;
42
+ color: #666; /* Medium Gray */
43
  user-select: none;
44
  }
45
 
 
46
  #game-board {
47
+ border-radius: 8px; /* Slightly Rounded Corners */
48
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Very Subtle Shadow */
49
+ border: 1px solid #ddd; /* Light Gray Border */
50
+ background-color: #fff; /* White Background */
51
+ touch-action: none;
 
52
  }
53
 
 
54
  #ai-first {
55
  margin-top: 1em;
56
  padding: 10px 20px;
57
  font-size: 1em;
58
+ border: 1px solid #ccc; /* Light Gray Border */
59
  border-radius: 5px;
60
+ background-color: #f9f9f9; /* Almost White */
61
+ color: #555; /* Dark Gray Text */
62
  cursor: pointer;
63
+ transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
64
  user-select: none;
65
  }
66
 
67
  #ai-first:hover {
68
+ background-color: #eee; /* Slightly Darker on Hover */
69
+ border-color: #bbb; /* Darker Border on Hover */
70
+ color: #333;
71
  }
72
  #ai-first:active {
73
+ transform: translateY(1px);
74
+ background-color: #ddd;
75
  }
76
 
77
+ /* Loading indicator */
 
78
  #loading-indicator {
79
+ display: none;
80
  margin-top: 20px;
81
+ border: 6px solid #f3f3f3;
82
+ border-top: 6px solid #999; /* Medium Gray */
83
  border-radius: 50%;
84
  width: 50px;
85
  height: 50px;
86
+ animation: spin 1s linear infinite; /* Faster Spin */
87
  }
88
 
89
  @keyframes spin {
 
91
  100% { transform: rotate(360deg); }
92
  }
93
 
 
 
94
  @media (max-width: 600px) {
95
+ #game-title {
96
+ font-size: 1.5em;
97
+ }
98
+
99
+ #game-instructions {
100
+ font-size: 0.9em;
101
+ }
 
102
  }
103
  </style>
104
  </head>
 
112
  <div id="loading-indicator"></div>
113
  </div>
114
  <script>
115
+ // ... (rest of your JavaScript code, with changes below) ...
116
+
117
  function BoardGame(agent, num_rows, num_cols) {
118
  this.agent = agent;
119
+ this.audio = new Audio('/stone.ogg'); // Consider a more subtle sound.
120
  this.num_cols = num_cols;
121
  this.num_rows = num_rows;
122
  var this_ = this;
123
  this.canvas_ctx = document.getElementById("game-board").getContext("2d");
124
+ this.board_scale = 1; // Initial scale
125
 
 
126
  this.calculateBoardScale = function() {
127
  const containerWidth = document.getElementById("game-container").offsetWidth;
128
  const containerHeight = window.innerHeight - document.getElementById("game-instructions").offsetHeight
129
+ - document.getElementById("ai-first").offsetHeight - 80;
130
 
131
 
132
+ const canvasWidth = containerWidth * 0.95; // Use 95% of container width
133
  const canvasHeight = canvasWidth * (num_rows + 1) / (num_cols + 1);
134
 
 
135
  if(canvasHeight > containerHeight) {
136
+ const adjustedCanvasHeight = containerHeight * 0.9;
137
  const adjustedCanvasWidth = adjustedCanvasHeight * (num_cols+1) / (num_rows+1);
138
  this.board_scale = adjustedCanvasHeight / (num_rows + 1);
139
  this_.canvas_ctx.canvas.width = adjustedCanvasWidth;
 
144
  this_.canvas_ctx.canvas.width = canvasWidth;
145
  this_.canvas_ctx.canvas.height = canvasHeight;
146
  }
 
147
  this_.canvas_ctx.setTransform(1, 0, 0, 1, 0, 0); // Reset transform
148
  this_.canvas_ctx.scale(this.board_scale, this.board_scale);
149
  this_.canvas_ctx.translate(0.5, 0.5);
150
  }
151
+ this.calculateBoardScale();
152
 
153
 
154
  this.reset = function () {
 
165
  return this.board[this.num_cols * row + col];
166
  }
167
  this.is_terminated = function () {
 
168
  if (this.board.some((x) => x == 0) == false) return true;
169
  for (let i = 0; i < this.num_rows; i++) {
170
  for (let j = 0; j < this.num_cols; j++) {
 
171
  var p = this.get(i, j);
172
  if (p == 0) continue;
173
  for (let [dx, dy] of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
 
187
  return false;
188
  };
189
  this.submit_board = async function () {
190
+ document.getElementById("loading-indicator").style.display = "block";
 
191
  await new Promise(r => setTimeout(r, 1000));
192
  if (this_.is_terminated()) return { "terminated": true, "action": -1 };
193
  const obs = tf.tensor(this_.board, [this_.num_rows, this_.num_cols], 'float32');
194
  const normalized_obs = tf.mul(obs, this_.ai_player);
195
  const [action_logits, value] = this_.agent.execute(normalized_obs, ["output_0", "output_1"]);
196
  const action = await tf.argMax(action_logits).array();
197
+ document.getElementById("loading-indicator").style.display = "none";
198
  return {
199
  "terminated": false,
200
  "action": action,
 
207
  this.ai_play = function () {
208
  this_.submit_board().then(
209
  function (info) {
 
210
  let x = info["action"];
211
  if (x != -1) {
212
  let [_, y] = this_.get_candidate(x);
 
222
  }
223
  ).catch(function (e) {
224
  console.error("AI play error:", e);
225
+ });
226
  };
227
  document.getElementById("ai-first").onclick = function () {
228
  this_.reset();
 
230
  this_.ai_play();
231
  };
232
 
 
233
  this.handleClick = function(x, y) {
234
  var loc_x = Math.floor(x / this_.board_scale - 0.5);
235
  var loc_y = Math.floor(y / this_.board_scale - 0.5);
 
254
  }
255
  }
256
 
 
257
  document.getElementById("game-board").addEventListener('touchstart', function(e) {
258
+ e.preventDefault();
259
  var rect = this.getBoundingClientRect();
260
  var touch = e.touches[0];
261
  var x = touch.clientX - rect.left;
 
281
  ctx.fillStyle = color;
282
  ctx.fill();
283
  ctx.lineWidth = 0.02;
284
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.1)"; // Very Light Border
285
  ctx.stroke();
286
  };
287
  this.get_candidate = function (x) {
 
293
  };
294
  this.render = function () {
295
  let ctx = this.canvas_ctx;
296
+ ctx.clearRect(-1, -1, num_cols + 1, num_rows + 1);
297
+ ctx.fillStyle = "#fff"; // White board
 
 
298
  ctx.fillRect(-0.5, -0.5, num_cols, num_rows);
299
 
 
300
  ctx.lineWidth = 0.1 / 2;
301
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.1)"; // Very subtle grid lines
302
  for (let i = 0; i < this.num_cols; i++) {
303
  ctx.beginPath();
304
  ctx.moveTo(i, 0);
305
+ ctx.lineTo(i, this.num_rows-1);
306
  ctx.stroke();
307
  }
308
  for (let i = 0; i < this.num_rows; i++) {
 
312
  ctx.stroke();
313
  }
314
 
 
315
  for (let i = 0; i < this.board.length; i++) {
316
  let x = i % this.num_cols;
317
  let y = Math.floor(i / this.num_cols);
318
  if (this.board[i] == 0) continue;
319
+ let color = (this.board[i] == 1) ? "#555" : "#bbb"; // Dark Gray and Light Gray
320
  this.draw_stone(x, y, color);
321
  }
322
+
323
  if (
324
  this.mouse_x >= 0 &&
325
  this.mouse_y >= 0 &&
 
330
  if (x == -1) return;
331
  this.mouse_x = x;
332
  this.mouse_y = y;
333
+ let previewColor = (this.who_play == -1) ? "rgba(187, 187, 187, 0.5)" : "rgba(85, 85, 85, 0.5)"; // Semi-transparent grays
 
 
334
  this.draw_stone(x, y, previewColor);
 
335
  }
336
  };
337
 
 
338
  this.handleMove = function(e) {
339
  let rect = this.canvas_ctx.canvas.getBoundingClientRect();
340
  let x, y;
 
344
  y = e.clientY - rect.top;
345
  } else if (e.type === 'touchmove') {
346
  e.preventDefault();
347
+ if (e.touches.length > 0) {
348
  x = e.touches[0].clientX - rect.left;
349
  y = e.touches[0].clientY - rect.top;
350
  } else {
351
+ return;
352
  }
353
  } else {
354
+ return;
355
  }
356
 
357
  var loc_x = Math.floor(x / this_.board_scale - 0.5);
358
  var loc_y = Math.floor(y / this_.board_scale - 0.5);
359
  this_.mouse_x = loc_x;
360
+ this_.mouse_y = loc_y;
361
  this_.render();
362
  }
363
 
 
364
  document.getElementById("game-board").onmousemove = function (e) {
365
+ this_.handleMove(e);
366
  };
367
 
368
+ document.getElementById("game-board").addEventListener('touchmove', function(e) {
369
  this_.handleMove(e);
370
+ }, false);
 
371
 
372
 
 
373
  window.addEventListener('resize', function() {
374
  this_.calculateBoardScale();
375
  this_.render();
376
  });
 
 
377
  };
378
+ const modelUrl = '/model.json';
379
 
380
  const init_fn = async function () {
381
  await tf.setBackend('wasm');
 
389
 
390
  }).catch(error => {
391
  console.error("Error loading model:", error);
 
392
  document.getElementById("game-instructions").textContent = "Failed to load the AI. Please try refreshing the page.";
393
+ document.getElementById("ai-first").disabled = true;
394
  });
395
  });
396
  </script>