Warning, file /education/gcompris/src/activities/align4_2players/align4.js was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* GCompris - align4.js 0002 * 0003 * SPDX-FileCopyrightText: 2014 Bharath M S 0004 * 0005 * Authors: 0006 * Laurent Lacheny <laurent.lacheny@wanadoo.fr> (GTK+ version) 0007 * Bharath M S <brat.197@gmail.com> (Qt Quick port) 0008 * 0009 * SPDX-License-Identifier: GPL-3.0-or-later 0010 */ 0011 .pragma library 0012 .import QtQuick 2.12 as Quick 0013 .import "qrc:/gcompris/src/core/core.js" as Core 0014 0015 var numberOfLevel; 0016 var items; 0017 var url = "qrc:/gcompris/src/activities/align4_2players/resource/"; 0018 var currentPiece; 0019 var currentPlayer; 0020 var currentLocation; 0021 var twoPlayer; 0022 var weight = [[100, 50, 20, 100, 50, 20], 0023 [100, 50, 20, 100, 50, 20], 0024 [110, 55, 20, 100, 50, 20], 0025 [100, 50, 20, 110, 55, 20], 0026 [100, 50, 20, 110, 55, 20], 0027 [100, 50, 20, 110, 55, 20]]; 0028 var nextColumn; 0029 var depthMax; 0030 var randomMiss; 0031 0032 function start(items_, twoPlayer_) { 0033 items = items_; 0034 twoPlayer = twoPlayer_; 0035 numberOfLevel = twoPlayer ? 1 : weight.length; 0036 items.currentLevel = Core.getInitialLevel(numberOfLevel); 0037 initLevel(); 0038 } 0039 0040 function stop() { 0041 items.trigTuxMove.stop(); 0042 items.drop.stop(); 0043 items.pieces.clear(); 0044 } 0045 0046 function initLevel() { 0047 0048 items.counter = items.nextPlayerStart+1; 0049 0050 if(items.nextPlayerStart === 1) { 0051 items.player2score.endTurn(); 0052 items.player1score.beginTurn(); 0053 } 0054 else { 0055 items.player1score.endTurn(); 0056 items.player2score.beginTurn(); 0057 if(!twoPlayer) { 0058 items.trigTuxMove.start(); 0059 } 0060 } 0061 0062 items.gameDone = false; 0063 items.pieces.clear(); 0064 for(var y = 0; y < items.rows; y++) { 0065 for(var x = 0; x < items.columns; x++) { 0066 items.pieces.append({'stateTemp': "invisible"}); 0067 } 0068 } 0069 0070 nextColumn = 3; 0071 if(items.currentLevel < 2) 0072 depthMax = 2; 0073 else 0074 depthMax = 4; 0075 0076 if(items.currentLevel < 2) 0077 randomMiss = 1; 0078 else if(items.currentLevel < 4) 0079 randomMiss = 0.5; 0080 else 0081 randomMiss = 1; 0082 0083 setPieceLocationByIndex(3); 0084 } 0085 0086 function nextLevel() { 0087 items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel); 0088 initLevel(); 0089 } 0090 0091 function previousLevel() { 0092 items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel); 0093 initLevel(); 0094 } 0095 0096 function reset() { 0097 // If the previous game is not won, we switch the starting player 0098 // Else, the next player is the one who lost (set in continueGame()) 0099 if(!items.gameDone) { 0100 items.nextPlayerStart = (items.nextPlayerStart == 1) ? 2 : 1; 0101 } 0102 items.trigTuxMove.stop(); 0103 items.drop.stop(); // stop animation 0104 items.pieces.clear(); // Clear the board 0105 initLevel(); 0106 } 0107 0108 function whichColumn(mouseX, mouseY) { 0109 for(var i = 0; i < items.columns - 1; i++) { 0110 if(mouseX < items.repeater.itemAt(i + 1).x) { 0111 return i; 0112 } 0113 } 0114 return items.columns - 1; 0115 } 0116 0117 /* To move the piece before a column is chosen */ 0118 function setPieceLocation(mouseX, mouseY) { 0119 currentLocation = whichColumn(mouseX, mouseY); 0120 items.fallingPiece.y = items.repeater.itemAt(0).y - items.cellSize; 0121 items.fallingPiece.x = items.repeater.itemAt(currentLocation).x; 0122 } 0123 0124 function setPieceLocationByIndex(index) { 0125 setPieceLocation(items.repeater.itemAt(index).x, 0126 items.repeater.itemAt(0).y); 0127 } 0128 0129 function moveCurrentIndexRight() { 0130 if(currentLocation++ > items.columns) 0131 currentLocation = 0; 0132 setPieceLocationByIndex(currentLocation); 0133 } 0134 0135 function moveCurrentIndexLeft() { 0136 if(currentLocation-- <= 0) 0137 currentLocation = items.columns - 1; 0138 setPieceLocationByIndex(currentLocation); 0139 } 0140 0141 function isModelEmpty(model) { 0142 var state = model.stateTemp; 0143 return (state === "1" || state === "2") ? false : true; 0144 } 0145 0146 function getPieceAt(col, row) { 0147 return items.pieces.get(row * items.columns + col); 0148 } 0149 0150 function getNextFreeStop(col) { 0151 for(var row = items.rows - 1; row >= 0; row--) { 0152 if(isModelEmpty(getPieceAt(col, row))) 0153 return row; 0154 } 0155 // Full column 0156 return -1; 0157 } 0158 0159 function handleDrop(column) { 0160 var singleDropSize = items.cellSize; 0161 var nextFreeStop = getNextFreeStop(column); 0162 0163 if(nextFreeStop >= 0) { 0164 items.drop.to = items.repeater.itemAt(nextFreeStop * items.columns).y; 0165 currentPiece = nextFreeStop * items.columns + column; 0166 items.drop.start(); 0167 } 0168 } 0169 0170 function setPieceState(col, row, state) { 0171 items.pieces.set(row * items.columns + col, {"stateTemp": state}); 0172 } 0173 0174 function getPieceState(col, row) { 0175 return items.pieces.get(row * items.columns + col).stateTemp; 0176 } 0177 0178 function getBoardFromModel() { 0179 var board = []; 0180 var temp; 0181 0182 for(var i = 0; i < items.rows; i++) { 0183 temp = []; 0184 for(var j = 0; j < items.columns; j++) { 0185 temp.push(getPieceState(j, i)); 0186 } 0187 board.push(temp); 0188 } 0189 return board; 0190 } 0191 0192 function getFreeStopFromBoard(column, board) { 0193 for(var row = items.rows-1; row > -1; row--) { 0194 if(board[row][column] === "invisible") { 0195 return row; 0196 } 0197 } 0198 return -1; 0199 } 0200 0201 0202 function alphabeta(depth, alpha, beta, player, board) { 0203 var value = evaluateBoard(player, player % 2 ? 2 : 1, board); 0204 0205 if(depth === 0 || value === 100000 || value < -100000) { 0206 return value; 0207 } 0208 0209 if(player === 2) { 0210 var scores = []; 0211 0212 for(var c = 0; c < items.columns; c++) { 0213 var r = getFreeStopFromBoard(c, board); 0214 0215 if(r === -1) continue; 0216 0217 board[r][c] = "2"; 0218 0219 alpha = Math.max(alpha, alphabeta(depth - 1, alpha, beta, 1, board)); 0220 0221 board[r][c] = "invisible"; 0222 scores[c] = alpha; 0223 0224 if(beta <= alpha) break; 0225 } 0226 0227 if(depth === depthMax) { 0228 var max = -10000; 0229 for(var i = 0; i < scores.length; i++) { 0230 if(scores[i] > max) { 0231 max = scores[i]; 0232 nextColumn = i; 0233 } 0234 } 0235 } 0236 0237 return alpha; 0238 } else { 0239 for(var c = 0; c < items.columns; c++) { 0240 0241 var r = getFreeStopFromBoard(c, board); 0242 0243 if(r === -1) continue; 0244 0245 board[r][c] = "1"; 0246 0247 beta = Math.min(beta, alphabeta(depth - 1, alpha, beta, 2, board)); 0248 0249 board[r][c] = "invisible"; 0250 0251 if(beta <= alpha) break; 0252 } 0253 return beta; 0254 } 0255 } 0256 0257 function doMove() { 0258 var board = getBoardFromModel(); 0259 0260 alphabeta(depthMax, -10000, 10000, 2, board); 0261 0262 setPieceLocation(items.repeater.itemAt(nextColumn).x, 0263 items.repeater.itemAt(0).y); 0264 0265 handleDrop(nextColumn); 0266 } 0267 0268 function checkLine() { 0269 var score = 0; 0270 var count1, count2; 0271 0272 // Make the game easier, forget to analyse some line depending on the level 0273 if(Math.random() > randomMiss) 0274 return 0; 0275 0276 // Performance improvement, do not enter the processing loop 0277 // if there is nothing to look at. 0278 var gotOne = false; 0279 for(var i = 2; i < (arguments.length - 1); i++) { 0280 if(arguments[i] !== "invisible") { 0281 gotOne = true; 0282 break; 0283 } 0284 } 0285 0286 if(!gotOne) 0287 return 0; 0288 0289 var player1 = arguments[0].toString(); 0290 var player2 = arguments[1].toString(); 0291 0292 for(var i = 2; i < (arguments.length - 3); i++) { 0293 count1 = 0; 0294 count2 = 0; 0295 for(var j = 0; j < 4; j++) { 0296 if(arguments[i + j] === player1) { 0297 count1++; 0298 } else if( arguments[i + j] === player2) { 0299 count2++; 0300 } 0301 } 0302 0303 if((count1 > 0) && (count2 === 0)) { 0304 if(count1 === 4) { 0305 return 10000; 0306 } 0307 score += ((count1 / 3) * weight[items.currentLevel][0] + 0308 (count1 / 2) * weight[items.currentLevel][1] + 0309 count1 * weight[items.currentLevel][2]); 0310 } else if((count1 === 0) && (count2 > 0)) { 0311 if(count2 === 4) { 0312 return -10000; 0313 } 0314 score -= ((count2 / 3) * weight[items.currentLevel][3] + 0315 (count2 / 2) * weight[items.currentLevel][4] + 0316 count2 * weight[items.currentLevel][5]); 0317 } 0318 } 0319 return score; 0320 } 0321 0322 function evaluateBoard(player1, player2, board) { 0323 var score = 0; 0324 0325 //Horizontal 0326 for(var i = 0; i < items.rows; i++) { 0327 score += checkLine(player1, player2, board[i][0], 0328 board[i][1], board[i][2], board[i][3], 0329 board[i][4], board[i][5], board[i][6]); 0330 } 0331 0332 //Vertical 0333 for(var i = 0; i < items.columns; i++) { 0334 score += checkLine(player1, player2, board[0][i], 0335 board[1][i], board[2][i], 0336 board[3][i], board[4][i], board[5][i]); 0337 } 0338 0339 //Diagonal Bottom-Right 0340 score += checkLine(player1, player2, board[0][3], 0341 board[1][4], board[2][5], board[3][6]); 0342 score += checkLine(player1, player2, board[0][2], board[1][3], 0343 board[2][4], board[3][5], board[4][6]); 0344 score += checkLine(player1, player2, board[0][1], board[1][2], 0345 board[2][3], board[3][4], board[4][5], board[5][6]); 0346 score += checkLine(player1, player2, board[0][0], board[1][1], 0347 board[2][2], board[3][3], board[4][4], board[5][5]); 0348 score += checkLine(player1, player2, board[1][0], board[2][1], 0349 board[3][2], board[4][3], board[5][4]); 0350 score += checkLine(player1, player2, board[2][0], 0351 board[3][1], board[4][2], board[5][3]); 0352 0353 //Diagonal Top-Left 0354 score += checkLine(player1, player2, board[3][0], 0355 board[2][1], board[1][2], board[0][3]); 0356 score += checkLine(player1, player2, board[4][0], board[3][1], 0357 board[2][2], board[1][3], board[0][4]); 0358 score += checkLine(player1, player2, board[5][0], board[4][1], 0359 board[3][2], board[2][3], board[1][4], board[0][5]); 0360 score += checkLine(player1, player2, board[5][1], board[4][2], 0361 board[3][3], board[2][4], board[1][5], board[0][6]); 0362 score += checkLine(player1, player2, board[5][2], board[4][3], 0363 board[3][4], board[2][5], board[1][6]); 0364 score += checkLine(player1, player2, board[5][3], board[4][4], 0365 board[3][5], board[2][6]); 0366 0367 return score; 0368 } 0369 0370 function checkGameWon(currentPieceRow, currentPieceColumn) { 0371 0372 currentPlayer = getPieceState(currentPieceColumn, currentPieceRow); 0373 var crossed = "crossed" + currentPlayer; 0374 0375 // Horizontal 0376 var sameColor = 0; 0377 for(var col = 0; col < items.columns; col++) { 0378 if(getPieceState(col, currentPieceRow) === currentPlayer) { 0379 if(++sameColor == 4) { 0380 setPieceState(col, currentPieceRow, crossed); 0381 setPieceState(col - 1, currentPieceRow, crossed); 0382 setPieceState(col - 2, currentPieceRow, crossed); 0383 setPieceState(col - 3, currentPieceRow, crossed); 0384 return true; 0385 } 0386 } else { 0387 sameColor = 0; 0388 } 0389 } 0390 0391 // Vertical 0392 sameColor = 0; 0393 for(var row = 0; row < items.rows; row++) { 0394 if(getPieceState(currentPieceColumn, row) === currentPlayer) { 0395 if(++sameColor == 4) { 0396 setPieceState(currentPieceColumn, row, crossed); 0397 setPieceState(currentPieceColumn, row - 1, crossed); 0398 setPieceState(currentPieceColumn, row - 2, crossed); 0399 setPieceState(currentPieceColumn, row - 3, crossed); 0400 return true; 0401 } 0402 } else { 0403 sameColor = 0; 0404 } 0405 } 0406 0407 // Diagonal top left / bottom right 0408 sameColor = 0; 0409 var row = 0; 0410 for(var col = currentPieceColumn - currentPieceRow; 0411 col < items.columns; col++) { 0412 row++; 0413 if(col < 0) 0414 continue; 0415 0416 if(row > items.rows) 0417 break; 0418 0419 if(getPieceState(col, row-1) === currentPlayer) { 0420 if(++sameColor == 4) { 0421 setPieceState(col, row - 1, crossed); 0422 setPieceState(col - 1, row - 2, crossed); 0423 setPieceState(col - 2, row - 3, crossed); 0424 setPieceState(col - 3, row - 4, crossed); 0425 return true; 0426 } 0427 } else { 0428 sameColor = 0; 0429 } 0430 } 0431 0432 // Diagonal top right / bottom left 0433 sameColor = 0; 0434 var row = 0; 0435 for(var col = currentPieceColumn + currentPieceRow; 0436 col >= 0; col--) { 0437 row++; 0438 if(col >= items.columns) 0439 continue; 0440 0441 if(row > items.rows) 0442 break; 0443 0444 if(getPieceState(col, row-1) === currentPlayer) { 0445 if(++sameColor == 4) { 0446 setPieceState(col, row - 1, crossed); 0447 setPieceState(col + 1, row - 2, crossed); 0448 setPieceState(col + 2, row - 3, crossed); 0449 setPieceState(col + 3, row - 4, crossed); 0450 return true; 0451 } 0452 } else { 0453 sameColor = 0; 0454 } 0455 } 0456 } 0457 0458 function continueGame() { 0459 items.pieces.set(currentPiece, {"stateTemp": items.counter++ % 2 ? "2": "1"}); 0460 0461 /* Update score if game won */ 0462 if(twoPlayer) { 0463 if(checkGameWon(parseInt(currentPiece / items.columns), 0464 parseInt(currentPiece % items.columns))) { 0465 items.gameDone = true; 0466 if(currentPlayer === "1") { 0467 items.player1score.win(); 0468 items.player2score.endTurn(); 0469 items.nextPlayerStart = 2; 0470 } else { 0471 items.player2score.win(); 0472 items.player1score.endTurn(); 0473 items.nextPlayerStart = 1; 0474 } 0475 items.bonus.good("flower"); 0476 } 0477 else { 0478 if(currentPlayer === "2") { 0479 items.player1score.beginTurn(); 0480 items.player2score.endTurn(); 0481 } else { 0482 items.player2score.beginTurn(); 0483 items.player1score.endTurn(); 0484 } 0485 } 0486 } else { 0487 if(checkGameWon(parseInt(currentPiece / items.columns), 0488 parseInt(currentPiece % items.columns))) { 0489 items.gameDone = true; 0490 if(currentPlayer === "1") { 0491 items.player1score.win(); 0492 items.player2score.endTurn(); 0493 items.bonus.good("flower"); 0494 items.counter--; 0495 items.nextPlayerStart = 2; 0496 } else { 0497 items.player2score.win(); 0498 items.player1score.endTurn(); 0499 items.bonus.bad("flower"); 0500 items.nextPlayerStart = 1; 0501 } 0502 } 0503 if(items.counter % 2) { 0504 items.player1score.endTurn(); 0505 items.player2score.beginTurn(); 0506 items.trigTuxMove.start(); 0507 } 0508 } 0509 if(items.counter === 42) { 0510 items.player1score.endTurn(); 0511 items.player2score.endTurn(); 0512 items.bonus.bad("flower"); 0513 items.nextPlayerStart = (items.nextPlayerStart == 1) ? 2 : 1; 0514 } 0515 }