Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • 39-improve-app-metadata
  • 28-add-high-scores
  • 32-add-block-size-limit-parameter
  • 29-use-real-board-painter-to-draw-parameters-items
  • Release_0.9.0_47 protected
  • Release_0.8.2_46 protected
  • Release_0.8.1_45 protected
  • Release_0.8.0_44 protected
  • Release_0.7.0_43 protected
  • Release_0.6.0_42 protected
  • Release_0.5.0_41 protected
  • Release_0.4.2_40 protected
  • Release_0.4.1_39 protected
  • Release_0.4.0_38 protected
  • Release_0.3.1_37 protected
  • Release_0.3.0_36 protected
  • Release_0.2.1_35 protected
  • Release_0.2.0_34 protected
  • Release_0.1.1_33 protected
  • Release_0.1.0_32 protected
  • Release_0.0.31_31 protected
  • Release_0.0.30_30 protected
  • Release_0.0.29_29 protected
  • Release_0.0.28_28 protected
25 results

game.dart

Blame
  • game.dart 9.83 KiB
    import 'dart:math';
    
    import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
    
    import 'package:jeweled/config/color_theme.dart';
    import 'package:jeweled/config/default_global_settings.dart';
    import 'package:jeweled/models/game/board.dart';
    import 'package:jeweled/models/game/cell.dart';
    import 'package:jeweled/models/game/cell_location.dart';
    import 'package:jeweled/models/settings/settings_game.dart';
    import 'package:jeweled/models/settings/settings_global.dart';
    
    class Game {
      Game({
        // Settings
        required this.gameSettings,
        required this.globalSettings,
    
        // State
        this.isRunning = false,
        this.isStarted = false,
        this.isFinished = false,
        this.animationInProgress = false,
    
        // Base data
        required this.board,
    
        // Game data
        required this.shuffledColors,
        this.availableBlocksCount = 0,
        this.score = 0,
        this.movesCount = 0,
      });
    
      // Settings
      final GameSettings gameSettings;
      final GlobalSettings globalSettings;
    
      // State
      bool isRunning;
      bool isStarted;
      bool isFinished;
      bool animationInProgress;
    
      // Base data
      final Board board;
    
      // Game data
      List<int> shuffledColors;
      int availableBlocksCount;
      int score;
      int movesCount;
    
      factory Game.createEmpty() {
        return Game(
          // Settings
          gameSettings: GameSettings.createDefault(),
          globalSettings: GlobalSettings.createDefault(),
          // Base data
          board: Board.createEmpty(),
          // Game data
          shuffledColors: shuffleColors(DefaultGlobalSettings.defaultColorsThemeValue),
        );
      }
    
      factory Game.createNew({
        GameSettings? gameSettings,
        GlobalSettings? globalSettings,
      }) {
        final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
        final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
    
        return Game(
          // Settings
          gameSettings: newGameSettings,
          globalSettings: newGlobalSettings,
          // State
          isRunning: true,
          // Base data
          board: Board.createRandom(newGameSettings),
          // Game data
          shuffledColors: shuffleColors(newGlobalSettings.colorsTheme),
        );
      }
    
      bool get canBeResumed => isStarted && !isFinished;
    
      bool get gameWon => isRunning && isStarted && isFinished;
    
      static List<int> shuffleColors(final String colorsTheme) {
        List<int> values =
            List<int>.generate(ColorTheme.getColorsCount(colorsTheme), (i) => i + 1);
        values.shuffle();
    
        return values;
      }
    
      void shuffleColorsAgain(final String colorsTheme) {
        shuffledColors = shuffleColors(colorsTheme);
      }
    
      Cell getCell(CellLocation cellLocation) {
        return board.cells[cellLocation.row][cellLocation.col];
      }
    
      int? getCellValue(CellLocation cellLocation) {
        return getCell(cellLocation).value;
      }
    
      int? getCellValueShuffled(CellLocation cellLocation) {
        final int? value = getCell(cellLocation).value;
        return value != null ? shuffledColors[value - 1] : null;
      }
    
      void updateCellValue(CellLocation locationToUpdate, int? value) {
        board.cells[locationToUpdate.row][locationToUpdate.col].value = value;
      }
    
      void increaseMovesCount() {
        movesCount += 1;
      }
    
      void increaseScore(int? count) {
        score += (count ?? 0);
      }
    
      void updateAvailableBlocksCount() {
        availableBlocksCount = getAvailableBlocks(this).length;
      }
    
      List<CellLocation> getSiblingCells(
        final CellLocation referenceCellLocation,
        List<CellLocation> siblingCells,
      ) {
        final int boardSizeHorizontal = gameSettings.boardSizeValue;
        final int boardSizeVertical = gameSettings.boardSizeValue;
    
        final int? referenceValue = getCellValue(referenceCellLocation);
    
        for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
          for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
            if (deltaCol == 0 || deltaRow == 0) {
              final int candidateRow = referenceCellLocation.row + deltaRow;
              final int candidateCol = referenceCellLocation.col + deltaCol;
    
              if ((candidateRow >= 0 && candidateRow < boardSizeVertical) &&
                  (candidateCol >= 0 && candidateCol < boardSizeHorizontal)) {
                final candidateLocation = CellLocation.go(candidateRow, candidateCol);
    
                if (getCellValue(candidateLocation) == referenceValue) {
                  bool alreadyFound = false;
                  for (int index = 0; index < siblingCells.length; index++) {
                    if ((siblingCells[index].row == candidateRow) &&
                        (siblingCells[index].col == candidateCol)) {
                      alreadyFound = true;
                    }
                  }
                  if (!alreadyFound) {
                    siblingCells.add(candidateLocation);
                    siblingCells = getSiblingCells(candidateLocation, siblingCells);
                  }
                }
              }
            }
          }
        }
    
        return siblingCells;
      }
    
      List<List<CellLocation>> getAvailableBlocks(final Game game) {
        final int boardSizeHorizontal = game.gameSettings.boardSizeValue;
        final int boardSizeVertical = game.gameSettings.boardSizeValue;
    
        final List<List<CellLocation>> blocks = [];
    
        for (int row = 0; row < boardSizeVertical; row++) {
          for (int col = 0; col < boardSizeHorizontal; col++) {
            final CellLocation cellLocation = CellLocation.go(row, col);
            if (game.getCellValue(cellLocation) != null) {
              // if current cell not already in a found block
              bool alreadyFound = false;
    
              for (List<CellLocation> foundBlock in blocks) {
                for (CellLocation foundBlockCell in foundBlock) {
                  if ((foundBlockCell.row == row) && (foundBlockCell.col == col)) {
                    alreadyFound = true;
                  }
                }
              }
              if (!alreadyFound) {
                final List<CellLocation> block = game.getSiblingCells(cellLocation, []);
                if (block.length >= 3) {
                  blocks.add(block);
                }
              }
            }
          }
        }
    
        return blocks;
      }
    
      bool hasAtLeastOneAvailableBlock() {
        final int boardSizeHorizontal = gameSettings.boardSizeValue;
        final int boardSizeVertical = gameSettings.boardSizeValue;
    
        for (int row = 0; row < boardSizeVertical; row++) {
          for (int col = 0; col < boardSizeHorizontal; col++) {
            final CellLocation cellLocation = CellLocation.go(row, col);
            if (getCellValue(cellLocation) != null) {
              final List<CellLocation> block = getSiblingCells(cellLocation, []);
              if (block.length >= 3) {
                // found one block => ok, not locked
                return true;
              }
            }
          }
        }
    
        printlog('Board is locked!');
        return false;
      }
    
      bool isInBoard(CellLocation cell) {
        final int boardSize = gameSettings.boardSizeValue;
    
        if (cell.row > 0 && cell.row < boardSize && cell.col > 0 && cell.col < boardSize) {
          return true;
        }
        return false;
      }
    
      int getFillValue(CellLocation referenceCellLocation) {
        final int row = referenceCellLocation.row;
        final int col = referenceCellLocation.col;
    
        // build a list of values to pick one
        final List<int> values = [];
    
        // All eligible values (twice)
        final int maxValue = gameSettings.colorsCountValue;
        for (int i = 1; i <= maxValue; i++) {
          values.add(i);
          values.add(i);
        }
    
        // Add values of current col (twice)
        for (int r = 0; r <= gameSettings.boardSizeValue; r++) {
          if (isInBoard(CellLocation.go(r, col))) {
            final int? value = getCellValue(CellLocation.go(r, col));
            if (value != null) {
              values.add(value);
              values.add(value);
            }
          }
        }
    
        // Add values of sibling cols (twice for top rows)
        for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
          final int c = col + deltaCol;
          for (int r = 0; r < gameSettings.boardSizeValue; r++) {
            if (isInBoard(CellLocation.go(r, c))) {
              final int? value = getCellValue(CellLocation.go(r, c));
              if (value != null) {
                values.add(value);
                if (row < gameSettings.boardSizeValue / 3) {
                  values.add(value);
                }
              }
            }
          }
        }
    
        // Add values of sibling cells
        for (int deltaCol = -2; deltaCol <= 2; deltaCol++) {
          final int c = col + deltaCol;
          for (int deltaRow = -2; deltaRow <= 2; deltaRow++) {
            final int r = row + deltaRow;
            if (isInBoard(CellLocation.go(r, c))) {
              final int? value = getCellValue(CellLocation.go(r, c));
              if (value != null) {
                values.add(value);
              }
            }
          }
        }
    
        // Pick random value from "ponderated" list
        return values[Random().nextInt(values.length)];
      }
    
      void dump() {
        printlog('');
        printlog('## Current game dump:');
        printlog('');
        printlog('$Game:');
        printlog('  Settings');
        gameSettings.dump();
        globalSettings.dump();
        printlog('  State');
        printlog('    isRunning: $isRunning');
        printlog('    isStarted: $isStarted');
        printlog('    isFinished: $isFinished');
        printlog('    animationInProgress: $animationInProgress');
        printlog('  Base data');
        board.dump();
        printlog('  Game data');
        printlog('    shuffledColors: $shuffledColors');
        printlog('    availableBlocksCount: $availableBlocksCount');
        printlog('    score: $score');
        printlog('    movesCount: $movesCount');
        printlog('');
      }
    
      @override
      String toString() {
        return '$Game(${toJson()})';
      }
    
      Map<String, dynamic>? toJson() {
        return <String, dynamic>{
          // Settings
          'gameSettings': gameSettings.toJson(),
          'globalSettings': globalSettings.toJson(),
          // State
          'isRunning': isRunning,
          'isStarted': isStarted,
          'isFinished': isFinished,
          'animationInProgress': animationInProgress,
          // Base data
          'board': board.toJson(),
          // Game data
          'shuffledColors': shuffledColors,
          'availableBlocksCount': availableBlocksCount,
          'score': score,
          'movesCount': movesCount,
        };
      }
    }