257 lines
7.4 KiB
Dart
Raw Normal View History

2025-11-19 18:00:41 +09:00
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import '../models/finddiff_models.dart';
2025-11-21 15:54:17 +09:00
import '../utils/hangul_utils.dart';
2025-11-19 18:00:41 +09:00
class FindDiffController with ChangeNotifier {
late FindDiffDifficulty difficulty;
late String userId;
late String? userName;
List<FindDiffItem> _items = [];
List<FindDiffItem> get items => _items;
int _currentRound = 0;
int get currentRound => _currentRound;
int _score = 0;
int get score => _score;
2025-11-21 15:54:17 +09:00
int _incorrectCount = 0;
2025-11-19 18:00:41 +09:00
int get incorrectCount => _incorrectCount;
bool _isGameCompleted = false;
bool get isGameCompleted => _isGameCompleted;
Timer? _timer;
int _remainingTime = 0;
int get remainingTime => _remainingTime;
2025-11-21 15:54:17 +09:00
int _secondsElapsed = 0;
int get secondsElapsed => _secondsElapsed;
2025-11-19 18:00:41 +09:00
bool _isGameStarted = false;
bool get isGameStarted => _isGameStarted;
bool _showFeedback = false;
bool get showFeedback => _showFeedback;
bool _isLastAnswerCorrect = false;
bool get isLastAnswerCorrect => _isLastAnswerCorrect;
2025-11-21 15:54:17 +09:00
FindDiffType _currentRoundType = FindDiffType.color;
FindDiffType get currentRoundType => _currentRoundType;
2025-11-19 18:00:41 +09:00
final Random _random = Random();
void setUserInfo(String userId, String? userName) {
this.userId = userId;
this.userName = userName;
}
void startNewGame(FindDiffDifficulty level) {
difficulty = level;
_score = 0;
_incorrectCount = 0;
_currentRound = 1;
_isGameCompleted = false;
2025-11-21 15:54:17 +09:00
_secondsElapsed = 0;
_isGameStarted = false;
2025-11-19 18:00:41 +09:00
_showFeedback = false;
_generateLevel();
notifyListeners();
}
void restartGame() {
startNewGame(difficulty);
}
void startGameTimer() {
if (_isGameStarted) return;
_isGameStarted = true;
_startTimer();
notifyListeners();
}
void _generateLevel() {
_items.clear();
final int totalItems = difficulty.totalItems;
final int targetIndex = _random.nextInt(totalItems);
2025-11-21 15:54:17 +09:00
// 1. 문제 유형 결정
if (difficulty.diffType == FindDiffType.mix) {
const List<FindDiffType> types = [
FindDiffType.color, FindDiffType.icon, FindDiffType.rotate,
FindDiffType.category, FindDiffType.word
];
_currentRoundType = types[_random.nextInt(types.length)];
} else {
_currentRoundType = difficulty.diffType;
}
// 2. 기본 속성 준비
IconData? baseIcon;
String? baseText;
String? targetText;
2025-11-19 18:00:41 +09:00
Color baseColor = Colors.blue;
double baseAngle = 0.0;
2025-11-21 15:54:17 +09:00
// [🔥 핵심] 유형별 아이콘/데이터 소스 분리
if (_currentRoundType == FindDiffType.category) {
// 카테고리 (나중에 루프에서 결정)
} else if (_currentRoundType == FindDiffType.word) {
// 단어 & 오타
final String originalWord = FindDiffDifficulties.baseWords[
_random.nextInt(FindDiffDifficulties.baseWords.length)
];
final String modifiedWord = HangulUtils.generateSimilarWord(originalWord);
if (_random.nextBool()) {
baseText = originalWord;
targetText = modifiedWord;
} else {
baseText = modifiedWord;
targetText = originalWord;
}
baseColor = Colors.black87;
2025-11-19 18:00:41 +09:00
2025-11-21 15:54:17 +09:00
} else if (_currentRoundType == FindDiffType.icon) {
// 모양 찾기
2025-11-19 18:00:41 +09:00
final pair = FindDiffDifficulties.iconPairs[_random.nextInt(FindDiffDifficulties.iconPairs.length)];
2025-11-21 15:54:17 +09:00
baseIcon = pair[0];
baseColor = Colors.black87;
} else if (_currentRoundType == FindDiffType.rotate) {
// [🔥 수정] 회전 모드: rotateIcons에서 선택 (원, 네모 등 제외)
baseIcon = FindDiffDifficulties.rotateIcons[_random.nextInt(FindDiffDifficulties.rotateIcons.length)];
baseColor = Color.fromARGB(255, _random.nextInt(200), _random.nextInt(200), _random.nextInt(200));
2025-11-19 18:00:41 +09:00
} else {
2025-11-21 15:54:17 +09:00
// 색상 모드: colorIcons에서 선택
baseIcon = FindDiffDifficulties.colorIcons[_random.nextInt(FindDiffDifficulties.colorIcons.length)];
2025-11-19 18:00:41 +09:00
baseColor = Color.fromARGB(255, _random.nextInt(200), _random.nextInt(200), _random.nextInt(200));
}
2025-11-21 15:54:17 +09:00
// 카테고리 모드용 리스트 준비
List<String> baseCatList = [];
List<String> targetCatList = [];
if (_currentRoundType == FindDiffType.category) {
final keys = FindDiffDifficulties.emojiCategories.keys.toList();
String baseKey = keys[_random.nextInt(keys.length)];
String targetKey;
do { targetKey = keys[_random.nextInt(keys.length)]; } while (baseKey == targetKey);
baseCatList = FindDiffDifficulties.emojiCategories[baseKey]!;
targetCatList = FindDiffDifficulties.emojiCategories[targetKey]!;
}
// 3. 아이템 배치
2025-11-19 18:00:41 +09:00
for (int i = 0; i < totalItems; i++) {
bool isTarget = (i == targetIndex);
2025-11-21 15:54:17 +09:00
IconData? icon = baseIcon;
String? textContent = baseText;
2025-11-19 18:00:41 +09:00
Color color = baseColor;
double angle = baseAngle;
2025-11-21 15:54:17 +09:00
switch (_currentRoundType) {
case FindDiffType.color:
if (isTarget) {
// 난이도가 높거나 믹스 모드일 경우 색상 차이를 미세하게
int offset = (difficulty.levelIndex >= 5) ? 30 : 60;
color = Color.fromARGB(255, (baseColor.red + offset) % 255, (baseColor.green + offset) % 255, (baseColor.blue + offset) % 255);
}
break;
case FindDiffType.icon:
if (isTarget) {
final pair = FindDiffDifficulties.iconPairs.firstWhere((p) => p.contains(baseIcon));
icon = (baseIcon == pair[0]) ? pair[1] : pair[0];
}
break;
case FindDiffType.rotate:
if (isTarget) angle = 0.5; // 약 30도 회전
break;
case FindDiffType.category:
if (isTarget) {
textContent = targetCatList[_random.nextInt(targetCatList.length)];
} else {
textContent = baseCatList[_random.nextInt(baseCatList.length)];
}
color = Colors.black;
break;
case FindDiffType.word:
if (isTarget) {
textContent = targetText;
} else {
textContent = baseText;
}
break;
default:
break;
2025-11-19 18:00:41 +09:00
}
_items.add(FindDiffItem(
id: i,
icon: icon,
2025-11-21 15:54:17 +09:00
textContent: textContent,
2025-11-19 18:00:41 +09:00
color: color,
angle: angle,
isTarget: isTarget,
));
}
_remainingTime = difficulty.timeLimitSeconds;
}
2025-11-21 15:54:17 +09:00
// ... (나머지 메서드 동일) ...
2025-11-19 18:00:41 +09:00
void _startTimer() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
2025-11-21 15:54:17 +09:00
_secondsElapsed++;
2025-11-19 18:00:41 +09:00
_remainingTime--;
if (_remainingTime <= 0) {
_handleAnswer(false);
}
notifyListeners();
});
}
void onItemTapped(FindDiffItem item) {
if (!_isGameStarted || _isGameCompleted || _showFeedback) return;
_handleAnswer(item.isTarget);
}
void _handleAnswer(bool isCorrect) {
2025-11-21 15:54:17 +09:00
_timer?.cancel();
2025-11-19 18:00:41 +09:00
_showFeedback = true;
_isLastAnswerCorrect = isCorrect;
if (isCorrect) {
_score++;
} else {
_incorrectCount++;
}
notifyListeners();
Future.delayed(const Duration(milliseconds: 500), () {
2025-11-21 15:54:17 +09:00
if (_score >= 10) {
2025-11-19 18:00:41 +09:00
_isGameCompleted = true;
} else {
_showFeedback = false;
_generateLevel();
2025-11-21 15:54:17 +09:00
_startTimer();
2025-11-19 18:00:41 +09:00
}
notifyListeners();
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}