Spaces:
Running
Running
Update index.html
Browse files- index.html +77 -129
index.html
CHANGED
@@ -15,8 +15,8 @@
|
|
15 |
justify-content: center;
|
16 |
min-height: 100vh;
|
17 |
margin: 0;
|
18 |
-
background-color: #f5f5f5;
|
19 |
-
overflow
|
20 |
}
|
21 |
|
22 |
#game-container {
|
@@ -24,7 +24,7 @@
|
|
24 |
flex-direction: column;
|
25 |
align-items: center;
|
26 |
width: 100%;
|
27 |
-
max-width: 600px;
|
28 |
}
|
29 |
|
30 |
#game-title {
|
@@ -32,68 +32,58 @@
|
|
32 |
font-weight: bold;
|
33 |
margin-bottom: 0.5em;
|
34 |
text-align: center;
|
35 |
-
color: #444;
|
36 |
user-select: none;
|
37 |
}
|
38 |
|
39 |
#game-instructions {
|
40 |
text-align: center;
|
41 |
margin-bottom: 1em;
|
42 |
-
color: #666;
|
43 |
user-select: none;
|
44 |
-
width: 90%;
|
45 |
}
|
46 |
|
47 |
#game-board {
|
48 |
-
border-radius: 8px;
|
49 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
50 |
-
border: 1px solid #ddd;
|
51 |
-
background-color: #fff;
|
52 |
touch-action: none;
|
53 |
-
display: block;
|
54 |
-
margin: 0 auto;
|
55 |
}
|
56 |
|
57 |
-
#
|
58 |
-
display: flex;
|
59 |
-
gap: 10px;
|
60 |
margin-top: 1em;
|
61 |
-
justify-content: center;
|
62 |
-
width: 100%;
|
63 |
-
}
|
64 |
-
|
65 |
-
.game-button {
|
66 |
padding: 10px 20px;
|
67 |
font-size: 1em;
|
68 |
-
border: 1px solid #ccc;
|
69 |
border-radius: 5px;
|
70 |
-
background-color: #f9f9f9;
|
71 |
-
color: #555;
|
72 |
cursor: pointer;
|
73 |
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
74 |
user-select: none;
|
75 |
}
|
76 |
|
77 |
-
|
78 |
-
background-color: #eee;
|
79 |
-
border-color: #bbb;
|
80 |
color: #333;
|
81 |
}
|
82 |
-
|
83 |
-
.game-button:active {
|
84 |
transform: translateY(1px);
|
85 |
background-color: #ddd;
|
86 |
}
|
87 |
|
|
|
88 |
#loading-indicator {
|
89 |
display: none;
|
90 |
margin-top: 20px;
|
91 |
border: 6px solid #f3f3f3;
|
92 |
-
border-top: 6px solid #999;
|
93 |
border-radius: 50%;
|
94 |
width: 50px;
|
95 |
height: 50px;
|
96 |
-
animation: spin 1s linear infinite;
|
97 |
}
|
98 |
|
99 |
@keyframes spin {
|
@@ -109,10 +99,6 @@
|
|
109 |
#game-instructions {
|
110 |
font-size: 0.9em;
|
111 |
}
|
112 |
-
|
113 |
-
.game-button {
|
114 |
-
padding: 8px 16px;
|
115 |
-
}
|
116 |
}
|
117 |
</style>
|
118 |
</head>
|
@@ -122,64 +108,47 @@
|
|
122 |
<h1 id="game-title">Connect Four</h1>
|
123 |
<p id="game-instructions">Try to connect four stones in a row, a column, or a diagonal.</p>
|
124 |
<canvas id="game-board"></canvas>
|
125 |
-
<
|
126 |
-
<button type="button" id="ai-first" class="game-button">AI First</button>
|
127 |
-
<button type="button" id="reset-button" class="game-button">Restart</button>
|
128 |
-
</div>
|
129 |
<div id="loading-indicator"></div>
|
130 |
</div>
|
131 |
-
|
132 |
<script>
|
|
|
|
|
133 |
function BoardGame(agent, num_rows, num_cols) {
|
134 |
this.agent = agent;
|
135 |
-
this.audio = new Audio('/stone.ogg');
|
136 |
this.num_cols = num_cols;
|
137 |
this.num_rows = num_rows;
|
138 |
var this_ = this;
|
139 |
this.canvas_ctx = document.getElementById("game-board").getContext("2d");
|
140 |
-
this.board_scale = 1;
|
141 |
|
142 |
-
this.calculateBoardScale = function
|
143 |
-
// 1. Get the container width.
|
144 |
const containerWidth = document.getElementById("game-container").offsetWidth;
|
|
|
|
|
145 |
|
146 |
-
// 2. Calculate the *available* height. This is the crucial part.
|
147 |
-
// We subtract the heights of *all* other elements *and* a margin.
|
148 |
-
const containerHeight = window.innerHeight
|
149 |
-
- document.getElementById("game-title").offsetHeight
|
150 |
-
- document.getElementById("game-instructions").offsetHeight
|
151 |
-
- document.getElementById("button-container").offsetHeight
|
152 |
-
- document.getElementById("loading-indicator").offsetHeight // Include loading indicator
|
153 |
-
- 40; // Margin (adjust as needed)
|
154 |
|
155 |
-
|
156 |
-
const canvasWidth = containerWidth * 0.95;
|
157 |
-
|
158 |
-
// 4. Calculate the corresponding height to maintain the aspect ratio.
|
159 |
const canvasHeight = canvasWidth * (num_rows + 1) / (num_cols + 1);
|
160 |
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
const adjustedCanvasHeight = containerHeight * 0.9; // Use most of available height
|
165 |
-
const adjustedCanvasWidth = adjustedCanvasHeight * (num_cols + 1) / (num_rows + 1);
|
166 |
this.board_scale = adjustedCanvasHeight / (num_rows + 1);
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
// 5b. If it fits, use the calculated width and height.
|
172 |
this.board_scale = canvasWidth / (num_cols + 1);
|
173 |
-
|
174 |
-
|
175 |
}
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
};
|
182 |
-
this.calculateBoardScale(); // Initial calculation
|
183 |
|
184 |
|
185 |
this.reset = function () {
|
@@ -190,16 +159,12 @@
|
|
190 |
this.who_play = 1;
|
191 |
this.ai_player = -1;
|
192 |
this.game_ended = false;
|
193 |
-
this.render();
|
194 |
};
|
195 |
-
this.reset();
|
196 |
-
|
197 |
this.get = function (row, col) {
|
198 |
return this.board[this.num_cols * row + col];
|
199 |
-
}
|
200 |
-
|
201 |
this.is_terminated = function () {
|
202 |
-
// ... (rest of is_terminated is the same) ...
|
203 |
if (this.board.some((x) => x == 0) == false) return true;
|
204 |
for (let i = 0; i < this.num_rows; i++) {
|
205 |
for (let j = 0; j < this.num_cols; j++) {
|
@@ -221,9 +186,7 @@
|
|
221 |
}
|
222 |
return false;
|
223 |
};
|
224 |
-
|
225 |
this.submit_board = async function () {
|
226 |
-
// ... (rest of submit_board is the same) ...
|
227 |
document.getElementById("loading-indicator").style.display = "block";
|
228 |
await new Promise(r => setTimeout(r, 1000));
|
229 |
if (this_.is_terminated()) return { "terminated": true, "action": -1 };
|
@@ -237,15 +200,11 @@
|
|
237 |
"action": action,
|
238 |
};
|
239 |
};
|
240 |
-
|
241 |
this.end_game = function () {
|
242 |
this.game_ended = true;
|
243 |
-
setTimeout(function () { this_.reset(); }, 3000);
|
244 |
};
|
245 |
-
|
246 |
this.ai_play = function () {
|
247 |
-
if (this_.game_ended) return;
|
248 |
-
|
249 |
this_.submit_board().then(
|
250 |
function (info) {
|
251 |
let x = info["action"];
|
@@ -265,10 +224,13 @@
|
|
265 |
console.error("AI play error:", e);
|
266 |
});
|
267 |
};
|
|
|
|
|
|
|
|
|
|
|
268 |
|
269 |
-
this.handleClick = function
|
270 |
-
if (this_.game_ended) return;
|
271 |
-
|
272 |
var loc_x = Math.floor(x / this_.board_scale - 0.5);
|
273 |
var loc_y = Math.floor(y / this_.board_scale - 0.5);
|
274 |
this_.mouse_x = loc_x;
|
@@ -278,26 +240,27 @@
|
|
278 |
this_.mouse_x >= 0 &&
|
279 |
this_.mouse_y >= 0 &&
|
280 |
this_.mouse_x < this_.num_cols &&
|
281 |
-
this_.mouse_y < this_.num_rows
|
|
|
282 |
) {
|
283 |
-
if (this_.who_play == this_.ai_player) return;
|
284 |
let i = this_.mouse_y * this_.num_cols + this_.mouse_x;
|
285 |
-
if (this_.board[i] != 0) return;
|
286 |
this_.board[i] = this_.who_play;
|
287 |
this_.audio.play();
|
288 |
this_.who_play = -this_.who_play;
|
289 |
this_.render();
|
290 |
this_.ai_play();
|
291 |
}
|
292 |
-
}
|
|
|
293 |
document.getElementById("game-board").addEventListener('touchstart', function(e) {
|
294 |
-
e.preventDefault();
|
295 |
var rect = this.getBoundingClientRect();
|
296 |
var touch = e.touches[0];
|
297 |
var x = touch.clientX - rect.left;
|
298 |
var y = touch.clientY - rect.top;
|
299 |
-
this_.handleClick(x,y);
|
300 |
-
|
301 |
}, false);
|
302 |
|
303 |
|
@@ -308,37 +271,34 @@
|
|
308 |
this_.handleClick(x,y);
|
309 |
}, false);
|
310 |
|
|
|
|
|
311 |
this.draw_stone = function (x, y, color) {
|
312 |
-
|
313 |
-
let ctx = this.canvas_ctx;
|
314 |
y = this.num_rows - 1 - y;
|
315 |
ctx.beginPath();
|
316 |
ctx.arc(x, y, 0.40, 0, 2 * Math.PI, false);
|
317 |
ctx.fillStyle = color;
|
318 |
ctx.fill();
|
319 |
ctx.lineWidth = 0.02;
|
320 |
-
ctx.strokeStyle = "rgba(0, 0, 0, 0.1)";
|
321 |
ctx.stroke();
|
322 |
};
|
323 |
-
|
324 |
this.get_candidate = function (x) {
|
325 |
-
// ... (rest of get_candidate is the same) ...
|
326 |
for (let i = 0; i < this.num_rows; i++) {
|
327 |
let idx = i * this.num_cols + x;
|
328 |
if (this.board[idx] == 0) return [x, i];
|
329 |
}
|
330 |
return [-1, -1];
|
331 |
};
|
332 |
-
|
333 |
this.render = function () {
|
334 |
-
// ... (rest of render is the same, except for colors) ...
|
335 |
let ctx = this.canvas_ctx;
|
336 |
ctx.clearRect(-1, -1, num_cols + 1, num_rows + 1);
|
337 |
-
ctx.fillStyle = "#fff";
|
338 |
ctx.fillRect(-0.5, -0.5, num_cols, num_rows);
|
339 |
|
340 |
ctx.lineWidth = 0.1 / 2;
|
341 |
-
ctx.strokeStyle = "rgba(0, 0, 0, 0.1)";
|
342 |
for (let i = 0; i < this.num_cols; i++) {
|
343 |
ctx.beginPath();
|
344 |
ctx.moveTo(i, 0);
|
@@ -356,7 +316,7 @@
|
|
356 |
let x = i % this.num_cols;
|
357 |
let y = Math.floor(i / this.num_cols);
|
358 |
if (this.board[i] == 0) continue;
|
359 |
-
let color = (this.board[i] == 1) ? "#
|
360 |
this.draw_stone(x, y, color);
|
361 |
}
|
362 |
|
@@ -370,12 +330,12 @@
|
|
370 |
if (x == -1) return;
|
371 |
this.mouse_x = x;
|
372 |
this.mouse_y = y;
|
373 |
-
let previewColor = (this.who_play == -1) ? "rgba(
|
374 |
this.draw_stone(x, y, previewColor);
|
375 |
}
|
376 |
};
|
377 |
|
378 |
-
this.handleMove = function
|
379 |
let rect = this.canvas_ctx.canvas.getBoundingClientRect();
|
380 |
let x, y;
|
381 |
|
@@ -405,26 +365,16 @@
|
|
405 |
this_.handleMove(e);
|
406 |
};
|
407 |
|
408 |
-
|
409 |
this_.handleMove(e);
|
410 |
-
|
411 |
-
|
412 |
-
window.addEventListener('resize', function () {
|
413 |
-
this_.calculateBoardScale(); // Recalculate on resize
|
414 |
-
this_.render(); // Redraw
|
415 |
-
});
|
416 |
-
|
417 |
-
document.getElementById("ai-first").onclick = function () {
|
418 |
-
this_.reset();
|
419 |
-
this_.ai_player = 1;
|
420 |
-
this_.ai_play();
|
421 |
-
};
|
422 |
|
423 |
-
document.getElementById("reset-button").onclick = function () {
|
424 |
-
this_.reset();
|
425 |
-
};
|
426 |
-
}
|
427 |
|
|
|
|
|
|
|
|
|
|
|
428 |
const modelUrl = '/model.json';
|
429 |
|
430 |
const init_fn = async function () {
|
@@ -432,17 +382,15 @@
|
|
432 |
const model = await tf.loadGraphModel(modelUrl);
|
433 |
return model;
|
434 |
};
|
435 |
-
|
436 |
document.addEventListener("DOMContentLoaded", function (event) {
|
437 |
init_fn().then(function (agent) {
|
438 |
game = new BoardGame(agent, 6, 7);
|
439 |
-
game.render();
|
440 |
|
441 |
}).catch(error => {
|
442 |
console.error("Error loading model:", error);
|
443 |
document.getElementById("game-instructions").textContent = "Failed to load the AI. Please try refreshing the page.";
|
444 |
document.getElementById("ai-first").disabled = true;
|
445 |
-
document.getElementById("reset-button").disabled = true;
|
446 |
});
|
447 |
});
|
448 |
</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 {
|
|
|
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 {
|
|
|
99 |
#game-instructions {
|
100 |
font-size: 0.9em;
|
101 |
}
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
</style>
|
104 |
</head>
|
|
|
108 |
<h1 id="game-title">Connect Four</h1>
|
109 |
<p id="game-instructions">Try to connect four stones in a row, a column, or a diagonal.</p>
|
110 |
<canvas id="game-board"></canvas>
|
111 |
+
<button type="button" id="ai-first">AI goes first</button>
|
|
|
|
|
|
|
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;
|
140 |
+
this_.canvas_ctx.canvas.height = adjustedCanvasHeight;
|
141 |
+
}
|
142 |
+
else{
|
|
|
143 |
this.board_scale = canvasWidth / (num_cols + 1);
|
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 () {
|
|
|
159 |
this.who_play = 1;
|
160 |
this.ai_player = -1;
|
161 |
this.game_ended = false;
|
|
|
162 |
};
|
163 |
+
this.reset();
|
|
|
164 |
this.get = function (row, col) {
|
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++) {
|
|
|
186 |
}
|
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 };
|
|
|
200 |
"action": action,
|
201 |
};
|
202 |
};
|
|
|
203 |
this.end_game = function () {
|
204 |
this.game_ended = true;
|
205 |
+
setTimeout(function () { this_.reset(); this_.render(); }, 3000);
|
206 |
};
|
|
|
207 |
this.ai_play = function () {
|
|
|
|
|
208 |
this_.submit_board().then(
|
209 |
function (info) {
|
210 |
let x = info["action"];
|
|
|
224 |
console.error("AI play error:", e);
|
225 |
});
|
226 |
};
|
227 |
+
document.getElementById("ai-first").onclick = function () {
|
228 |
+
this_.reset();
|
229 |
+
this_.ai_player = 1;
|
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);
|
236 |
this_.mouse_x = loc_x;
|
|
|
240 |
this_.mouse_x >= 0 &&
|
241 |
this_.mouse_y >= 0 &&
|
242 |
this_.mouse_x < this_.num_cols &&
|
243 |
+
this_.mouse_y < this_.num_rows &&
|
244 |
+
this_.game_ended == false
|
245 |
) {
|
246 |
+
if (this_.who_play == this_.ai_player) return false;
|
247 |
let i = this_.mouse_y * this_.num_cols + this_.mouse_x;
|
248 |
+
if (this_.board[i] != 0) return false;
|
249 |
this_.board[i] = this_.who_play;
|
250 |
this_.audio.play();
|
251 |
this_.who_play = -this_.who_play;
|
252 |
this_.render();
|
253 |
this_.ai_play();
|
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;
|
262 |
var y = touch.clientY - rect.top;
|
263 |
+
this_.handleClick(x, y);
|
|
|
264 |
}, false);
|
265 |
|
266 |
|
|
|
271 |
this_.handleClick(x,y);
|
272 |
}, false);
|
273 |
|
274 |
+
|
275 |
+
|
276 |
this.draw_stone = function (x, y, color) {
|
277 |
+
let ctx = this.canvas_ctx;
|
|
|
278 |
y = this.num_rows - 1 - y;
|
279 |
ctx.beginPath();
|
280 |
ctx.arc(x, y, 0.40, 0, 2 * Math.PI, false);
|
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) {
|
|
|
288 |
for (let i = 0; i < this.num_rows; i++) {
|
289 |
let idx = i * this.num_cols + x;
|
290 |
if (this.board[idx] == 0) return [x, i];
|
291 |
}
|
292 |
return [-1, -1];
|
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);
|
|
|
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 |
|
|
|
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;
|
341 |
|
|
|
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 () {
|
|
|
382 |
const model = await tf.loadGraphModel(modelUrl);
|
383 |
return model;
|
384 |
};
|
|
|
385 |
document.addEventListener("DOMContentLoaded", function (event) {
|
386 |
init_fn().then(function (agent) {
|
387 |
game = new BoardGame(agent, 6, 7);
|
388 |
+
game.render();
|
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>
|