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 }