...
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
// packages/service_api/lib/models/game_difficulty.dart
|
||||
class GameDifficulty {
|
||||
/// 랭킹 Dropdown에 표시될 이름 (예: "중급 (9x9)", "1 Suit (Easy)")
|
||||
final String name;
|
||||
|
||||
/// API 조회 시 사용할 랭킹 ID (예: "SUDOKU_9x9_L2", "1_SUITS_4-3")
|
||||
final String contextId;
|
||||
|
||||
const GameDifficulty({
|
||||
required this.name,
|
||||
required this.contextId,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// lib/models/game_rank_dto.dart
|
||||
|
||||
class GameRankDto {
|
||||
final String playerName;
|
||||
final int primaryScore; // 시간 (초)
|
||||
final int? secondaryScore; // 점수 (저장된 값, 예: 0~4)
|
||||
|
||||
GameRankDto({
|
||||
required this.playerName,
|
||||
required this.primaryScore,
|
||||
this.secondaryScore
|
||||
});
|
||||
|
||||
factory GameRankDto.fromJson(Map<String, dynamic> json) {
|
||||
return GameRankDto(
|
||||
playerName: json['playerName'],
|
||||
primaryScore: (json['primaryScore'] as num).toInt(),
|
||||
secondaryScore: (json['secondaryScore'] as num?)?.toInt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔽 [신규 추가] 나의 랭킹 + 순위(숫자)를 담는 DTO
|
||||
class GameRankWithRankNumber {
|
||||
final GameRankDto rankData;
|
||||
final int rankNumber;
|
||||
|
||||
GameRankWithRankNumber({
|
||||
required this.rankData,
|
||||
required this.rankNumber,
|
||||
});
|
||||
|
||||
factory GameRankWithRankNumber.fromJson(Map<String, dynamic> json) {
|
||||
return GameRankWithRankNumber(
|
||||
rankData: GameRankDto.fromJson(json['rankData']),
|
||||
rankNumber: (json['rankNumber'] as num).toInt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔽 [신규 추가] 랭킹 등록 시 서버가 반환하는 최종 DTO
|
||||
class RankSubmissionResult {
|
||||
final List<GameRankDto> topRanks; // 상위 10개 랭킹
|
||||
final GameRankWithRankNumber? myRank; // 나의 랭킹 정보 (순위 포함)
|
||||
|
||||
RankSubmissionResult({
|
||||
required this.topRanks,
|
||||
this.myRank,
|
||||
});
|
||||
|
||||
factory RankSubmissionResult.fromJson(Map<String, dynamic> json) {
|
||||
// topRanks 파싱
|
||||
final List<dynamic> topRanksJson = json['topRanks'] ?? [];
|
||||
final List<GameRankDto> topRanksList = topRanksJson
|
||||
.map((item) => GameRankDto.fromJson(item))
|
||||
.toList();
|
||||
|
||||
// myRank 파싱 (null일 수 있음)
|
||||
final Map<String, dynamic>? myRankJson = json['myRank'];
|
||||
final GameRankWithRankNumber? myRankData =
|
||||
myRankJson != null ? GameRankWithRankNumber.fromJson(myRankJson) : null;
|
||||
|
||||
return RankSubmissionResult(
|
||||
topRanks: topRanksList,
|
||||
myRank: myRankData,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// lib/models/sudoku_game_dto.dart
|
||||
|
||||
class SudokuGameDto {
|
||||
final int puzzleId; // 👈 [추가] 서버에서 보낸 ID
|
||||
final String question;
|
||||
final String solution;
|
||||
final int blockSize;
|
||||
final int gridSize;
|
||||
|
||||
SudokuGameDto({
|
||||
required this.puzzleId, // 👈 [추가]
|
||||
required this.question,
|
||||
required this.solution,
|
||||
required this.blockSize,
|
||||
}) : gridSize = blockSize * blockSize;
|
||||
|
||||
factory SudokuGameDto.fromJson(Map<String, dynamic> json) {
|
||||
int bs = json['blockSize'] ?? 3;
|
||||
return SudokuGameDto(
|
||||
puzzleId: json['puzzleId'], // 👈 [추가] 서버의 puzzleId 매핑
|
||||
question: json['question'],
|
||||
solution: json['solution'],
|
||||
blockSize: bs,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// lib/models/sudoku_theme.dart
|
||||
|
||||
// 1. SudokuTheme 클래스
|
||||
// '게임 시작' 시점에 동적으로 생성될 객체입니다.
|
||||
class SudokuTheme {
|
||||
final String name; // "숫자", "알파벳", "과일"
|
||||
final List<String> symbols; // 👈 '게임에 실제 사용할' 무작위로 뽑힌 기호 리스트
|
||||
|
||||
const SudokuTheme({required this.name, required this.symbols});
|
||||
|
||||
// 1-based 정수(1)를 테마 기호("🍎")로 변환
|
||||
String getSymbol(int value) {
|
||||
if (value > 0 && value <= symbols.length) {
|
||||
return symbols[value - 1]; // 1 -> index 0
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
// 테마 기호("🍎")를 1-based 정수(1)로 변환
|
||||
int getValue(String symbol) {
|
||||
int index = symbols.indexOf(symbol);
|
||||
if (index != -1) {
|
||||
return index + 1; // index 0 -> 1
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. AppThemes 클래스 (테마 저장소 역할)
|
||||
class AppThemes {
|
||||
|
||||
// --- 테마 이름 정의 ---
|
||||
static const String random = "랜덤";
|
||||
static const String numbers = "숫자";
|
||||
static const String letters = "알파벳";
|
||||
static const String fruits = "과일";
|
||||
static const String korean = "한글";
|
||||
static const String animals = "동물";
|
||||
|
||||
// --- 1. 거대한 '상징 풀' 정의 (25개 이상) ---
|
||||
static const List<String> _numberPool = [
|
||||
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
|
||||
"17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30"
|
||||
];
|
||||
|
||||
static const List<String> _letterPool = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
|
||||
];
|
||||
|
||||
static const List<String> _fruitPool = [
|
||||
"🍎", "🍌", "🍇", "🍓", "🍊", "🍋", "🍉", "🍑", "🍒", "🥝", "🥥", "🍍", "🥑", "🍆", "🍅", "🌽",
|
||||
"🥕", "🫑", "🌶️", "🥦", "🥬", "🥒", "🍄", "🥜", "🫘", "🍏", "🍐", "🍈", "🥭", "🫒"
|
||||
];
|
||||
|
||||
static const List<String> _koreanPool = [
|
||||
"가", "나", "다", "라", "마", "바", "사", "아", "자", "차", "카", "타", "파", "하", "고", "노",
|
||||
"도", "로", "모", "보", "소", "오", "조", "초", "코", "구", "누", "두", "루", "무"
|
||||
];
|
||||
|
||||
static const List<String> _animalPool = [
|
||||
"🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", "🐷", "🐸", "🐵", "🐔",
|
||||
"🐧", "🐦", "🐤", "🦆", "🦅", "🦉", "🦇", "🐺", "🐗", "🐴", "🦄", "🐝", "🐛", "🦋"
|
||||
];
|
||||
|
||||
// --- 2. 홈 화면 '선택' 메뉴에 표시될 이름 리스트 ---
|
||||
static final List<String> selectableThemeNames = [
|
||||
random,
|
||||
numbers,
|
||||
letters,
|
||||
fruits,
|
||||
korean,
|
||||
animals
|
||||
];
|
||||
|
||||
// --- 3. 테마 이름과 실제 '상징 풀'을 매핑 ---
|
||||
static final Map<String, List<String>> _themePools = {
|
||||
numbers: _numberPool,
|
||||
letters: _letterPool,
|
||||
fruits: _fruitPool,
|
||||
korean: _koreanPool,
|
||||
animals: _animalPool,
|
||||
};
|
||||
|
||||
// --- 4. [핵심] 게임 시작 시 호출될 테마 '빌더' 함수 ---
|
||||
static SudokuTheme buildGameTheme(String themeName, int gridSize, {bool isEasyMode = false}) { // 👈 [수정]
|
||||
String effectiveThemeName = themeName;
|
||||
|
||||
if (themeName == random) {
|
||||
final actualThemes = _themePools.keys.toList();
|
||||
effectiveThemeName = (actualThemes..shuffle()).first;
|
||||
}
|
||||
|
||||
final List<String> pool = _themePools[effectiveThemeName] ?? _numberPool;
|
||||
|
||||
if (pool.length < gridSize) {
|
||||
throw Exception("$effectiveThemeName 테마의 상징이 ${pool.length}개뿐입니다. $gridSize개가 필요합니다.");
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// lib/models/unified_rank_dto.dart
|
||||
|
||||
class UnifiedRankDto {
|
||||
final String userId; // 👈 [수정] 앱-고유 ID
|
||||
final String gameType;
|
||||
final String? contextId;
|
||||
final String playerName;
|
||||
final int primaryScore;
|
||||
final int? secondaryScore;
|
||||
|
||||
UnifiedRankDto({
|
||||
required this.userId, // 👈 [수정] 생성자에 추가
|
||||
required this.gameType,
|
||||
this.contextId,
|
||||
required this.playerName,
|
||||
required this.primaryScore,
|
||||
this.secondaryScore,
|
||||
});
|
||||
|
||||
// Dart 객체를 JSON으로 변환 (서버 전송용)
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'userId': userId, // 👈 [수정]
|
||||
'gameType': gameType,
|
||||
'contextId': contextId,
|
||||
'playerName': playerName,
|
||||
'primaryScore': primaryScore,
|
||||
'secondaryScore': secondaryScore,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class ValidateResultDto {
|
||||
final bool isCorrect;
|
||||
|
||||
ValidateResultDto({required this.isCorrect});
|
||||
|
||||
factory ValidateResultDto.fromJson(Map<String, dynamic> json) {
|
||||
return ValidateResultDto(
|
||||
isCorrect: json['correct'] ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user