// packages/feature_game_cardflip/lib/controllers/cardflip_controller.dart import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import '../models/cardflip_models.dart'; class CardFlipController with ChangeNotifier { late CardFlipDifficulty difficulty; late String userId; late String? userName; List _cards = []; List get cards => _cards; CardItem? _firstFlippedCard; bool _isProcessing = false; bool get isProcessing => _isProcessing; bool _isGameCompleted = false; bool get isGameCompleted => _isGameCompleted; bool _isTimeOut = false; bool get isTimeOut => _isTimeOut; int _flipCount = 0; int get flipCount => _flipCount; Timer? _timer; int _secondsElapsed = 0; 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; } void startNewGame(CardFlipDifficulty level) { difficulty = level; _flipCount = 0; _secondsElapsed = 0; _remainingTime = level.timeLimitSeconds; _isGameCompleted = false; _isTimeOut = false; _isProcessing = false; _isGameStarted = false; _firstFlippedCard = null; _generateCards(); notifyListeners(); } void restartGame() { startNewGame(difficulty); } void startGameTimer() { if (_isGameStarted) return; _isGameStarted = true; _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { _secondsElapsed++; _remainingTime--; if (_remainingTime <= 0) { _timer?.cancel(); _isTimeOut = true; _isGameCompleted = true; } notifyListeners(); }); notifyListeners(); } void _generateCards() { final int totalCards = difficulty.totalCards; final int pairsCount = totalCards ~/ 2; List deck = []; final Random random = Random(); if (difficulty.contentType == CardContentType.calculation) { // [πŸ”₯ μˆ˜μ •] μ—°μ‚° λͺ¨λ“œ final Set usedResults = {}; // 레벨 4인지 확인 (ν•œ 자릿수 λ§μ…ˆ/λΊ„μ…ˆλ§Œ) final bool isLevel4 = difficulty.levelIndex == 4; for (int i = 0; i < pairsCount; i++) { String equation; String answer; int result; while (true) { // Lv 4: 0(λ§μ…ˆ), 1(λΊ„μ…ˆ)만 선택 / κ·Έ μ™Έ: 0, 1, 2(κ³±μ…ˆ) 선택 final int opType = isLevel4 ? random.nextInt(2) : random.nextInt(3); int a, b; if (opType == 0) { // λ§μ…ˆ (+) if (isLevel4) { // ν•œ 자릿수 (1~9) a = random.nextInt(9) + 1; b = random.nextInt(9) + 1; } else { // 두 자릿수 포함 (1~15) a = random.nextInt(15) + 1; b = random.nextInt(15) + 1; } result = a + b; equation = "$a + $b"; } else if (opType == 1) { // λΊ„μ…ˆ (-) if (isLevel4) { // κ²°κ³Όκ°€ μ–‘μˆ˜μ΄κ³  ν•œ 자릿수 λ‚΄μ—μ„œ μ²˜λ¦¬λ˜λ„λ‘ a = random.nextInt(9) + 2; // 2~10 b = random.nextInt(a - 1) + 1; } else { a = random.nextInt(20) + 5; b = random.nextInt(a - 1) + 1; } result = a - b; equation = "$a - $b"; } else { // κ³±μ…ˆ (*) // Lv 4μ—μ„œλŠ” 이 블둝에 λ“€μ–΄μ˜€μ§€ μ•ŠμŒ 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) { // 연상 λͺ¨λ“œ var entries = CardFlipDifficulties.wordPairs.entries.toList()..shuffle(); for (int i = 0; i < pairsCount; i++) { var entry = entries[i % entries.length]; String matchKey = "PAIR_$i"; deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.key)); deck.add(CardItem(id: 0, matchId: matchKey, displayContent: entry.value)); } } else { // 동일 λ§€μΉ­ λͺ¨λ“œ List pool = List.of(CardFlipDifficulties.emojis)..shuffle(); for (int i = 0; i < pairsCount; i++) { String content; String matchKey = "SAME_$i"; if (difficulty.contentType == CardContentType.number) { content = (i + 1).toString(); } else if (difficulty.contentType == CardContentType.icon) { content = "ICON_$i"; } else { content = pool[i % pool.length]; } deck.add(CardItem(id: 0, matchId: matchKey, displayContent: content)); deck.add(CardItem(id: 0, matchId: matchKey, displayContent: content)); } } deck.shuffle(random); for (int i = 0; i < deck.length; i++) { deck[i] = CardItem( id: i, matchId: deck[i].matchId, displayContent: deck[i].displayContent ); } _cards = deck; } void onCardTapped(CardItem card) { if (!_isGameStarted || _isGameCompleted || _isProcessing || card.isFaceUp || card.isMatched) return; card.isFaceUp = true; _flipCount++; notifyListeners(); if (_firstFlippedCard == null) { _firstFlippedCard = card; } else { _isProcessing = true; _checkMatch(_firstFlippedCard!, card); _firstFlippedCard = null; } } void _checkMatch(CardItem card1, CardItem card2) { if (card1.matchId == card2.matchId) { card1.isMatched = true; card2.isMatched = true; _isProcessing = false; if (_cards.every((c) => c.isMatched)) { _isGameCompleted = true; _timer?.cancel(); } notifyListeners(); } else { Future.delayed(const Duration(milliseconds: 800), () { card1.isFaceUp = false; card2.isFaceUp = false; _isProcessing = false; notifyListeners(); }); } } @override void dispose() { _timer?.cancel(); super.dispose(); } }