Skip to content

Commit

Permalink
Merge branch 'feature/reverse_action'
Browse files Browse the repository at this point in the history
  • Loading branch information
yusufgngor committed Jan 12, 2025
2 parents e52a2f9 + 9587766 commit f3ac0ad
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 91 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
Expand Down
7 changes: 7 additions & 0 deletions lib/models/cell_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Cell {
}

clear() {
error = false;
value = null;
notes = [];
}
Expand All @@ -45,4 +46,10 @@ class Cell {
String toJson() => json.encode(toMap());

factory Cell.fromJson(String source) => Cell.fromMap(json.decode(source));

copy() {
final cell = Cell(index: index, value: value);
cell.notes = List<int>.from(notes);
return cell;
}
}
11 changes: 11 additions & 0 deletions lib/view/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ class SettingArea extends StatelessWidget {
width: 200,
child: Column(
children: [
RowButton(
children: [
AppButton(
onPressed: () {
vm.undo();
},
text: "Back",
isSelected: vm.inputMode == InputMode.note),
],
),
const SizedBox(height: 3),
RowButton(
children: [
AppButton(
Expand Down
2 changes: 1 addition & 1 deletion lib/view_modal/keyboard_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class SudokuKeyboardListener {
// 1-9 keys
case LogicalKeyboardKey.numpad1:
case LogicalKeyboardKey.digit1:
sudokuNotifier.updateCell(selectedCell.index, 1);
updateCell(selectedCell.index, 1);
break;
case LogicalKeyboardKey.numpad2:
case LogicalKeyboardKey.digit2:
Expand Down
49 changes: 36 additions & 13 deletions lib/view_modal/save_manager.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
import 'dart:collection';
import 'dart:convert';

import 'package:my_sudoku_table/models/cell_model.dart';
import 'package:my_sudoku_table/view_modal/sudoku_view_model.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SaveManager {
static final SaveManager _singleton = SaveManager._internal();
factory SaveManager() {
return _singleton;
}
SaveManager._internal();
final SudokuNotifier _sudokuNotifier;

SaveManager(this._sudokuNotifier);

Future<bool> save(SudokuNotifier sudokuNotifier) async {
Future<bool> save() async {
//save cells
final sp = await SharedPreferences.getInstance();
final cellsJson = jsonEncode(sudokuNotifier.cells);

return await sp.setString("cells", cellsJson);
final cellsJson = jsonEncode(_sudokuNotifier.cells);
final actionsJson = jsonEncode(_sudokuNotifier.lastActions.toList());

final jobs = await Future.wait([
sp.setString("cells", cellsJson),
sp.setString("actions", actionsJson),
]);

return jobs.first && jobs.last;
}

Future<List<Cell>?> load() async {
Future<(List<Cell>?, Queue<Cell>?)> load() async {
final sp = await SharedPreferences.getInstance();

List<Cell>? cells;
Queue<Cell>? actions;

final cellsJson = sp.getString("cells");
if (cellsJson == null) {
return null;
if (cellsJson != null) {
final List<dynamic> cellsList = jsonDecode(cellsJson);
cells = cellsList.map((e) => Cell.fromJson(e)).toList();
}
//load history
final actionsJson = sp.getString("actions");
if (actionsJson != null) {
final List<dynamic> cellsList = jsonDecode(actionsJson);
final actionList = cellsList.map<Cell>((e) => Cell.fromJson(e));
actions = Queue.from(actionList);
}

return (cells, actions);
}

final List<dynamic> cellsList = jsonDecode(cellsJson);
return cellsList.map((e) => Cell.fromJson(e)).toList();

void clear() async {
final sp = await SharedPreferences.getInstance();
await sp.clear();
}
}
139 changes: 105 additions & 34 deletions lib/view_modal/sudoku_view_model.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:my_sudoku_table/models/cell_model.dart';
import 'package:my_sudoku_table/view_modal/keyboard_listener.dart';
Expand All @@ -8,33 +10,56 @@ enum InputMode {
note,
}

abstract class CellAction {
final Cell cell;

const CellAction(this.cell);
}

class CellValueAction extends CellAction {
final int value;

const CellValueAction(super.cell, this.value);
}

class CellNoteAction extends CellAction {
final int value;

const CellNoteAction(super.cell, this.value);
}

class CellClearAction extends CellAction {
const CellClearAction(super.cell);
}

class CellUndoAction extends CellAction {
const CellUndoAction(super.cell);
}

class SudokuNotifier extends ChangeNotifier {
bool loading = true;
late final List<Cell> _cells;
late final SaveManager _saveManager = SaveManager(this);

late List<Cell> _cells;
final List<List<Cell>> rows = List.generate(9, (_) => [], growable: false);
final List<List<Cell>> columns = List.generate(9, (_) => [], growable: false);
final List<List<Cell>> boxes = List.generate(9, (_) => [], growable: false);

late Queue<Cell> _lastActions;

List<Cell> get cells => _cells;
Queue<Cell> get lastActions => _lastActions;

Cell? selectedCell;
InputMode inputMode = InputMode.value;

void init() async {
final loadedCells = await SaveManager().load();
if (loadedCells != null) {
_cells = loadedCells;
} else {
_cells = List.generate(81, (index) => Cell(index: index));
}

for (var cell in _cells) {
rows[cell.rowNumber].add(cell);
columns[cell.columnNumber].add(cell);
boxes[cell.boxNumber].add(cell);
}

SudokuKeyboardListener().register(this);
late final emptyCells = List.generate(81, (index) => Cell(index: index));

final (loadedCells, actions) = await _saveManager.load();
_lastActions = actions ?? Queue();
setCells(loadedCells ?? emptyCells);

loading = false;
notifyListeners();
Expand All @@ -44,39 +69,69 @@ class SudokuNotifier extends ChangeNotifier {
init();
}

void setCells(List<Cell> newCells) {
_cells = newCells;
for (var cell in _cells) {
rows[cell.rowNumber].add(cell);
columns[cell.columnNumber].add(cell);
boxes[cell.boxNumber].add(cell);
}
checkErrors();
notifyListeners();
}

void updateCell(int index, int value) {
final cell = _cells[index];
if (inputMode == InputMode.note) {
updateCellNote(index, value);
updateCellAction(CellNoteAction(cell, value));
} else {
updateCellValue(index, value);
updateCellAction(CellValueAction(cell, value));
}
}

void updateCellAction(CellAction action) {
final cell = action.cell;

if (action is CellNoteAction) {
_lastActions.add(cell.copy());
updateCellNote(cell, action.value);
} else if (action is CellValueAction) {
_lastActions.add(cell.copy());

updateCellValue(cell, action.value);
} else if (action is CellClearAction) {
_lastActions.add(cell.copy());

clearCell(cell);
} else if (action is CellUndoAction) {
_cells[cell.index].value = cell.value;
}
checkErrors();
_saveManager.save();

notifyListeners();
SaveManager().save(this);
}

void updateCellNote(int index, int value) {
if (_cells[index].notes.contains(value)) {
_cells[index].removeNoteFromCell(value);
void updateCellNote(Cell cell, int value) {
if (cell.notes.contains(value)) {
cell.removeNoteFromCell(value);
} else {
_cells[index].addNoteToCell(value);
cell.addNoteToCell(value);
}
}

void updateCellValue(int index, int value) {
final cell = _cells[index];
void updateCellValue(Cell cell, int value) {
cell.error = false;

// remove the value from the notes of the cells in the same row, column and box
for (var cell in rows[cell.rowNumber]) {
cell.removeNoteFromCell(value);
for (var c in rows[cell.rowNumber]) {
c.removeNoteFromCell(value);
}
for (var cell in columns[cell.columnNumber]) {
cell.removeNoteFromCell(value);
for (var c in columns[cell.columnNumber]) {
c.removeNoteFromCell(value);
}
for (var cell in boxes[cell.boxNumber]) {
cell.removeNoteFromCell(value);
for (var c in boxes[cell.boxNumber]) {
c.removeNoteFromCell(value);
}

cell.value = value;
Expand All @@ -85,14 +140,13 @@ class SudokuNotifier extends ChangeNotifier {
void checkErrors() {
for (var cell in _cells) {
cell.error = false;
}

for (var cell in _cells) {
if (cell.value == null) {
continue;
}

for (var c in rows[cell.rowNumber]) {
final row = rows[cell.rowNumber];
for (var c in row) {
if (c != cell && c.value == cell.value) {
c.error = true;
cell.error = true;
Expand All @@ -116,14 +170,24 @@ class SudokuNotifier extends ChangeNotifier {
}

void clearCell(Cell? cell) {
cell?.clear();
if (cell != null) {
updateCellAction(CellClearAction(cell));
}
notifyListeners();
}

void clearAll() {
final emptyCellList = List.generate(81, (index) => Cell(index: index));
//clear rows, columns and boxes
for (var cell in _cells) {
cell.clear();
rows[cell.rowNumber].clear();
columns[cell.columnNumber].clear();
boxes[cell.boxNumber].clear();
}

setCells(emptyCellList);
_lastActions.clear();
_saveManager.clear();
notifyListeners();
}

Expand All @@ -136,4 +200,11 @@ class SudokuNotifier extends ChangeNotifier {
this.inputMode = inputMode;
notifyListeners();
}

void undo() {
if (_lastActions.isNotEmpty) {
final cell = _lastActions.removeLast();
updateCellAction(CellUndoAction(cell));
}
}
}
4 changes: 4 additions & 0 deletions macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}

override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
Loading

0 comments on commit f3ac0ad

Please sign in to comment.