import 'dart:math'; import 'package:bubbletwist/game/stone/SpecialStone.dart'; import '../enums/StoneColor.dart'; import 'Game.dart'; import 'stone/PentaSpecialStone.dart'; import 'stone/Stone.dart'; import 'stone/StoneLocation.dart'; class Board { static const int boardSize = 8; final Game game; late final List> stones = List.filled(boardSize, List.filled(boardSize, Stone())); Board(this.game) { for (var row in stones) { for (var stone in row) { stone = Stone(); } } // The map generates with pairs of 3+ stones already in place. // To fix that we just let the game remove all 3+ stone pairs until none are left //while (checkBoard()); } void updateBoard() { game.updateBoard(); } bool swapStones(StoneLocation sl1, StoneLocation sl2) { // Check if the two stones that have been clicked are next to each other if (!(sl1.row >= sl2.row - 1 && sl1.row <= sl2.row + 1) || !(sl1.column >= sl2.column - 1 && sl1.column <= sl2.column + 1)) { return false; // If they are not next to each other just abort } final s1 = game.getStone(sl1); final ss1 = s1 is PentaSpecialStone ? s1 : null; if (ss1 != null) { ss1.stoneColor = getStone(sl2)!.stoneColor; performSpecialStone(ss1, sl1); return true; } final s2 = game.getStone(sl2); final ss2 = s2 is PentaSpecialStone ? s2 : null; if (ss2 != null) { ss2.stoneColor = getStone(sl1)!.stoneColor; performSpecialStone(ss2, sl2); return true; } // Swap stones swap(sl1, sl2); if (checkBoard()) { // Check if swapping was a valid move // If Swap is valid, Stones have been removed and others have to fall down applyGravity(); while (checkBoard()) { // Then we have to keep checking until no more 3+ stones are created by falling stones applyGravity(); } return true; } else { // If Swap is invalid, the stones get swapped back swap(sl1, sl2); return false; } } bool checkBoard() { bool stuffDeleted = false; // FOR HORIZONTALS for (int row = 0; row < boardSize; ++row) { // First go through each row int startPosition = 0; int counter = 1; for (int column = 1; column < boardSize; ++column) { // And then through each column if (stones[row][column] == null || stones[row][column - 1] == null) { // Ignore deleted stones continue; } final colorsAreSame = stones[row][column]!.stoneColor == stones[row][column - 1]!.stoneColor; // If both stones have the same color increase counter if (colorsAreSame) { counter++; } // If they are not the same color or we reach the end of the Board, // check the counter to see if the last 3+ stones had the same color if (!colorsAreSame || column == boardSize - 1) { if (counter >= 3) { // And if we had 3+ stones, remove them if (game.isRunning()) { game.addPoints(counter); game.addTime(3); } for (int k = 0; k < counter; ++k) { final st = StoneLocation(row: row, column: startPosition + k); removeStone(st); stuffDeleted = true; } if (counter == 3 && game.isRunning()) { //stones[row][startPosition] = TripleSpecialStone(this); } if (counter == 4 && game.isRunning()) { //stones[row][startPosition] = QuadSpecialStone(this, false); } if (counter == 5 && game.isRunning()) { stones[row][startPosition] = PentaSpecialStone(this); } } // Reset counter and start position after each color change counter = 1; startPosition = column; } } } // FOR VERTICALS for (int column = 0; column < boardSize; ++column) { // First go through each column int startPosition = 0; int counter = 1; for (int row = 1; row < boardSize; ++row) { // And then through each row if (stones[row][column] == null || stones[row - 1][column] == null) { // Ignore deleted stones continue; } // If both stones have the same color increase counter final colorsAreSame = stones[row][column]!.stoneColor == stones[row - 1][column]!.stoneColor; if (colorsAreSame) { counter++; } // If they are not the same color or we reach the end of the Board, // check the counter to see if the last 3+ stones had the same color if (!colorsAreSame || row == boardSize - 1) { if (counter >= 3) { //And if we had 3+ stones, remove them if (game.isRunning()) { game.addPoints(counter); game.addTime(3); } for (int k = 0; k < counter; ++k) { final st = StoneLocation(row: startPosition + k, column: column); removeStone(st); stuffDeleted = true; } if (counter == 3 && game.isRunning()) { //stones[startPosition][column] = TripleSpecialStone(this); } if (counter == 4 && game.isRunning()) { //stones[startPosition][column] = QuadSpecialStone(this, true); } if (counter == 5 && game.isRunning()) { stones[startPosition][column] = PentaSpecialStone(this); } } // Reset counter and start position after each color change counter = 1; startPosition = row; } } } if (stuffDeleted) { // If stuff has been deleted we have to update the Gui game.updateBoard(); } return stuffDeleted; } void removeStone(StoneLocation sl) { if (sl.row < 0 || sl.row >= boardSize || sl.column < 0 || sl.column >= boardSize) { return; } if (!game.isRunning()) { // If game is not running, just give the stones a new color stones[sl.row][sl.column]!.stoneColor = StoneColors.values[Random().nextInt(StoneColors.values.length)]; } else { // Otherwise mark stones as deleted stones[sl.row][sl.column] = null; } } void applyGravity() { bool stonesHaveFallenDown; // Move Stones down 1 row if the stone below them is marked as deleted do { stonesHaveFallenDown = false; for (int column = 0; column < boardSize; ++column) { for (int row = boardSize - 1; row > 0; --row) { if (stones[row][column] == null) { if (stones[row - 1][column] == null) { continue; } stones[row][column] = stones[row - 1][column]; stones[row - 1][column] = null; stonesHaveFallenDown = true; } } } } while ( stonesHaveFallenDown); // Continue doing so until all deleted Stones are at the top most position // Then generate new stones that rain from the sky) for (var row in stones) { for (var stone in row) { if (stone == null) { stone = Stone(); } } } game.updateBoard(); } void performSpecialStone(SpecialStone ss, StoneLocation sl) { //Delete Stones ss.performSpecialStoneAction(); removeStone(sl); //show Deleted Stones updateBoard(); //Make Stones fall down into new "holes" applyGravity(); updateBoard(); //Show the Gravity Effect while (checkBoard()) { // Then we have to keep checking until no more 3+ stones are created by falling stones applyGravity(); } } void swap(StoneLocation sl1, StoneLocation sl2) { final tmp = stones[sl1.row][sl1.column]; stones[sl1.row][sl1.column] = stones[sl2.row][sl2.column]; stones[sl2.row][sl2.column] = tmp; game.updateBoard(); } Stone? getStone(StoneLocation sl) { return stones[sl.row][sl.column]; } }