128 lines
5.1 KiB
Java
128 lines
5.1 KiB
Java
package com.example.mindflux.service;
|
||
|
||
import com.example.mindflux.dto.GameProgress;
|
||
import com.example.mindflux.dto.GameStats;
|
||
import com.example.mindflux.dto.ProgressData;
|
||
import com.example.mindflux.dto.WeeklyActivity;
|
||
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import java.time.LocalDate;
|
||
import java.util.ArrayList;
|
||
import java.util.LinkedHashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Comparator;
|
||
import java.time.temporal.TemporalAdjusters;
|
||
import java.time.DayOfWeek;
|
||
|
||
@Service
|
||
public class ProgressService {
|
||
|
||
/** Spiel-Kennung -> Anzeigename, in Dashboard-Reihenfolge. */
|
||
private static final Map<String, String> GAMES = new LinkedHashMap<>();
|
||
static {
|
||
GAMES.put("mindmatch", "MindMatch");
|
||
GAMES.put("techtacular", "TechTacular");
|
||
GAMES.put("devlab", "DevLab");
|
||
GAMES.put("syntax-saga", "SyntaxSaga");
|
||
GAMES.put("codeescape", "SystemEscape");
|
||
GAMES.put("serviceclash", "ServiceClash");
|
||
GAMES.put("bughunter", "BugHunter");
|
||
GAMES.put("networker", "NetWorker");
|
||
}
|
||
|
||
private final GameScoreService gameScoreService;
|
||
|
||
public ProgressService(GameScoreService gameScoreService) {
|
||
this.gameScoreService = gameScoreService;
|
||
}
|
||
|
||
/** XP pro Level: Level n ist erreicht ab (n-1) * 1000 Gesamt-XP. */
|
||
static final int XP_PER_LEVEL = 1000;
|
||
|
||
/** Echter Gesamtfortschritt aus game_scores: XP = Summe aller Scores, Level linear. */
|
||
public ProgressData getOverallProgress(String username) {
|
||
long totalXp = gameScoreService.totalScore(username);
|
||
int level = (int) (totalXp / XP_PER_LEVEL) + 1;
|
||
long nextLevelAt = (long) level * XP_PER_LEVEL;
|
||
int withinLevel = (int) (totalXp - (long) (level - 1) * XP_PER_LEVEL);
|
||
|
||
ProgressData data = new ProgressData();
|
||
// Näherung: moves = je nach Spiel gelöste Aufgaben bzw. richtige Antworten
|
||
// (bei MindMatch sind es Zug-Versuche – bewusst in Kauf genommen).
|
||
data.setCorrectAnswers((int) Math.min(gameScoreService.totalMoves(username), Integer.MAX_VALUE));
|
||
data.setCurrentXp((int) Math.min(totalXp, Integer.MAX_VALUE));
|
||
data.setXpForNextLevel((int) Math.min(nextLevelAt, Integer.MAX_VALUE));
|
||
data.setUserLevel(level);
|
||
data.setProgressPercent(Math.round(withinLevel * 100f / XP_PER_LEVEL));
|
||
data.setStreakDays(computeStreak(gameScoreService.playDaysDesc(username), LocalDate.now()));
|
||
return data;
|
||
}
|
||
|
||
/**
|
||
* Tages-Streak: Anzahl zusammenhängender Spieltage bis heute. Ein Tag Karenz –
|
||
* wer heute noch nicht gespielt hat, behält die Serie von gestern.
|
||
*/
|
||
static int computeStreak(List<LocalDate> playDaysDesc, LocalDate today) {
|
||
if (playDaysDesc.isEmpty()) {
|
||
return 0;
|
||
}
|
||
LocalDate anchor = playDaysDesc.get(0);
|
||
if (anchor.isBefore(today.minusDays(1))) {
|
||
return 0;
|
||
}
|
||
int streak = 0;
|
||
LocalDate expected = anchor;
|
||
for (LocalDate day : playDaysDesc) {
|
||
if (day.equals(expected)) {
|
||
streak++;
|
||
expected = expected.minusDays(1);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
return streak;
|
||
}
|
||
|
||
/** Echter Spiel-Fortschritt pro Spiel aus der Datenbank (game_scores). */
|
||
public List<GameProgress> getGameProgress(String username) {
|
||
List<GameProgress> result = new ArrayList<>();
|
||
for (Map.Entry<String, String> entry : GAMES.entrySet()) {
|
||
GameStats stats = gameScoreService.getStats(username, entry.getKey());
|
||
result.add(toGameProgress(entry.getValue(), stats));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/** Aktuelle Wochenwerte, jeweils Montag bis Sonntag. */
|
||
public WeeklyActivity getCurrentWeekActivity(String username) {
|
||
LocalDate monday = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
|
||
return gameScoreService.weeklyActivity(username, monday);
|
||
}
|
||
|
||
/** Die tatsächlich gespielten Spiele, nach ihrem Highscore sortiert. */
|
||
public List<GameProgress> getTopGames(String username, int limit) {
|
||
List<GameProgress> games = getGameProgress(username).stream()
|
||
.filter(game -> game.getPlays() > 0)
|
||
.sorted(Comparator.comparingInt(GameProgress::getHighscore).reversed()
|
||
.thenComparing(Comparator.comparingInt(GameProgress::getPlays).reversed()))
|
||
.limit(limit)
|
||
.toList();
|
||
int topScore = games.stream().mapToInt(GameProgress::getHighscore).max().orElse(1);
|
||
games.forEach(game -> game.setProgressPercent(Math.round(game.getHighscore() * 100f / topScore)));
|
||
return games;
|
||
}
|
||
|
||
private GameProgress toGameProgress(String displayName, GameStats stats) {
|
||
GameProgress game = new GameProgress();
|
||
game.setName(displayName);
|
||
game.setHighscore(stats.getHighscore());
|
||
game.setPlays((int) stats.getPlays());
|
||
game.setLastPlayed(stats.getLastPlayed());
|
||
// Fortschritt auf der Score-Skala (max ~1500 Punkte -> 100 %).
|
||
game.setProgressPercent(Math.min(100, Math.round(stats.getHighscore() / 15.0f)));
|
||
return game;
|
||
}
|
||
}
|