MoodCanvas/lib/providers/puzzle_provider.dart
fengshengxiong 91b7eebbf2 接入TopON
2026-01-22 16:34:55 +08:00

242 lines
6.5 KiB
Dart

import 'dart:async';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:aesthetica_wallpaper/models/puzzle_game.dart';
class PuzzleProvider extends ChangeNotifier {
PuzzleGame? _currentGame;
Timer? _timer;
List<GameRecord> _records = [];
PuzzleGame? get currentGame => _currentGame;
List<GameRecord> get records => _records;
/// 创建新游戏
Future<void> createGame({
required String imagePath,
required GameDifficulty difficulty,
GameMode mode = GameMode.classic,
}) async {
// 停止之前的计时器
_timer?.cancel();
// 加载并分割图片
final pieces = await _splitImage(imagePath, difficulty.gridSize);
// 打乱拼图
_shufflePieces(pieces, difficulty.gridSize);
// 创建游戏
_currentGame = PuzzleGame(
pieces: pieces,
difficulty: difficulty,
mode: mode,
emptyPosition: mode == GameMode.classic ? pieces.length - 1 : null,
);
// 开始计时
_startTimer();
notifyListeners();
}
/// 分割图片为拼图块
Future<List<PuzzlePiece>> _splitImage(String imagePath, int gridSize) async {
final pieces = <PuzzlePiece>[];
// 加载图片
final ByteData data = await rootBundle.load(imagePath);
final codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
final frame = await codec.getNextFrame();
final image = frame.image;
final pieceWidth = image.width ~/ gridSize;
final pieceHeight = image.height ~/ gridSize;
// 分割图片
for (int row = 0; row < gridSize; row++) {
for (int col = 0; col < gridSize; col++) {
final position = row * gridSize + col;
// 创建图片块
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
canvas.drawImageRect(
image,
Rect.fromLTWH(
col * pieceWidth.toDouble(),
row * pieceHeight.toDouble(),
pieceWidth.toDouble(),
pieceHeight.toDouble(),
),
Rect.fromLTWH(0, 0, pieceWidth.toDouble(), pieceHeight.toDouble()),
Paint(),
);
final picture = recorder.endRecording();
final pieceImage = await picture.toImage(pieceWidth, pieceHeight);
pieces.add(
PuzzlePiece(
id: position,
correctPosition: position,
currentPosition: position,
image: MemoryImage(
(await pieceImage.toByteData(
format: ui.ImageByteFormat.png,
))!.buffer.asUint8List(),
),
),
);
}
}
return pieces;
}
/// 打乱拼图
void _shufflePieces(List<PuzzlePiece> pieces, int gridSize) {
final random = math.Random();
// 执行多次随机交换
for (int i = 0; i < pieces.length * 10; i++) {
final index1 = random.nextInt(pieces.length);
final index2 = random.nextInt(pieces.length);
final temp = pieces[index1].currentPosition;
pieces[index1].currentPosition = pieces[index2].currentPosition;
pieces[index2].currentPosition = temp;
}
// 确保拼图是可解的(对于滑动模式)
// TODO: 实现可解性检查
}
/// 移动拼图块
void movePiece(int pieceIndex) {
if (_currentGame == null || _currentGame!.isComplete) return;
final piece = _currentGame!.pieces[pieceIndex];
final gridSize = _currentGame!.gridSize;
if (_currentGame!.mode == GameMode.classic) {
// 滑动模式:只能移动到空格
if (!_canMoveToEmpty(piece.currentPosition, gridSize)) return;
final emptyPos = _currentGame!.emptyPosition!;
piece.currentPosition = emptyPos;
_currentGame = _currentGame!.copyWith(emptyPosition: pieceIndex);
}
// 增加步数
_currentGame = _currentGame!.copyWith(moves: _currentGame!.moves + 1);
// 检查是否完成
if (_currentGame!.checkComplete()) {
_completeGame();
}
notifyListeners();
}
/// 交换两个拼图块(交换模式)
void swapPieces(int index1, int index2) {
if (_currentGame == null || _currentGame!.isComplete) return;
if (_currentGame!.mode == GameMode.classic) return;
final piece1 = _currentGame!.pieces[index1];
final piece2 = _currentGame!.pieces[index2];
final temp = piece1.currentPosition;
piece1.currentPosition = piece2.currentPosition;
piece2.currentPosition = temp;
_currentGame = _currentGame!.copyWith(moves: _currentGame!.moves + 1);
if (_currentGame!.checkComplete()) {
_completeGame();
}
notifyListeners();
}
/// 检查是否可以移动到空格
bool _canMoveToEmpty(int position, int gridSize) {
final emptyPos = _currentGame!.emptyPosition!;
final row = position ~/ gridSize;
final col = position % gridSize;
final emptyRow = emptyPos ~/ gridSize;
final emptyCol = emptyPos % gridSize;
// 检查是否相邻
return (row == emptyRow && (col - emptyCol).abs() == 1) ||
(col == emptyCol && (row - emptyRow).abs() == 1);
}
/// 开始计时
void _startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_currentGame != null && !_currentGame!.isComplete) {
_currentGame = _currentGame!.copyWith(
elapsedTime: _currentGame!.elapsedTime + const Duration(seconds: 1),
);
notifyListeners();
}
});
}
/// 完成游戏
void _completeGame() {
_timer?.cancel();
_currentGame = _currentGame!.copyWith(isComplete: true);
// 保存记录
final record = GameRecord(
imageId: 'temp', // TODO: 使用实际图片ID
difficulty: _currentGame!.difficulty,
time: _currentGame!.elapsedTime,
moves: _currentGame!.moves,
stars: _currentGame!.getStarRating(),
completedAt: DateTime.now(),
);
_records.add(record);
// TODO: 持久化保存记录
}
/// 重新开始游戏
void restartGame() {
if (_currentGame == null) return;
_timer?.cancel();
_shufflePieces(_currentGame!.pieces, _currentGame!.gridSize);
_currentGame = _currentGame!.copyWith(
moves: 0,
elapsedTime: Duration.zero,
isComplete: false,
);
_startTimer();
notifyListeners();
}
/// 使用提示
void useHint() {
if (_currentGame == null || _currentGame!.isComplete) return;
// TODO: 实现提示逻辑
// 找到一个不在正确位置的块,并高亮显示其正确位置
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}