User prompt
rakip Ai güçlendir ve 3 oyuncusunu da taktiğine dahil edebilsin
User prompt
son eklediğimiz 4 konum şöyle olsun - (x+2, y+1) - (x-1, y+1) - (x-2, y-1) - (x+1, y-1)
User prompt
4 konum daha ekleyelim - (x+1, y+1) - (x-1, y+1) - (x-1, y-1) - (x+1, y-1) toplamda gidebileceği 12 kare olsun
User prompt
(x, y) konumunu - (x+1, y+3) - (x+3, y+1) - (x-1, y+3) - (x-3, y+1) - (x+1, y-3) - (x+3, y-1) - (x-1, y-3) - (x-3, y-1) yapabilir misin bizim ve Ai rakibin
User prompt
evet gösterge çok iyi çalışıyor şut atıldıktan sonra topun yol aldığı mesafeyi de 7 bloğa düşür
User prompt
şut mesafesini 7 bloğa düşür top 7 bloktan fazla gitmesin
User prompt
duvar çıkma şansını %5 e düşür
User prompt
Please fix the bug: 'TypeError: tween.create is not a function' in or related to this line: 'tween.create(ball).to({' Line Number: 941
User prompt
gol olduktan sonra topu ortaya koy oyunu öyle bitir
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(ball, {' Line Number: 938
User prompt
Please fix the bug: 'TypeError: tween.create is not a function' in or related to this line: 'tween.create(ball).to({' Line Number: 938
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(ball, {' Line Number: 938
User prompt
top usteki kale bloğuna geldiğinde bizim takıma sayı ver ve sahayı sıfırla duvarları sil. top alttaki kale bloğuna geldiğinde rakibe sayı ver ve aynen sahayı sifirla. skoru unutma sıfırlamada
User prompt
tamam eski haline getir
User prompt
bizim kalenin ve rakip kalenin yanındaki saha bloklarınına yeni texture ekle bakalım haklı mısın
User prompt
kale ekle takımlara ve güzel bi gol mekaniği yap topla
User prompt
add game elemets
User prompt
tüm sahayı %5 küçült
User prompt
top kaleye giremiyor
User prompt
top rakip kaleye girmiyor skor olmuyor hata var
User prompt
kalemizi 1 blok yukarı taşı oyuncunun kalesini
User prompt
tüm sahayı %5 küçült
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'indexOf')' in or related to this line: 'if (goalBotRows.indexOf(py) !== -1) {' Line Number: 205
User prompt
üst orta 3 kareye rakip kale. alt orta 3 kareye bizim kaleyi oluştur oyuncular kaleye giremez ve top kaleye girerse gol atan takım skor alır ve oyun baştan başlar
User prompt
kale için texture ayarla
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // --- Ball --- var Ball = Container.expand(function () { var self = Container.call(this); self.gridX = 0; self.gridY = 0; self.ballAsset = self.attachAsset('ball', { width: cellSize * 0.6 * 1.75, height: cellSize * 0.6 * 1.75, color: 0xffffff, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5 }); self.setGridPos = function (gx, gy) { self.gridX = gx; self.gridY = gy; self.x = gridOriginX + gx * cellSize + cellSize / 2; self.y = gridOriginY + gy * cellSize + cellSize / 2; }; self.flash = function () { LK.effects.flashObject(self, 0xffa500, 200); }; return self; }); // --- PlayerPawn: For both player and AI pawns --- var PlayerPawn = Container.expand(function () { var self = Container.call(this); // Use different colors for player and AI self.isAI = false; self.gridX = 0; self.gridY = 0; self.hasBall = false; self.pawnAsset = null; self.init = function (isAI) { self.isAI = isAI; var color = isAI ? 0x1e90ff : 0x32cd32; self.pawnAsset = self.attachAsset('pawn_' + (isAI ? 'ai' : 'player'), { width: (cellSize - 8) * 1.75, height: (cellSize - 8) * 1.75, color: color, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5 }); }; self.setGridPos = function (gx, gy) { self.gridX = gx; self.gridY = gy; self.x = gridOriginX + gx * cellSize + cellSize / 2; self.y = gridOriginY + gy * cellSize + cellSize / 2; }; self.flash = function () { LK.effects.flashObject(self, 0xffff00, 300); }; return self; }); // --- Wall: Tetris block wall, occupies multiple cells --- var Wall = Container.expand(function () { var self = Container.call(this); self.cells = []; // [{x, y}] self.wallId = null; self.blocks = []; self.init = function (cells, wallId) { self.cells = cells; self.wallId = wallId; for (var i = 0; i < cells.length; i++) { var block = self.attachAsset('wall', { width: cellSize - 6, height: cellSize - 6, color: 0x888888, shape: 'box', anchorX: 0.5, anchorY: 0.5, x: (cells[i].x - cells[0].x) * cellSize, y: (cells[i].y - cells[0].y) * cellSize }); self.blocks.push(block); } // Position wall at first cell self.x = gridOriginX + cells[0].x * cellSize + cellSize / 2; self.y = gridOriginY + cells[0].y * cellSize + cellSize / 2; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // --- GridCell: For grid logic, not a display object --- // --- Grid Setup --- var GridCell = function GridCell(x, y) { this.x = x; this.y = y; this.occupied = null; // null, 'player', 'ai', 'wall', 'ball' this.wallId = null; // If wall, which wall }; var gridCols = 21; var gridRows = 31; var cellSize = Math.floor(Math.min(2048 / gridCols, 2732 / gridRows)); var gridWidth = gridCols * cellSize; var gridHeight = gridRows * cellSize; var gridOriginX = Math.floor((2048 - gridWidth) / 2); var gridOriginY = Math.floor((2732 - gridHeight) / 2); // --- Game State --- var grid = []; for (var y = 0; y < gridRows; y++) { var row = []; for (var x = 0; x < gridCols; x++) { row.push(new GridCell(x, y)); } grid.push(row); } var playerPawns = []; var aiPawns = []; var ball = null; var walls = []; var wallIdCounter = 1; var turn = 'player'; // 'player' or 'ai' var selectedPawn = null; var validMoves = []; var validShots = []; var gameLocked = false; var playerScore = 0; var aiScore = 0; var goalToWin = 3; var scoreTxt = null; var infoTxt = null; // --- Draw grid cell backgrounds --- for (var gy = 0; gy < gridRows; gy++) { for (var gx = 0; gx < gridCols; gx++) { var cellBg = LK.getAsset('grid_cell', { width: cellSize, height: cellSize, anchorX: 0, anchorY: 0, x: gridOriginX + gx * cellSize, y: gridOriginY + gy * cellSize }); game.addChild(cellBg); } } // --- Draw grid lines (for visual aid) --- for (var gx = 0; gx <= gridCols; gx++) { var vline = LK.getAsset('wall_block', { width: 4, height: gridHeight, color: 0x222222, shape: 'box', anchorX: 0.5, anchorY: 0, x: gridOriginX + gx * cellSize - 2, y: gridOriginY }); game.addChild(vline); } for (var gy = 0; gy <= gridRows; gy++) { var hline = LK.getAsset('wall_block', { width: gridWidth, height: 4, color: 0x222222, shape: 'box', anchorX: 0, anchorY: 0.5, x: gridOriginX, y: gridOriginY + gy * cellSize - 2 }); game.addChild(hline); } // --- Place Pawns --- function placePawns() { // Player: 3 pawns, bottom center var pxs = [Math.floor(gridCols / 2) - 2, Math.floor(gridCols / 2), Math.floor(gridCols / 2) + 2]; for (var i = 0; i < 3; i++) { var pawn = new PlayerPawn(); pawn.init(false); pawn.setGridPos(pxs[i], gridRows - 3); // Prevent pawn from being placed in the goal area if (gridRows - 3 === 0 && pxs[i] >= Math.floor(gridCols / 2) - 2 && pxs[i] <= Math.floor(gridCols / 2) + 2) { continue; } playerPawns.push(pawn); game.addChild(pawn); grid[gridRows - 3][pxs[i]].occupied = 'player'; } // AI: 3 pawns, top center var axs = [Math.floor(gridCols / 2) - 2, Math.floor(gridCols / 2), Math.floor(gridCols / 2) + 2]; for (var i = 0; i < 3; i++) { var pawn = new PlayerPawn(); pawn.init(true); pawn.setGridPos(axs[i], 2); // Prevent pawn from being placed in the goal area if (2 === gridRows - 1 && axs[i] >= Math.floor(gridCols / 2) - 2 && axs[i] <= Math.floor(gridCols / 2) + 2) { continue; } aiPawns.push(pawn); game.addChild(pawn); grid[2][axs[i]].occupied = 'ai'; } } placePawns(); // --- Place Ball --- function placeBall(center) { if (ball) { ball.destroy(); } ball = new Ball(); var bx = center ? Math.floor(gridCols / 2) : playerPawns[1].gridX; var by = center ? Math.floor(gridRows / 2) : playerPawns[1].gridY - 1; ball.setGridPos(bx, by); game.addChild(ball); grid[by][bx].occupied = 'ball'; } placeBall(true); // --- Draw Goals (visual only) --- var goalWidth = 5; var goalColor = 0xffd700; var goalTop = LK.getAsset('goal', { width: cellSize * goalWidth, height: cellSize * 0.7, anchorX: 0.5, anchorY: 0, x: gridOriginX + Math.floor(gridCols / 2) * cellSize + cellSize / 2, y: gridOriginY - cellSize * 0.7 }); game.addChild(goalTop); var goalBot = LK.getAsset('goal', { width: cellSize * goalWidth, height: cellSize * 0.7, anchorX: 0.5, anchorY: 1, x: gridOriginX + Math.floor(gridCols / 2) * cellSize + cellSize / 2, y: gridOriginY + gridHeight + cellSize * 0.7 }); game.addChild(goalBot); // --- Score Display --- scoreTxt = new Text2('0 : 0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Info Text --- infoTxt = new Text2('', { size: 60, fill: "#fff" }); infoTxt.anchor.set(0.5, 0); LK.gui.bottom.addChild(infoTxt); // --- Utility Functions --- function updateScore() { scoreTxt.setText(playerScore + ' : ' + aiScore); } function setInfo(msg) { infoTxt.setText(msg); } function clearInfo() { infoTxt.setText(''); } function isInsideGrid(x, y) { return x >= 0 && x < gridCols && y >= 0 && y < gridRows; } function isGoal(x, y, forPlayer) { // Top row for player, bottom row for AI var goalCols = []; for (var i = Math.floor(gridCols / 2) - 2; i <= Math.floor(gridCols / 2) + 2; i++) { goalCols.push(i); } if (forPlayer && y === 0 && goalCols.indexOf(x) !== -1) return true; if (!forPlayer && y === gridRows - 1 && goalCols.indexOf(x) !== -1) return true; return false; } function getPawnAt(x, y) { for (var i = 0; i < playerPawns.length; i++) { if (playerPawns[i].gridX === x && playerPawns[i].gridY === y) return playerPawns[i]; } for (var i = 0; i < aiPawns.length; i++) { if (aiPawns[i].gridX === x && aiPawns[i].gridY === y) return aiPawns[i]; } return null; } function getWallAt(x, y) { for (var i = 0; i < walls.length; i++) { var wall = walls[i]; for (var j = 0; j < wall.cells.length; j++) { if (wall.cells[j].x === x && wall.cells[j].y === y) return wall; } } return null; } function cellBlocked(x, y) { if (!isInsideGrid(x, y)) return true; var occ = grid[y][x].occupied; // Prevent pawns from entering goal area var goalCols = []; for (var i = Math.floor(gridCols / 2) - 2; i <= Math.floor(gridCols / 2) + 2; i++) { goalCols.push(i); } if ( // Top goal row y === 0 && goalCols.indexOf(x) !== -1 || // Bottom goal row y === gridRows - 1 && goalCols.indexOf(x) !== -1) { return true; } return occ === 'player' || occ === 'ai' || occ === 'wall'; } function cellFree(x, y) { if (!isInsideGrid(x, y)) return false; var occ = grid[y][x].occupied; return occ === null || occ === 'ball'; } function knightMoves(x, y) { var moves = [{ dx: 1, dy: 2 }, { dx: 2, dy: 1 }, { dx: -1, dy: 2 }, { dx: -2, dy: 1 }, { dx: 1, dy: -2 }, { dx: 2, dy: -1 }, { dx: -1, dy: -2 }, { dx: -2, dy: -1 }]; var res = []; for (var i = 0; i < moves.length; i++) { var nx = x + moves[i].dx; var ny = y + moves[i].dy; if (isInsideGrid(nx, ny) && cellFree(nx, ny)) { res.push({ x: nx, y: ny }); } } return res; } function straightLineShots(x, y) { // Up, Down, Left, Right var dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }]; var shots = []; for (var d = 0; d < dirs.length; d++) { var nx = x, ny = y; while (true) { nx += dirs[d].dx; ny += dirs[d].dy; if (!isInsideGrid(nx, ny)) break; if (cellBlocked(nx, ny)) break; shots.push({ x: nx, y: ny, dir: dirs[d] }); if (isGoal(nx, ny, turn === 'player')) break; } } return shots; } function highlightCells(cells, color) { for (var i = 0; i < cells.length; i++) { var hl = LK.getAsset('target_indicator', { width: cellSize - 10, height: cellSize - 10, anchorX: 0.5, anchorY: 0.5, x: gridOriginX + cells[i].x * cellSize + cellSize / 2, y: gridOriginY + cells[i].y * cellSize + cellSize / 2, alpha: 0.7 }); game.addChild(hl); highlightOverlays.push(hl); } } function clearHighlights() { for (var i = 0; i < highlightOverlays.length; i++) { highlightOverlays[i].destroy(); } highlightOverlays = []; } var highlightOverlays = []; // --- Wall Generation (Tetris shapes) --- var tetrisShapes = [ // I [{ x: 0, y: 0 }, { x: 0, y: 1 }, { x: 0, y: 2 }, { x: 0, y: 3 }], // O [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }], // T [{ x: 0, y: 0 }, { x: -1, y: 1 }, { x: 0, y: 1 }, { x: 1, y: 1 }], // L [{ x: 0, y: 0 }, { x: 0, y: 1 }, { x: 0, y: 2 }, { x: 1, y: 2 }], // S [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 1 }]]; function randomWallShape() { var idx = Math.floor(Math.random() * tetrisShapes.length); return tetrisShapes[idx]; } function canPlaceWallAt(shape, ox, oy) { for (var i = 0; i < shape.length; i++) { var x = ox + shape[i].x; var y = oy + shape[i].y; if (!isInsideGrid(x, y)) return false; if (grid[y][x].occupied) return false; // Don't block ball or pawns if (ball && ball.gridX === x && ball.gridY === y) return false; if (getPawnAt(x, y)) return false; } return true; } function placeRandomWall() { // Try up to 10 times to find a spot for (var tries = 0; tries < 10; tries++) { var shape = randomWallShape(); var ox = Math.floor(Math.random() * (gridCols - 2)) + 1; var oy = Math.floor(Math.random() * (gridRows - 2)) + 1; if (canPlaceWallAt(shape, ox, oy)) { var cells = []; for (var i = 0; i < shape.length; i++) { var x = ox + shape[i].x; var y = oy + shape[i].y; cells.push({ x: x, y: y }); } var wall = new Wall(); wall.init(cells, wallIdCounter); wall.wallId = wallIdCounter; wallIdCounter++; walls.push(wall); game.addChild(wall); for (var i = 0; i < cells.length; i++) { grid[cells[i].y][cells[i].x].occupied = 'wall'; grid[cells[i].y][cells[i].x].wallId = wall.wallId; } break; } } } // --- Turn Logic --- function startPlayerTurn() { turn = 'player'; setInfo("Your turn: Tap a pawn to move or shoot"); clearHighlights(); selectedPawn = null; validMoves = []; validShots = []; gameLocked = false; } function startAITurn() { turn = 'ai'; setInfo("AI's turn..."); clearHighlights(); selectedPawn = null; validMoves = []; validShots = []; gameLocked = true; LK.setTimeout(aiTakeTurn, 700); } function endTurn() { // After each turn, maybe spawn a wall if (Math.random() < 0.35) { placeRandomWall(); } if (turn === 'player') { startAITurn(); } else { startPlayerTurn(); } } // --- Ball Movement and Goal Check --- function moveBallTo(x, y, onFinish) { // Remove old ball from grid grid[ball.gridY][ball.gridX].occupied = null; ball.setGridPos(x, y); grid[y][x].occupied = 'ball'; ball.flash(); if (onFinish) LK.setTimeout(onFinish, 200); } function shootBall(fromX, fromY, toX, toY, dir, onFinish) { // Animate ball along the path var path = []; var nx = fromX, ny = fromY; while (true) { nx += dir.dx; ny += dir.dy; if (!isInsideGrid(nx, ny)) break; if (cellBlocked(nx, ny)) break; path.push({ x: nx, y: ny }); if (isGoal(nx, ny, turn === 'player')) break; if (isGoal(nx, ny, turn === 'ai')) break; } function animateStep(idx) { if (idx >= path.length) { if (onFinish) onFinish(); return; } moveBallTo(path[idx].x, path[idx].y, function () { animateStep(idx + 1); }); } animateStep(0); } // --- Player Input Handling --- game.down = function (x, y, obj) { if (gameLocked) return; // Convert to grid coordinates var gx = Math.floor((x - gridOriginX) / cellSize); var gy = Math.floor((y - gridOriginY) / cellSize); if (!isInsideGrid(gx, gy)) return; // If no pawn selected, select a pawn if (!selectedPawn) { for (var i = 0; i < playerPawns.length; i++) { var pawn = playerPawns[i]; if (pawn.gridX === gx && pawn.gridY === gy) { selectedPawn = pawn; pawn.flash(); // Show knight moves validMoves = knightMoves(gx, gy); highlightCells(validMoves, 0x00ff00); // If pawn is adjacent to ball, allow to pick up if (Math.abs(ball.gridX - gx) + Math.abs(ball.gridY - gy) === 1) { validMoves.push({ x: ball.gridX, y: ball.gridY }); } // If pawn is on ball, allow to shoot if (gx === ball.gridX && gy === ball.gridY) { validShots = straightLineShots(gx, gy); highlightCells(validShots, 0xffa500); } return; } } } else { // If clicked on a valid move, move pawn for (var i = 0; i < validMoves.length; i++) { if (validMoves[i].x === gx && validMoves[i].y === gy) { // Prevent pawn from moving into the goal area var goalCols = []; for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) { goalCols.push(gi); } if (gy === 0 && goalCols.indexOf(gx) !== -1 || gy === gridRows - 1 && goalCols.indexOf(gx) !== -1) { setInfo("You can't enter the goal area!"); return; } // Move pawn grid[selectedPawn.gridY][selectedPawn.gridX].occupied = null; selectedPawn.setGridPos(gx, gy); grid[gy][gx].occupied = 'player'; // If moved onto ball, pick up if (gx === ball.gridX && gy === ball.gridY) { selectedPawn.hasBall = true; setInfo("You have the ball! Move again or tap a direction to shoot."); // Allow dribbling: highlight knight moves and shots again validMoves = knightMoves(gx, gy); validShots = straightLineShots(gx, gy); clearHighlights(); highlightCells(validMoves, 0x00ff00); highlightCells(validShots, 0xffa500); return; } clearHighlights(); endTurn(); return; } } // If pawn has ball and clicked on valid shot, shoot if (selectedPawn.hasBall) { // Check for shot for (var i = 0; i < validShots.length; i++) { if (validShots[i].x === gx && validShots[i].y === gy) { selectedPawn.hasBall = false; clearHighlights(); shootBall(selectedPawn.gridX, selectedPawn.gridY, gx, gy, validShots[i].dir, function () { // Check for goal if (isGoal(gx, gy, true)) { playerScore++; updateScore(); setInfo("GOAL! You scored!"); if (playerScore >= goalToWin) { LK.showYouWin(); return; } LK.setTimeout(function () { resetAfterGoal(false); }, 1200); return; } else if (isGoal(gx, gy, false)) { aiScore++; updateScore(); setInfo("AI scored!"); if (aiScore >= goalToWin) { LK.showGameOver(); return; } LK.setTimeout(function () { resetAfterGoal(true); }, 1200); return; } else { endTurn(); } }); return; } } // Check for dribble move (knight move with ball) for (var i = 0; i < validMoves.length; i++) { if (validMoves[i].x === gx && validMoves[i].y === gy) { // Prevent pawn from dribbling into the goal area var goalCols = []; for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) { goalCols.push(gi); } if (gy === 0 && goalCols.indexOf(gx) !== -1 || gy === gridRows - 1 && goalCols.indexOf(gx) !== -1) { setInfo("You can't enter the goal area!"); return; } // Move pawn and ball together grid[selectedPawn.gridY][selectedPawn.gridX].occupied = null; selectedPawn.setGridPos(gx, gy); grid[gy][gx].occupied = 'player'; moveBallTo(gx, gy); // Allow shoot after dribble setInfo("Tap a direction to shoot!"); validShots = straightLineShots(gx, gy); validMoves = []; // Only allow shoot after dribble, not another move clearHighlights(); highlightCells(validShots, 0xffa500); return; } } } // Deselect if clicked elsewhere clearHighlights(); selectedPawn = null; validMoves = []; validShots = []; } }; // --- AI Logic --- function aiTakeTurn() { // Simple AI: pick pawn closest to ball, try to move towards ball or shoot if on ball var bestPawn = null; var minDist = 9999; for (var i = 0; i < aiPawns.length; i++) { var pawn = aiPawns[i]; var dist = Math.abs(pawn.gridX - ball.gridX) + Math.abs(pawn.gridY - ball.gridY); if (dist < minDist) { minDist = dist; bestPawn = pawn; } } if (!bestPawn) { endTurn(); return; } // If on ball, shoot towards player goal if (bestPawn.gridX === ball.gridX && bestPawn.gridY === ball.gridY) { var shots = straightLineShots(bestPawn.gridX, bestPawn.gridY); // Prefer shooting downwards (towards player goal) var bestShot = null; for (var i = 0; i < shots.length; i++) { if (shots[i].dir.dy > 0) { bestShot = shots[i]; break; } } if (!bestShot && shots.length > 0) bestShot = shots[0]; if (bestShot) { shootBall(bestPawn.gridX, bestPawn.gridY, bestShot.x, bestShot.y, bestShot.dir, function () { if (isGoal(bestShot.x, bestShot.y, false)) { aiScore++; updateScore(); setInfo("AI scored!"); if (aiScore >= goalToWin) { LK.showGameOver(); return; } LK.setTimeout(function () { resetAfterGoal(true); }, 1200); } else { endTurn(); } }); return; } } // Else, move pawn towards ball using knight move var moves = knightMoves(bestPawn.gridX, bestPawn.gridY); var bestMove = null; minDist = 9999; for (var i = 0; i < moves.length; i++) { var dist = Math.abs(moves[i].x - ball.gridX) + Math.abs(moves[i].y - ball.gridY); if (dist < minDist) { minDist = dist; bestMove = moves[i]; } } if (bestMove) { // Prevent AI pawn from moving into the goal area var goalCols = []; for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) { goalCols.push(gi); } if (!(bestMove.y === 0 && goalCols.indexOf(bestMove.x) !== -1 || bestMove.y === gridRows - 1 && goalCols.indexOf(bestMove.x) !== -1)) { grid[bestPawn.gridY][bestPawn.gridX].occupied = null; bestPawn.setGridPos(bestMove.x, bestMove.y); grid[bestMove.y][bestMove.x].occupied = 'ai'; // If moved onto ball, pick up and shoot next turn if (bestMove.x === ball.gridX && bestMove.y === ball.gridY) { bestPawn.hasBall = true; } } } endTurn(); } // --- Reset After Goal --- function resetAfterGoal(aiScored) { // Remove all walls for (var i = 0; i < walls.length; i++) { walls[i].destroy(); } walls = []; // Clear grid for (var y = 0; y < gridRows; y++) { for (var x = 0; x < gridCols; x++) { grid[y][x].occupied = null; grid[y][x].wallId = null; } } // Reset pawns for (var i = 0; i < playerPawns.length; i++) { playerPawns[i].destroy(); } for (var i = 0; i < aiPawns.length; i++) { aiPawns[i].destroy(); } playerPawns = []; aiPawns = []; placePawns(); // Reset ball placeBall(true); clearHighlights(); selectedPawn = null; validMoves = []; validShots = []; gameLocked = false; setInfo(''); if (aiScored) { startPlayerTurn(); } else { startAITurn(); } } // --- Game Update (not used for logic, but could be for animations) --- game.update = function () { // No per-frame logic needed for turn-based }; // --- Start Game --- updateScore(); startPlayerTurn();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// --- Ball ---
var Ball = Container.expand(function () {
var self = Container.call(this);
self.gridX = 0;
self.gridY = 0;
self.ballAsset = self.attachAsset('ball', {
width: cellSize * 0.6 * 1.75,
height: cellSize * 0.6 * 1.75,
color: 0xffffff,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPos = function (gx, gy) {
self.gridX = gx;
self.gridY = gy;
self.x = gridOriginX + gx * cellSize + cellSize / 2;
self.y = gridOriginY + gy * cellSize + cellSize / 2;
};
self.flash = function () {
LK.effects.flashObject(self, 0xffa500, 200);
};
return self;
});
// --- PlayerPawn: For both player and AI pawns ---
var PlayerPawn = Container.expand(function () {
var self = Container.call(this);
// Use different colors for player and AI
self.isAI = false;
self.gridX = 0;
self.gridY = 0;
self.hasBall = false;
self.pawnAsset = null;
self.init = function (isAI) {
self.isAI = isAI;
var color = isAI ? 0x1e90ff : 0x32cd32;
self.pawnAsset = self.attachAsset('pawn_' + (isAI ? 'ai' : 'player'), {
width: (cellSize - 8) * 1.75,
height: (cellSize - 8) * 1.75,
color: color,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
};
self.setGridPos = function (gx, gy) {
self.gridX = gx;
self.gridY = gy;
self.x = gridOriginX + gx * cellSize + cellSize / 2;
self.y = gridOriginY + gy * cellSize + cellSize / 2;
};
self.flash = function () {
LK.effects.flashObject(self, 0xffff00, 300);
};
return self;
});
// --- Wall: Tetris block wall, occupies multiple cells ---
var Wall = Container.expand(function () {
var self = Container.call(this);
self.cells = []; // [{x, y}]
self.wallId = null;
self.blocks = [];
self.init = function (cells, wallId) {
self.cells = cells;
self.wallId = wallId;
for (var i = 0; i < cells.length; i++) {
var block = self.attachAsset('wall', {
width: cellSize - 6,
height: cellSize - 6,
color: 0x888888,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: (cells[i].x - cells[0].x) * cellSize,
y: (cells[i].y - cells[0].y) * cellSize
});
self.blocks.push(block);
}
// Position wall at first cell
self.x = gridOriginX + cells[0].x * cellSize + cellSize / 2;
self.y = gridOriginY + cells[0].y * cellSize + cellSize / 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// --- GridCell: For grid logic, not a display object ---
// --- Grid Setup ---
var GridCell = function GridCell(x, y) {
this.x = x;
this.y = y;
this.occupied = null; // null, 'player', 'ai', 'wall', 'ball'
this.wallId = null; // If wall, which wall
};
var gridCols = 21;
var gridRows = 31;
var cellSize = Math.floor(Math.min(2048 / gridCols, 2732 / gridRows));
var gridWidth = gridCols * cellSize;
var gridHeight = gridRows * cellSize;
var gridOriginX = Math.floor((2048 - gridWidth) / 2);
var gridOriginY = Math.floor((2732 - gridHeight) / 2);
// --- Game State ---
var grid = [];
for (var y = 0; y < gridRows; y++) {
var row = [];
for (var x = 0; x < gridCols; x++) {
row.push(new GridCell(x, y));
}
grid.push(row);
}
var playerPawns = [];
var aiPawns = [];
var ball = null;
var walls = [];
var wallIdCounter = 1;
var turn = 'player'; // 'player' or 'ai'
var selectedPawn = null;
var validMoves = [];
var validShots = [];
var gameLocked = false;
var playerScore = 0;
var aiScore = 0;
var goalToWin = 3;
var scoreTxt = null;
var infoTxt = null;
// --- Draw grid cell backgrounds ---
for (var gy = 0; gy < gridRows; gy++) {
for (var gx = 0; gx < gridCols; gx++) {
var cellBg = LK.getAsset('grid_cell', {
width: cellSize,
height: cellSize,
anchorX: 0,
anchorY: 0,
x: gridOriginX + gx * cellSize,
y: gridOriginY + gy * cellSize
});
game.addChild(cellBg);
}
}
// --- Draw grid lines (for visual aid) ---
for (var gx = 0; gx <= gridCols; gx++) {
var vline = LK.getAsset('wall_block', {
width: 4,
height: gridHeight,
color: 0x222222,
shape: 'box',
anchorX: 0.5,
anchorY: 0,
x: gridOriginX + gx * cellSize - 2,
y: gridOriginY
});
game.addChild(vline);
}
for (var gy = 0; gy <= gridRows; gy++) {
var hline = LK.getAsset('wall_block', {
width: gridWidth,
height: 4,
color: 0x222222,
shape: 'box',
anchorX: 0,
anchorY: 0.5,
x: gridOriginX,
y: gridOriginY + gy * cellSize - 2
});
game.addChild(hline);
}
// --- Place Pawns ---
function placePawns() {
// Player: 3 pawns, bottom center
var pxs = [Math.floor(gridCols / 2) - 2, Math.floor(gridCols / 2), Math.floor(gridCols / 2) + 2];
for (var i = 0; i < 3; i++) {
var pawn = new PlayerPawn();
pawn.init(false);
pawn.setGridPos(pxs[i], gridRows - 3);
// Prevent pawn from being placed in the goal area
if (gridRows - 3 === 0 && pxs[i] >= Math.floor(gridCols / 2) - 2 && pxs[i] <= Math.floor(gridCols / 2) + 2) {
continue;
}
playerPawns.push(pawn);
game.addChild(pawn);
grid[gridRows - 3][pxs[i]].occupied = 'player';
}
// AI: 3 pawns, top center
var axs = [Math.floor(gridCols / 2) - 2, Math.floor(gridCols / 2), Math.floor(gridCols / 2) + 2];
for (var i = 0; i < 3; i++) {
var pawn = new PlayerPawn();
pawn.init(true);
pawn.setGridPos(axs[i], 2);
// Prevent pawn from being placed in the goal area
if (2 === gridRows - 1 && axs[i] >= Math.floor(gridCols / 2) - 2 && axs[i] <= Math.floor(gridCols / 2) + 2) {
continue;
}
aiPawns.push(pawn);
game.addChild(pawn);
grid[2][axs[i]].occupied = 'ai';
}
}
placePawns();
// --- Place Ball ---
function placeBall(center) {
if (ball) {
ball.destroy();
}
ball = new Ball();
var bx = center ? Math.floor(gridCols / 2) : playerPawns[1].gridX;
var by = center ? Math.floor(gridRows / 2) : playerPawns[1].gridY - 1;
ball.setGridPos(bx, by);
game.addChild(ball);
grid[by][bx].occupied = 'ball';
}
placeBall(true);
// --- Draw Goals (visual only) ---
var goalWidth = 5;
var goalColor = 0xffd700;
var goalTop = LK.getAsset('goal', {
width: cellSize * goalWidth,
height: cellSize * 0.7,
anchorX: 0.5,
anchorY: 0,
x: gridOriginX + Math.floor(gridCols / 2) * cellSize + cellSize / 2,
y: gridOriginY - cellSize * 0.7
});
game.addChild(goalTop);
var goalBot = LK.getAsset('goal', {
width: cellSize * goalWidth,
height: cellSize * 0.7,
anchorX: 0.5,
anchorY: 1,
x: gridOriginX + Math.floor(gridCols / 2) * cellSize + cellSize / 2,
y: gridOriginY + gridHeight + cellSize * 0.7
});
game.addChild(goalBot);
// --- Score Display ---
scoreTxt = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Info Text ---
infoTxt = new Text2('', {
size: 60,
fill: "#fff"
});
infoTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(infoTxt);
// --- Utility Functions ---
function updateScore() {
scoreTxt.setText(playerScore + ' : ' + aiScore);
}
function setInfo(msg) {
infoTxt.setText(msg);
}
function clearInfo() {
infoTxt.setText('');
}
function isInsideGrid(x, y) {
return x >= 0 && x < gridCols && y >= 0 && y < gridRows;
}
function isGoal(x, y, forPlayer) {
// Top row for player, bottom row for AI
var goalCols = [];
for (var i = Math.floor(gridCols / 2) - 2; i <= Math.floor(gridCols / 2) + 2; i++) {
goalCols.push(i);
}
if (forPlayer && y === 0 && goalCols.indexOf(x) !== -1) return true;
if (!forPlayer && y === gridRows - 1 && goalCols.indexOf(x) !== -1) return true;
return false;
}
function getPawnAt(x, y) {
for (var i = 0; i < playerPawns.length; i++) {
if (playerPawns[i].gridX === x && playerPawns[i].gridY === y) return playerPawns[i];
}
for (var i = 0; i < aiPawns.length; i++) {
if (aiPawns[i].gridX === x && aiPawns[i].gridY === y) return aiPawns[i];
}
return null;
}
function getWallAt(x, y) {
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
for (var j = 0; j < wall.cells.length; j++) {
if (wall.cells[j].x === x && wall.cells[j].y === y) return wall;
}
}
return null;
}
function cellBlocked(x, y) {
if (!isInsideGrid(x, y)) return true;
var occ = grid[y][x].occupied;
// Prevent pawns from entering goal area
var goalCols = [];
for (var i = Math.floor(gridCols / 2) - 2; i <= Math.floor(gridCols / 2) + 2; i++) {
goalCols.push(i);
}
if (
// Top goal row
y === 0 && goalCols.indexOf(x) !== -1 ||
// Bottom goal row
y === gridRows - 1 && goalCols.indexOf(x) !== -1) {
return true;
}
return occ === 'player' || occ === 'ai' || occ === 'wall';
}
function cellFree(x, y) {
if (!isInsideGrid(x, y)) return false;
var occ = grid[y][x].occupied;
return occ === null || occ === 'ball';
}
function knightMoves(x, y) {
var moves = [{
dx: 1,
dy: 2
}, {
dx: 2,
dy: 1
}, {
dx: -1,
dy: 2
}, {
dx: -2,
dy: 1
}, {
dx: 1,
dy: -2
}, {
dx: 2,
dy: -1
}, {
dx: -1,
dy: -2
}, {
dx: -2,
dy: -1
}];
var res = [];
for (var i = 0; i < moves.length; i++) {
var nx = x + moves[i].dx;
var ny = y + moves[i].dy;
if (isInsideGrid(nx, ny) && cellFree(nx, ny)) {
res.push({
x: nx,
y: ny
});
}
}
return res;
}
function straightLineShots(x, y) {
// Up, Down, Left, Right
var dirs = [{
dx: 0,
dy: -1
}, {
dx: 0,
dy: 1
}, {
dx: -1,
dy: 0
}, {
dx: 1,
dy: 0
}];
var shots = [];
for (var d = 0; d < dirs.length; d++) {
var nx = x,
ny = y;
while (true) {
nx += dirs[d].dx;
ny += dirs[d].dy;
if (!isInsideGrid(nx, ny)) break;
if (cellBlocked(nx, ny)) break;
shots.push({
x: nx,
y: ny,
dir: dirs[d]
});
if (isGoal(nx, ny, turn === 'player')) break;
}
}
return shots;
}
function highlightCells(cells, color) {
for (var i = 0; i < cells.length; i++) {
var hl = LK.getAsset('target_indicator', {
width: cellSize - 10,
height: cellSize - 10,
anchorX: 0.5,
anchorY: 0.5,
x: gridOriginX + cells[i].x * cellSize + cellSize / 2,
y: gridOriginY + cells[i].y * cellSize + cellSize / 2,
alpha: 0.7
});
game.addChild(hl);
highlightOverlays.push(hl);
}
}
function clearHighlights() {
for (var i = 0; i < highlightOverlays.length; i++) {
highlightOverlays[i].destroy();
}
highlightOverlays = [];
}
var highlightOverlays = [];
// --- Wall Generation (Tetris shapes) ---
var tetrisShapes = [
// I
[{
x: 0,
y: 0
}, {
x: 0,
y: 1
}, {
x: 0,
y: 2
}, {
x: 0,
y: 3
}],
// O
[{
x: 0,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: 1
}, {
x: 1,
y: 1
}],
// T
[{
x: 0,
y: 0
}, {
x: -1,
y: 1
}, {
x: 0,
y: 1
}, {
x: 1,
y: 1
}],
// L
[{
x: 0,
y: 0
}, {
x: 0,
y: 1
}, {
x: 0,
y: 2
}, {
x: 1,
y: 2
}],
// S
[{
x: 0,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: 1
}, {
x: -1,
y: 1
}]];
function randomWallShape() {
var idx = Math.floor(Math.random() * tetrisShapes.length);
return tetrisShapes[idx];
}
function canPlaceWallAt(shape, ox, oy) {
for (var i = 0; i < shape.length; i++) {
var x = ox + shape[i].x;
var y = oy + shape[i].y;
if (!isInsideGrid(x, y)) return false;
if (grid[y][x].occupied) return false;
// Don't block ball or pawns
if (ball && ball.gridX === x && ball.gridY === y) return false;
if (getPawnAt(x, y)) return false;
}
return true;
}
function placeRandomWall() {
// Try up to 10 times to find a spot
for (var tries = 0; tries < 10; tries++) {
var shape = randomWallShape();
var ox = Math.floor(Math.random() * (gridCols - 2)) + 1;
var oy = Math.floor(Math.random() * (gridRows - 2)) + 1;
if (canPlaceWallAt(shape, ox, oy)) {
var cells = [];
for (var i = 0; i < shape.length; i++) {
var x = ox + shape[i].x;
var y = oy + shape[i].y;
cells.push({
x: x,
y: y
});
}
var wall = new Wall();
wall.init(cells, wallIdCounter);
wall.wallId = wallIdCounter;
wallIdCounter++;
walls.push(wall);
game.addChild(wall);
for (var i = 0; i < cells.length; i++) {
grid[cells[i].y][cells[i].x].occupied = 'wall';
grid[cells[i].y][cells[i].x].wallId = wall.wallId;
}
break;
}
}
}
// --- Turn Logic ---
function startPlayerTurn() {
turn = 'player';
setInfo("Your turn: Tap a pawn to move or shoot");
clearHighlights();
selectedPawn = null;
validMoves = [];
validShots = [];
gameLocked = false;
}
function startAITurn() {
turn = 'ai';
setInfo("AI's turn...");
clearHighlights();
selectedPawn = null;
validMoves = [];
validShots = [];
gameLocked = true;
LK.setTimeout(aiTakeTurn, 700);
}
function endTurn() {
// After each turn, maybe spawn a wall
if (Math.random() < 0.35) {
placeRandomWall();
}
if (turn === 'player') {
startAITurn();
} else {
startPlayerTurn();
}
}
// --- Ball Movement and Goal Check ---
function moveBallTo(x, y, onFinish) {
// Remove old ball from grid
grid[ball.gridY][ball.gridX].occupied = null;
ball.setGridPos(x, y);
grid[y][x].occupied = 'ball';
ball.flash();
if (onFinish) LK.setTimeout(onFinish, 200);
}
function shootBall(fromX, fromY, toX, toY, dir, onFinish) {
// Animate ball along the path
var path = [];
var nx = fromX,
ny = fromY;
while (true) {
nx += dir.dx;
ny += dir.dy;
if (!isInsideGrid(nx, ny)) break;
if (cellBlocked(nx, ny)) break;
path.push({
x: nx,
y: ny
});
if (isGoal(nx, ny, turn === 'player')) break;
if (isGoal(nx, ny, turn === 'ai')) break;
}
function animateStep(idx) {
if (idx >= path.length) {
if (onFinish) onFinish();
return;
}
moveBallTo(path[idx].x, path[idx].y, function () {
animateStep(idx + 1);
});
}
animateStep(0);
}
// --- Player Input Handling ---
game.down = function (x, y, obj) {
if (gameLocked) return;
// Convert to grid coordinates
var gx = Math.floor((x - gridOriginX) / cellSize);
var gy = Math.floor((y - gridOriginY) / cellSize);
if (!isInsideGrid(gx, gy)) return;
// If no pawn selected, select a pawn
if (!selectedPawn) {
for (var i = 0; i < playerPawns.length; i++) {
var pawn = playerPawns[i];
if (pawn.gridX === gx && pawn.gridY === gy) {
selectedPawn = pawn;
pawn.flash();
// Show knight moves
validMoves = knightMoves(gx, gy);
highlightCells(validMoves, 0x00ff00);
// If pawn is adjacent to ball, allow to pick up
if (Math.abs(ball.gridX - gx) + Math.abs(ball.gridY - gy) === 1) {
validMoves.push({
x: ball.gridX,
y: ball.gridY
});
}
// If pawn is on ball, allow to shoot
if (gx === ball.gridX && gy === ball.gridY) {
validShots = straightLineShots(gx, gy);
highlightCells(validShots, 0xffa500);
}
return;
}
}
} else {
// If clicked on a valid move, move pawn
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].x === gx && validMoves[i].y === gy) {
// Prevent pawn from moving into the goal area
var goalCols = [];
for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) {
goalCols.push(gi);
}
if (gy === 0 && goalCols.indexOf(gx) !== -1 || gy === gridRows - 1 && goalCols.indexOf(gx) !== -1) {
setInfo("You can't enter the goal area!");
return;
}
// Move pawn
grid[selectedPawn.gridY][selectedPawn.gridX].occupied = null;
selectedPawn.setGridPos(gx, gy);
grid[gy][gx].occupied = 'player';
// If moved onto ball, pick up
if (gx === ball.gridX && gy === ball.gridY) {
selectedPawn.hasBall = true;
setInfo("You have the ball! Move again or tap a direction to shoot.");
// Allow dribbling: highlight knight moves and shots again
validMoves = knightMoves(gx, gy);
validShots = straightLineShots(gx, gy);
clearHighlights();
highlightCells(validMoves, 0x00ff00);
highlightCells(validShots, 0xffa500);
return;
}
clearHighlights();
endTurn();
return;
}
}
// If pawn has ball and clicked on valid shot, shoot
if (selectedPawn.hasBall) {
// Check for shot
for (var i = 0; i < validShots.length; i++) {
if (validShots[i].x === gx && validShots[i].y === gy) {
selectedPawn.hasBall = false;
clearHighlights();
shootBall(selectedPawn.gridX, selectedPawn.gridY, gx, gy, validShots[i].dir, function () {
// Check for goal
if (isGoal(gx, gy, true)) {
playerScore++;
updateScore();
setInfo("GOAL! You scored!");
if (playerScore >= goalToWin) {
LK.showYouWin();
return;
}
LK.setTimeout(function () {
resetAfterGoal(false);
}, 1200);
return;
} else if (isGoal(gx, gy, false)) {
aiScore++;
updateScore();
setInfo("AI scored!");
if (aiScore >= goalToWin) {
LK.showGameOver();
return;
}
LK.setTimeout(function () {
resetAfterGoal(true);
}, 1200);
return;
} else {
endTurn();
}
});
return;
}
}
// Check for dribble move (knight move with ball)
for (var i = 0; i < validMoves.length; i++) {
if (validMoves[i].x === gx && validMoves[i].y === gy) {
// Prevent pawn from dribbling into the goal area
var goalCols = [];
for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) {
goalCols.push(gi);
}
if (gy === 0 && goalCols.indexOf(gx) !== -1 || gy === gridRows - 1 && goalCols.indexOf(gx) !== -1) {
setInfo("You can't enter the goal area!");
return;
}
// Move pawn and ball together
grid[selectedPawn.gridY][selectedPawn.gridX].occupied = null;
selectedPawn.setGridPos(gx, gy);
grid[gy][gx].occupied = 'player';
moveBallTo(gx, gy);
// Allow shoot after dribble
setInfo("Tap a direction to shoot!");
validShots = straightLineShots(gx, gy);
validMoves = []; // Only allow shoot after dribble, not another move
clearHighlights();
highlightCells(validShots, 0xffa500);
return;
}
}
}
// Deselect if clicked elsewhere
clearHighlights();
selectedPawn = null;
validMoves = [];
validShots = [];
}
};
// --- AI Logic ---
function aiTakeTurn() {
// Simple AI: pick pawn closest to ball, try to move towards ball or shoot if on ball
var bestPawn = null;
var minDist = 9999;
for (var i = 0; i < aiPawns.length; i++) {
var pawn = aiPawns[i];
var dist = Math.abs(pawn.gridX - ball.gridX) + Math.abs(pawn.gridY - ball.gridY);
if (dist < minDist) {
minDist = dist;
bestPawn = pawn;
}
}
if (!bestPawn) {
endTurn();
return;
}
// If on ball, shoot towards player goal
if (bestPawn.gridX === ball.gridX && bestPawn.gridY === ball.gridY) {
var shots = straightLineShots(bestPawn.gridX, bestPawn.gridY);
// Prefer shooting downwards (towards player goal)
var bestShot = null;
for (var i = 0; i < shots.length; i++) {
if (shots[i].dir.dy > 0) {
bestShot = shots[i];
break;
}
}
if (!bestShot && shots.length > 0) bestShot = shots[0];
if (bestShot) {
shootBall(bestPawn.gridX, bestPawn.gridY, bestShot.x, bestShot.y, bestShot.dir, function () {
if (isGoal(bestShot.x, bestShot.y, false)) {
aiScore++;
updateScore();
setInfo("AI scored!");
if (aiScore >= goalToWin) {
LK.showGameOver();
return;
}
LK.setTimeout(function () {
resetAfterGoal(true);
}, 1200);
} else {
endTurn();
}
});
return;
}
}
// Else, move pawn towards ball using knight move
var moves = knightMoves(bestPawn.gridX, bestPawn.gridY);
var bestMove = null;
minDist = 9999;
for (var i = 0; i < moves.length; i++) {
var dist = Math.abs(moves[i].x - ball.gridX) + Math.abs(moves[i].y - ball.gridY);
if (dist < minDist) {
minDist = dist;
bestMove = moves[i];
}
}
if (bestMove) {
// Prevent AI pawn from moving into the goal area
var goalCols = [];
for (var gi = Math.floor(gridCols / 2) - 2; gi <= Math.floor(gridCols / 2) + 2; gi++) {
goalCols.push(gi);
}
if (!(bestMove.y === 0 && goalCols.indexOf(bestMove.x) !== -1 || bestMove.y === gridRows - 1 && goalCols.indexOf(bestMove.x) !== -1)) {
grid[bestPawn.gridY][bestPawn.gridX].occupied = null;
bestPawn.setGridPos(bestMove.x, bestMove.y);
grid[bestMove.y][bestMove.x].occupied = 'ai';
// If moved onto ball, pick up and shoot next turn
if (bestMove.x === ball.gridX && bestMove.y === ball.gridY) {
bestPawn.hasBall = true;
}
}
}
endTurn();
}
// --- Reset After Goal ---
function resetAfterGoal(aiScored) {
// Remove all walls
for (var i = 0; i < walls.length; i++) {
walls[i].destroy();
}
walls = [];
// Clear grid
for (var y = 0; y < gridRows; y++) {
for (var x = 0; x < gridCols; x++) {
grid[y][x].occupied = null;
grid[y][x].wallId = null;
}
}
// Reset pawns
for (var i = 0; i < playerPawns.length; i++) {
playerPawns[i].destroy();
}
for (var i = 0; i < aiPawns.length; i++) {
aiPawns[i].destroy();
}
playerPawns = [];
aiPawns = [];
placePawns();
// Reset ball
placeBall(true);
clearHighlights();
selectedPawn = null;
validMoves = [];
validShots = [];
gameLocked = false;
setInfo('');
if (aiScored) {
startPlayerTurn();
} else {
startAITurn();
}
}
// --- Game Update (not used for logic, but could be for animations) ---
game.update = function () {
// No per-frame logic needed for turn-based
};
// --- Start Game ---
updateScore();
startPlayerTurn();