This commit is contained in:
2025-11-19 17:16:43 +09:00
parent 09665fa073
commit 3228e370c5
3 changed files with 91 additions and 58 deletions
@@ -32,10 +32,15 @@ class CardFlipController with ChangeNotifier {
int _remainingTime = 0;
int get remainingTime => _remainingTime;
// [🔥 신규] 게임이 시작되었는지(타이머가 도는지) 여부
bool _isGameStarted = false;
bool get isGameStarted => _isGameStarted;
// [🔥 신규] 매칭된 쌍의 개수
int get matchedPairsCount => _cards.where((c) => c.isMatched).length ~/ 2;
// [🔥 신규] 총 쌍의 개수
int get totalPairsCount => _cards.length ~/ 2;
void setUserInfo(String userId, String? userName) {
this.userId = userId;
this.userName = userName;
@@ -49,21 +54,17 @@ class CardFlipController with ChangeNotifier {
_isGameCompleted = false;
_isTimeOut = false;
_isProcessing = false;
_isGameStarted = false; // 타이머 대기 상태
_isGameStarted = false;
_firstFlippedCard = null;
_generateCards();
// [🔥 수정] startNewGame에서는 타이머를 시작하지 않음 (가이드 확인 후 시작)
notifyListeners();
}
void restartGame() {
startNewGame(difficulty);
// 재시작 시에는 가이드 없이 바로 시작하고 싶다면 여기서 startTimer 호출
// 하지만 일관성을 위해 UI에서 다시 가이드를 띄우도록 유도
}
// [🔥 수정] Public으로 변경 (UI에서 호출)
void startGameTimer() {
if (_isGameStarted) return;
_isGameStarted = true;
@@ -83,38 +84,61 @@ class CardFlipController with ChangeNotifier {
notifyListeners();
}
// 🔽 [🔥 핵심] 카드 생성 로직 (타입별 분기)
void _generateCards() {
final int totalCards = difficulty.totalCards;
final int pairsCount = totalCards ~/ 2;
List<CardItem> deck = [];
final Random random = Random();
if (difficulty.contentType == CardContentType.calculation) {
// 1. 연산 모드 (식 ↔ 답)
var entries = CardFlipDifficulties.calculationPairs.entries.toList()..shuffle();
final Set<int> usedResults = {};
for (int i = 0; i < pairsCount; i++) {
var entry = entries[i % entries.length];
String matchKey = "CALC_$i"; // 논리적 ID
// 식 카드
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.key));
// 답 카드
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.value));
String equation;
String answer;
int result;
while (true) {
final int opType = random.nextInt(3);
int a, b;
if (opType == 0) { // +
a = random.nextInt(15) + 1;
b = random.nextInt(15) + 1;
result = a + b;
equation = "$a + $b";
} else if (opType == 1) { // -
a = random.nextInt(20) + 5;
b = random.nextInt(a - 1) + 1;
result = a - b;
equation = "$a - $b";
} else { // *
a = random.nextInt(9) + 2;
b = random.nextInt(5) + 2;
result = a * b;
equation = "$a x $b";
}
if (!usedResults.contains(result)) {
usedResults.add(result);
answer = result.toString();
break;
}
}
String matchKey = "CALC_$i";
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: equation));
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: answer));
}
}
else if (difficulty.contentType == CardContentType.pairWord) {
// 2. 연상 모드 (A ↔ B)
var entries = CardFlipDifficulties.wordPairs.entries.toList()..shuffle();
for (int i = 0; i < pairsCount; i++) {
var entry = entries[i % entries.length];
String matchKey = "PAIR_$i";
// 단어 A
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.key));
// 단어 B
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.value));
}
}
else {
// 3. 동일 매칭 모드 (이모지, 아이콘, 숫자)
List<String> pool = List.of(CardFlipDifficulties.emojis)..shuffle();
for (int i = 0; i < pairsCount; i++) {
String content;
@@ -128,16 +152,13 @@ class CardFlipController with ChangeNotifier {
content = pool[i % pool.length];
}
// 똑같은 카드 2장
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: content));
deck.add(CardItem(id: 0, matchId: matchKey, displayContent: content));
}
}
// 4. 전체 섞기 및 ID 부여
deck.shuffle(Random());
deck.shuffle(random);
for (int i = 0; i < deck.length; i++) {
// 기존 객체를 복사하며 고유 ID 부여
deck[i] = CardItem(
id: i,
matchId: deck[i].matchId,
@@ -147,9 +168,7 @@ class CardFlipController with ChangeNotifier {
_cards = deck;
}
// 🔽 [핵심] 카드 뒤집기 로직
void onCardTapped(CardItem card) {
// 게임이 시작되지 않았거나, 이미 완료되었거나, 처리 중이면 무시
if (!_isGameStarted || _isGameCompleted || _isProcessing || card.isFaceUp || card.isMatched) return;
card.isFaceUp = true;
@@ -165,10 +184,8 @@ class CardFlipController with ChangeNotifier {
}
}
// 🔽 [🔥 수정] 매칭 로직: matchId로 비교
void _checkMatch(CardItem card1, CardItem card2) {
if (card1.matchId == card2.matchId) {
// 정답
card1.isMatched = true;
card2.isMatched = true;
_isProcessing = false;
@@ -179,7 +196,6 @@ class CardFlipController with ChangeNotifier {
}
notifyListeners();
} else {
// 오답
Future.delayed(const Duration(milliseconds: 800), () {
card1.isFaceUp = false;
card2.isFaceUp = false;