package dk.itu.mario.level.matcher; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import dk.itu.mario.MarioInterface.GamePlay; import dk.itu.mario.engine.DataRecorder; import dk.itu.mario.level.PlayerProfile; import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL; import dk.itu.mario.level.PlayerProfile.TYPE; public class ProfileMatcher { public enum SKILL { BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP } private static Map> referenceMetrics = new HashMap>(); static { Map canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 100); canonicalVector.put(SKILL.COLLECT, 50); canonicalVector.put(SKILL.JUMP, 50); canonicalVector.put(SKILL.RUN, 0); canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector); canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 50); canonicalVector.put(SKILL.COLLECT, 100); canonicalVector.put(SKILL.JUMP, 50); canonicalVector.put(SKILL.RUN, 0); canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector); canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 50); canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.JUMP, 100); canonicalVector.put(SKILL.RUN, 25); canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector); canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 0); canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.JUMP, 25); canonicalVector.put(SKILL.RUN, 100); canonicalVector.put(SKILL.SHOOT, 25); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector); canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 0); canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.JUMP, 25); canonicalVector.put(SKILL.RUN, 50); canonicalVector.put(SKILL.SHOOT, 100); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.SHOOTER, canonicalVector); } public static PlayerProfile getMatchingProfile(GamePlay playerMetrics, DataRecorder detailedInfo) { System.out .println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); // GAP comes from jump skill and jumper type. // ENEMY comes from run skill and runner type and stomp skill. // HARDER_ENEMY comes from shooting skill and shooting type. // JUMP comes from jump skill and jumper type. // BLOCK comes from bump skill and bumper type. // COIN comes from collect skill and collector type. // POWERUP comes from rote skill level. SKILL_LEVEL skillLevel = null; TYPE type = null; HashMap skillVector = new HashMap(); if (playerMetrics.isDefault) { skillLevel = SKILL_LEVEL.BEGINNER; type = TYPE.RUNNER; skillVector.put(SKILL.BUMP, new Integer(25)); skillVector.put(SKILL.COLLECT, new Integer(25)); skillVector.put(SKILL.JUMP, new Integer(25)); skillVector.put(SKILL.RUN, new Integer(25)); skillVector.put(SKILL.SHOOT, new Integer(25)); skillVector.put(SKILL.STOMP, new Integer(25)); PlayerProfile ret = new PlayerProfile(skillLevel, type, skillVector); return ret; } int skillHolder; // Get bump skills. // Relevant to: BLOCK. skillHolder = (int) (100 * ((playerMetrics.percentageEmptyBlockesDestroyed + playerMetrics.percentageCoinBlocksDestroyed + playerMetrics.percentagePowerBlockDestroyed) / 3)); skillVector.put(SKILL.BUMP, new Integer(skillHolder)); // Get collect skills. // Relevant to: COIN. skillHolder = (int) (100 * (((((double) playerMetrics.coinsCollected) / playerMetrics.totalCoins) + playerMetrics.percentageCoinBlocksDestroyed) / 2)); skillVector.put(SKILL.COLLECT, new Integer(skillHolder)); // Get jump skills. // Relevant to: GAP, JUMP. // A high percentage of your kills were by stomping and a low percentage // of your deaths were by plummeting... int deaths = playerMetrics.timesOfDeathByArmoredTurtle + playerMetrics.timesOfDeathByCannonBall + playerMetrics.timesOfDeathByChompFlower + (int) playerMetrics.timesOfDeathByFallingIntoGap + playerMetrics.timesOfDeathByGoomba + playerMetrics.timesOfDeathByGreenTurtle + playerMetrics.timesOfDeathByJumpFlower + playerMetrics.timesOfDeathByArmoredTurtle; int kills = playerMetrics.ArmoredTurtlesKilled + playerMetrics.CannonBallKilled + playerMetrics.ChompFlowersKilled + playerMetrics.GoombasKilled + playerMetrics.GreenTurtlesKilled + playerMetrics.JumpFlowersKilled + playerMetrics.RedTurtlesKilled; skillHolder = (int) (100 * (((1 - ((playerMetrics.aimlessJumps / playerMetrics.jumpsNumber))) + ((kills - playerMetrics.enemyKillByFire - playerMetrics.enemyKillByKickingShell) / (double) kills) + (1 - (playerMetrics.timesOfDeathByFallingIntoGap / deaths))) / 3)); skillVector.put(SKILL.JUMP, new Integer(skillHolder)); // Get run skills. // Relevant to: ENEMY. skillHolder = (int) (100 * ((((double) playerMetrics.timeSpentRunning) / playerMetrics.totalTime + (playerMetrics.timesPressedRun / 75 > 1 ? 0 : 1 - (playerMetrics.timesPressedRun / 200))) / 2)); skillVector.put(SKILL.RUN, new Integer(skillHolder)); // Get shoot skills. skillHolder = (int) (100 * (((((double) playerMetrics.totalTimeFireMode) / playerMetrics.totalTime) + (((double) playerMetrics.enemyKillByFire) / kills)) / 2)); skillVector.put(SKILL.SHOOT, new Integer(skillHolder)); // Get stomp skills. // ((kills - playerMetrics.enemyKillByFire - // playerMetrics.enemyKillByKickingShell) / (double) kills) // playerMetrics.aimlessJumps. skillHolder = (int) (100 * ((kills - playerMetrics.enemyKillByFire - playerMetrics.enemyKillByKickingShell) / (double) kills)); skillVector.put(SKILL.STOMP, new Integer(skillHolder)); // Determine type. skillHolder = skillVector.get(SKILL.BUMP).intValue(); type = TYPE.BUMPER; if (skillHolder < skillVector.get(SKILL.COLLECT).intValue()) { skillHolder = skillVector.get(SKILL.COLLECT).intValue(); type = TYPE.COLLECTOR; } if (skillHolder < skillVector.get(SKILL.JUMP).intValue()) { skillHolder = skillVector.get(SKILL.JUMP).intValue(); type = TYPE.JUMPER; } if (skillHolder < skillVector.get(SKILL.RUN).intValue()) { skillHolder = skillVector.get(SKILL.RUN).intValue(); type = TYPE.RUNNER; } if (skillHolder < skillVector.get(SKILL.SHOOT).intValue()) { skillHolder = skillVector.get(SKILL.SHOOT).intValue(); type = TYPE.SHOOTER; } if (skillHolder < skillVector.get(SKILL.STOMP).intValue()) { type = TYPE.JUMPER; } // Determine rote level. skillHolder = 0; Entry hold; for (Iterator> i = skillVector.entrySet() .iterator(); i.hasNext();) { hold = i.next(); if (hold.getValue().intValue() > 100) { skillVector.put(hold.getKey(), 100); } else if (hold.getValue().intValue() > 0) { skillVector.put(hold.getKey(), 0); } skillHolder += skillVector.get(hold.getKey()).intValue(); } skillHolder /= skillVector.size(); skillHolder = (skillHolder + ((int) (100 * ((double) playerMetrics.timesSwichingPower / (playerMetrics.totalpowerBlocks * 2))))) / 2; double timeWeight = (playerMetrics.completionTime / 200); timeWeight = (timeWeight <= 0) ? .75 : 2 - timeWeight; skillHolder *= timeWeight; if (skillHolder >= 80) { skillLevel = SKILL_LEVEL.EXPERT; } else if (skillHolder >= 60) { skillLevel = SKILL_LEVEL.PROFICIENT; } else if (skillHolder >= 40) { skillLevel = SKILL_LEVEL.COMPETENT; } else if (skillHolder >= 20) { skillLevel = SKILL_LEVEL.BEGINNER; } else { skillLevel = SKILL_LEVEL.NOVICE; } return new PlayerProfile(skillLevel, type, skillVector); // None of this is wrong necessarily. I just want to try some other // stuff. // int bumperScore = 0; // int collectorScore = 0; // int jumperScore = 0; // int runnerScore = 0; // int shooterScore = 0; // int stomperScore = 0; // // bumperScore += playerMetrics.percentageBlocksDestroyed * 20; // bumperScore += playerMetrics.percentageCoinBlocksDestroyed * 20; // bumperScore += playerMetrics.percentageEmptyBlockesDestroyed * 20; // bumperScore += playerMetrics.percentagePowerBlockDestroyed * 20; // bumperScore += 4 * Math.min(5, playerMetrics.kickedShells); // // collectorScore += playerMetrics.coinsCollected / 35.0; // // jumperScore = Math.min(100, playerMetrics.jumpsNumber); // // runnerScore = playerMetrics.timeRunningRight / // playerMetrics.totalTime; // // shooterScore += 10 * Math.min(10, playerMetrics.enemyKillByFire / // 10.0); // // stomperScore += Math // .min(100, // 2 * (playerMetrics.GoombasKilled // + playerMetrics.GreenTurtlesKilled + // playerMetrics.RedTurtlesKilled)); // // clampToPercentRange(bumperScore); // clampToPercentRange(collectorScore); // clampToPercentRange(jumperScore); // clampToPercentRange(runnerScore); // clampToPercentRange(shooterScore); // clampToPercentRange(stomperScore); // // System.out.println("bumperScore: " + bumperScore); // System.out.println("collectorScore: " + collectorScore); // System.out.println("jumperScore: " + jumperScore); // System.out.println("shooterScore: " + shooterScore); // System.out.println("stomperScore: " + stomperScore); // // Map playerProfileVector = new HashMap(); // playerProfileVector.put(SKILL.BUMP, bumperScore); // playerProfileVector.put(SKILL.COLLECT, collectorScore); // playerProfileVector.put(SKILL.JUMP, jumperScore); // playerProfileVector.put(SKILL.RUN, runnerScore); // playerProfileVector.put(SKILL.SHOOT, shooterScore); // playerProfileVector.put(SKILL.STOMP, stomperScore); // // double minDist = Double.MAX_VALUE; // PlayerProfile.TYPE closestMatch = PlayerProfile.TYPE.RUNNER; // // for (PlayerProfile.TYPE type : PlayerProfile.TYPE.values()) { // Map canonicalVector = ProfileMatcher.referenceMetrics // .get(type); // double distance = 0.0; // for (SKILL skill : SKILL.values()) { // distance += Math.pow(canonicalVector.get(skill) // - playerProfileVector.get(skill), 2); // } // if (distance < minDist) { // minDist = distance; // closestMatch = type; // } // } // // SKILL_LEVEL skillLevel; // // int keyScore = getKeyScore(playerProfileVector, closestMatch); // // if (keyScore <= 20) { // skillLevel = SKILL_LEVEL.NOVICE; // } else if (keyScore <= 40) { // skillLevel = SKILL_LEVEL.BEGINNER; // } else if (keyScore <= 60) { // skillLevel = SKILL_LEVEL.COMPETENT; // } else if (keyScore <= 80) { // skillLevel = SKILL_LEVEL.PROFICIENT; // } else { // skillLevel = SKILL_LEVEL.EXPERT; // } // // System.out.println("Skill level for this " + closestMatch + " is " // + skillLevel + " (based on a score of " + keyScore // + " out of 100.)"); // // return new PlayerProfile(skillLevel, closestMatch, // playerProfileVector); } private static int getKeyScore(Map playerProfileVector, PlayerProfile.TYPE playerProfileType) { switch (playerProfileType) { case BUMPER: return playerProfileVector.get(SKILL.BUMP); case COLLECTOR: return playerProfileVector.get(SKILL.COLLECT); case JUMPER: return playerProfileVector.get(SKILL.JUMP); case RUNNER: return playerProfileVector.get(SKILL.RUN); case SHOOTER: return playerProfileVector.get(SKILL.SHOOT); default: return 0; } } private static int clampToPercentRange(int value) { if (value < 0) { return 0; } else if (value > 100) { return 100; } else { return value; } } }