ScoreSystem.java

package com.skloch.game;

import java.util.*;

public class ScoreSystem {
    private static ScoreSystem instance;

    private final Map<String, Float> activityCounts;
    private final Map<String, Float> activityTotalScores;
    private final Map<String, Map<Integer, Integer>> activityHistory;

    private ScoreSystem() {
        activityCounts = new HashMap<>();
        activityTotalScores = new HashMap<>();
        activityHistory = new HashMap<>();

        // Initialize activity histories with counts set to 0
        activityHistory.put("Eat", new HashMap<>());
        activityHistory.put("Sleep", new HashMap<>());
        activityHistory.put("Study", new HashMap<>());
        activityHistory.put("Fun", new HashMap<>());

        // Initialize activity types with counts set to 0
        activityCounts.put("Eat", 0f);
        activityCounts.put("Sleep", 0f);
        activityCounts.put("Study", 0f);
        activityCounts.put("Fun", 0f);

        // Initialize activity types with total scores set to 0
        activityTotalScores.put("Eat", 0f);
        activityTotalScores.put("Sleep", 0f);
        activityTotalScores.put("Study", 0f);
        activityTotalScores.put("Fun", 0f);
    }

    public static ScoreSystem getInstance() {
        if (instance == null) {
            instance = new ScoreSystem();
        }
        return instance;
    }

    public void incrementActivity(String activityType, float currentEnergy, String achievementBadge, int activityTime, int currentDay) {
        activityCounts.put(activityType, activityCounts.get(activityType) + 1);

        // Random score from 100-current energy to 100
        float randomScore = (float) (Math.random() * (100 - currentEnergy + 1)) + currentEnergy;

        // update the history
        Map<Integer, Integer> history = activityHistory.get(activityType);
        history.put(currentDay, history.getOrDefault(currentDay, 0) + 1);

        // Check for consecutive days and calculate streak multiplier
        int streakCount = getStreakCount(history, currentDay);
        float streakMultiplier = getStreakMultiplier(history, currentDay);

        // Apply streak multiplier
        float streakMultipliedScore = randomScore * streakMultiplier;

        // Apply diminishing returns for consecutive activities on the same day
        int consecutiveCount = history.get(currentDay);
        float diminishingMultiplier = (float) Math.pow(0.9, consecutiveCount - 1);
        float diminishedScore = streakMultipliedScore * diminishingMultiplier;

        // Apply activity time multiplier
        float hoursMultiplier = getHoursMultiplier(activityTime);
        float hoursMultipliedScore = hoursMultiplier * diminishedScore;

        // Apply the multiplier based on the achievement tier
        float badgeMultiplier = getBadgeMultiplier(achievementBadge);
        float badgeMultipliedScore = hoursMultipliedScore * badgeMultiplier;

        // Clamp the final score between 0 and 100
        float finalScore = Math.max(0, Math.min(badgeMultipliedScore, 100));

        // Add the clamped score to the activity total score
        activityTotalScores.put(activityType, activityTotalScores.get(activityType) + finalScore);

        System.out.println(activityType + " was done " + activityCounts.get(activityType) + " times and has a total score of " + activityTotalScores.get(activityType) + ".");
        System.out.println("Current " + activityType + " streak: " + streakCount + " days in a row.");
        System.out.println(activityType + " performed " + consecutiveCount + " times on day " + currentDay + ".");
    }

    private float getBadgeMultiplier(String achievementTier) {
        switch (achievementTier) {
            case "Bronze":
                return 1.05f;
            case "Silver":
                return 1.1f;
            case "Gold":
                return 1.15f;
            case "Diamond":
                return 1.2f;
            default:
                return 1.0f;
        }
    }

    private float getHoursMultiplier(int activityTime) {
        switch (activityTime) {
            case 3:
                return 1.12f;
            case 4:
                return 1.25f;
            case 5:
                return 1.35f;
            case 6:
                return 1.40f;
            default:
                return 1.0f;
        }
    }

    private float getStreakMultiplier(Map<Integer, Integer> history, int currentDay) {
        int streakCount = getStreakCount(history, currentDay);
        // Streak multipliers based on the number of consecutive days
        switch (streakCount) {
            case 3:
                return 1.02f;
            case 4:
                return 1.05f;
            case 5:
                return 1.08f;
            case 6:
                return 1.12f;
            case 7:
                return 1.15f;
            default:
                return 1.0f;
        }
    }

    private int getStreakCount(Map<Integer, Integer> history, int currentDay) {
        int streakCount = 0;
        while (history.containsKey(currentDay - streakCount)) {
            streakCount++;
        }
        return streakCount;
    }

    private String getStreakName(String activityType, int streakCount) {
        Map<String, String[]> streakNames = new HashMap<>();
        streakNames.put("Eat", new String[] {"Nibbler", "Foodie", "Gourmet"});
        streakNames.put("Sleep", new String[] {"Napper", "Sleeper", "Dreamer"});
        streakNames.put("Study", new String[] {"Reader", "Scholar", "Bookworm"});
        streakNames.put("Fun", new String[] {"Adventurer", "Enthusiast", "Joy Seeker"});

        if (!streakNames.containsKey(activityType) || streakCount < 3) {
            return "";  // No streak name for counts less than 3
        }

        String[] names = streakNames.get(activityType);

        switch (streakCount) {
            case 3:
            case 4:
                return names[0];
            case 5:
            case 6:
                return names[1];
            case 7:
                return names[2];
            default:
                return "";
        }
    }


    /* to prevent getting a really high score for sleeping without even sleeping
     for percentage calculations check if the player slept at least 6 times to divide total score by sleep count
      else divide it by 6. this way if the player slept 4 times, it will be 400/6 instead of 400/4. */

    public float calculatePercentage(String activityType) {
        float totalScore = activityTotalScores.getOrDefault(activityType, 0f);
        float count = activityCounts.getOrDefault(activityType, 0f);
        float divisor = Math.max(count, 6);  // Ensures we divide by either the count or 6, whichever is greater

        if (divisor == 0) {
            return 0;  // To avoid division by zero
        }

        return (totalScore / divisor) * 100;  // Multiplied by 100 to get a percentage
    }

    public Map<String, Float> getActivityTotalScores() {
        return activityTotalScores;
    }

    public Map<String, Float> getActivityCounts() {
        return activityCounts;
    }

    public Map<String, Map<Integer, Integer>> getHistory() {
        return activityHistory;
    }
}