You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
8.2 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. import 'dart:async';
  2. import 'dart:developer';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/widgets.dart';
  5. import 'package:flutter_riverpod/flutter_riverpod.dart';
  6. import 'package:my_flutter_puzzle/application/states/puzzle_state.dart';
  7. import 'package:my_flutter_puzzle/models/puzzle_data.dart';
  8. import '../../utils/puzzle_solver.dart';
  9. class PuzzleNotifier extends StateNotifier<PuzzleState> {
  10. final PuzzleSolverClient _solverClient;
  11. PuzzleNotifier(this._solverClient) : super(const PuzzleState());
  12. // List<List<int>>? _board2D;
  13. // List<int>? myList;
  14. // int _moves = 0;
  15. // final _puzzleSize = 3;
  16. // final int _animationSpeedInMilliseconds = 300;
  17. // bool _isComputing = false;
  18. // bool _isAutoSolving = false;
  19. // bool _isSolved = false;
  20. // Map<int, FractionalOffset>? _offsetMap;
  21. final List<int> _solvedList = [];
  22. generateSolvedList() {
  23. var _puzzleSize = _solverClient.size;
  24. for (int i = 1; i < _puzzleSize * _puzzleSize; i++) {
  25. _solvedList.add(i);
  26. }
  27. _solvedList.add(0);
  28. }
  29. generateInitialPuzzle() {
  30. List<int> solvedList = [];
  31. var _puzzleSize = _solverClient.size;
  32. for (int i = 1; i < _puzzleSize * _puzzleSize; i++) {
  33. solvedList.add(i);
  34. }
  35. solvedList.add(0);
  36. final initialOffsetMap = createOffset(solvedList);
  37. final initialPuzzleData = PuzzleData(
  38. board2D: [],
  39. board1D: solvedList,
  40. offsetMap: initialOffsetMap,
  41. moves: 0,
  42. tiles: 0,
  43. puzzleSize: _puzzleSize,
  44. );
  45. return initialPuzzleData;
  46. }
  47. restartPuzzle() async {
  48. final initialPuzzle = generateInitialPuzzle();
  49. state = PuzzleState.scrambling(initialPuzzle);
  50. await Future.delayed(const Duration(seconds: 2));
  51. initializePuzzle(initialPuzzleData: initialPuzzle);
  52. }
  53. initializePuzzle({required PuzzleData initialPuzzleData}) {
  54. state = const PuzzleState.initializing();
  55. _solvedList.clear();
  56. _solverClient.setSize = initialPuzzleData.puzzleSize;
  57. generateSolvedList();
  58. int start = 0;
  59. const period = Duration(milliseconds: 1200);
  60. PuzzleData currentPuzzleData = initialPuzzleData;
  61. currentPuzzleData = scrambleBoard(currentPuzzleData);
  62. Timer.periodic(period, (Timer timer) async {
  63. currentPuzzleData = scrambleBoard(currentPuzzleData);
  64. start++;
  65. if (start == 2) {
  66. timer.cancel();
  67. await Future.delayed(const Duration(milliseconds: 1000));
  68. state = PuzzleState.current(currentPuzzleData);
  69. }
  70. });
  71. }
  72. Map<int, FractionalOffset> createOffset(List<int> board) {
  73. Map<int, FractionalOffset> offsetMap = {};
  74. int j = 0;
  75. log('BOARD: $board');
  76. for (int i = 0; i < board.length; i++) {
  77. var _puzzleSize = _solverClient.size;
  78. final xMod = i % _puzzleSize;
  79. double x = xMod / (_puzzleSize - 1);
  80. if (x % i == 0 && i != 0) j++;
  81. int yMod = j % _puzzleSize;
  82. double y = yMod / (_puzzleSize - 1);
  83. offsetMap.addEntries([
  84. MapEntry<int, FractionalOffset>(
  85. board[i],
  86. FractionalOffset(x, y),
  87. )
  88. ]);
  89. }
  90. log('INITIAL OFFSET MAP: $offsetMap');
  91. return offsetMap;
  92. }
  93. onClick({required int index, required PuzzleData prev}) {
  94. log('-----------------------');
  95. log('Tapped index: $index');
  96. var board1D = prev.board1D;
  97. var offsetMap = prev.offsetMap;
  98. var _puzzleSize = prev.puzzleSize;
  99. var _moves = prev.moves;
  100. int emptyTilePosIndex = board1D.indexOf(0);
  101. int emptyTilePosRow = emptyTilePosIndex ~/ _puzzleSize;
  102. int emptyTilePosCol = emptyTilePosIndex % _puzzleSize;
  103. int currentTileRow = index ~/ _puzzleSize;
  104. int currentTileCol = index % _puzzleSize;
  105. //current element moves up
  106. if ((currentTileRow - 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) {
  107. board1D[emptyTilePosIndex] = board1D[index];
  108. board1D[index] = 0;
  109. _moves++;
  110. }
  111. //current element moves down
  112. else if ((currentTileRow + 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) {
  113. board1D[emptyTilePosIndex] = board1D[index];
  114. board1D[index] = 0;
  115. _moves++;
  116. }
  117. //current element moves left
  118. else if ((currentTileRow == emptyTilePosRow) && (currentTileCol + 1 == emptyTilePosCol)) {
  119. board1D[emptyTilePosIndex] = board1D[index];
  120. board1D[index] = 0;
  121. _moves++;
  122. }
  123. //current element moves right
  124. else if ((currentTileRow == emptyTilePosRow) && (currentTileCol - 1 == emptyTilePosCol)) {
  125. board1D[emptyTilePosIndex] = board1D[index];
  126. board1D[index] = 0;
  127. _moves++;
  128. } else {
  129. if (currentTileCol == emptyTilePosCol) {
  130. int low;
  131. int high;
  132. // multiple elements move up
  133. if (emptyTilePosRow < currentTileRow) {
  134. low = emptyTilePosRow;
  135. high = currentTileRow;
  136. int i = low;
  137. while (i < high) {
  138. board1D[(i * _puzzleSize) + emptyTilePosCol] = board1D[(((i + 1) * _puzzleSize) + emptyTilePosCol)];
  139. i += 1;
  140. }
  141. board1D[(high * _puzzleSize) + emptyTilePosCol] = 0;
  142. _moves++;
  143. }
  144. //multiple elements move down
  145. else {
  146. low = emptyTilePosRow;
  147. high = currentTileRow;
  148. int i = low;
  149. while (i > high) {
  150. board1D[(i * _puzzleSize) + emptyTilePosCol] = board1D[(((i - 1) * _puzzleSize) + emptyTilePosCol)];
  151. i -= 1;
  152. }
  153. board1D[(high * _puzzleSize) + emptyTilePosCol] = 0;
  154. _moves++;
  155. }
  156. }
  157. // multiple elements move left or right
  158. if (currentTileRow == emptyTilePosRow) {
  159. int low;
  160. int high;
  161. // multiple elements move left
  162. if (emptyTilePosCol < currentTileCol) {
  163. low = emptyTilePosCol;
  164. high = currentTileCol;
  165. int i = low;
  166. while (i < high) {
  167. board1D[(emptyTilePosRow * _puzzleSize) + i] = board1D[(emptyTilePosRow * _puzzleSize) + (i + 1)];
  168. i += 1;
  169. }
  170. board1D[high + (emptyTilePosRow * _puzzleSize)] = 0;
  171. _moves++;
  172. }
  173. //multiple elements move right
  174. else {
  175. low = emptyTilePosCol;
  176. high = currentTileCol;
  177. int i = low;
  178. while (i > high) {
  179. board1D[(i + (emptyTilePosRow * _puzzleSize))] = board1D[(i - 1) + (emptyTilePosRow * _puzzleSize)];
  180. i -= 1;
  181. }
  182. board1D[high + (emptyTilePosRow * _puzzleSize)] = 0;
  183. _moves++;
  184. }
  185. }
  186. }
  187. updateOffset(board1D, _puzzleSize, offsetMap);
  188. var tiles = calculateCorrectTiles(board: board1D);
  189. PuzzleData updatedData = PuzzleData(
  190. board2D: prev.board2D,
  191. board1D: board1D,
  192. offsetMap: offsetMap,
  193. moves: _moves,
  194. tiles: tiles,
  195. puzzleSize: _puzzleSize,
  196. );
  197. state = PuzzleState.current(updatedData);
  198. if (listEquals(board1D, _solvedList)) {
  199. state = PuzzleState.solved(updatedData);
  200. }
  201. }
  202. int calculateCorrectTiles({required List<int> board}) {
  203. int correctTiles = 0;
  204. for (int i = 0; i < _solverClient.size * _solverClient.size; i++) {
  205. if (board[i] != _solvedList[i] && board[i] != 0) {
  206. correctTiles++;
  207. }
  208. }
  209. return correctTiles;
  210. }
  211. void updateOffset(
  212. List<int> board1D,
  213. int size,
  214. Map<int, FractionalOffset> offsetMap,
  215. ) {
  216. int j = 0;
  217. for (int i = 0; i < board1D.length; i++) {
  218. final xMod = i % size;
  219. double x = xMod / (size - 1);
  220. if (x % i == 0 && i != 0) j++;
  221. int yMod = j % size;
  222. double y = yMod / (size - 1);
  223. offsetMap[board1D[i]] = FractionalOffset(x, y);
  224. }
  225. log('OFFSET MAP: $offsetMap');
  226. log('BOARD: $board1D');
  227. }
  228. scrambleBoard(PuzzleData puzzleData) {
  229. final generated2DBoard = _solverClient.createRandomBoard();
  230. final generated1DBoard = _solverClient.convertTo1D(generated2DBoard);
  231. updateOffset(generated1DBoard, puzzleData.puzzleSize, puzzleData.offsetMap);
  232. var tiles = calculateCorrectTiles(board: generated1DBoard);
  233. final newPuzzle = PuzzleData(
  234. board2D: generated2DBoard,
  235. board1D: generated1DBoard,
  236. offsetMap: puzzleData.offsetMap,
  237. moves: 0,
  238. tiles: tiles,
  239. puzzleSize: puzzleData.puzzleSize,
  240. );
  241. state = PuzzleState.scrambling(newPuzzle);
  242. return newPuzzle;
  243. }
  244. }