bubbletwist/lib/game/board.dart

253 lines
8.2 KiB
Dart
Raw Permalink Normal View History

import 'dart:math';
2024-06-19 23:40:31 +02:00
import 'package:bubbletwist/game/stone/quad_special_stone.dart';
2024-06-17 16:24:51 +02:00
import 'package:bubbletwist/game/stone/special_stone.dart';
2024-06-19 23:40:31 +02:00
import 'package:bubbletwist/game/stone/triple_special_stone.dart';
2024-06-17 16:24:51 +02:00
import '../enums/stone_color.dart';
import 'game.dart';
import 'stone/penta_special_stone.dart';
import 'stone/stone.dart';
import 'stone/stone_location.dart';
class Board {
static const int boardSize = 8;
final Game game;
2024-06-19 23:40:31 +02:00
late final List<List<Stone?>> stones =
List.generate(boardSize, (i) => List.generate(boardSize, (j) => Stone()));
bool specialStonesEnabled = false;
Board(this.game) {
// 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
2024-06-19 23:40:31 +02:00
while (checkBoard()) {}
}
2024-06-19 23:40:31 +02:00
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;
}
2024-06-19 23:40:31 +02:00
if (counter == 3 && game.isRunning() && specialStonesEnabled) {
stones[row][startPosition] = TripleSpecialStone(this);
}
2024-06-19 23:40:31 +02:00
if (counter == 4 && game.isRunning() && specialStonesEnabled) {
stones[row][startPosition] = QuadSpecialStone(this, false);
}
2024-06-19 23:40:31 +02:00
if (counter == 5 && game.isRunning() && specialStonesEnabled) {
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;
}
2024-06-19 23:40:31 +02:00
if (counter == 3 && game.isRunning() && specialStonesEnabled) {
stones[startPosition][column] = TripleSpecialStone(this);
}
2024-06-19 23:40:31 +02:00
if (counter == 4 && game.isRunning() && specialStonesEnabled) {
stones[startPosition][column] = QuadSpecialStone(this, true);
}
2024-06-19 23:40:31 +02:00
if (counter == 5 && game.isRunning() && specialStonesEnabled) {
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 =
2024-06-19 23:40:31 +02:00
StoneColors.values[Random().nextInt(StoneColors.values.length - 1)];
} 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)
2024-06-19 23:40:31 +02:00
for (int i = 0; i < boardSize; i++) {
for (int j = 0; j < boardSize; j++) {
if (stones[i][j] == null) {
stones[i][j] = 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];
}
}