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.

320 lines
8.3 KiB

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) &&
  107. (currentTileCol == emptyTilePosCol)) {
  108. board1D[emptyTilePosIndex] = board1D[index];
  109. board1D[index] = 0;
  110. _moves++;
  111. }
  112. //current element moves down
  113. else if ((currentTileRow + 1 == emptyTilePosRow) &&
  114. (currentTileCol == emptyTilePosCol)) {
  115. board1D[emptyTilePosIndex] = board1D[index];
  116. board1D[index] = 0;
  117. _moves++;
  118. }
  119. //current element moves left
  120. else if ((currentTileRow == emptyTilePosRow) &&
  121. (currentTileCol + 1 == emptyTilePosCol)) {
  122. board1D[emptyTilePosIndex] = board1D[index];
  123. board1D[index] = 0;
  124. _moves++;
  125. }
  126. //current element moves right
  127. else if ((currentTileRow == emptyTilePosRow) &&
  128. (currentTileCol - 1 == emptyTilePosCol)) {
  129. board1D[emptyTilePosIndex] = board1D[index];
  130. board1D[index] = 0;
  131. _moves++;
  132. } else {
  133. if (currentTileCol == emptyTilePosCol) {
  134. int low;
  135. int high;
  136. // multiple elements move up
  137. if (emptyTilePosRow < currentTileRow) {
  138. low = emptyTilePosRow;
  139. high = currentTileRow;
  140. int i = low;
  141. while (i < high) {
  142. board1D[(i * _puzzleSize) + emptyTilePosCol] =
  143. board1D[(((i + 1) * _puzzleSize) + emptyTilePosCol)];
  144. i += 1;
  145. }
  146. board1D[(high * _puzzleSize) + emptyTilePosCol] = 0;
  147. _moves++;
  148. }
  149. //multiple elements move down
  150. else {
  151. low = emptyTilePosRow;
  152. high = currentTileRow;
  153. int i = low;
  154. while (i > high) {
  155. board1D[(i * _puzzleSize) + emptyTilePosCol] =
  156. board1D[(((i - 1) * _puzzleSize) + emptyTilePosCol)];
  157. i -= 1;
  158. }
  159. board1D[(high * _puzzleSize) + emptyTilePosCol] = 0;
  160. _moves++;
  161. }
  162. }
  163. // multiple elements move left or right
  164. if (currentTileRow == emptyTilePosRow) {
  165. int low;
  166. int high;
  167. // multiple elements move left
  168. if (emptyTilePosCol < currentTileCol) {
  169. low = emptyTilePosCol;
  170. high = currentTileCol;
  171. int i = low;
  172. while (i < high) {
  173. board1D[(emptyTilePosRow * _puzzleSize) + i] =
  174. board1D[(emptyTilePosRow * _puzzleSize) + (i + 1)];
  175. i += 1;
  176. }
  177. board1D[high + (emptyTilePosRow * _puzzleSize)] = 0;
  178. _moves++;
  179. }
  180. //multiple elements move right
  181. else {
  182. low = emptyTilePosCol;
  183. high = currentTileCol;
  184. int i = low;
  185. while (i > high) {
  186. board1D[(i + (emptyTilePosRow * _puzzleSize))] =
  187. board1D[(i - 1) + (emptyTilePosRow * _puzzleSize)];
  188. i -= 1;
  189. }
  190. board1D[high + (emptyTilePosRow * _puzzleSize)] = 0;
  191. _moves++;
  192. }
  193. }
  194. }
  195. updateOffset(board1D, _puzzleSize, offsetMap);
  196. var tiles = calculateCorrectTiles(board: board1D);
  197. PuzzleData updatedData = PuzzleData(
  198. board2D: prev.board2D,
  199. board1D: board1D,
  200. offsetMap: offsetMap,
  201. moves: _moves,
  202. tiles: tiles,
  203. puzzleSize: _puzzleSize,
  204. );
  205. state = PuzzleState.current(updatedData);
  206. if (listEquals(board1D, _solvedList)) {
  207. state = PuzzleState.solved(updatedData);
  208. }
  209. log('List: $board1D');
  210. log('-----------------------');
  211. }
  212. int calculateCorrectTiles({required List<int> board}) {
  213. int correctTiles = 0;
  214. for (int i = 0; i < _solverClient.size * _solverClient.size; i++) {
  215. if (board[i] != _solvedList[i] && board[i] != 0) {
  216. correctTiles++;
  217. }
  218. }
  219. return correctTiles;
  220. }
  221. void updateOffset(
  222. List<int> board1D,
  223. int size,
  224. Map<int, FractionalOffset> offsetMap,
  225. ) {
  226. int j = 0;
  227. for (int i = 0; i < board1D.length; i++) {
  228. final xMod = i % size;
  229. double x = xMod / (size - 1);
  230. if (x % i == 0 && i != 0) j++;
  231. int yMod = j % size;
  232. double y = yMod / (size - 1);
  233. offsetMap[board1D[i]] = FractionalOffset(x, y);
  234. }
  235. log('OFFSET MAP: $offsetMap');
  236. log('BOARD: $board1D');
  237. }
  238. scrambleBoard(PuzzleData puzzleData) {
  239. final generated2DBoard = _solverClient.createRandomBoard();
  240. final generated1DBoard = _solverClient.convertTo1D(generated2DBoard);
  241. updateOffset(generated1DBoard, puzzleData.puzzleSize, puzzleData.offsetMap);
  242. var tiles = calculateCorrectTiles(board: generated1DBoard);
  243. final newPuzzle = PuzzleData(
  244. board2D: generated2DBoard,
  245. board1D: generated1DBoard,
  246. offsetMap: puzzleData.offsetMap,
  247. moves: 0,
  248. tiles: tiles,
  249. puzzleSize: puzzleData.puzzleSize,
  250. );
  251. state = PuzzleState.scrambling(newPuzzle);
  252. return newPuzzle;
  253. }
  254. }