Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
  • 84-improve-app-metadata
  • 82-fix-colors
  • 23-add-timer
  • 65-update-icons
  • Release_0.0.16_16
  • Release_0.0.17_17
  • Release_0.0.18_18
  • Release_0.0.19_19
  • Release_0.0.20_20
  • Release_0.0.21_21
  • Release_0.0.22_22
  • Release_0.0.23_23
  • Release_0.0.24_24
  • Release_0.0.25_25
  • Release_0.0.26_26
  • Release_0.0.27_27
  • Release_0.0.28_28
  • Release_0.0.29_29
  • Release_0.0.30_30
  • Release_0.0.31_31
  • Release_0.0.32_32
  • Release_0.0.33_33
  • Release_0.0.34_34
  • Release_0.0.35_35
  • Release_0.0.36_36
  • Release_0.0.37_37
  • Release_0.0.38_38
  • Release_0.0.39_39
  • Release_0.0.40_40
  • Release_0.0.41_41
  • Release_0.0.42_42
  • Release_0.0.43_43
  • Release_0.0.44_44
  • Release_0.0.45_45
  • Release_0.0.46_46
  • Release_0.0.47_47
  • Release_0.0.48_48
  • Release_0.1.0_49
  • Release_0.1.10_59
  • Release_0.1.11_60
  • Release_0.1.12_61
  • Release_0.1.13_62
  • Release_0.1.14_63
  • Release_0.1.15_64
  • Release_0.1.16_65
  • Release_0.1.17_66
  • Release_0.1.18_67
  • Release_0.1.19_68
  • Release_0.1.1_50
  • Release_0.1.20_69
  • Release_0.1.21_70
  • Release_0.1.22_71
  • Release_0.1.2_51
  • Release_0.1.3_52
  • Release_0.1.4_53
  • Release_0.1.5_54
  • Release_0.1.6_55
  • Release_0.1.7_56
  • Release_0.1.8_57
  • Release_0.1.9_58
  • Release_0.10.0_87
  • Release_0.10.1_88
  • Release_0.10.2_89
  • Release_0.10.3_90
  • Release_0.10.4_91
  • Release_0.11.0_92
  • Release_0.12.0_93
  • Release_0.12.1_94
  • Release_0.13.0_95
  • Release_0.2.0_72
  • Release_0.2.1_73
  • Release_0.3.0_74
  • Release_0.3.1_75
  • Release_0.4.0_76
  • Release_0.4.1_77
  • Release_0.5.0_78
  • Release_0.5.1_79
  • Release_0.5.2_80
  • Release_0.6.0_81
  • Release_0.7.0_82
  • Release_0.8.0_83
  • Release_0.9.0_84
  • Release_0.9.1_85
  • Release_0.9.2_86
85 results

Target

Select target project
  • android / org.benoitharrault.sudoku
1 result
Select Git revision
  • master
  • 84-improve-app-metadata
  • 82-fix-colors
  • 23-add-timer
  • 65-update-icons
  • Release_0.0.16_16
  • Release_0.0.17_17
  • Release_0.0.18_18
  • Release_0.0.19_19
  • Release_0.0.20_20
  • Release_0.0.21_21
  • Release_0.0.22_22
  • Release_0.0.23_23
  • Release_0.0.24_24
  • Release_0.0.25_25
  • Release_0.0.26_26
  • Release_0.0.27_27
  • Release_0.0.28_28
  • Release_0.0.29_29
  • Release_0.0.30_30
  • Release_0.0.31_31
  • Release_0.0.32_32
  • Release_0.0.33_33
  • Release_0.0.34_34
  • Release_0.0.35_35
  • Release_0.0.36_36
  • Release_0.0.37_37
  • Release_0.0.38_38
  • Release_0.0.39_39
  • Release_0.0.40_40
  • Release_0.0.41_41
  • Release_0.0.42_42
  • Release_0.0.43_43
  • Release_0.0.44_44
  • Release_0.0.45_45
  • Release_0.0.46_46
  • Release_0.0.47_47
  • Release_0.0.48_48
  • Release_0.1.0_49
  • Release_0.1.10_59
  • Release_0.1.11_60
  • Release_0.1.12_61
  • Release_0.1.13_62
  • Release_0.1.14_63
  • Release_0.1.15_64
  • Release_0.1.16_65
  • Release_0.1.17_66
  • Release_0.1.18_67
  • Release_0.1.19_68
  • Release_0.1.1_50
  • Release_0.1.20_69
  • Release_0.1.21_70
  • Release_0.1.22_71
  • Release_0.1.2_51
  • Release_0.1.3_52
  • Release_0.1.4_53
  • Release_0.1.5_54
  • Release_0.1.6_55
  • Release_0.1.7_56
  • Release_0.1.8_57
  • Release_0.1.9_58
  • Release_0.10.0_87
  • Release_0.10.1_88
  • Release_0.10.2_89
  • Release_0.10.3_90
  • Release_0.10.4_91
  • Release_0.11.0_92
  • Release_0.12.0_93
  • Release_0.12.1_94
  • Release_0.13.0_95
  • Release_0.2.0_72
  • Release_0.2.1_73
  • Release_0.3.0_74
  • Release_0.3.1_75
  • Release_0.4.0_76
  • Release_0.4.1_77
  • Release_0.5.0_78
  • Release_0.5.1_79
  • Release_0.5.2_80
  • Release_0.6.0_81
  • Release_0.7.0_82
  • Release_0.8.0_83
  • Release_0.9.0_84
  • Release_0.9.1_85
  • Release_0.9.2_86
85 results
Show changes

Commits on Source 6

24 files
+ 572
248
Compare changes
  • Side-by-side
  • Inline

Files

+2 −13
Original line number Original line Diff line number Diff line
image: ghcr.io/cirruslabs/flutter:latest
image: ghcr.io/cirruslabs/flutter:latest


stages:
stages:
  - update
  - build-debug
  - build-debug
  - build-release
  - build-release
  - release
  - release
  - deploy
  - deploy


update:
  stage: update
  except:
    - tags
  script:
    - flutter packages get
    - flutter packages upgrade
  interruptible: true

android:build-debug:
android:build-debug:
  stage: build-debug
  stage: build-debug
  except:
  except:
    - tags
    - tags
    - master
  script:
  script:
    # Flutter local configuration
    # Flutter local configuration
    - echo flutter.sdk=$FLUTTER_PATH > android/local.properties
    - echo flutter.sdk=$FLUTTER_PATH > android/local.properties
@@ -49,8 +40,6 @@ android:build-release:
    - master
    - master
  except:
  except:
    - tags
    - tags
  dependencies:
    - android:build-debug
  script:
  script:
    # Flutter local configuration
    # Flutter local configuration
    - echo flutter.sdk=$FLUTTER_PATH > android/local.properties
    - echo flutter.sdk=$FLUTTER_PATH > android/local.properties
@@ -121,4 +110,4 @@ android:deploy:
  dependencies:
  dependencies:
    - application:release
    - application:release
  script:
  script:
    - curl "${REPOSITORY_UPDATE_WEBHOOK}?token=${REPOSITORY_TOKEN}"
    - curl "${REPOSITORY_UPDATE_WEBHOOK}?token=${REPOSITORY_TOKEN}" --fail
+2 −2
Original line number Original line Diff line number Diff line
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.useAndroidX=true
android.enableJetifier=true
android.enableJetifier=true
app.versionName=0.1.15
app.versionName=0.1.18
app.versionCode=64
app.versionCode=67
+1 −0
Original line number Original line Diff line number Diff line
Improve CI/CD, remove "update" step, disable "build-debug" in master branch.
+1 −0
Original line number Original line Diff line number Diff line
Improve game architecture.
+1 −0
Original line number Original line Diff line number Diff line
Add a timer after use of "help" button.
+1 −0
Original line number Original line Diff line number Diff line
Amélioration de la CI/CD. Suppression de "update" et désactivation de "build-debug" sur master.
+1 −0
Original line number Original line Diff line number Diff line
Amélioration de la conception du jeu.
+1 −0
Original line number Original line Diff line number Diff line
Ajout d'une temporisation pour réactiver le bouton "aide" après usage.
+29 −20
Original line number Original line Diff line number Diff line
@@ -5,27 +5,30 @@ import 'package:sudoku/utils/board_animate.dart';
import 'package:sudoku/utils/board_utils.dart';
import 'package:sudoku/utils/board_utils.dart';


class Cell {
class Cell {
  int value;
  const Cell({
  bool isFixed;
    required this.row,
  int conflictsCount = 0;
    required this.col,
    required this.value,
    required this.isFixed,
  });


  bool isAnimated = false;
  final int row;
  final int col;
  final int value;
  final bool isFixed;


  Cell(
  static const Cell none = Cell(row: 0, col: 0, value: 0, isFixed: true);
    this.value,
    this.isFixed,
  );


  /*
  /*
  * Build widget for board cell, with interactions
  * Build widget for board cell, with interactions
  */
  */
  Container widget(Data myProvider, int row, int col) {
  Widget widget(Data myProvider) {
    final String imageAsset = getImageAssetName(myProvider);
    final String imageAsset = getImageAssetName(myProvider);


    return Container(
    return Container(
      decoration: BoxDecoration(
      decoration: BoxDecoration(
        color: getBackgroundColor(myProvider),
        color: getBackgroundColor(myProvider),
        border: getCellBorders(myProvider, row, col),
        border: getCellBorders(myProvider),
      ),
      ),
      child: GestureDetector(
      child: GestureDetector(
        child: AnimatedSwitcher(
        child: AnimatedSwitcher(
@@ -40,7 +43,7 @@ class Cell {
          ),
          ),
        ),
        ),
        onTap: () {
        onTap: () {
          if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) {
          if (col != myProvider.selectedCellCol || row != myProvider.selectedCellRow) {
            myProvider.selectCell(col, row);
            myProvider.selectCell(col, row);
          } else {
          } else {
            myProvider.selectCell(null, null);
            myProvider.selectCell(null, null);
@@ -62,14 +65,14 @@ class Cell {
    Color backgroundColor = Colors.grey.shade200;
    Color backgroundColor = Colors.grey.shade200;


    if (myProvider.showConflicts &&
    if (myProvider.showConflicts &&
        myProvider.currentCellCol != null &&
        myProvider.selectedCellCol != null &&
        myProvider.currentCellRow != null) {
        myProvider.selectedCellRow != null) {
      final List<List<Cell>> cells = myProvider.cells;
      final Board cells = myProvider.board;
      final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
      final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
      final int blockSizeVertical = myProvider.blockSizeVertical;
      final int blockSizeVertical = myProvider.blockSizeVertical;


      if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
      if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
          myProvider.currentCellCol, myProvider.currentCellRow, value)) {
          myProvider.selectedCellCol, myProvider.selectedCellRow, value)) {
        backgroundColor = Colors.pink.shade100;
        backgroundColor = Colors.pink.shade100;
      }
      }
    }
    }
@@ -85,9 +88,9 @@ class Cell {
        child: GestureDetector(
        child: GestureDetector(
          child: Image(image: AssetImage(imageAsset), fit: BoxFit.fill),
          child: Image(image: AssetImage(imageAsset), fit: BoxFit.fill),
          onTap: () {
          onTap: () {
            if (myProvider.currentCellCol != null && myProvider.currentCellRow != null) {
            if (myProvider.selectedCellCol != null && myProvider.selectedCellRow != null) {
              myProvider.updateCellValue(
              myProvider.updateCellValue(
                  myProvider.currentCellCol, myProvider.currentCellRow, value);
                  myProvider.selectedCellCol, myProvider.selectedCellRow, value);
            }
            }
            myProvider.selectCell(null, null);
            myProvider.selectCell(null, null);
            if (BoardUtils.checkBoardIsSolved(myProvider)) {
            if (BoardUtils.checkBoardIsSolved(myProvider)) {
@@ -126,6 +129,8 @@ class Cell {
      backgroundColor = fixedCellColor;
      backgroundColor = fixedCellColor;
    }
    }


    final int conflictsCount = myProvider.boardConflicts[row][col];

    if (myProvider.showConflicts && (conflictsCount != 0)) {
    if (myProvider.showConflicts && (conflictsCount != 0)) {
      if (isFixed) {
      if (isFixed) {
        backgroundColor = fixedCellColorConflict;
        backgroundColor = fixedCellColorConflict;
@@ -134,7 +139,7 @@ class Cell {
      }
      }
    }
    }


    if (myProvider.showConflicts && (value == myProvider.currentCellValue)) {
    if (myProvider.showConflicts && (value == myProvider.selectedCellValue)) {
      if (isFixed) {
      if (isFixed) {
        backgroundColor = fixedSelectedValueColor;
        backgroundColor = fixedSelectedValueColor;
      } else {
      } else {
@@ -142,6 +147,8 @@ class Cell {
      }
      }
    }
    }


    final bool isAnimated = myProvider.boardAnimated[row][col];

    if (isAnimated) {
    if (isAnimated) {
      if (isFixed) {
      if (isFixed) {
        backgroundColor = fixedAnimated;
        backgroundColor = fixedAnimated;
@@ -154,7 +161,9 @@ class Cell {
  }
  }


  // Compute cell borders, from board size and cell state
  // Compute cell borders, from board size and cell state
  Border getCellBorders(Data myProvider, int row, int col) {
  Border getCellBorders(Data myProvider) {
    // final int row = , int col;

    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeVertical = myProvider.blockSizeVertical;
    final int blockSizeVertical = myProvider.blockSizeVertical;


@@ -184,7 +193,7 @@ class Cell {
    );
    );


    // Update cell borders if not currently selected cell
    // Update cell borders if not currently selected cell
    if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) {
    if (col != myProvider.selectedCellCol || row != myProvider.selectedCellRow) {
      borders = Border(
      borders = Border(
        top: BorderSide(
        top: BorderSide(
            width: cellBorderWidth,
            width: cellBorderWidth,
+1 −4
Original line number Original line Diff line number Diff line
@@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:overlay_support/overlay_support.dart';


import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/screens/home.dart';
import 'package:sudoku/ui/screens/home.dart';


void main() {
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  WidgetsFlutterBinding.ensureInitialized();
@@ -27,9 +27,6 @@ class MyApp extends StatelessWidget {
              visualDensity: VisualDensity.adaptivePlatformDensity,
              visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            ),
            home: const Home(),
            home: const Home(),
            routes: {
              Home.id: (context) => const Home(),
            },
          ),
          ),
        );
        );
      }),
      }),
+117 −48
Original line number Original line Diff line number Diff line
import 'dart:async';
import 'dart:convert';
import 'dart:convert';


import 'package:flutter/foundation.dart';
import 'package:flutter/foundation.dart';
@@ -6,6 +7,11 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/utils/tools.dart';
import 'package:sudoku/utils/tools.dart';


typedef Board = List<List<Cell>>;
typedef ConflictsCount = List<List<int>>;
typedef AnimatedBoard = List<List<bool>>;
typedef AnimatedBoardSequence = List<AnimatedBoard>;

class Data extends ChangeNotifier {
class Data extends ChangeNotifier {
  // Configuration available values
  // Configuration available values
  final List<String> _availableParameters = ['level', 'size', 'skin'];
  final List<String> _availableParameters = ['level', 'size', 'skin'];
@@ -39,15 +45,18 @@ class Data extends ChangeNotifier {
  bool _animationInProgress = false;
  bool _animationInProgress = false;
  int _blockSizeVertical = 0;
  int _blockSizeVertical = 0;
  int _blockSizeHorizontal = 0;
  int _blockSizeHorizontal = 0;
  List<List<Cell>> _cells = [];
  Board _board = [];
  List<List<Cell>> _cellsSolved = [];
  Board _boardSolved = [];
  ConflictsCount _boardConflicts = [];
  AnimatedBoard _boardAnimated = [];
  List<int> _shuffledCellValues = [];
  List<int> _shuffledCellValues = [];
  int? _currentCellCol;
  int? _selectedCellCol;
  int? _currentCellRow;
  int? _selectedCellRow;
  int? _currentCellValue;
  int? _selectedCellValue;
  bool _showConflicts = false;
  bool _showConflicts = false;
  int _givenTipsCount = 0;
  int _givenTipsCount = 0;
  String _currentState = '';
  int _givenTipsCountEnableCountdown = 0;
  String _currentSavedState = '';


  void updateParameterLevel(String parameterLevel) {
  void updateParameterLevel(String parameterLevel) {
    _parameterLevel = parameterLevel;
    _parameterLevel = parameterLevel;
@@ -115,15 +124,15 @@ class Data extends ChangeNotifier {
    setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault);
    setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault);
  }
  }


  String get currentState => _currentState;
  String get currentSavedState => _currentSavedState;


  String computeCurrentGameState() {
  String computeCurrentGameState() {
    String cellsValues = '';
    String cellsValues = '';
    const String stringValues = '0123456789ABCDEFG';
    const String stringValues = '0123456789ABCDEFG';
    for (int rowIndex = 0; rowIndex < _cells.length; rowIndex++) {
    for (int rowIndex = 0; rowIndex < _board.length; rowIndex++) {
      for (int colIndex = 0; colIndex < _cells[rowIndex].length; colIndex++) {
      for (int colIndex = 0; colIndex < _board[rowIndex].length; colIndex++) {
        cellsValues += stringValues[_cells[rowIndex][colIndex].value];
        cellsValues += stringValues[_board[rowIndex][colIndex].value];
        cellsValues += _cells[rowIndex][colIndex].isFixed ? 'x' : ' ';
        cellsValues += _board[rowIndex][colIndex].isFixed ? 'x' : ' ';
      }
      }
    }
    }


@@ -140,35 +149,35 @@ class Data extends ChangeNotifier {


  void saveCurrentGameState() async {
  void saveCurrentGameState() async {
    if (_gameIsRunning) {
    if (_gameIsRunning) {
      _currentState = computeCurrentGameState();
      _currentSavedState = computeCurrentGameState();


      final prefs = await SharedPreferences.getInstance();
      final prefs = await SharedPreferences.getInstance();
      prefs.setString('savedState', _currentState);
      prefs.setString('savedState', _currentSavedState);
    } else {
    } else {
      resetCurrentSavedState();
      resetCurrentSavedState();
    }
    }
  }
  }


  void resetCurrentSavedState() async {
  void resetCurrentSavedState() async {
    _currentState = '';
    _currentSavedState = '';


    final prefs = await SharedPreferences.getInstance();
    final prefs = await SharedPreferences.getInstance();
    prefs.setString('savedState', _currentState);
    prefs.setString('savedState', _currentSavedState);
    notifyListeners();
    notifyListeners();
  }
  }


  void loadCurrentSavedState() async {
  void loadCurrentSavedState() async {
    final prefs = await SharedPreferences.getInstance();
    final prefs = await SharedPreferences.getInstance();
    _currentState = prefs.getString('savedState') ?? '';
    _currentSavedState = prefs.getString('savedState') ?? '';
  }
  }


  bool hasCurrentSavedState() {
  bool hasCurrentSavedState() {
    return (_currentState != '');
    return (_currentSavedState != '');
  }
  }


  Map<String, dynamic> getCurrentSavedState() {
  Map<String, dynamic> getCurrentSavedState() {
    if (_currentState != '') {
    if (_currentSavedState != '') {
      Map<String, dynamic> savedState = json.decode(_currentState);
      Map<String, dynamic> savedState = json.decode(_currentSavedState);
      if (savedState.isNotEmpty) {
      if (savedState.isNotEmpty) {
        return savedState;
        return savedState;
      }
      }
@@ -187,17 +196,20 @@ class Data extends ChangeNotifier {
    _assetsPreloaded = assetsPreloaded;
    _assetsPreloaded = assetsPreloaded;
  }
  }


  List<List<Cell>> get cells => _cells;
  Board get board => _board;
  void updateCells(List<List<Cell>> cells) {
  void updateCells(Board board) {
    _cells = cells;
    _board = board;
    notifyListeners();
    notifyListeners();
  }
  }


  List<List<Cell>> get cellsSolved => _cellsSolved;
  Board get boardSolved => _boardSolved;
  void updateCellsSolved(List<List<Cell>> cells) {
  void updateCellsSolved(Board boardSolved) {
    _cellsSolved = cells;
    _boardSolved = boardSolved;
  }
  }


  AnimatedBoard get boardAnimated => _boardAnimated;
  ConflictsCount get boardConflicts => _boardConflicts;

  void shuffleCellValues() {
  void shuffleCellValues() {
    const int maxCellValue = 16;
    const int maxCellValue = 16;
    final List<int> values = List<int>.generate(maxCellValue, (i) => i + 1);
    final List<int> values = List<int>.generate(maxCellValue, (i) => i + 1);
@@ -218,56 +230,100 @@ class Data extends ChangeNotifier {
    return _shuffledCellValues[originalValue - 1];
    return _shuffledCellValues[originalValue - 1];
  }
  }


  int? get currentCellCol => _currentCellCol;
  int? get selectedCellCol => _selectedCellCol;
  set updateCurrentCellCol(int? currentCellCol) {
  set updateCelectedCellCol(int? selectedCellCol) {
    _currentCellCol = currentCellCol;
    _selectedCellCol = selectedCellCol;
    notifyListeners();
    notifyListeners();
  }
  }


  int? get currentCellRow => _currentCellRow;
  int? get selectedCellRow => _selectedCellRow;
  set updateCurrentCellRow(int? currentCellRow) {
  set updateSelectedCellRow(int? selectedCellRow) {
    _currentCellRow = currentCellRow;
    _selectedCellRow = selectedCellRow;
    notifyListeners();
    notifyListeners();
  }
  }


  int? get currentCellValue => _currentCellValue;
  int? get selectedCellValue => _selectedCellValue;
  set updateCurrentCellValue(int? currentCellValue) {
  set updateSelectedCellValue(int? selectedCellValue) {
    _currentCellValue = currentCellValue;
    _selectedCellValue = selectedCellValue;
    notifyListeners();
    notifyListeners();
  }
  }


  int get givenTipsCountEnableCountdown => _givenTipsCountEnableCountdown;

  int get givenTipsCount => _givenTipsCount;
  int get givenTipsCount => _givenTipsCount;
  increaseGivenTipsCount() {
  void increaseGivenTipsCount() {
    _givenTipsCount = _givenTipsCount + 1;
    _givenTipsCount = _givenTipsCount + 1;
    _givenTipsCountEnableCountdown = 60;
    const Duration interval = Duration(milliseconds: 500);
    Timer.periodic(
      interval,
      (Timer timer) {
        if (_givenTipsCountEnableCountdown == 0) {
          timer.cancel();
          notifyListeners();
        } else {
          _givenTipsCountEnableCountdown--;
          notifyListeners();
        }
      },
    );

    saveCurrentGameState();
    saveCurrentGameState();
    notifyListeners();
    notifyListeners();
  }
  }


  setGivenTipsCount(int value) {
  void setGivenTipsCount(int value) {
    _givenTipsCount = value;
    _givenTipsCount = value;
    _givenTipsCountEnableCountdown = 0;
    notifyListeners();
    notifyListeners();
  }
  }


  resetGivenTipsCount() {
  bool canGiveTip() {
    return (_givenTipsCountEnableCountdown == 0);
  }

  void resetGivenTipsCount() {
    setGivenTipsCount(0);
    setGivenTipsCount(0);
  }
  }


  selectCell(int? col, int? row) {
  void selectCell(int? col, int? row) {
    _currentCellCol = col;
    _selectedCellCol = col;
    _currentCellRow = row;
    _selectedCellRow = row;
    _currentCellValue = null;
    _selectedCellValue = null;
    if (row != null && col != null) {
    if (row != null && col != null) {
      if (_cells[row][col].value != 0) {
      if (_board[row][col].value != 0) {
        _currentCellValue = _cells[row][col].value;
        _selectedCellValue = _board[row][col].value;
      }
      }
    }
    }
    notifyListeners();
    notifyListeners();
  }
  }


  updateCellValue(int? col, int? row, int value) {
  void initConflictsBoard() {
    ConflictsCount nonConflictedBoard = [];
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    for (int row = 0; row < boardSideLength; row++) {
      List<int> line = [];
      for (int col = 0; col < boardSideLength; col++) {
        line.add(0);
      }
      nonConflictedBoard.add(line);
    }
    _boardConflicts = nonConflictedBoard;
  }

  void updateConflicts(ConflictsCount conflictsCount) {
    _boardConflicts = conflictsCount;
  }

  void updateCellValue(int? col, int? row, int value) {
    if ((col != null) && (row != null)) {
    if ((col != null) && (row != null)) {
      if (!_cells[row][col].isFixed) {
      if (!_board[row][col].isFixed) {
        _cells[row][col].value = value;
        _board[row][col] = Cell(
          row: row,
          col: col,
          value: value,
          isFixed: false,
        );


        saveCurrentGameState();
        saveCurrentGameState();
        notifyListeners();
        notifyListeners();
@@ -292,11 +348,24 @@ class Data extends ChangeNotifier {
    notifyListeners();
    notifyListeners();
  }
  }


  void initAnimatedBackground() {
    AnimatedBoard staticBoard = [];
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    for (int row = 0; row < boardSideLength; row++) {
      List<bool> line = [];
      for (int col = 0; col < boardSideLength; col++) {
        line.add(false);
      }
      staticBoard.add(line);
    }
    _boardAnimated = staticBoard;
  }

  void setAnimatedBackground(List animatedCellsPattern) {
  void setAnimatedBackground(List animatedCellsPattern) {
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    for (int row = 0; row < boardSideLength; row++) {
    for (int row = 0; row < boardSideLength; row++) {
      for (int col = 0; col < boardSideLength; col++) {
      for (int col = 0; col < boardSideLength; col++) {
        _cells[row][col].isAnimated = animatedCellsPattern[row][col];
        _boardAnimated[row][col] = animatedCellsPattern[row][col];
      }
      }
    }
    }
    notifyListeners();
    notifyListeners();
@@ -306,7 +375,7 @@ class Data extends ChangeNotifier {
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
    for (int row = 0; row < boardSideLength; row++) {
    for (int row = 0; row < boardSideLength; row++) {
      for (int col = 0; col < boardSideLength; col++) {
      for (int col = 0; col < boardSideLength; col++) {
        _cells[row][col].isAnimated = false;
        _boardAnimated[row][col] = false;
      }
      }
    }
    }
  }
  }
+48 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';


import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';


class Board {
class BoardLayout extends StatelessWidget {
  static Container buildGameBoard(Data myProvider) {
  const BoardLayout({super.key, required this.myProvider});

  final Data myProvider;

  @override
  Container build(BuildContext context) {
    const Color borderColor = Colors.black;
    const Color borderColor = Colors.black;
    final int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;

    final Widget gameTileset = Table(
      defaultColumnWidth: const IntrinsicColumnWidth(),
      children: [
        for (int row = 0; row < boardSize; row++)
          TableRow(
            children: [
              for (int col = 0; col < boardSize; col++)
                Column(
                  children: [myProvider.board[row][col].widget(myProvider)],
                ),
            ],
          ),
      ],
    );


    return Container(
    return Container(
      margin: const EdgeInsets.all(2),
      margin: const EdgeInsets.all(2),
@@ -20,22 +40,9 @@ class Board {
      ),
      ),
      child: Column(
      child: Column(
        children: [
        children: [
          buildGameTileset(myProvider),
          gameTileset,
        ],
        ],
      ),
      ),
    );
    );
  }
  }

  static Widget buildGameTileset(Data myProvider) {
    final int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    final List<List<Cell>> cells = myProvider.cells;

    return Table(defaultColumnWidth: const IntrinsicColumnWidth(), children: [
      for (int row = 0; row < boardSize; row++)
        TableRow(children: [
          for (int col = 0; col < boardSize; col++)
            Column(children: [cells[row][col].widget(myProvider, row, col)]),
        ]),
    ]);
  }
}
}
+30 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';

import 'package:sudoku/ui/layout/board.dart';
import 'package:sudoku/ui/widgets/message_game_end.dart';
import 'package:sudoku/ui/widgets/bar_select_cell_value.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_utils.dart';

class Game extends StatelessWidget {
  const Game({super.key, required this.myProvider});

  final Data myProvider;

  @override
  Widget build(BuildContext context) {
    final bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider);

    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        BoardLayout(myProvider: myProvider),
        const SizedBox(height: 2),
        gameIsFinished
            ? EndGameMessage(myProvider: myProvider)
            : SelectCellValueBar(myProvider: myProvider),
      ],
    );
  }
}
+13 −67
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';


import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart';
import 'package:sudoku/ui/widgets/button_game_resume.dart';
import 'package:sudoku/ui/widgets/button_game_start_new.dart';

class Parameters extends StatelessWidget {
  const Parameters({super.key, required this.myProvider});

  final Data myProvider;


class Parameters {
  static const double separatorHeight = 2.0;
  static const double separatorHeight = 2.0;
  static const double blockMargin = 3.0;
  static const double blockMargin = 3.0;
  static const double blockPadding = 2.0;
  static const double blockPadding = 2.0;
@@ -15,7 +20,8 @@ class Parameters {
  static const double buttonPadding = 0.0;
  static const double buttonPadding = 0.0;
  static const double buttonMargin = 0.0;
  static const double buttonMargin = 0.0;


  static Widget buildParametersSelector(Data myProvider) {
  @override
  Widget build(BuildContext context) {
    List<Widget> lines = [];
    List<Widget> lines = [];


    List<String> parameters = myProvider.availableParameters;
    List<String> parameters = myProvider.availableParameters;
@@ -26,8 +32,8 @@ class Parameters {


    myProvider.loadCurrentSavedState();
    myProvider.loadCurrentSavedState();
    Widget buttonsBlock = myProvider.hasCurrentSavedState()
    Widget buttonsBlock = myProvider.hasCurrentSavedState()
        ? buildResumeGameButton(myProvider)
        ? ResumeGameButton(myProvider: myProvider)
        : buildStartNewGameButton(myProvider);
        : StartNewGameButton(myProvider: myProvider);


    return Column(
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      mainAxisAlignment: MainAxisAlignment.start,
@@ -73,66 +79,7 @@ class Parameters {
    );
    );
  }
  }


  static Container buildStartNewGameButton(Data myProvider) {
  Widget buildParameterSelector(Data myProvider, String parameterCode) {
    return Container(
      margin: const EdgeInsets.all(blockMargin),
      padding: const EdgeInsets.all(blockPadding),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              buildDecorationImageWidget(),
              Column(
                children: [
                  TextButton(
                    child: buildImageContainerWidget('button_start'),
                    onPressed: () => GameUtils.startNewGame(myProvider),
                  ),
                ],
              ),
              buildDecorationImageWidget(),
            ],
          ),
        ],
      ),
    );
  }

  static Container buildResumeGameButton(Data myProvider) {
    return Container(
      margin: const EdgeInsets.all(blockMargin),
      padding: const EdgeInsets.all(blockPadding),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              Column(
                children: [
                  TextButton(
                    child: buildImageContainerWidget('button_delete_saved_game'),
                    onPressed: () => GameUtils.deleteSavedGame(myProvider),
                  ),
                ],
              ),
              Column(
                children: [
                  TextButton(
                    child: buildImageContainerWidget('button_resume_game'),
                    onPressed: () => GameUtils.resumeSavedGame(myProvider),
                  ),
                ],
              ),
              buildDecorationImageWidget(),
            ],
          ),
        ],
      ),
    );
  }

  static Widget buildParameterSelector(Data myProvider, String parameterCode) {
    List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode);
    List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode);


    if (availableValues.length == 1) {
    if (availableValues.length == 1) {
@@ -156,8 +103,7 @@ class Parameters {
    );
    );
  }
  }


  static Widget _buildParameterButton(
  Widget _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
      Data myProvider, String parameterCode, String parameterValue) {
    String currentValue = myProvider.getParameterValue(parameterCode).toString();
    String currentValue = myProvider.getParameterValue(parameterCode).toString();


    bool isActive = (parameterValue == currentValue);
    bool isActive = (parameterValue == currentValue);
+12 −11
Original line number Original line Diff line number Diff line
@@ -3,14 +3,12 @@ import 'package:provider/provider.dart';
import 'package:badges/badges.dart' as badges;
import 'package:badges/badges.dart' as badges;
import 'package:overlay_support/overlay_support.dart';
import 'package:overlay_support/overlay_support.dart';


import 'package:sudoku/layout/game.dart';
import 'package:sudoku/ui/layout/game.dart';
import 'package:sudoku/layout/parameters.dart';
import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart';
import 'package:sudoku/utils/game_utils.dart';


class Home extends StatefulWidget {
class Home extends StatefulWidget {
  static const String id = 'home';

  const Home({super.key});
  const Home({super.key});


  @override
  @override
@@ -69,7 +67,7 @@ class HomeState extends State<Home> {
    final Data myProvider = Provider.of<Data>(context);
    final Data myProvider = Provider.of<Data>(context);


    if (!myProvider.assetsPreloaded) {
    if (!myProvider.assetsPreloaded) {
      List<String> assets = getImagesAssets(myProvider);
      final List<String> assets = getImagesAssets(myProvider);
      for (String asset in assets) {
      for (String asset in assets) {
        precacheImage(AssetImage(asset), context);
        precacheImage(AssetImage(asset), context);
      }
      }
@@ -119,13 +117,16 @@ class HomeState extends State<Home> {
              myProvider.givenTipsCount == 0 ? '' : myProvider.givenTipsCount.toString(),
              myProvider.givenTipsCount == 0 ? '' : myProvider.givenTipsCount.toString(),
              style: const TextStyle(color: Colors.white),
              style: const TextStyle(color: Colors.white),
            ),
            ),
            child: Container(
              padding: EdgeInsets.all(myProvider.givenTipsCountEnableCountdown / 4),
              child: const Image(
              child: const Image(
                image: AssetImage('assets/icons/button_help.png'),
                image: AssetImage('assets/icons/button_help.png'),
                fit: BoxFit.fill,
                fit: BoxFit.fill,
              ),
              ),
            ),
            ),
          ),
          ),
        onPressed: () => GameUtils.showTip(myProvider),
        ),
        onPressed: () => myProvider.canGiveTip() ? GameUtils.showTip(myProvider) : null,
      ));
      ));
      menuActions.add(const Spacer());
      menuActions.add(const Spacer());
      menuActions.add(TextButton(
      menuActions.add(TextButton(
@@ -155,8 +156,8 @@ class HomeState extends State<Home> {
      body: SafeArea(
      body: SafeArea(
        child: Center(
        child: Center(
          child: myProvider.gameIsRunning
          child: myProvider.gameIsRunning
              ? Game.buildGameWidget(myProvider)
              ? Game(myProvider: myProvider)
              : Parameters.buildParametersSelector(myProvider),
              : Parameters(myProvider: myProvider),
        ),
        ),
      ),
      ),
    );
    );
+56 −0
Original line number Original line Diff line number Diff line
@@ -3,35 +3,21 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';


import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/layout/board.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_utils.dart';
import 'package:sudoku/utils/game_utils.dart';


class Game {
class SelectCellValueBar extends StatelessWidget {
  static Widget buildGameWidget(Data myProvider) {
  const SelectCellValueBar({super.key, required this.myProvider});
    final bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider);


    return Column(
  final Data myProvider;
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Board.buildGameBoard(myProvider),
        const SizedBox(height: 2),
        gameIsFinished
            ? Game.buildEndGameMessage(myProvider)
            : Game.buildSelectCellValueBar(myProvider),
      ],
    );
  }


  static Container buildSelectCellValueBar(Data myProvider) {
  @override
    final List<List<Cell>> cells = myProvider.cells;
  Widget build(BuildContext context) {
    final Board cells = myProvider.board;


    final bool isCellSelected =
    final bool isCellSelected =
        (myProvider.currentCellCol != null && myProvider.currentCellRow != null);
        (myProvider.selectedCellCol != null && myProvider.selectedCellRow != null);
    final bool isUpdatableCellSelected = isCellSelected
    final bool isUpdatableCellSelected = isCellSelected
        ? !cells[myProvider.currentCellRow ?? 0][myProvider.currentCellCol ?? 0].isFixed
        ? !cells[myProvider.selectedCellRow ?? 0][myProvider.selectedCellCol ?? 0].isFixed
        : false;
        : false;
    final int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    final int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;


@@ -53,55 +39,14 @@ class Game {
                    value++)
                    value++)
                  Column(
                  Column(
                    children: [
                    children: [
                      Cell(isUpdatableCellSelected ? (value <= maxValue ? value : -1) : -1,
                      Cell(
                              false)
                        row: 1,
                          .widgetUpdateValue(myProvider)
                        col: value,
                    ],
                        value: isUpdatableCellSelected ? (value <= maxValue ? value : -1) : -1,
                  ),
                        isFixed: false,
              ],
                      ).widgetUpdateValue(myProvider)
            ),
                    ],
                    ],
                  ),
                  ),
    );
  }

  static Widget buildRestartGameButton(Data myProvider) {
    return TextButton(
      child: const Image(
        image: AssetImage('assets/icons/button_back.png'),
        fit: BoxFit.fill,
      ),
      onPressed: () => GameUtils.quitGame(myProvider),
    );
  }

  static Container buildEndGameMessage(Data myProvider) {
    const Image decorationImage = Image(
      image: AssetImage('assets/icons/game_win.png'),
      fit: BoxFit.fill,
    );

    return Container(
      margin: const EdgeInsets.all(2),
      padding: const EdgeInsets.all(2),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              const Column(
                children: [decorationImage],
              ),
              Column(
                children: [
                  myProvider.animationInProgress
                      ? decorationImage
                      : buildRestartGameButton(myProvider)
                ],
              ),
              const Column(
                children: [decorationImage],
              ),
              ],
              ],
            ),
            ),
        ],
        ],
+21 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';

import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart';

class RestartGameButton extends StatelessWidget {
  const RestartGameButton({super.key, required this.myProvider});

  final Data myProvider;

  @override
  Widget build(BuildContext context) {
    return TextButton(
      child: const Image(
        image: AssetImage('assets/icons/button_back.png'),
        fit: BoxFit.fill,
      ),
      onPressed: () => GameUtils.quitGame(myProvider),
    );
  }
}
+42 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';

import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/utils/game_utils.dart';

class ResumeGameButton extends Parameters {
  const ResumeGameButton({super.key, required super.myProvider});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(Parameters.blockMargin),
      padding: const EdgeInsets.all(Parameters.blockPadding),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              Column(
                children: [
                  TextButton(
                    child: Parameters.buildImageContainerWidget('button_delete_saved_game'),
                    onPressed: () => GameUtils.deleteSavedGame(myProvider),
                  ),
                ],
              ),
              Column(
                children: [
                  TextButton(
                    child: Parameters.buildImageContainerWidget('button_resume_game'),
                    onPressed: () => GameUtils.resumeSavedGame(myProvider),
                  ),
                ],
              ),
              Parameters.buildDecorationImageWidget(),
            ],
          ),
        ],
      ),
    );
  }
}
+38 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';

import 'package:sudoku/provider/data.dart';
import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/utils/game_utils.dart';

class StartNewGameButton extends StatelessWidget {
  const StartNewGameButton({super.key, required this.myProvider});

  final Data myProvider;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(Parameters.blockMargin),
      padding: const EdgeInsets.all(Parameters.blockPadding),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              Parameters.buildDecorationImageWidget(),
              Column(
                children: [
                  TextButton(
                    child: Parameters.buildImageContainerWidget('button_start'),
                    onPressed: () => GameUtils.startNewGame(myProvider),
                  ),
                ],
              ),
              Parameters.buildDecorationImageWidget(),
            ],
          ),
        ],
      ),
    );
  }
}
+45 −0
Original line number Original line Diff line number Diff line
import 'package:flutter/material.dart';

import 'package:sudoku/ui/widgets/button_game_restart.dart';
import 'package:sudoku/provider/data.dart';

class EndGameMessage extends StatelessWidget {
  const EndGameMessage({super.key, required this.myProvider});

  final Data myProvider;

  @override
  Widget build(BuildContext context) {
    const Image decorationImage = Image(
      image: AssetImage('assets/icons/game_win.png'),
      fit: BoxFit.fill,
    );

    return Container(
      margin: const EdgeInsets.all(2),
      padding: const EdgeInsets.all(2),
      child: Table(
        defaultColumnWidth: const IntrinsicColumnWidth(),
        children: [
          TableRow(
            children: [
              const Column(
                children: [decorationImage],
              ),
              Column(
                children: [
                  myProvider.animationInProgress
                      ? decorationImage
                      : RestartGameButton(myProvider: myProvider)
                ],
              ),
              const Column(
                children: [decorationImage],
              ),
            ],
          ),
        ],
      ),
    );
  }
}
+10 −10
Original line number Original line Diff line number Diff line
@@ -4,14 +4,14 @@ import 'package:sudoku/provider/data.dart';


class BoardAnimate {
class BoardAnimate {
  // Start game animation: blinking tiles
  // Start game animation: blinking tiles
  static List<List<List<bool>>> createStartGameAnimationPatterns(Data myProvider) {
  static AnimatedBoardSequence createStartGameAnimationPatterns(Data myProvider) {
    List<List<List<bool>>> patterns = [];
    AnimatedBoardSequence patterns = [];


    int patternsCount = 3;
    int patternsCount = 3;
    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;


    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List<bool>> pattern = [];
      AnimatedBoard pattern = [];
      for (int row = 0; row < boardSideLength; row++) {
      for (int row = 0; row < boardSideLength; row++) {
        List<bool> patternRow = [];
        List<bool> patternRow = [];
        for (int col = 0; col < boardSideLength; col++) {
        for (int col = 0; col < boardSideLength; col++) {
@@ -26,14 +26,14 @@ class BoardAnimate {
  }
  }


  // Win game animation: fill board with colored rows, from bottom to top
  // Win game animation: fill board with colored rows, from bottom to top
  static List<List<List<bool>>> createWinGameAnimationPatterns(Data myProvider) {
  static AnimatedBoardSequence createWinGameAnimationPatterns(Data myProvider) {
    List<List<List<bool>>> patterns = [];
    AnimatedBoardSequence patterns = [];


    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    int patternsCount = boardSideLength + 6;
    int patternsCount = boardSideLength + 6;


    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List<bool>> pattern = [];
      AnimatedBoard pattern = [];
      for (int row = 0; row < boardSideLength; row++) {
      for (int row = 0; row < boardSideLength; row++) {
        List<bool> patternRow = [];
        List<bool> patternRow = [];
        for (int col = 0; col < boardSideLength; col++) {
        for (int col = 0; col < boardSideLength; col++) {
@@ -48,14 +48,14 @@ class BoardAnimate {
  }
  }


  // Default multi-purpose animation: sliding stripes, from top left to right bottom
  // Default multi-purpose animation: sliding stripes, from top left to right bottom
  static List<List<List<bool>>> createDefaultAnimationPatterns(Data myProvider) {
  static AnimatedBoardSequence createDefaultAnimationPatterns(Data myProvider) {
    List<List<List<bool>>> patterns = [];
    AnimatedBoardSequence patterns = [];


    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
    int patternsCount = boardSideLength;
    int patternsCount = boardSideLength;


    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
    for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
      List<List<bool>> pattern = [];
      AnimatedBoard pattern = [];
      for (int row = 0; row < boardSideLength; row++) {
      for (int row = 0; row < boardSideLength; row++) {
        List<bool> patternRow = [];
        List<bool> patternRow = [];
        for (int col = 0; col < boardSideLength; col++) {
        for (int col = 0; col < boardSideLength; col++) {
@@ -70,7 +70,7 @@ class BoardAnimate {
  }
  }


  static void startAnimation(Data myProvider, String animationType) {
  static void startAnimation(Data myProvider, String animationType) {
    List<List<List<bool>>> patterns = [];
    AnimatedBoardSequence patterns = [];


    switch (animationType) {
    switch (animationType) {
      case 'start':
      case 'start':
+91 −65
Original line number Original line Diff line number Diff line
@@ -6,7 +6,7 @@ import 'package:sudoku/utils/random_pick_grid.dart';
import 'package:sudoku/utils/tools.dart';
import 'package:sudoku/utils/tools.dart';


class BoardUtils {
class BoardUtils {
  static printGrid(List<List<Cell>> cells, List<List<Cell>> solvedCells) {
  static printGrid(Board cells, Board solvedCells) {
    String stringValues = '0123456789ABCDEFG';
    String stringValues = '0123456789ABCDEFG';
    printlog('');
    printlog('');
    printlog('-------');
    printlog('-------');
@@ -42,16 +42,21 @@ class BoardUtils {
      myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider));
      myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider));
      myProvider.selectCell(null, null);
      myProvider.selectCell(null, null);


      printGrid(myProvider.cells, myProvider.cellsSolved);
      printGrid(myProvider.board, myProvider.boardSolved);
    }
    }
  }
  }


  static List<List<Cell>> createEmptyBoard(final int boardSize) {
  static Board createEmptyBoard(final int boardSize) {
    final List<List<Cell>> cells = [];
    final Board cells = [];
    for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
    for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
      final List<Cell> row = [];
      final List<Cell> row = [];
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
        row.add(Cell(0, false));
        row.add(Cell(
          row: rowIndex,
          col: colIndex,
          value: 0,
          isFixed: false,
        ));
      }
      }
      cells.add(row);
      cells.add(row);
    }
    }
@@ -59,8 +64,8 @@ class BoardUtils {
    return cells;
    return cells;
  }
  }


  static List<List<Cell>> createBoardFromSavedState(Data myProvider, String savedBoard) {
  static Board createBoardFromSavedState(Data myProvider, String savedBoard) {
    final List<List<Cell>> cells = [];
    final Board cells = [];
    final int boardSize = int.parse(pow((savedBoard.length / 2), 1 / 2).toStringAsFixed(0));
    final int boardSize = int.parse(pow((savedBoard.length / 2), 1 / 2).toStringAsFixed(0));


    const String stringValues = '0123456789ABCDEFG';
    const String stringValues = '0123456789ABCDEFG';
@@ -75,7 +80,12 @@ class BoardUtils {
        final String isFixedString = savedBoard[index++];
        final String isFixedString = savedBoard[index++];
        final bool isFixed = (isFixedString != ' ');
        final bool isFixed = (isFixedString != ' ');


        row.add(Cell(value, isFixed));
        row.add(Cell(
          row: rowIndex,
          col: colIndex,
          value: value,
          isFixed: isFixed,
        ));
      }
      }
      cells.add(row);
      cells.add(row);
    }
    }
@@ -83,12 +93,17 @@ class BoardUtils {
    return cells;
    return cells;
  }
  }


  static List<List<Cell>> copyBoard(List cells) {
  static Board copyBoard(List cells) {
    final List<List<Cell>> copiedGrid = [];
    final Board copiedGrid = [];
    for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
    for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
      final List<Cell> row = [];
      final List<Cell> row = [];
      for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
      for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
        row.add(Cell(cells[rowIndex][colIndex].value, false));
        row.add(Cell(
          row: rowIndex,
          col: colIndex,
          value: cells[rowIndex][colIndex].value,
          isFixed: false,
        ));
      }
      }
      copiedGrid.add(row);
      copiedGrid.add(row);
    }
    }
@@ -96,8 +111,8 @@ class BoardUtils {
    return copiedGrid;
    return copiedGrid;
  }
  }


  static List<List<Cell>> createBoardFromTemplate(String grid, bool isSymetric) {
  static Board createBoardFromTemplate(String grid, bool isSymetric) {
    List<List<Cell>> cells = [];
    Board cells = [];
    final int boardSize = int.parse(pow(grid.length, 1 / 2).toStringAsFixed(0));
    final int boardSize = int.parse(pow(grid.length, 1 / 2).toStringAsFixed(0));


    const String stringValues = '0123456789ABCDEFG';
    const String stringValues = '0123456789ABCDEFG';
@@ -108,7 +123,12 @@ class BoardUtils {
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
        final String stringValue = grid[index++];
        final String stringValue = grid[index++];
        final int value = stringValues.indexOf(stringValue);
        final int value = stringValues.indexOf(stringValue);
        row.add(Cell(value, (value != 0)));
        row.add(Cell(
          row: rowIndex,
          col: colIndex,
          value: value,
          isFixed: (value != 0),
        ));
      }
      }
      cells.add(row);
      cells.add(row);
    }
    }
@@ -131,11 +151,15 @@ class BoardUtils {
    switch (flip) {
    switch (flip) {
      case 'horizontal':
      case 'horizontal':
        {
        {
          final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize);
          final Board transformedBoard = createEmptyBoard(boardSize);
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
              transformedBoard[rowIndex][colIndex].value =
              transformedBoard[rowIndex][colIndex] = Cell(
                  cells[boardSize - rowIndex - 1][colIndex].value;
                row: rowIndex,
                col: colIndex,
                value: cells[boardSize - rowIndex - 1][colIndex].value,
                isFixed: false,
              );
            }
            }
          }
          }
          cells = transformedBoard;
          cells = transformedBoard;
@@ -143,11 +167,15 @@ class BoardUtils {
        break;
        break;
      case 'vertical':
      case 'vertical':
        {
        {
          final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize);
          final Board transformedBoard = createEmptyBoard(boardSize);
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
              transformedBoard[rowIndex][colIndex].value =
              transformedBoard[rowIndex][colIndex] = Cell(
                  cells[rowIndex][boardSize - colIndex - 1].value;
                row: rowIndex,
                col: colIndex,
                value: cells[rowIndex][boardSize - colIndex - 1].value,
                isFixed: false,
              );
            }
            }
          }
          }
          cells = transformedBoard;
          cells = transformedBoard;
@@ -158,11 +186,15 @@ class BoardUtils {
    switch (rotate) {
    switch (rotate) {
      case 'left':
      case 'left':
        {
        {
          final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize);
          final Board transformedBoard = createEmptyBoard(boardSize);
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
              transformedBoard[rowIndex][colIndex].value =
              transformedBoard[rowIndex][colIndex] = Cell(
                  cells[colIndex][boardSize - rowIndex - 1].value;
                row: rowIndex,
                col: colIndex,
                value: cells[colIndex][boardSize - rowIndex - 1].value,
                isFixed: false,
              );
            }
            }
          }
          }
          cells = transformedBoard;
          cells = transformedBoard;
@@ -170,11 +202,15 @@ class BoardUtils {
        break;
        break;
      case 'right':
      case 'right':
        {
        {
          final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize);
          final Board transformedBoard = createEmptyBoard(boardSize);
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
          for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
            for (int colIndex = 0; colIndex < boardSize; colIndex++) {
              transformedBoard[rowIndex][colIndex].value =
              transformedBoard[rowIndex][colIndex] = Cell(
                  cells[boardSize - colIndex - 1][rowIndex].value;
                row: rowIndex,
                col: colIndex,
                value: cells[boardSize - colIndex - 1][rowIndex].value,
                isFixed: false,
              );
            }
            }
          }
          }
          cells = transformedBoard;
          cells = transformedBoard;
@@ -185,8 +221,12 @@ class BoardUtils {
    // Fix cells fixed states
    // Fix cells fixed states
    for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
    for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
      for (int colIndex = 0; colIndex < boardSize; colIndex++) {
        cells[rowIndex][colIndex].isFixed =
        cells[rowIndex][colIndex] = Cell(
            (cells[rowIndex][colIndex].value != 0) ? true : false;
          row: rowIndex,
          col: colIndex,
          value: cells[rowIndex][colIndex].value,
          isFixed: (cells[rowIndex][colIndex].value != 0) ? true : false,
        );
      }
      }
    }
    }


@@ -194,7 +234,8 @@ class BoardUtils {
  }
  }


  static bool checkBoardIsSolved(Data myProvider) {
  static bool checkBoardIsSolved(Data myProvider) {
    final List<List<Cell>> cells = myProvider.cells;
    final Board cells = myProvider.board;
    final ConflictsCount conflicts = myProvider.boardConflicts;


    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeVertical = myProvider.blockSizeVertical;
    final int blockSizeVertical = myProvider.blockSizeVertical;
@@ -207,7 +248,7 @@ class BoardUtils {
    // check grid is fully completed and does not contain conflict
    // check grid is fully completed and does not contain conflict
    for (int row = 0; row < boardSize; row++) {
    for (int row = 0; row < boardSize; row++) {
      for (int col = 0; col < boardSize; col++) {
      for (int col = 0; col < boardSize; col++) {
        if (cells[row][col].value == 0 || cells[row][col].conflictsCount != 0) {
        if (cells[row][col].value == 0 || conflicts[row][col] != 0) {
          return false;
          return false;
        }
        }
      }
      }
@@ -219,7 +260,7 @@ class BoardUtils {
  }
  }


  static bool isValueAllowed(
  static bool isValueAllowed(
    List<List<Cell>> cells,
    Board cells,
    int blockSizeHorizontal,
    int blockSizeHorizontal,
    int blockSizeVertical,
    int blockSizeVertical,
    int? candidateCol,
    int? candidateCol,
@@ -300,7 +341,8 @@ class BoardUtils {
  }
  }


  static void computeConflictsInBoard(Data myProvider) {
  static void computeConflictsInBoard(Data myProvider) {
    final List<List<Cell>> cells = myProvider.cells;
    final Board cells = myProvider.board;
    final ConflictsCount conflicts = myProvider.boardConflicts;


    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeVertical = myProvider.blockSizeVertical;
    final int blockSizeVertical = myProvider.blockSizeVertical;
@@ -310,7 +352,7 @@ class BoardUtils {
    // reset conflict states
    // reset conflict states
    for (int row = 0; row < boardSize; row++) {
    for (int row = 0; row < boardSize; row++) {
      for (int col = 0; col < boardSize; col++) {
      for (int col = 0; col < boardSize; col++) {
        cells[row][col].conflictsCount = 0;
        conflicts[row][col] = 0;
      }
      }
    }
    }


@@ -328,7 +370,7 @@ class BoardUtils {
        printlog('line $row contains duplicates');
        printlog('line $row contains duplicates');
        // Add line to cells in conflict
        // Add line to cells in conflict
        for (int col = 0; col < boardSize; col++) {
        for (int col = 0; col < boardSize; col++) {
          cells[row][col].conflictsCount++;
          conflicts[row][col]++;
        }
        }
      }
      }
    }
    }
@@ -347,7 +389,7 @@ class BoardUtils {
        printlog('column $col contains duplicates');
        printlog('column $col contains duplicates');
        // Add column to cells in conflict
        // Add column to cells in conflict
        for (int row = 0; row < boardSize; row++) {
        for (int row = 0; row < boardSize; row++) {
          cells[row][col].conflictsCount++;
          conflicts[row][col]++;
        }
        }
      }
      }
    }
    }
@@ -378,17 +420,19 @@ class BoardUtils {
            for (int colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
            for (int colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
              int row = (blockRow * blockSizeVertical) + rowInBlock;
              int row = (blockRow * blockSizeVertical) + rowInBlock;
              int col = (blockCol * blockSizeHorizontal) + colInBlock;
              int col = (blockCol * blockSizeHorizontal) + colInBlock;
              cells[row][col].conflictsCount++;
              conflicts[row][col]++;
            }
            }
          }
          }
        }
        }
      }
      }
    }
    }

    myProvider.updateConflicts(conflicts);
  }
  }


  static List<List<int>> getCellsWithWrongValue(
  static List<List<int>> getCellsWithWrongValue(
    final List<List<Cell>> cells,
    final Board cells,
    final List<List<Cell>> cellsSolved,
    final Board cellsSolved,
    final int blockSizeHorizontal,
    final int blockSizeHorizontal,
    final int blockSizeVertical,
    final int blockSizeVertical,
  ) {
  ) {
@@ -407,31 +451,8 @@ class BoardUtils {
    return cellsWithWrongValue;
    return cellsWithWrongValue;
  }
  }


  static List<List<int>> getConflictingCells(
    final List<List<Cell>> cells,
    final int blockSizeHorizontal,
    final int blockSizeVertical,
  ) {
    final List<List<int>> conflictingCells = [];
    final int boardSize = blockSizeHorizontal * blockSizeVertical;

    for (int row = 0; row < boardSize; row++) {
      for (int col = 0; col < boardSize; col++) {
        if (!cells[row][col].isFixed && cells[row][col].value != 0) {
          if (cells[row][col].conflictsCount != 0 &&
              !BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, col,
                  row, cells[row][col].value)) {
            conflictingCells.add([col, row]);
          }
        }
      }
    }

    return conflictingCells;
  }

  static List<List<int>> getCellsWithUniqueAvailableValue(
  static List<List<int>> getCellsWithUniqueAvailableValue(
    List<List<Cell>> cells,
    Board cells,
    final int blockSizeHorizontal,
    final int blockSizeHorizontal,
    final int blockSizeVertical,
    final int blockSizeVertical,
  ) {
  ) {
@@ -460,8 +481,8 @@ class BoardUtils {
    return candidateCells;
    return candidateCells;
  }
  }


  static List<List<Cell>> getSolvedGrid(Data myProvider) {
  static Board getSolvedGrid(Data myProvider) {
    final List<List<Cell>> cells = copyBoard(myProvider.cells);
    final Board cells = copyBoard(myProvider.board);
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    int blockSizeVertical = myProvider.blockSizeVertical;
    int blockSizeVertical = myProvider.blockSizeVertical;


@@ -479,7 +500,12 @@ class BoardUtils {
        int col = cellsWithUniqueAvailableValue[i][0];
        int col = cellsWithUniqueAvailableValue[i][0];
        int row = cellsWithUniqueAvailableValue[i][1];
        int row = cellsWithUniqueAvailableValue[i][1];
        int value = cellsWithUniqueAvailableValue[i][2];
        int value = cellsWithUniqueAvailableValue[i][2];
        cells[row][col].value = value;
        cells[row][col] = Cell(
          row: row,
          col: col,
          value: value,
          isFixed: cells[row][col].isFixed,
        );
      }
      }
    } while (true);
    } while (true);


+8 −7
Original line number Original line Diff line number Diff line
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_animate.dart';
import 'package:sudoku/utils/board_animate.dart';
import 'package:sudoku/utils/board_utils.dart';
import 'package:sudoku/utils/board_utils.dart';
@@ -19,6 +18,8 @@ class GameUtils {
    myProvider.shuffleCellValues();
    myProvider.shuffleCellValues();
    myProvider.updateCells(BoardUtils.createEmptyBoard(
    myProvider.updateCells(BoardUtils.createEmptyBoard(
        myProvider.blockSizeHorizontal * myProvider.blockSizeVertical));
        myProvider.blockSizeHorizontal * myProvider.blockSizeVertical));
    myProvider.initAnimatedBackground();
    myProvider.initConflictsBoard();
    BoardUtils.pickGrid(myProvider);
    BoardUtils.pickGrid(myProvider);
    BoardAnimate.startAnimation(myProvider, 'start');
    BoardAnimate.startAnimation(myProvider, 'start');
  }
  }
@@ -57,7 +58,7 @@ class GameUtils {
  }
  }


  static void showTip(Data myProvider) {
  static void showTip(Data myProvider) {
    if (myProvider.currentCellCol == null || myProvider.currentCellRow == null) {
    if (myProvider.selectedCellCol == null || myProvider.selectedCellRow == null) {
      // no selected cell -> pick one
      // no selected cell -> pick one
      GameUtils.helpSelectCell(myProvider);
      GameUtils.helpSelectCell(myProvider);
    } else {
    } else {
@@ -68,14 +69,14 @@ class GameUtils {
  }
  }


  static void helpSelectCell(Data myProvider) {
  static void helpSelectCell(Data myProvider) {
    final List<List<Cell>> cells = myProvider.cells;
    final Board cells = myProvider.board;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeVertical = myProvider.blockSizeVertical;
    final int blockSizeVertical = myProvider.blockSizeVertical;


    // pick one of wrong value cells, if found
    // pick one of wrong value cells, if found
    final List<List<int>> wrongValueCells = BoardUtils.getCellsWithWrongValue(
    final List<List<int>> wrongValueCells = BoardUtils.getCellsWithWrongValue(
      cells,
      cells,
      myProvider.cellsSolved,
      myProvider.boardSolved,
      blockSizeHorizontal,
      blockSizeHorizontal,
      blockSizeVertical,
      blockSizeVertical,
    );
    );
@@ -116,7 +117,7 @@ class GameUtils {
  }
  }


  static void helpFillCell(Data myProvider) {
  static void helpFillCell(Data myProvider) {
    final List<List<Cell>> cells = myProvider.cells;
    final Board cells = myProvider.board;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
    final int blockSizeVertical = myProvider.blockSizeVertical;
    final int blockSizeVertical = myProvider.blockSizeVertical;


@@ -129,13 +130,13 @@ class GameUtils {
    int allowedValuesCount = 0;
    int allowedValuesCount = 0;
    for (int value = 1; value <= boardSize; value++) {
    for (int value = 1; value <= boardSize; value++) {
      if (BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
      if (BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
          myProvider.currentCellCol, myProvider.currentCellRow, value)) {
          myProvider.selectedCellCol, myProvider.selectedCellRow, value)) {
        allowedValuesCount++;
        allowedValuesCount++;
        eligibleValue = value;
        eligibleValue = value;
      }
      }
    }
    }


    myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow,
    myProvider.updateCellValue(myProvider.selectedCellCol, myProvider.selectedCellRow,
        allowedValuesCount == 1 ? eligibleValue : 0);
        allowedValuesCount == 1 ? eligibleValue : 0);
    myProvider.selectCell(null, null);
    myProvider.selectCell(null, null);
    if (BoardUtils.checkBoardIsSolved(myProvider)) {
    if (BoardUtils.checkBoardIsSolved(myProvider)) {
+1 −1
Original line number Original line Diff line number Diff line
name: sudoku
name: sudoku
description: A sudoku game application.
description: A sudoku game application.
publish_to: 'none'
publish_to: 'none'
version: 0.1.15+64
version: 0.1.18+67


environment:
environment:
  sdk: '^3.0.0'
  sdk: '^3.0.0'