This commit is contained in:
2025-11-11 14:38:15 +09:00
parent 1883fef583
commit 5a0785f262
9 changed files with 358 additions and 208 deletions
+94
View File
@@ -0,0 +1,94 @@
// lib/models/game_level.dart
// 11단계 레벨의 모든 속성을 정의하는 클래스
class GameLevel {
final int levelIndex; // 1-11
final String name; // "입문 (4x4)"
final int blockSize; // 2, 3, 4
final int generatorLevel; // 서버에 요청할 생성기 난이도 (1~5)
final String contextId; // 랭킹 ID "SUDOKU_4x4_L1"
// 🔽 [신규] 테마 정책
final bool isSequentialNumbers; // L1, L4, L9 (숫자 고정)
final bool isSequentialLetters; // L2, L5, L10 (문자 고정)
// (둘 다 false이면 HomeScreen에서 선택한 랜덤 테마 사용)
const GameLevel({
required this.levelIndex,
required this.name,
required this.blockSize,
required this.generatorLevel,
required this.contextId,
this.isSequentialNumbers = false,
this.isSequentialLetters = false,
});
}
// 앱 전역에서 사용할 11단계 레벨 정의
class AppLevels {
static final List<GameLevel> allLevels = [
// --- 2x2 (blockSize = 2) ---
const GameLevel(
levelIndex: 1, name: "입문 (4x4)", blockSize: 2, generatorLevel: 1,
contextId: "SUDOKU_4x4_L1", isSequentialNumbers: true // 👈 순차 숫자
),
const GameLevel(
levelIndex: 2, name: "초급 (4x4)", blockSize: 2, generatorLevel: 3,
contextId: "SUDOKU_4x4_L3", isSequentialLetters: true // 👈 순차 문자
),
const GameLevel(
levelIndex: 3, name: "숙련 (4x4)", blockSize: 2, generatorLevel: 5,
contextId: "SUDOKU_4x4_L5" // 👈 랜덤 테마
),
// --- 3x3 (blockSize = 3) ---
const GameLevel(
levelIndex: 4, name: "쉬움 (9x9)", blockSize: 3, generatorLevel: 1,
contextId: "SUDOKU_9x9_L1", isSequentialNumbers: true // 👈 순차 숫자
),
const GameLevel(
levelIndex: 5, name: "중급 (9x9)", blockSize: 3, generatorLevel: 2,
contextId: "SUDOKU_9x9_L2", isSequentialLetters: true // 👈 순차 문자
),
const GameLevel(
levelIndex: 6, name: "상급 (9x9)", blockSize: 3, generatorLevel: 3,
contextId: "SUDOKU_9x9_L3" // 👈 랜덤 테마
),
const GameLevel(
levelIndex: 7, name: "어려움 (9x9)", blockSize: 3, generatorLevel: 4,
contextId: "SUDOKU_9x9_L4" // 👈 랜덤 테마
),
const GameLevel(
levelIndex: 8, name: "최상급 (9x9)", blockSize: 3, generatorLevel: 5,
contextId: "SUDOKU_9x9_L5" // 👈 랜덤 테마
),
// --- 4x4 (blockSize = 4) ---
const GameLevel(
levelIndex: 9, name: "전문가 (16x16)", blockSize: 4, generatorLevel: 1,
contextId: "SUDOKU_16x16_L1", isSequentialNumbers: true // 👈 순차 숫자
),
const GameLevel(
levelIndex: 10, name: "마스터 (16x16)", blockSize: 4, generatorLevel: 3,
contextId: "SUDOKU_16x16_L3", isSequentialLetters: true // 👈 순차 문자
),
const GameLevel(
levelIndex: 11, name: "지옥 (16x16)", blockSize: 4, generatorLevel: 5,
contextId: "SUDOKU_16x16_L5" // 👈 랜덤 테마
),
];
// 인덱스(1-11)로 레벨 정보 찾기
static GameLevel getLevel(int levelIndex) {
if (levelIndex < 1) levelIndex = 1;
if (levelIndex > allLevels.length) levelIndex = allLevels.length;
return allLevels.firstWhere((level) => level.levelIndex == levelIndex,
orElse: () => allLevels[0] // 못찾으면 L1 반환
);
}
// 랭킹 화면용 맵 (ContextId -> 이름)
static Map<String, String> get contextIdToNameMap {
return { for (var level in allLevels) level.contextId : level.name };
}
}
+14 -9
View File
@@ -84,29 +84,34 @@ class AppThemes {
};
// --- 4. [핵심] 게임 시작 시 호출될 테마 '빌더' 함수 ---
static SudokuTheme buildGameTheme(String themeName, int gridSize) {
static SudokuTheme buildGameTheme(String themeName, int gridSize, {bool isEasyMode = false}) { // 👈 [수정]
String effectiveThemeName = themeName;
// 1. "랜덤"이 선택된 경우
if (themeName == random) {
// "랜덤"을 제외한 실제 테마 이름 리스트에서 무작위로 하나 선택
final actualThemes = _themePools.keys.toList();
effectiveThemeName = (actualThemes..shuffle()).first;
}
// 2. 해당 테마의 '거대 풀'을 가져옴 (없으면 숫자로 대체)
final List<String> pool = _themePools[effectiveThemeName] ?? _numberPool;
// 3. 거대 풀을 섞은 뒤, 게임에 필요한 만큼(gridSize)만 뽑음
if (pool.length < gridSize) {
throw Exception("$effectiveThemeName 테마의 상징이 ${pool.length}개뿐입니다. $gridSize개가 필요합니다.");
}
final List<String> selectedSymbols = (pool.toList()..shuffle()).sublist(0, gridSize);
// 4. 이 게임만을 위한 '일회용' SudokuTheme 객체를 생성하여 반환
List<String> selectedSymbols;
// 🔽 [수정] 'isEasyMode'가 true이면 섞지 않고 순서대로 뽑음
if (isEasyMode) {
// (예: 4x4 Easy -> 1,2,3,4 또는 A,B,C,D)
selectedSymbols = pool.sublist(0, gridSize);
} else {
// 그 외: 거대 풀을 섞은 뒤, gridSize만큼 뽑음
selectedSymbols = (pool.toList()..shuffle()).sublist(0, gridSize);
}
return SudokuTheme(
name: effectiveThemeName, // 실제 사용된 테마 이름 (예: "과일")
symbols: selectedSymbols, // 무작위로 뽑힌 기호 리스트
name: effectiveThemeName,
symbols: selectedSymbols,
);
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
// lib/models/unified_rank_dto.dart
class UnifiedRankDto {
final String userId; // 👈 [추가] 앱-고유 ID
final String userId; // 👈 [수정] 앱-고유 ID
final String gameType;
final String? contextId;
final String playerName;
@@ -9,7 +9,7 @@ class UnifiedRankDto {
final int? secondaryScore;
UnifiedRankDto({
required this.userId, // 👈 [추가] 생성자에 추가
required this.userId, // 👈 [수정] 생성자에 추가
required this.gameType,
this.contextId,
required this.playerName,
@@ -20,7 +20,7 @@ class UnifiedRankDto {
// Dart 객체를 JSON으로 변환 (서버 전송용)
Map<String, dynamic> toJson() {
return {
'userId': userId, // 👈 [추가]
'userId': userId, // 👈 [수정]
'gameType': gameType,
'contextId': contextId,
'playerName': playerName,