Fixed NPE when the default player.txt does not exist. Fixed bug when requesting PowerUp component of invalid length. Change default lives to 1.

This commit is contained in:
Woody Folsom
2012-03-18 18:14:11 -04:00
parent 0619f95f33
commit 18643d17e4
8 changed files with 176 additions and 81 deletions

12
README.TXT Normal file
View File

@@ -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 <woody.folsom@gatech.edu> or Marshall Gillson <mgillson1@gmail.com>.

View File

@@ -9,7 +9,8 @@ import java.io.Serializable;
public class GamePlay implements Serializable { public class GamePlay implements Serializable {
private static final long serialVersionUID = 1L; 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, public int completionTime; // counts only the current run on the level,
// excluding death games // excluding death games
public int totalTime;// sums all the time, including from previous games if 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 // TODO Auto-generated catch block
//e.printStackTrace(); //e.printStackTrace();
System.out.println("Unable to read from GamePlay file: " + fileName + ", initializing a new GamePlay instance."); System.out.println("Unable to read from GamePlay file: " + fileName + ", initializing a new GamePlay instance.");
gp = GamePlay.DEFAULT_PROFILE;
} }
return gp; return gp;
} }

View File

@@ -164,7 +164,7 @@ public class MarioComponent extends JComponent implements Runnable,
} }
public void run() { public void run() {
//while (true) {
graphicsConfiguration = getGraphicsConfiguration(); graphicsConfiguration = getGraphicsConfiguration();
Art.init(graphicsConfiguration, sound); Art.init(graphicsConfiguration, sound);
@@ -191,7 +191,7 @@ public class MarioComponent extends JComponent implements Runnable,
Mario.fire = false; Mario.fire = false;
Mario.large = false; Mario.large = false;
Mario.coins = 0; Mario.coins = 0;
Mario.lives = 3; Mario.lives = 1;
randomLevel.init(); randomLevel.init();
randomLevel.setSound(sound); randomLevel.setSound(sound);
@@ -262,6 +262,7 @@ public class MarioComponent extends JComponent implements Runnable,
} }
Art.stopMusic(); Art.stopMusic();
//}
} }
private void drawString(Graphics g, String text, int x, int y, int c) { private void drawString(Graphics g, String text, int x, int y, int c) {

View File

@@ -1137,46 +1137,52 @@ public class PCGLevel extends Level {
LevelComponent.TYPE lctype = lcomp.getType(); LevelComponent.TYPE lctype = lcomp.getType();
System.out.println("Building for: " + lcomp); System.out.println("Building for: " + lcomp);
while (length < Math.min(width - 64, lcomp.getEnd())) { while (length < Math.min(width - 64, lcomp.getEnd())) {
int lcLength;
switch (lctype) { switch (lctype) {
case FLAT_LO: case FLAT_LO:
case FLAT_HI: case FLAT_HI:
length += buildStraight(length, lcomp.getEnd(), true); lcLength = buildStraight(length, lcomp.getEnd(), true);
break; break;
case PIPE_JUMP: case PIPE_JUMP:
length += buildPipeJump(length, width - 64 - length); lcLength = buildPipeJump(length, width - 64 - length);
break; break;
case PLATFORM_JUMP: case PLATFORM_JUMP:
length += buildPlatformJump(length, width - 64 - length); lcLength = buildPlatformJump(length, width - 64 - length);
break; break;
case MAZE: case MAZE:
length += buildMaze(length, width - 64 - length); lcLength = buildMaze(length, width - 64 - length);
break; break;
case BLOCKS: case BLOCKS:
length += buildMaze(length, width - 64 - length); lcLength = buildMaze(length, width - 64 - length);
break; break;
case BOWLING_ALLEY : case BOWLING_ALLEY :
length += buildBowlingAlley(length, width - 64 - length); lcLength = buildBowlingAlley(length, width - 64 - length);
break; break;
case CANNON_LINE : case CANNON_LINE :
length += buildCannonLine(length, width - 64 - length); lcLength = buildCannonLine(length, width - 64 - length);
break; break;
case COIN_DIVE : case COIN_DIVE :
length += buildCoinDive(length, width - 64 - length); lcLength = buildCoinDive(length, width - 64 - length);
break; break;
case LEMMING_TRAP : case LEMMING_TRAP :
length += buildLemmingTrap(length, width - 64 - length); lcLength = buildLemmingTrap(length, width - 64 - length);
break; break;
case POWER_UP : case POWER_UP :
length += buildFreebie(length, width - 64 - length); lcLength = buildFreebie(length, width - 64 - length);
break; break;
case SINGLE_PIT : case SINGLE_PIT :
length += buildSinglePit(length, width - 64 - length); lcLength = buildSinglePit(length, width - 64 - length);
break; break;
default: default:
lcLength = 0;
System.out System.out
.println("Cannot build level segment for unrecognized LevelComponent type: " .println("Cannot build level segment for unrecognized LevelComponent type: "
+ type); + type);
} }
if (lcLength == 0) {
lcLength = buildStraight(length, lcomp.getEnd(), true);
}
length += lcLength;
} }
} }
} }
@@ -1247,8 +1253,7 @@ public class PCGLevel extends Level {
playerMetrics, dataRecorder); playerMetrics, dataRecorder);
System.out.println("PlayerProfile: " + profile); System.out.println("PlayerProfile: " + profile);
LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype( LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype(profile);
playerMetrics, dataRecorder);
System.out.println("LevelArchetype: " + archetype); System.out.println("LevelArchetype: " + archetype);
System.out.println("Creating level grammar"); System.out.println("Creating level grammar");

View File

@@ -7,7 +7,11 @@ import dk.itu.mario.MarioInterface.LevelGenerator;
import dk.itu.mario.MarioInterface.LevelInterface; import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.DataRecorder; import dk.itu.mario.engine.DataRecorder;
import dk.itu.mario.level.Level; import dk.itu.mario.level.Level;
import dk.itu.mario.level.LevelArchetype;
import dk.itu.mario.level.PCGLevel; 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 public class PCGLevelGenerator implements
LevelGenerator { LevelGenerator {
@@ -20,7 +24,21 @@ public class PCGLevelGenerator implements
@Override @Override
public int generateLevelType(GamePlay playerMetrics, DataRecorder dataRecorder) { public int generateLevelType(GamePlay playerMetrics, DataRecorder dataRecorder) {
System.out.println("Generating level type based on playerMetrics, dataRecorder: TYPE_OVERGROUND"); 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 @Override

View File

@@ -1,13 +1,48 @@
package dk.itu.mario.level.matcher; 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;
import dk.itu.mario.level.LevelArchetype.TYPE; import dk.itu.mario.level.LevelArchetype.TYPE;
import dk.itu.mario.level.PlayerProfile;
public class ArchetypeMatcher { public class ArchetypeMatcher {
public static LevelArchetype getMatchingArchetype(GamePlay playerMetrics, DataRecorder detailedInfo) { public static LevelArchetype getMatchingArchetype(
System.out.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); PlayerProfile playerProfile) {
return new LevelArchetype(TYPE.OVERGROUND,1); 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));
} }
} }

View File

@@ -9,12 +9,14 @@ import dk.itu.mario.level.PlayerProfile;
import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL; import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL;
public class ProfileMatcher { public class ProfileMatcher {
public enum SKILL { BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP } public enum SKILL {
BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP
private static Map<PlayerProfile.TYPE, Map<SKILL,Integer>> referenceMetrics = new HashMap<PlayerProfile.TYPE, Map<SKILL,Integer>>(); }
private static Map<PlayerProfile.TYPE, Map<SKILL, Integer>> referenceMetrics = new HashMap<PlayerProfile.TYPE, Map<SKILL, Integer>>();
static { static {
Map<SKILL,Integer> canonicalVector = new HashMap<SKILL,Integer>(); Map<SKILL, Integer> canonicalVector = new HashMap<SKILL, Integer>();
canonicalVector.put(SKILL.BUMP, 100); canonicalVector.put(SKILL.BUMP, 100);
canonicalVector.put(SKILL.COLLECT, 50); canonicalVector.put(SKILL.COLLECT, 50);
canonicalVector.put(SKILL.JUMP, 50); canonicalVector.put(SKILL.JUMP, 50);
@@ -22,17 +24,17 @@ public class ProfileMatcher {
canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.SHOOT, 0);
canonicalVector.put(SKILL.STOMP, 0); canonicalVector.put(SKILL.STOMP, 0);
referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector); referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector);
canonicalVector = new HashMap<SKILL,Integer>(); canonicalVector = new HashMap<SKILL, Integer>();
canonicalVector.put(SKILL.BUMP, 50); canonicalVector.put(SKILL.BUMP, 50);
canonicalVector.put(SKILL.COLLECT, 100); canonicalVector.put(SKILL.COLLECT, 100);
canonicalVector.put(SKILL.JUMP, 50); canonicalVector.put(SKILL.JUMP, 50);
canonicalVector.put(SKILL.RUN, 0); canonicalVector.put(SKILL.RUN, 0);
canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.SHOOT, 0);
canonicalVector.put(SKILL.STOMP, 0); canonicalVector.put(SKILL.STOMP, 0);
referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector); referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector);
canonicalVector = new HashMap<SKILL,Integer>(); canonicalVector = new HashMap<SKILL, Integer>();
canonicalVector.put(SKILL.BUMP, 50); canonicalVector.put(SKILL.BUMP, 50);
canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.COLLECT, 0);
canonicalVector.put(SKILL.JUMP, 100); canonicalVector.put(SKILL.JUMP, 100);
@@ -40,17 +42,17 @@ public class ProfileMatcher {
canonicalVector.put(SKILL.SHOOT, 0); canonicalVector.put(SKILL.SHOOT, 0);
canonicalVector.put(SKILL.STOMP, 0); canonicalVector.put(SKILL.STOMP, 0);
referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector); referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector);
canonicalVector = new HashMap<SKILL,Integer>(); canonicalVector = new HashMap<SKILL, Integer>();
canonicalVector.put(SKILL.BUMP, 0); canonicalVector.put(SKILL.BUMP, 0);
canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.COLLECT, 0);
canonicalVector.put(SKILL.JUMP, 25); canonicalVector.put(SKILL.JUMP, 25);
canonicalVector.put(SKILL.RUN, 100); canonicalVector.put(SKILL.RUN, 100);
canonicalVector.put(SKILL.SHOOT, 25); canonicalVector.put(SKILL.SHOOT, 25);
canonicalVector.put(SKILL.STOMP, 0); canonicalVector.put(SKILL.STOMP, 0);
referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector); referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector);
canonicalVector = new HashMap<SKILL,Integer>(); canonicalVector = new HashMap<SKILL, Integer>();
canonicalVector.put(SKILL.BUMP, 0); canonicalVector.put(SKILL.BUMP, 0);
canonicalVector.put(SKILL.COLLECT, 0); canonicalVector.put(SKILL.COLLECT, 0);
canonicalVector.put(SKILL.JUMP, 25); canonicalVector.put(SKILL.JUMP, 25);
@@ -59,73 +61,90 @@ public class ProfileMatcher {
canonicalVector.put(SKILL.STOMP, 0); canonicalVector.put(SKILL.STOMP, 0);
referenceMetrics.put(PlayerProfile.TYPE.SHOOTER, canonicalVector); referenceMetrics.put(PlayerProfile.TYPE.SHOOTER, canonicalVector);
} }
public static PlayerProfile getMatchingProfile(GamePlay playerMetrics, DataRecorder detailedInfo) { public static PlayerProfile getMatchingProfile(GamePlay playerMetrics,
System.out.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); DataRecorder detailedInfo) {
System.out
.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs.");
int bumperScore = 0; int bumperScore = 0;
int collectorScore = 0; int collectorScore = 0;
int jumperScore = 0; int jumperScore = 0;
int runnerScore = 0; int runnerScore = 0;
int shooterScore = 0; int shooterScore = 0;
int stomperScore = 0; int stomperScore = 0;
bumperScore += playerMetrics.percentageBlocksDestroyed * 20; if (playerMetrics == GamePlay.DEFAULT_PROFILE) {
bumperScore += playerMetrics.percentageCoinBlocksDestroyed * 20; bumperScore = 50;
bumperScore += playerMetrics.percentageEmptyBlockesDestroyed * 20; collectorScore = 50;
bumperScore += playerMetrics.percentagePowerBlockDestroyed * 20; jumperScore = 50;
bumperScore += 4 * Math.min(5, playerMetrics.kickedShells); runnerScore = 50;
shooterScore = 50;
collectorScore += playerMetrics.coinsCollected / 35.0; stomperScore = 50;
} else {
jumperScore = Math.min(100, playerMetrics.jumpsNumber); bumperScore += playerMetrics.percentageBlocksDestroyed * 20;
bumperScore += playerMetrics.percentageCoinBlocksDestroyed * 20;
runnerScore = playerMetrics.timeRunningRight / playerMetrics.totalTime; bumperScore += playerMetrics.percentageEmptyBlockesDestroyed * 20;
bumperScore += playerMetrics.percentagePowerBlockDestroyed * 20;
shooterScore += 10 * Math.min(10, playerMetrics.enemyKillByFire / 10.0); bumperScore += 4 * Math.min(5, playerMetrics.kickedShells);
stomperScore += Math.min(100, 2 * (playerMetrics.GoombasKilled + playerMetrics.GreenTurtlesKilled + playerMetrics.RedTurtlesKilled)); 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(bumperScore);
clampToPercentRange(collectorScore); clampToPercentRange(collectorScore);
clampToPercentRange(jumperScore); clampToPercentRange(jumperScore);
clampToPercentRange(runnerScore); clampToPercentRange(runnerScore);
clampToPercentRange(shooterScore); clampToPercentRange(shooterScore);
clampToPercentRange(stomperScore); clampToPercentRange(stomperScore);
System.out.println("bumperScore: " + bumperScore); System.out.println("bumperScore: " + bumperScore);
System.out.println("collectorScore: " + collectorScore); System.out.println("collectorScore: " + collectorScore);
System.out.println("jumperScore: " + jumperScore); System.out.println("jumperScore: " + jumperScore);
System.out.println("shooterScore: " + shooterScore); System.out.println("shooterScore: " + shooterScore);
System.out.println("stomperScore: " + stomperScore); System.out.println("stomperScore: " + stomperScore);
Map<SKILL,Integer> playerProfileVector = new HashMap<SKILL,Integer>(); Map<SKILL, Integer> playerProfileVector = new HashMap<SKILL, Integer>();
playerProfileVector.put(SKILL.BUMP, bumperScore); playerProfileVector.put(SKILL.BUMP, bumperScore);
playerProfileVector.put(SKILL.COLLECT, collectorScore); playerProfileVector.put(SKILL.COLLECT, collectorScore);
playerProfileVector.put(SKILL.JUMP, jumperScore); playerProfileVector.put(SKILL.JUMP, jumperScore);
playerProfileVector.put(SKILL.RUN, runnerScore); playerProfileVector.put(SKILL.RUN, runnerScore);
playerProfileVector.put(SKILL.SHOOT, shooterScore); playerProfileVector.put(SKILL.SHOOT, shooterScore);
playerProfileVector.put(SKILL.STOMP, stomperScore); playerProfileVector.put(SKILL.STOMP, stomperScore);
double minDist = Double.MAX_VALUE; double minDist = Double.MAX_VALUE;
PlayerProfile.TYPE closestMatch = PlayerProfile.TYPE.RUNNER; PlayerProfile.TYPE closestMatch = PlayerProfile.TYPE.RUNNER;
for (PlayerProfile.TYPE type : PlayerProfile.TYPE.values()) { for (PlayerProfile.TYPE type : PlayerProfile.TYPE.values()) {
Map<SKILL,Integer> canonicalVector = ProfileMatcher.referenceMetrics.get(type); Map<SKILL, Integer> canonicalVector = ProfileMatcher.referenceMetrics
.get(type);
double distance = 0.0; double distance = 0.0;
for (SKILL skill : SKILL.values()) { 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) { if (distance < minDist) {
minDist = distance; minDist = distance;
closestMatch = type; closestMatch = type;
} }
} }
SKILL_LEVEL skillLevel; SKILL_LEVEL skillLevel;
int keyScore = getKeyScore(playerProfileVector, closestMatch); int keyScore = getKeyScore(playerProfileVector, closestMatch);
if (keyScore <= 20) { if (keyScore <= 20) {
skillLevel = SKILL_LEVEL.NOVICE; skillLevel = SKILL_LEVEL.NOVICE;
} else if (keyScore <= 40) { } else if (keyScore <= 40) {
@@ -137,29 +156,32 @@ public class ProfileMatcher {
} else { } else {
skillLevel = SKILL_LEVEL.EXPERT; 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); return new PlayerProfile(skillLevel, closestMatch, playerProfileVector);
} }
private static int getKeyScore(Map<SKILL, Integer> playerProfileVector, PlayerProfile.TYPE playerProfileType) { private static int getKeyScore(Map<SKILL, Integer> playerProfileVector,
PlayerProfile.TYPE playerProfileType) {
switch (playerProfileType) { switch (playerProfileType) {
case BUMPER : case BUMPER:
return playerProfileVector.get(SKILL.BUMP); return playerProfileVector.get(SKILL.BUMP);
case COLLECTOR : case COLLECTOR:
return playerProfileVector.get(SKILL.COLLECT); return playerProfileVector.get(SKILL.COLLECT);
case JUMPER : case JUMPER:
return playerProfileVector.get(SKILL.JUMP); return playerProfileVector.get(SKILL.JUMP);
case RUNNER : case RUNNER:
return playerProfileVector.get(SKILL.RUN); return playerProfileVector.get(SKILL.RUN);
case SHOOTER : case SHOOTER:
return playerProfileVector.get(SKILL.SHOOT); return playerProfileVector.get(SKILL.SHOOT);
default : default:
return 0; return 0;
} }
} }
private static int clampToPercentRange(int value) { private static int clampToPercentRange(int value) {
if (value < 0) { if (value < 0) {
return 0; return 0;

View File

@@ -66,7 +66,7 @@ public class LevelSceneCustom extends LevelScene {
int levelType = clg.generateLevelType(gp,dataRecorder); int levelType = clg.generateLevelType(gp,dataRecorder);
currentLevel = (Level) clg.generateLevel(gp,dataRecorder); currentLevel = (Level) clg.generateLevel(gp,dataRecorder);
try { try {
level = currentLevel.clone(); level = currentLevel.clone();
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {