Spaces:
Running
Running
Update index.html
Browse files- index.html +141 -68
index.html
CHANGED
|
@@ -16,7 +16,6 @@
|
|
| 16 |
align-items: center;
|
| 17 |
flex-direction: column;
|
| 18 |
}
|
| 19 |
-
|
| 20 |
.game-container {
|
| 21 |
background: rgba(255, 255, 255, 0.95);
|
| 22 |
border-radius: 20px;
|
|
@@ -24,7 +23,6 @@
|
|
| 24 |
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
| 25 |
backdrop-filter: blur(10px);
|
| 26 |
}
|
| 27 |
-
|
| 28 |
h1 {
|
| 29 |
text-align: center;
|
| 30 |
color: #333;
|
|
@@ -32,7 +30,6 @@
|
|
| 32 |
font-size: 2.5rem;
|
| 33 |
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
| 34 |
}
|
| 35 |
-
|
| 36 |
.game-info {
|
| 37 |
display: flex;
|
| 38 |
justify-content: space-between;
|
|
@@ -40,7 +37,6 @@
|
|
| 40 |
margin-bottom: 20px;
|
| 41 |
gap: 20px;
|
| 42 |
}
|
| 43 |
-
|
| 44 |
.player-info {
|
| 45 |
background: rgba(0, 0, 0, 0.05);
|
| 46 |
padding: 15px;
|
|
@@ -48,18 +44,15 @@
|
|
| 48 |
min-width: 150px;
|
| 49 |
text-align: center;
|
| 50 |
}
|
| 51 |
-
|
| 52 |
.player-info.active {
|
| 53 |
background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
|
| 54 |
box-shadow: 0 4px 15px rgba(252, 182, 159, 0.4);
|
| 55 |
}
|
| 56 |
-
|
| 57 |
.board-container {
|
| 58 |
display: flex;
|
| 59 |
justify-content: center;
|
| 60 |
margin-bottom: 20px;
|
| 61 |
}
|
| 62 |
-
|
| 63 |
.board {
|
| 64 |
width: 400px;
|
| 65 |
height: 400px;
|
|
@@ -68,20 +61,16 @@
|
|
| 68 |
border-radius: 10px;
|
| 69 |
border: 3px solid #333;
|
| 70 |
}
|
| 71 |
-
|
| 72 |
.board-line {
|
| 73 |
position: absolute;
|
| 74 |
background-color: #333;
|
| 75 |
}
|
| 76 |
-
|
| 77 |
.horizontal {
|
| 78 |
height: 3px;
|
| 79 |
}
|
| 80 |
-
|
| 81 |
.vertical {
|
| 82 |
width: 3px;
|
| 83 |
}
|
| 84 |
-
|
| 85 |
.position {
|
| 86 |
position: absolute;
|
| 87 |
width: 30px;
|
|
@@ -96,44 +85,40 @@
|
|
| 96 |
align-items: center;
|
| 97 |
justify-content: center;
|
| 98 |
}
|
| 99 |
-
|
| 100 |
.position:hover {
|
| 101 |
box-shadow: 0 0 20px rgba(102, 126, 234, 0.6);
|
| 102 |
transform: translate(-50%, -50%) scale(1.1);
|
| 103 |
}
|
| 104 |
-
|
| 105 |
.position.occupied {
|
| 106 |
cursor: default;
|
| 107 |
}
|
| 108 |
-
|
| 109 |
.position.red {
|
| 110 |
background: #dc3545;
|
| 111 |
border-color: #a02834;
|
| 112 |
box-shadow: 0 4px 10px rgba(220, 53, 69, 0.5);
|
| 113 |
}
|
| 114 |
-
|
| 115 |
.position.black {
|
| 116 |
background: #333;
|
| 117 |
border-color: #666;
|
| 118 |
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
|
| 119 |
}
|
| 120 |
-
|
| 121 |
.position.selected {
|
| 122 |
box-shadow: 0 0 25px #ffd700;
|
| 123 |
border-color: #ffd700;
|
| 124 |
border-width: 4px;
|
| 125 |
}
|
| 126 |
-
|
| 127 |
.position.valid-move {
|
| 128 |
background: rgba(144, 238, 144, 0.8);
|
| 129 |
animation: pulse 1s infinite;
|
| 130 |
}
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
| 132 |
@keyframes pulse {
|
| 133 |
0%, 100% { transform: translate(-50%, -50%) scale(1); }
|
| 134 |
50% { transform: translate(-50%, -50%) scale(1.1); }
|
| 135 |
}
|
| 136 |
-
|
| 137 |
.game-status {
|
| 138 |
text-align: center;
|
| 139 |
padding: 15px;
|
|
@@ -142,19 +127,16 @@
|
|
| 142 |
margin-top: 20px;
|
| 143 |
font-size: 1.1rem;
|
| 144 |
}
|
| 145 |
-
|
| 146 |
.status-placing { color: #2196F3; }
|
| 147 |
.status-moving { color: #4CAF50; }
|
| 148 |
.status-removing { color: #FF5722; }
|
| 149 |
.status-won { color: #9C27B0; font-weight: bold; }
|
| 150 |
-
|
| 151 |
.controls {
|
| 152 |
display: flex;
|
| 153 |
justify-content: center;
|
| 154 |
gap: 15px;
|
| 155 |
margin-top: 20px;
|
| 156 |
}
|
| 157 |
-
|
| 158 |
button {
|
| 159 |
padding: 12px 24px;
|
| 160 |
border: none;
|
|
@@ -167,30 +149,30 @@
|
|
| 167 |
transition: all 0.3s ease;
|
| 168 |
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
| 169 |
}
|
| 170 |
-
|
| 171 |
button:hover {
|
| 172 |
transform: translateY(-2px);
|
| 173 |
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
| 174 |
}
|
| 175 |
-
|
| 176 |
.rules {
|
| 177 |
margin-top: 30px;
|
| 178 |
padding: 20px;
|
| 179 |
background: rgba(0, 0, 0, 0.05);
|
| 180 |
border-radius: 10px;
|
| 181 |
-
max-width:
|
| 182 |
}
|
| 183 |
-
|
| 184 |
.rules h3 {
|
| 185 |
margin-top: 0;
|
| 186 |
color: #333;
|
| 187 |
}
|
| 188 |
-
|
| 189 |
.rules ul {
|
| 190 |
text-align: left;
|
| 191 |
color: #555;
|
| 192 |
line-height: 1.6;
|
| 193 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
</style>
|
| 195 |
</head>
|
| 196 |
<body>
|
|
@@ -200,11 +182,13 @@
|
|
| 200 |
<div class="game-info">
|
| 201 |
<div class="player-info" id="player1-info">
|
| 202 |
<div><strong>Player 1 (Red)</strong></div>
|
| 203 |
-
<div>Pieces: <span id="red-pieces">9</span></div>
|
|
|
|
| 204 |
</div>
|
| 205 |
<div class="player-info" id="player2-info">
|
| 206 |
<div><strong>Player 2 (Black)</strong></div>
|
| 207 |
-
<div>Pieces: <span id="black-pieces">9</span></div>
|
|
|
|
| 208 |
</div>
|
| 209 |
</div>
|
| 210 |
|
|
@@ -238,6 +222,7 @@
|
|
| 238 |
</div>
|
| 239 |
|
| 240 |
<div class="game-status" id="game-status">
|
|
|
|
| 241 |
<div class="status-placing">Player 1's turn - Place a piece</div>
|
| 242 |
</div>
|
| 243 |
|
|
@@ -247,13 +232,14 @@
|
|
| 247 |
</div>
|
| 248 |
|
| 249 |
<div class="rules" id="rules" style="display: none;">
|
| 250 |
-
<h3>
|
| 251 |
<ul>
|
| 252 |
-
<li><strong>Phase 1:</strong> Take turns placing your 9 pieces on any empty intersection
|
| 253 |
-
<li><strong>Phase 2:</strong> Move pieces along lines to
|
| 254 |
-
<li><strong>
|
| 255 |
-
<li><strong>
|
| 256 |
-
<li><strong>
|
|
|
|
| 257 |
</ul>
|
| 258 |
</div>
|
| 259 |
</div>
|
|
@@ -270,6 +256,7 @@
|
|
| 270 |
this.blackPiecesOnBoard = 0;
|
| 271 |
this.selectedPosition = null;
|
| 272 |
this.mustRemovePiece = false;
|
|
|
|
| 273 |
|
| 274 |
// Define board positions (x, y coordinates)
|
| 275 |
this.positions = [
|
|
@@ -362,7 +349,9 @@
|
|
| 362 |
|
| 363 |
if (this.checkForMill(index)) {
|
| 364 |
this.mustRemovePiece = true;
|
|
|
|
| 365 |
} else {
|
|
|
|
| 366 |
if (this.redPieces === 0 && this.blackPieces === 0) {
|
| 367 |
this.gamePhase = 'moving';
|
| 368 |
}
|
|
@@ -375,9 +364,9 @@
|
|
| 375 |
selectPiece(index) {
|
| 376 |
if (this.board[index] !== this.currentPlayer) return;
|
| 377 |
|
| 378 |
-
// Check if piece can move
|
| 379 |
-
const
|
| 380 |
-
|
| 381 |
|
| 382 |
if (!canFly) {
|
| 383 |
const canMove = this.adjacentPositions[index].some(adj => this.board[adj] === null);
|
|
@@ -395,9 +384,10 @@
|
|
| 395 |
|
| 396 |
movePiece(index) {
|
| 397 |
if (this.board[index] !== null) return;
|
|
|
|
| 398 |
|
| 399 |
-
const
|
| 400 |
-
|
| 401 |
|
| 402 |
// Check if move is valid
|
| 403 |
if (!canFly && !this.adjacentPositions[this.selectedPosition].includes(index)) {
|
|
@@ -407,10 +397,12 @@
|
|
| 407 |
// Move the piece
|
| 408 |
this.board[index] = this.currentPlayer;
|
| 409 |
this.board[this.selectedPosition] = null;
|
|
|
|
| 410 |
this.selectedPosition = null;
|
| 411 |
|
| 412 |
if (this.checkForMill(index)) {
|
| 413 |
this.mustRemovePiece = true;
|
|
|
|
| 414 |
} else {
|
| 415 |
this.switchPlayer();
|
| 416 |
}
|
|
@@ -424,9 +416,11 @@
|
|
| 424 |
|
| 425 |
if (this.board[index] !== opponent) return;
|
| 426 |
|
| 427 |
-
// Check if piece is part of a mill
|
| 428 |
-
if (this.isPartOfMill(index)
|
| 429 |
-
|
|
|
|
|
|
|
| 430 |
}
|
| 431 |
|
| 432 |
this.board[index] = null;
|
|
@@ -438,6 +432,13 @@
|
|
| 438 |
}
|
| 439 |
|
| 440 |
this.mustRemovePiece = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
this.switchPlayer();
|
| 442 |
this.updateDisplay();
|
| 443 |
this.checkGameEnd();
|
|
@@ -451,6 +452,7 @@
|
|
| 451 |
}
|
| 452 |
|
| 453 |
isPartOfMill(index) {
|
|
|
|
| 454 |
return this.mills.some(mill => {
|
| 455 |
return mill.includes(index) &&
|
| 456 |
mill.every(pos => this.board[pos] === this.board[index]);
|
|
@@ -466,41 +468,72 @@
|
|
| 466 |
return false;
|
| 467 |
}
|
| 468 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
switchPlayer() {
|
| 470 |
this.currentPlayer = this.currentPlayer === 'red' ? 'black' : 'red';
|
| 471 |
}
|
| 472 |
|
| 473 |
checkGameEnd() {
|
| 474 |
-
|
| 475 |
-
|
|
|
|
|
|
|
| 476 |
|
| 477 |
-
// Check if
|
| 478 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
this.gamePhase = 'game-over';
|
| 480 |
return;
|
| 481 |
}
|
| 482 |
|
| 483 |
-
// Check if current player has no valid moves
|
| 484 |
-
if (
|
| 485 |
-
this.switchPlayer(); //
|
| 486 |
this.gamePhase = 'game-over';
|
| 487 |
}
|
| 488 |
}
|
| 489 |
|
| 490 |
hasValidMoves() {
|
| 491 |
-
const
|
| 492 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
|
|
|
|
| 494 |
for (let i = 0; i < 24; i++) {
|
| 495 |
if (this.board[i] === this.currentPlayer) {
|
| 496 |
-
if (
|
| 497 |
-
|
| 498 |
-
if (this.board.some(pos => pos === null)) return true;
|
| 499 |
-
} else {
|
| 500 |
-
// Check if piece has adjacent empty space
|
| 501 |
-
if (this.adjacentPositions[i].some(adj => this.board[adj] === null)) {
|
| 502 |
-
return true;
|
| 503 |
-
}
|
| 504 |
}
|
| 505 |
}
|
| 506 |
}
|
|
@@ -523,20 +556,30 @@
|
|
| 523 |
positionEl.classList.add('selected');
|
| 524 |
}
|
| 525 |
|
| 526 |
-
// Show valid moves
|
| 527 |
-
if (this.selectedPosition !== null && this.board[i] === null) {
|
| 528 |
-
const
|
| 529 |
-
|
| 530 |
|
| 531 |
if (canFly || this.adjacentPositions[this.selectedPosition].includes(i)) {
|
| 532 |
positionEl.classList.add('valid-move');
|
| 533 |
}
|
| 534 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
}
|
| 536 |
|
| 537 |
// Update player info
|
| 538 |
document.getElementById('red-pieces').textContent = this.redPieces;
|
| 539 |
document.getElementById('black-pieces').textContent = this.blackPieces;
|
|
|
|
|
|
|
| 540 |
|
| 541 |
// Update active player
|
| 542 |
document.getElementById('player1-info').classList.toggle('active', this.currentPlayer === 'red');
|
|
@@ -546,14 +589,44 @@
|
|
| 546 |
const statusEl = document.getElementById('game-status');
|
| 547 |
statusEl.className = 'game-status';
|
| 548 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
if (this.gamePhase === 'game-over') {
|
| 550 |
-
statusEl.innerHTML =
|
|
|
|
|
|
|
|
|
|
| 551 |
} else if (this.mustRemovePiece) {
|
| 552 |
-
statusEl.innerHTML =
|
|
|
|
|
|
|
|
|
|
| 553 |
} else if (this.gamePhase === 'placing') {
|
| 554 |
-
statusEl.innerHTML =
|
|
|
|
|
|
|
|
|
|
| 555 |
} else {
|
| 556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
}
|
| 558 |
}
|
| 559 |
}
|
|
|
|
| 16 |
align-items: center;
|
| 17 |
flex-direction: column;
|
| 18 |
}
|
|
|
|
| 19 |
.game-container {
|
| 20 |
background: rgba(255, 255, 255, 0.95);
|
| 21 |
border-radius: 20px;
|
|
|
|
| 23 |
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
| 24 |
backdrop-filter: blur(10px);
|
| 25 |
}
|
|
|
|
| 26 |
h1 {
|
| 27 |
text-align: center;
|
| 28 |
color: #333;
|
|
|
|
| 30 |
font-size: 2.5rem;
|
| 31 |
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
| 32 |
}
|
|
|
|
| 33 |
.game-info {
|
| 34 |
display: flex;
|
| 35 |
justify-content: space-between;
|
|
|
|
| 37 |
margin-bottom: 20px;
|
| 38 |
gap: 20px;
|
| 39 |
}
|
|
|
|
| 40 |
.player-info {
|
| 41 |
background: rgba(0, 0, 0, 0.05);
|
| 42 |
padding: 15px;
|
|
|
|
| 44 |
min-width: 150px;
|
| 45 |
text-align: center;
|
| 46 |
}
|
|
|
|
| 47 |
.player-info.active {
|
| 48 |
background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
|
| 49 |
box-shadow: 0 4px 15px rgba(252, 182, 159, 0.4);
|
| 50 |
}
|
|
|
|
| 51 |
.board-container {
|
| 52 |
display: flex;
|
| 53 |
justify-content: center;
|
| 54 |
margin-bottom: 20px;
|
| 55 |
}
|
|
|
|
| 56 |
.board {
|
| 57 |
width: 400px;
|
| 58 |
height: 400px;
|
|
|
|
| 61 |
border-radius: 10px;
|
| 62 |
border: 3px solid #333;
|
| 63 |
}
|
|
|
|
| 64 |
.board-line {
|
| 65 |
position: absolute;
|
| 66 |
background-color: #333;
|
| 67 |
}
|
|
|
|
| 68 |
.horizontal {
|
| 69 |
height: 3px;
|
| 70 |
}
|
|
|
|
| 71 |
.vertical {
|
| 72 |
width: 3px;
|
| 73 |
}
|
|
|
|
| 74 |
.position {
|
| 75 |
position: absolute;
|
| 76 |
width: 30px;
|
|
|
|
| 85 |
align-items: center;
|
| 86 |
justify-content: center;
|
| 87 |
}
|
|
|
|
| 88 |
.position:hover {
|
| 89 |
box-shadow: 0 0 20px rgba(102, 126, 234, 0.6);
|
| 90 |
transform: translate(-50%, -50%) scale(1.1);
|
| 91 |
}
|
|
|
|
| 92 |
.position.occupied {
|
| 93 |
cursor: default;
|
| 94 |
}
|
|
|
|
| 95 |
.position.red {
|
| 96 |
background: #dc3545;
|
| 97 |
border-color: #a02834;
|
| 98 |
box-shadow: 0 4px 10px rgba(220, 53, 69, 0.5);
|
| 99 |
}
|
|
|
|
| 100 |
.position.black {
|
| 101 |
background: #333;
|
| 102 |
border-color: #666;
|
| 103 |
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
|
| 104 |
}
|
|
|
|
| 105 |
.position.selected {
|
| 106 |
box-shadow: 0 0 25px #ffd700;
|
| 107 |
border-color: #ffd700;
|
| 108 |
border-width: 4px;
|
| 109 |
}
|
|
|
|
| 110 |
.position.valid-move {
|
| 111 |
background: rgba(144, 238, 144, 0.8);
|
| 112 |
animation: pulse 1s infinite;
|
| 113 |
}
|
| 114 |
+
.position.removable {
|
| 115 |
+
background: rgba(255, 182, 193, 0.8);
|
| 116 |
+
animation: pulse 1s infinite;
|
| 117 |
+
}
|
| 118 |
@keyframes pulse {
|
| 119 |
0%, 100% { transform: translate(-50%, -50%) scale(1); }
|
| 120 |
50% { transform: translate(-50%, -50%) scale(1.1); }
|
| 121 |
}
|
|
|
|
| 122 |
.game-status {
|
| 123 |
text-align: center;
|
| 124 |
padding: 15px;
|
|
|
|
| 127 |
margin-top: 20px;
|
| 128 |
font-size: 1.1rem;
|
| 129 |
}
|
|
|
|
| 130 |
.status-placing { color: #2196F3; }
|
| 131 |
.status-moving { color: #4CAF50; }
|
| 132 |
.status-removing { color: #FF5722; }
|
| 133 |
.status-won { color: #9C27B0; font-weight: bold; }
|
|
|
|
| 134 |
.controls {
|
| 135 |
display: flex;
|
| 136 |
justify-content: center;
|
| 137 |
gap: 15px;
|
| 138 |
margin-top: 20px;
|
| 139 |
}
|
|
|
|
| 140 |
button {
|
| 141 |
padding: 12px 24px;
|
| 142 |
border: none;
|
|
|
|
| 149 |
transition: all 0.3s ease;
|
| 150 |
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
| 151 |
}
|
|
|
|
| 152 |
button:hover {
|
| 153 |
transform: translateY(-2px);
|
| 154 |
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
| 155 |
}
|
|
|
|
| 156 |
.rules {
|
| 157 |
margin-top: 30px;
|
| 158 |
padding: 20px;
|
| 159 |
background: rgba(0, 0, 0, 0.05);
|
| 160 |
border-radius: 10px;
|
| 161 |
+
max-width: 600px;
|
| 162 |
}
|
|
|
|
| 163 |
.rules h3 {
|
| 164 |
margin-top: 0;
|
| 165 |
color: #333;
|
| 166 |
}
|
|
|
|
| 167 |
.rules ul {
|
| 168 |
text-align: left;
|
| 169 |
color: #555;
|
| 170 |
line-height: 1.6;
|
| 171 |
}
|
| 172 |
+
.phase-indicator {
|
| 173 |
+
font-weight: bold;
|
| 174 |
+
margin-bottom: 10px;
|
| 175 |
+
}
|
| 176 |
</style>
|
| 177 |
</head>
|
| 178 |
<body>
|
|
|
|
| 182 |
<div class="game-info">
|
| 183 |
<div class="player-info" id="player1-info">
|
| 184 |
<div><strong>Player 1 (Red)</strong></div>
|
| 185 |
+
<div>Pieces to place: <span id="red-pieces">9</span></div>
|
| 186 |
+
<div>Pieces on board: <span id="red-on-board">0</span></div>
|
| 187 |
</div>
|
| 188 |
<div class="player-info" id="player2-info">
|
| 189 |
<div><strong>Player 2 (Black)</strong></div>
|
| 190 |
+
<div>Pieces to place: <span id="black-pieces">9</span></div>
|
| 191 |
+
<div>Pieces on board: <span id="black-on-board">0</span></div>
|
| 192 |
</div>
|
| 193 |
</div>
|
| 194 |
|
|
|
|
| 222 |
</div>
|
| 223 |
|
| 224 |
<div class="game-status" id="game-status">
|
| 225 |
+
<div class="phase-indicator">Phase 1: Placing Pieces</div>
|
| 226 |
<div class="status-placing">Player 1's turn - Place a piece</div>
|
| 227 |
</div>
|
| 228 |
|
|
|
|
| 232 |
</div>
|
| 233 |
|
| 234 |
<div class="rules" id="rules" style="display: none;">
|
| 235 |
+
<h3>Nine Men's Morris Rules:</h3>
|
| 236 |
<ul>
|
| 237 |
+
<li><strong>Phase 1 - Placing:</strong> Take turns placing your 9 pieces on any empty intersection. Form mills (3 pieces in a row) to capture opponent pieces.</li>
|
| 238 |
+
<li><strong>Phase 2 - Moving:</strong> Move pieces to adjacent empty positions along the lines. Continue forming mills to capture pieces.</li>
|
| 239 |
+
<li><strong>Phase 3 - Flying:</strong> When you have only 3 pieces left, you can move to any empty position (not just adjacent).</li>
|
| 240 |
+
<li><strong>Capturing:</strong> When you form a mill, remove one opponent piece. Pieces in mills can only be captured if no other pieces are available.</li>
|
| 241 |
+
<li><strong>Mill Breaking:</strong> You can break your own mill and reform it to capture again.</li>
|
| 242 |
+
<li><strong>Winning:</strong> Reduce your opponent to 2 pieces or block all their moves.</li>
|
| 243 |
</ul>
|
| 244 |
</div>
|
| 245 |
</div>
|
|
|
|
| 256 |
this.blackPiecesOnBoard = 0;
|
| 257 |
this.selectedPosition = null;
|
| 258 |
this.mustRemovePiece = false;
|
| 259 |
+
this.millFormed = false;
|
| 260 |
|
| 261 |
// Define board positions (x, y coordinates)
|
| 262 |
this.positions = [
|
|
|
|
| 349 |
|
| 350 |
if (this.checkForMill(index)) {
|
| 351 |
this.mustRemovePiece = true;
|
| 352 |
+
this.millFormed = true;
|
| 353 |
} else {
|
| 354 |
+
// Check if placing phase is complete
|
| 355 |
if (this.redPieces === 0 && this.blackPieces === 0) {
|
| 356 |
this.gamePhase = 'moving';
|
| 357 |
}
|
|
|
|
| 364 |
selectPiece(index) {
|
| 365 |
if (this.board[index] !== this.currentPlayer) return;
|
| 366 |
|
| 367 |
+
// Check if piece can move
|
| 368 |
+
const piecesOnBoard = this.currentPlayer === 'red' ? this.redPiecesOnBoard : this.blackPiecesOnBoard;
|
| 369 |
+
const canFly = piecesOnBoard === 3;
|
| 370 |
|
| 371 |
if (!canFly) {
|
| 372 |
const canMove = this.adjacentPositions[index].some(adj => this.board[adj] === null);
|
|
|
|
| 384 |
|
| 385 |
movePiece(index) {
|
| 386 |
if (this.board[index] !== null) return;
|
| 387 |
+
if (this.selectedPosition === null) return;
|
| 388 |
|
| 389 |
+
const piecesOnBoard = this.currentPlayer === 'red' ? this.redPiecesOnBoard : this.blackPiecesOnBoard;
|
| 390 |
+
const canFly = piecesOnBoard === 3;
|
| 391 |
|
| 392 |
// Check if move is valid
|
| 393 |
if (!canFly && !this.adjacentPositions[this.selectedPosition].includes(index)) {
|
|
|
|
| 397 |
// Move the piece
|
| 398 |
this.board[index] = this.currentPlayer;
|
| 399 |
this.board[this.selectedPosition] = null;
|
| 400 |
+
const movedFromPosition = this.selectedPosition;
|
| 401 |
this.selectedPosition = null;
|
| 402 |
|
| 403 |
if (this.checkForMill(index)) {
|
| 404 |
this.mustRemovePiece = true;
|
| 405 |
+
this.millFormed = true;
|
| 406 |
} else {
|
| 407 |
this.switchPlayer();
|
| 408 |
}
|
|
|
|
| 416 |
|
| 417 |
if (this.board[index] !== opponent) return;
|
| 418 |
|
| 419 |
+
// Check if piece is part of a mill - can only remove if no non-mill pieces available
|
| 420 |
+
if (this.isPartOfMill(index)) {
|
| 421 |
+
if (this.hasNonMillPieces(opponent)) {
|
| 422 |
+
return; // Cannot remove piece from mill when other pieces available
|
| 423 |
+
}
|
| 424 |
}
|
| 425 |
|
| 426 |
this.board[index] = null;
|
|
|
|
| 432 |
}
|
| 433 |
|
| 434 |
this.mustRemovePiece = false;
|
| 435 |
+
this.millFormed = false;
|
| 436 |
+
|
| 437 |
+
// Check if placing phase is complete after removal
|
| 438 |
+
if (this.gamePhase === 'placing' && this.redPieces === 0 && this.blackPieces === 0) {
|
| 439 |
+
this.gamePhase = 'moving';
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
this.switchPlayer();
|
| 443 |
this.updateDisplay();
|
| 444 |
this.checkGameEnd();
|
|
|
|
| 452 |
}
|
| 453 |
|
| 454 |
isPartOfMill(index) {
|
| 455 |
+
if (this.board[index] === null) return false;
|
| 456 |
return this.mills.some(mill => {
|
| 457 |
return mill.includes(index) &&
|
| 458 |
mill.every(pos => this.board[pos] === this.board[index]);
|
|
|
|
| 468 |
return false;
|
| 469 |
}
|
| 470 |
|
| 471 |
+
getRemovablePieces() {
|
| 472 |
+
const opponent = this.currentPlayer === 'red' ? 'black' : 'red';
|
| 473 |
+
const removable = [];
|
| 474 |
+
|
| 475 |
+
// First, try to find non-mill pieces
|
| 476 |
+
for (let i = 0; i < 24; i++) {
|
| 477 |
+
if (this.board[i] === opponent && !this.isPartOfMill(i)) {
|
| 478 |
+
removable.push(i);
|
| 479 |
+
}
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
// If no non-mill pieces, all opponent pieces are removable
|
| 483 |
+
if (removable.length === 0) {
|
| 484 |
+
for (let i = 0; i < 24; i++) {
|
| 485 |
+
if (this.board[i] === opponent) {
|
| 486 |
+
removable.push(i);
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
return removable;
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
switchPlayer() {
|
| 495 |
this.currentPlayer = this.currentPlayer === 'red' ? 'black' : 'red';
|
| 496 |
}
|
| 497 |
|
| 498 |
checkGameEnd() {
|
| 499 |
+
if (this.gamePhase === 'placing') return; // Don't check end conditions during placing phase
|
| 500 |
+
|
| 501 |
+
const redCount = this.redPiecesOnBoard;
|
| 502 |
+
const blackCount = this.blackPiecesOnBoard;
|
| 503 |
|
| 504 |
+
// Check if someone has only 2 pieces left
|
| 505 |
+
if (redCount < 3) {
|
| 506 |
+
this.currentPlayer = 'black'; // Black wins
|
| 507 |
+
this.gamePhase = 'game-over';
|
| 508 |
+
return;
|
| 509 |
+
}
|
| 510 |
+
if (blackCount < 3) {
|
| 511 |
+
this.currentPlayer = 'red'; // Red wins
|
| 512 |
this.gamePhase = 'game-over';
|
| 513 |
return;
|
| 514 |
}
|
| 515 |
|
| 516 |
+
// Check if current player has no valid moves (is blocked)
|
| 517 |
+
if (!this.hasValidMoves()) {
|
| 518 |
+
this.switchPlayer(); // Other player wins
|
| 519 |
this.gamePhase = 'game-over';
|
| 520 |
}
|
| 521 |
}
|
| 522 |
|
| 523 |
hasValidMoves() {
|
| 524 |
+
const piecesOnBoard = this.currentPlayer === 'red' ? this.redPiecesOnBoard : this.blackPiecesOnBoard;
|
| 525 |
+
const canFly = piecesOnBoard === 3;
|
| 526 |
+
|
| 527 |
+
// If can fly, just check if any empty position exists
|
| 528 |
+
if (canFly) {
|
| 529 |
+
return this.board.some(pos => pos === null);
|
| 530 |
+
}
|
| 531 |
|
| 532 |
+
// Otherwise, check if any piece can move to adjacent empty space
|
| 533 |
for (let i = 0; i < 24; i++) {
|
| 534 |
if (this.board[i] === this.currentPlayer) {
|
| 535 |
+
if (this.adjacentPositions[i].some(adj => this.board[adj] === null)) {
|
| 536 |
+
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
}
|
| 538 |
}
|
| 539 |
}
|
|
|
|
| 556 |
positionEl.classList.add('selected');
|
| 557 |
}
|
| 558 |
|
| 559 |
+
// Show valid moves for selected piece
|
| 560 |
+
if (this.selectedPosition !== null && this.board[i] === null && !this.mustRemovePiece) {
|
| 561 |
+
const piecesOnBoard = this.currentPlayer === 'red' ? this.redPiecesOnBoard : this.blackPiecesOnBoard;
|
| 562 |
+
const canFly = piecesOnBoard === 3;
|
| 563 |
|
| 564 |
if (canFly || this.adjacentPositions[this.selectedPosition].includes(i)) {
|
| 565 |
positionEl.classList.add('valid-move');
|
| 566 |
}
|
| 567 |
}
|
| 568 |
+
|
| 569 |
+
// Show removable pieces
|
| 570 |
+
if (this.mustRemovePiece) {
|
| 571 |
+
const removablePieces = this.getRemovablePieces();
|
| 572 |
+
if (removablePieces.includes(i)) {
|
| 573 |
+
positionEl.classList.add('removable');
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
}
|
| 577 |
|
| 578 |
// Update player info
|
| 579 |
document.getElementById('red-pieces').textContent = this.redPieces;
|
| 580 |
document.getElementById('black-pieces').textContent = this.blackPieces;
|
| 581 |
+
document.getElementById('red-on-board').textContent = this.redPiecesOnBoard;
|
| 582 |
+
document.getElementById('black-on-board').textContent = this.blackPiecesOnBoard;
|
| 583 |
|
| 584 |
// Update active player
|
| 585 |
document.getElementById('player1-info').classList.toggle('active', this.currentPlayer === 'red');
|
|
|
|
| 589 |
const statusEl = document.getElementById('game-status');
|
| 590 |
statusEl.className = 'game-status';
|
| 591 |
|
| 592 |
+
let phaseText = '';
|
| 593 |
+
if (this.gamePhase === 'placing') {
|
| 594 |
+
phaseText = 'Phase 1: Placing Pieces';
|
| 595 |
+
} else if (this.gamePhase === 'moving') {
|
| 596 |
+
const redCanFly = this.redPiecesOnBoard === 3;
|
| 597 |
+
const blackCanFly = this.blackPiecesOnBoard === 3;
|
| 598 |
+
if (redCanFly || blackCanFly) {
|
| 599 |
+
phaseText = 'Phase 3: Flying (3 pieces remaining)';
|
| 600 |
+
} else {
|
| 601 |
+
phaseText = 'Phase 2: Moving Pieces';
|
| 602 |
+
}
|
| 603 |
+
} else {
|
| 604 |
+
phaseText = 'Game Over';
|
| 605 |
+
}
|
| 606 |
+
|
| 607 |
if (this.gamePhase === 'game-over') {
|
| 608 |
+
statusEl.innerHTML = `
|
| 609 |
+
<div class="phase-indicator">${phaseText}</div>
|
| 610 |
+
<div class="status-won">${this.currentPlayer === 'red' ? 'Player 1 (Red)' : 'Player 2 (Black)'} Wins!</div>
|
| 611 |
+
`;
|
| 612 |
} else if (this.mustRemovePiece) {
|
| 613 |
+
statusEl.innerHTML = `
|
| 614 |
+
<div class="phase-indicator">${phaseText}</div>
|
| 615 |
+
<div class="status-removing">${this.currentPlayer === 'red' ? 'Player 1' : 'Player 2'} formed a mill! Remove an opponent's piece</div>
|
| 616 |
+
`;
|
| 617 |
} else if (this.gamePhase === 'placing') {
|
| 618 |
+
statusEl.innerHTML = `
|
| 619 |
+
<div class="phase-indicator">${phaseText}</div>
|
| 620 |
+
<div class="status-placing">${this.currentPlayer === 'red' ? 'Player 1' : 'Player 2'}'s turn - Place a piece</div>
|
| 621 |
+
`;
|
| 622 |
} else {
|
| 623 |
+
const piecesOnBoard = this.currentPlayer === 'red' ? this.redPiecesOnBoard : this.blackPiecesOnBoard;
|
| 624 |
+
const canFly = piecesOnBoard === 3;
|
| 625 |
+
const actionText = canFly ? 'Move to any empty position' : 'Move a piece to adjacent position';
|
| 626 |
+
statusEl.innerHTML = `
|
| 627 |
+
<div class="phase-indicator">${phaseText}</div>
|
| 628 |
+
<div class="status-moving">${this.currentPlayer === 'red' ? 'Player 1' : 'Player 2'}'s turn - ${actionText}</div>
|
| 629 |
+
`;
|
| 630 |
}
|
| 631 |
}
|
| 632 |
}
|