From 18643d17e44277c46fea972cf5b05a4ec597e3b5 Mon Sep 17 00:00:00 2001 From: Woody Folsom Date: Sun, 18 Mar 2012 18:14:11 -0400 Subject: [PATCH] Fixed NPE when the default player.txt does not exist. Fixed bug when requesting PowerUp component of invalid length. Change default lives to 1. --- README.TXT | 12 ++ src/dk/itu/mario/MarioInterface/GamePlay.java | 4 +- src/dk/itu/mario/engine/MarioComponent.java | 5 +- src/dk/itu/mario/level/PCGLevel.java | 31 ++-- .../level/generator/PCGLevelGenerator.java | 20 ++- .../mario/level/matcher/ArchetypeMatcher.java | 45 +++++- .../mario/level/matcher/ProfileMatcher.java | 138 ++++++++++-------- src/dk/itu/mario/scene/LevelSceneCustom.java | 2 +- 8 files changed, 176 insertions(+), 81 deletions(-) create mode 100644 README.TXT diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..b0f6a69 --- /dev/null +++ b/README.TXT @@ -0,0 +1,12 @@ +Adaptive Mario Instructions +--------------------------- + +Unzip the attached archive 'CS8803_P3.zip' and change to the new directory. + +To build Adaptive Mario: run 'ant clean' then 'ant' in this directory. + +To run Adaptive Mario: change to the 'dist' directory, then run java -jar CS8803_P3.jar + +For full instructions, see writeup/CS8803_P3.PDF. + +For assistance, please contact Woody Folsom or Marshall Gillson . \ No newline at end of file diff --git a/src/dk/itu/mario/MarioInterface/GamePlay.java b/src/dk/itu/mario/MarioInterface/GamePlay.java index 6a19451..6beebc2 100644 --- a/src/dk/itu/mario/MarioInterface/GamePlay.java +++ b/src/dk/itu/mario/MarioInterface/GamePlay.java @@ -9,7 +9,8 @@ import java.io.Serializable; public class GamePlay implements Serializable { private static final long serialVersionUID = 1L; - + public static final GamePlay DEFAULT_PROFILE = new GamePlay(); + public int completionTime; // counts only the current run on the level, // excluding death games public int totalTime;// sums all the time, including from previous games if @@ -96,6 +97,7 @@ public class GamePlay implements Serializable { // TODO Auto-generated catch block //e.printStackTrace(); System.out.println("Unable to read from GamePlay file: " + fileName + ", initializing a new GamePlay instance."); + gp = GamePlay.DEFAULT_PROFILE; } return gp; } diff --git a/src/dk/itu/mario/engine/MarioComponent.java b/src/dk/itu/mario/engine/MarioComponent.java index 5f48b24..1e085ef 100644 --- a/src/dk/itu/mario/engine/MarioComponent.java +++ b/src/dk/itu/mario/engine/MarioComponent.java @@ -164,7 +164,7 @@ public class MarioComponent extends JComponent implements Runnable, } public void run() { - + //while (true) { graphicsConfiguration = getGraphicsConfiguration(); Art.init(graphicsConfiguration, sound); @@ -191,7 +191,7 @@ public class MarioComponent extends JComponent implements Runnable, Mario.fire = false; Mario.large = false; Mario.coins = 0; - Mario.lives = 3; + Mario.lives = 1; randomLevel.init(); randomLevel.setSound(sound); @@ -262,6 +262,7 @@ public class MarioComponent extends JComponent implements Runnable, } Art.stopMusic(); + //} } private void drawString(Graphics g, String text, int x, int y, int c) { diff --git a/src/dk/itu/mario/level/PCGLevel.java b/src/dk/itu/mario/level/PCGLevel.java index 27505d3..31fe565 100644 --- a/src/dk/itu/mario/level/PCGLevel.java +++ b/src/dk/itu/mario/level/PCGLevel.java @@ -1137,46 +1137,52 @@ public class PCGLevel extends Level { LevelComponent.TYPE lctype = lcomp.getType(); System.out.println("Building for: " + lcomp); while (length < Math.min(width - 64, lcomp.getEnd())) { + int lcLength; switch (lctype) { case FLAT_LO: case FLAT_HI: - length += buildStraight(length, lcomp.getEnd(), true); + lcLength = buildStraight(length, lcomp.getEnd(), true); break; case PIPE_JUMP: - length += buildPipeJump(length, width - 64 - length); + lcLength = buildPipeJump(length, width - 64 - length); break; case PLATFORM_JUMP: - length += buildPlatformJump(length, width - 64 - length); + lcLength = buildPlatformJump(length, width - 64 - length); break; case MAZE: - length += buildMaze(length, width - 64 - length); + lcLength = buildMaze(length, width - 64 - length); break; case BLOCKS: - length += buildMaze(length, width - 64 - length); + lcLength = buildMaze(length, width - 64 - length); break; case BOWLING_ALLEY : - length += buildBowlingAlley(length, width - 64 - length); + lcLength = buildBowlingAlley(length, width - 64 - length); break; case CANNON_LINE : - length += buildCannonLine(length, width - 64 - length); + lcLength = buildCannonLine(length, width - 64 - length); break; case COIN_DIVE : - length += buildCoinDive(length, width - 64 - length); + lcLength = buildCoinDive(length, width - 64 - length); break; case LEMMING_TRAP : - length += buildLemmingTrap(length, width - 64 - length); + lcLength = buildLemmingTrap(length, width - 64 - length); break; case POWER_UP : - length += buildFreebie(length, width - 64 - length); + lcLength = buildFreebie(length, width - 64 - length); break; case SINGLE_PIT : - length += buildSinglePit(length, width - 64 - length); + lcLength = buildSinglePit(length, width - 64 - length); break; default: + lcLength = 0; System.out .println("Cannot build level segment for unrecognized LevelComponent type: " + type); } + if (lcLength == 0) { + lcLength = buildStraight(length, lcomp.getEnd(), true); + } + length += lcLength; } } } @@ -1247,8 +1253,7 @@ public class PCGLevel extends Level { playerMetrics, dataRecorder); System.out.println("PlayerProfile: " + profile); - LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype( - playerMetrics, dataRecorder); + LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype(profile); System.out.println("LevelArchetype: " + archetype); System.out.println("Creating level grammar"); diff --git a/src/dk/itu/mario/level/generator/PCGLevelGenerator.java b/src/dk/itu/mario/level/generator/PCGLevelGenerator.java index 9f9cd6c..de0cd31 100644 --- a/src/dk/itu/mario/level/generator/PCGLevelGenerator.java +++ b/src/dk/itu/mario/level/generator/PCGLevelGenerator.java @@ -7,7 +7,11 @@ import dk.itu.mario.MarioInterface.LevelGenerator; import dk.itu.mario.MarioInterface.LevelInterface; import dk.itu.mario.engine.DataRecorder; import dk.itu.mario.level.Level; +import dk.itu.mario.level.LevelArchetype; import dk.itu.mario.level.PCGLevel; +import dk.itu.mario.level.PlayerProfile; +import dk.itu.mario.level.matcher.ArchetypeMatcher; +import dk.itu.mario.level.matcher.ProfileMatcher; public class PCGLevelGenerator implements LevelGenerator { @@ -20,7 +24,21 @@ public class PCGLevelGenerator implements @Override public int generateLevelType(GamePlay playerMetrics, DataRecorder dataRecorder) { System.out.println("Generating level type based on playerMetrics, dataRecorder: TYPE_OVERGROUND"); - return Level.TYPE_OVERGROUND; + PlayerProfile profile = ProfileMatcher.getMatchingProfile( + playerMetrics, dataRecorder); + System.out.println("PlayerProfile: " + profile); + + LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype(profile); + switch (archetype.getType()) { + case CASTLE : + return LevelInterface.TYPE_CASTLE; + case OVERGROUND : + return LevelInterface.TYPE_OVERGROUND; + case UNDERGROUND : + return LevelInterface.TYPE_UNDERGROUND; + default : + return LevelInterface.TYPE_OVERGROUND; + } } @Override diff --git a/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java index 2193535..a597c4f 100644 --- a/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java +++ b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java @@ -1,13 +1,48 @@ package dk.itu.mario.level.matcher; -import dk.itu.mario.MarioInterface.GamePlay; -import dk.itu.mario.engine.DataRecorder; import dk.itu.mario.level.LevelArchetype; import dk.itu.mario.level.LevelArchetype.TYPE; +import dk.itu.mario.level.PlayerProfile; public class ArchetypeMatcher { - public static LevelArchetype getMatchingArchetype(GamePlay playerMetrics, DataRecorder detailedInfo) { - System.out.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); - return new LevelArchetype(TYPE.OVERGROUND,1); + public static LevelArchetype getMatchingArchetype( + PlayerProfile playerProfile) { + System.out + .println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); + + TYPE levelType; + int primarySkill; + + switch (playerProfile.getType()) { + case BUMPER: + levelType = TYPE.OVERGROUND; + primarySkill = (int) Math + .round(playerProfile.getBumpSkill() / 10.0); + break; + case COLLECTOR: + levelType = TYPE.UNDERGROUND; + primarySkill = (int) Math + .round(playerProfile.getCollectSkill() / 10.0); + break; + case JUMPER: + levelType = TYPE.CASTLE; + primarySkill = (int) Math + .round(playerProfile.getJumpSkill() / 10.0); + break; + case RUNNER: + levelType = TYPE.OVERGROUND; + primarySkill = (int) Math.round(playerProfile.getRunSkill() / 10.0); + break; + case SHOOTER: + levelType = TYPE.UNDERGROUND; + primarySkill = (int) Math + .round(playerProfile.getShootSkill() / 10.0); + break; + default: + levelType = TYPE.CASTLE; + primarySkill = 1; + } + + return new LevelArchetype(levelType, Math.max(primarySkill,1)); } } diff --git a/src/dk/itu/mario/level/matcher/ProfileMatcher.java b/src/dk/itu/mario/level/matcher/ProfileMatcher.java index cd01b0e..469bd46 100644 --- a/src/dk/itu/mario/level/matcher/ProfileMatcher.java +++ b/src/dk/itu/mario/level/matcher/ProfileMatcher.java @@ -9,12 +9,14 @@ import dk.itu.mario.level.PlayerProfile; import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL; public class ProfileMatcher { - public enum SKILL { BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP } - - private static Map> referenceMetrics = new HashMap>(); - + public enum SKILL { + BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP + } + + private static Map> referenceMetrics = new HashMap>(); + static { - Map canonicalVector = new HashMap(); + Map canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 100); canonicalVector.put(SKILL.COLLECT, 50); canonicalVector.put(SKILL.JUMP, 50); @@ -22,17 +24,17 @@ public class ProfileMatcher { canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector); - - canonicalVector = new HashMap(); + + 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); + canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector); - - canonicalVector = new HashMap(); + + canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 50); canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.JUMP, 100); @@ -40,17 +42,17 @@ public class ProfileMatcher { canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector); - - canonicalVector = new HashMap(); + + 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); + canonicalVector.put(SKILL.STOMP, 0); referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector); - - canonicalVector = new HashMap(); + + canonicalVector = new HashMap(); canonicalVector.put(SKILL.BUMP, 0); canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.JUMP, 25); @@ -59,73 +61,90 @@ public class ProfileMatcher { 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."); - + + public static PlayerProfile getMatchingProfile(GamePlay playerMetrics, + DataRecorder detailedInfo) { + System.out + .println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); + 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)); - + + if (playerMetrics == GamePlay.DEFAULT_PROFILE) { + bumperScore = 50; + collectorScore = 50; + jumperScore = 50; + runnerScore = 50; + shooterScore = 50; + stomperScore = 50; + } else { + 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(); + + 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); + 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); + 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) { @@ -137,29 +156,32 @@ public class ProfileMatcher { } else { skillLevel = SKILL_LEVEL.EXPERT; } - - System.out.println("Skill level for this " + closestMatch + " is " + skillLevel + " (based on a score of " + keyScore + " out of 100.)"); - + + 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) { + + private static int getKeyScore(Map playerProfileVector, + PlayerProfile.TYPE playerProfileType) { switch (playerProfileType) { - case BUMPER : + case BUMPER: return playerProfileVector.get(SKILL.BUMP); - case COLLECTOR : + case COLLECTOR: return playerProfileVector.get(SKILL.COLLECT); - case JUMPER : + case JUMPER: return playerProfileVector.get(SKILL.JUMP); - case RUNNER : + case RUNNER: return playerProfileVector.get(SKILL.RUN); - case SHOOTER : + case SHOOTER: return playerProfileVector.get(SKILL.SHOOT); - default : + default: return 0; } } - + private static int clampToPercentRange(int value) { if (value < 0) { return 0; diff --git a/src/dk/itu/mario/scene/LevelSceneCustom.java b/src/dk/itu/mario/scene/LevelSceneCustom.java index 092fbac..8fdd030 100644 --- a/src/dk/itu/mario/scene/LevelSceneCustom.java +++ b/src/dk/itu/mario/scene/LevelSceneCustom.java @@ -66,7 +66,7 @@ public class LevelSceneCustom extends LevelScene { int levelType = clg.generateLevelType(gp,dataRecorder); currentLevel = (Level) clg.generateLevel(gp,dataRecorder); - + try { level = currentLevel.clone(); } catch (CloneNotSupportedException e) {