Compare commits
15 Commits
952ccdc23b
...
0bb5d3fa65
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bb5d3fa65 | |||
|
|
a2a6df8aa6 | ||
|
|
b57a8da512 | ||
|
|
cc15541e89 | ||
|
|
54bf5937f0 | ||
|
|
6ae266ee89 | ||
|
|
3c0673febf | ||
|
|
f1a7c2b4ce | ||
|
|
f03577a822 | ||
|
|
aa5d9099f1 | ||
|
|
fdedf70546 | ||
|
|
d5b8e0b85e | ||
|
|
18643d17e4 | ||
|
|
0619f95f33 | ||
|
|
992f2eef46 |
12
README.TXT
Normal file
12
README.TXT
Normal 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>.
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<target name="compile" depends="copy-resources">
|
||||
<!-- Compile the java code from ${src} into ${build} -->
|
||||
<javac srcdir="${src}" destdir="${build}" classpathref="build.classpath" debug="true" source="1.6" target="1.6"/>
|
||||
<javac srcdir="${src}" destdir="${build}" classpathref="build.classpath" debug="true" source="1.8" target="1.8"/>
|
||||
</target>
|
||||
|
||||
<target name="compile-test" depends="compile">
|
||||
|
||||
@@ -15,3 +15,19 @@ rule "BeginnerJumper"
|
||||
System.out.println("PlayerProfile indicates Beginner (or better) Jumper. Pipe challenge enabled!"); // consequence
|
||||
playerProfile.setEnabled(LevelComponent.TYPE.PIPE_JUMP);
|
||||
end
|
||||
|
||||
rule "NoviceRunner"
|
||||
when
|
||||
playerProfile : PlayerProfile( runSkill <= 20 ) // condition
|
||||
then
|
||||
System.out.println("PlayerProfile indicates NoviceRunner. Disabling Maze challenge."); // consequence
|
||||
playerProfile.setDisabled(LevelComponent.TYPE.MAZE);
|
||||
end
|
||||
|
||||
rule "BeginnerRunner"
|
||||
when
|
||||
playerProfile : PlayerProfile( runSkill > 20 ) // condition
|
||||
then
|
||||
System.out.println("PlayerProfile indicates Beginner (or better) Runner. Maze challenge enabled!"); // consequence
|
||||
playerProfile.setEnabled(LevelComponent.TYPE.MAZE);
|
||||
end
|
||||
@@ -9,6 +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 boolean isDefault = false;
|
||||
|
||||
public int completionTime; // counts only the current run on the level,
|
||||
// excluding death games
|
||||
@@ -94,8 +96,11 @@ public class GamePlay implements Serializable {
|
||||
gp = (GamePlay) in.readObject();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
//e.printStackTrace();
|
||||
System.out.println("Unable to read from GamePlay file: " + fileName + ", initializing a new GamePlay instance.");
|
||||
// e.printStackTrace();
|
||||
System.out.println("Unable to read from GamePlay file: " + fileName
|
||||
+ ", initializing a new GamePlay instance.");
|
||||
gp = GamePlay.DEFAULT_PROFILE;
|
||||
gp.isDefault = true;
|
||||
}
|
||||
return gp;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class MarioComponent extends JComponent implements Runnable,
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
//while (true) {
|
||||
graphicsConfiguration = getGraphicsConfiguration();
|
||||
|
||||
Art.init(graphicsConfiguration, 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) {
|
||||
|
||||
@@ -8,7 +8,23 @@ public class FitnessEvaluator {
|
||||
public static boolean isFit(LevelParseTree parseTree, PlayerProfile playerProfile, LevelArchetype levelArchetype) {
|
||||
System.out.println("Evaluating LevelParseTree for fitness");
|
||||
List<LevelComponent> levelTemplate = parseTree.getLevelTemplate();
|
||||
//a good level has 8-16 components, plus some additional complexity depending on the player's skill level
|
||||
return levelTemplate.size() > 9 && levelTemplate.size() < 25;
|
||||
//a good level has 8-24 components, plus some additional complexity depending on the player's skill level
|
||||
|
||||
if (levelTemplate.size() < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (levelTemplate.size() > 24) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (LevelComponent lc : levelTemplate) {
|
||||
if (!playerProfile.isEnabled(lc.getType())) {
|
||||
System.out.println("Level is not fit: " + lc.getType() + " is not enabled.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ public class PCGLevel extends Level {
|
||||
|
||||
setSpriteTemplate(xo + 2, floor - 1, new SpriteTemplate(
|
||||
SpriteTemplate.RED_TURTLE, false));
|
||||
ENEMIES++;
|
||||
|
||||
for (int x = 5; x < 26; x++) {
|
||||
for (int y = floor - 3; y < height; y++) {
|
||||
@@ -216,30 +217,37 @@ public class PCGLevel extends Level {
|
||||
&& x >= 11 && x <= 23) {
|
||||
if (x == 15) {
|
||||
setBlock(xo + x, floor - 6, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 16) {
|
||||
setBlock(xo + x, floor - 7, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 18) {
|
||||
setBlock(xo + x, floor - 8, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 19) {
|
||||
setBlock(xo + x, floor - 8, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 20) {
|
||||
setBlock(xo + x, floor - 8, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 22) {
|
||||
setBlock(xo + x, floor - 7, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (x == 23) {
|
||||
setBlock(xo + x, floor - 6, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,21 +255,30 @@ public class PCGLevel extends Level {
|
||||
&& reward == PlayerProfile.ChallengeRewardType.COIN
|
||||
&& x >= 10 && x <= 20) {
|
||||
setBlock(xo + x, floor - 6, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
||||
&& x >= 13 && x <= 17) {
|
||||
setBlock(
|
||||
xo + x,
|
||||
floor - 6,
|
||||
(random.nextDouble() <= probability
|
||||
byte tile = (random.nextDouble() <= probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP)) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
|
||||
setBlock(xo + x, floor - 6, tile);
|
||||
|
||||
if (tile == Level.BLOCK_COIN) {
|
||||
BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
BLOCKS_POWER++;
|
||||
}
|
||||
}
|
||||
|
||||
if (x >= 26 - numEnemies) {
|
||||
setSpriteTemplate(xo + x, floor - 4, new SpriteTemplate(
|
||||
enemyType, false));
|
||||
ENEMIES++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,16 +329,22 @@ public class PCGLevel extends Level {
|
||||
|
||||
if (x > 2 && x < 6) {
|
||||
setBlock(xo + x, floor - 3, Level.BLOCK_EMPTY);
|
||||
this.BLOCKS_EMPTY++;
|
||||
} else if (x > 2 && x < 11) {
|
||||
setBlock(xo + x, floor - 5, Level.BLOCK_EMPTY);
|
||||
this.BLOCKS_EMPTY++;
|
||||
} else if (x == 11 || x == 12) {
|
||||
setBlock(xo + x, floor - 6, Level.COIN);
|
||||
COINS++;
|
||||
} else if (x == 13) {
|
||||
setBlock(xo + x, floor - 5, Level.COIN);
|
||||
COINS++;
|
||||
} else if (x == 14) {
|
||||
setBlock(xo + x, floor - 4, Level.COIN);
|
||||
COINS++;
|
||||
} else if (x == 15) {
|
||||
setBlock(xo + x, floor - 3, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,11 +372,14 @@ public class PCGLevel extends Level {
|
||||
|
||||
setSpriteTemplate(xo + 6, floor - 1, new SpriteTemplate(
|
||||
SpriteTemplate.GREEN_TURTLE, false));
|
||||
|
||||
this.BLOCKS_EMPTY++;
|
||||
this.BLOCKS_POWER++;
|
||||
this.ENEMIES++;
|
||||
}
|
||||
|
||||
else {
|
||||
int powerupLoc = random.nextInt(5);
|
||||
|
||||
setBlock(xo + 2, floor - 3,
|
||||
powerupLoc == 0 ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
@@ -369,6 +395,9 @@ public class PCGLevel extends Level {
|
||||
setBlock(xo + 6, floor - 3,
|
||||
powerupLoc == 4 ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
|
||||
this.BLOCKS_COINS += 4;
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
return 9;
|
||||
@@ -387,12 +416,18 @@ public class PCGLevel extends Level {
|
||||
setBlock(xo, floor - 1, Level.BLOCK_EMPTY);
|
||||
setBlock(xo + 4, floor - 1, Level.BLOCK_POWERUP);
|
||||
|
||||
this.BLOCKS_EMPTY++;
|
||||
this.BLOCKS_POWER++;
|
||||
|
||||
setSpriteTemplate(xo + 3, floor - 1, new SpriteTemplate(
|
||||
SpriteTemplate.GREEN_TURTLE, false));
|
||||
|
||||
this.ENEMIES++;
|
||||
}
|
||||
|
||||
else {
|
||||
setBlock(xo + 2, floor - 3, Level.BLOCK_POWERUP);
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
return 5;
|
||||
@@ -427,21 +462,30 @@ public class PCGLevel extends Level {
|
||||
if (x > 6 && x % 2 == 0) {
|
||||
setSpriteTemplate(xo + x, floor - 5, new SpriteTemplate(
|
||||
enemyType, flying));
|
||||
ENEMIES++;
|
||||
}
|
||||
|
||||
if (reward == PlayerProfile.ChallengeRewardType.COIN && x >= 7
|
||||
&& x <= 16) {
|
||||
setBlock(xo + x, floor - 7, Level.COIN);
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
||||
&& x >= 10 && x <= 13) {
|
||||
setBlock(
|
||||
xo + x,
|
||||
floor - 7,
|
||||
random.nextDouble() < probability
|
||||
byte tile = random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
|
||||
setBlock(xo + x, floor - 7, tile);
|
||||
|
||||
if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 18;
|
||||
@@ -589,6 +633,7 @@ public class PCGLevel extends Level {
|
||||
setSpriteTemplate(xo + soFar + x, this.height
|
||||
- heightMod, new SpriteTemplate(enemyType,
|
||||
false));
|
||||
ENEMIES++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,6 +697,7 @@ public class PCGLevel extends Level {
|
||||
setSpriteTemplate(xo + length, height - localHeight,
|
||||
new SpriteTemplate(SpriteTemplate.JUMP_FLOWER,
|
||||
false));
|
||||
ENEMIES++;
|
||||
}
|
||||
|
||||
length += 2;
|
||||
@@ -821,11 +867,13 @@ public class PCGLevel extends Level {
|
||||
enemyType,
|
||||
(enemyType == SpriteTemplate.RED_TURTLE && shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY))
|
||||
|| enemyType != SpriteTemplate.RED_TURTLE));
|
||||
ENEMIES++;
|
||||
}
|
||||
|
||||
reward = shouldAddReward();
|
||||
if (reward == PlayerProfile.ChallengeRewardType.COIN
|
||||
&& hold != LevelComponent.PlatformLevel.TOP) {
|
||||
|
||||
setBlock(xo + length, this.height - heightMod - 3,
|
||||
Level.COIN);
|
||||
setBlock(xo + length + 1, this.height - heightMod - 3,
|
||||
@@ -834,22 +882,38 @@ public class PCGLevel extends Level {
|
||||
Level.COIN);
|
||||
setBlock(xo + length + 3, this.height - heightMod - 3,
|
||||
Level.COIN);
|
||||
|
||||
COINS += 4;
|
||||
}
|
||||
|
||||
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
||||
&& hold != LevelComponent.PlatformLevel.TOP) {
|
||||
setBlock(
|
||||
xo + length + 1,
|
||||
this.height - heightMod - 3,
|
||||
random.nextDouble() < probability
|
||||
|
||||
byte tile = random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + length + 2,
|
||||
this.height - heightMod - 3,
|
||||
random.nextDouble() < probability
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + length + 1, this.height - heightMod - 3, tile);
|
||||
|
||||
if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + length + 2, this.height - heightMod - 3, tile);
|
||||
|
||||
if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
}
|
||||
|
||||
length += 4 + gapLength;
|
||||
@@ -953,10 +1017,12 @@ public class PCGLevel extends Level {
|
||||
new SpriteTemplate(
|
||||
enemyType,
|
||||
shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)));
|
||||
ENEMIES++;
|
||||
}
|
||||
}
|
||||
|
||||
if (reward != null) {
|
||||
byte tile;
|
||||
if (length >= 13) {
|
||||
setBlock(xo + (length - 11), floor - 3, Level.COIN);
|
||||
setBlock(xo + (length - 10), floor - 4, Level.COIN);
|
||||
@@ -965,48 +1031,104 @@ public class PCGLevel extends Level {
|
||||
setBlock(xo + (length - 6), floor - 5, Level.COIN);
|
||||
setBlock(xo + (length - 4), floor - 4, Level.COIN);
|
||||
setBlock(xo + (length - 3), floor - 3, Level.COIN);
|
||||
|
||||
COINS += 7;
|
||||
}
|
||||
|
||||
else if (length >= 7) {
|
||||
int start = length / 2;
|
||||
boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
|
||||
|
||||
setBlock(
|
||||
xo + (start - 2),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + (start - 1),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start - 2), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + start,
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start - 1), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + (start + 1),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + start, floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start + 1), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
if (length % 2 != 0) {
|
||||
setBlock(
|
||||
xo + (start + 2),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start + 2), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,27 +1136,59 @@ public class PCGLevel extends Level {
|
||||
int start = length / 2;
|
||||
boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
|
||||
|
||||
setBlock(
|
||||
xo + (start - 1),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + start,
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start - 1), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
setBlock(
|
||||
xo + (start + 1),
|
||||
floor - 3,
|
||||
coins ? Level.COIN
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + start, floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
|
||||
tile = coins ? Level.COIN
|
||||
: random.nextDouble() < probability
|
||||
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
||||
: Level.BLOCK_COIN);
|
||||
: Level.BLOCK_COIN;
|
||||
setBlock(xo + (start + 1), floor - 3, tile);
|
||||
|
||||
if (coins) {
|
||||
COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_COIN) {
|
||||
this.BLOCKS_COINS++;
|
||||
}
|
||||
|
||||
else if (tile == Level.BLOCK_POWERUP) {
|
||||
this.BLOCKS_POWER++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,7 +1265,7 @@ public class PCGLevel extends Level {
|
||||
System.out.println("Generating level for component list: ");
|
||||
LevelParseTree parseTree = grammar.generateRandomTree(seed, width);
|
||||
|
||||
int MAX_REGENS = 10;
|
||||
int MAX_REGENS = 30;
|
||||
int nRegens = 0;
|
||||
while (!FitnessEvaluator.isFit(parseTree, profile, archetype)
|
||||
&& nRegens < MAX_REGENS) {
|
||||
@@ -1121,58 +1275,70 @@ public class PCGLevel extends Level {
|
||||
nRegens++;
|
||||
}
|
||||
|
||||
List<LevelComponent> levelTemplate = parseTree.getLevelTemplate();
|
||||
|
||||
if (nRegens == MAX_REGENS) {
|
||||
System.out.println("Failed to generate a fit level after "
|
||||
+ nRegens + " attempts. Proceeding with unfit level.");
|
||||
} else {
|
||||
System.out.println("Found fit level:");
|
||||
for (LevelComponent lc : levelTemplate) {
|
||||
System.out.println(lc);
|
||||
}
|
||||
}
|
||||
|
||||
List<LevelComponent> levelTemplate = parseTree.getLevelTemplate();
|
||||
|
||||
for (LevelComponent lcomp : levelTemplate) {
|
||||
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);
|
||||
case BOWLING_ALLEY:
|
||||
lcLength = buildBowlingAlley(length, width - 64
|
||||
- length);
|
||||
break;
|
||||
case CANNON_LINE :
|
||||
length += buildCannonLine(length, width - 64 - length);
|
||||
case CANNON_LINE:
|
||||
lcLength = buildCannonLine(length, width - 64 - length);
|
||||
break;
|
||||
case COIN_DIVE :
|
||||
length += buildCoinDive(length, width - 64 - length);
|
||||
case COIN_DIVE:
|
||||
lcLength = buildCoinDive(length, width - 64 - length);
|
||||
break;
|
||||
case LEMMING_TRAP :
|
||||
length += buildLemmingTrap(length, width - 64 - length);
|
||||
case LEMMING_TRAP:
|
||||
lcLength = buildLemmingTrap(length, width - 64 - length);
|
||||
break;
|
||||
case POWER_UP :
|
||||
length += buildFreebie(length, width - 64 - length);
|
||||
//length += buildStraight(length, lcomp.getEnd(), true);
|
||||
case POWER_UP:
|
||||
lcLength = buildFreebie(length, width - 64 - length);
|
||||
break;
|
||||
case SINGLE_PIT :
|
||||
length += buildSinglePit(length, width - 64 - length);
|
||||
case SINGLE_PIT:
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1243,8 +1409,8 @@ 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");
|
||||
|
||||
@@ -52,33 +52,31 @@ public class PlayerProfile {
|
||||
return skillLevel;
|
||||
}
|
||||
|
||||
public int getBumpSkill() {
|
||||
return skillVector.get(SKILL.BUMP);
|
||||
}
|
||||
|
||||
public int getCollectSkill() {
|
||||
return skillVector.get(SKILL.COLLECT);
|
||||
}
|
||||
|
||||
public int getJumpSkill() {
|
||||
switch (type) {
|
||||
case JUMPER:
|
||||
switch (skillLevel) {
|
||||
case NOVICE:
|
||||
return 20;
|
||||
case BEGINNER:
|
||||
return 40;
|
||||
case COMPETENT:
|
||||
return 60;
|
||||
case PROFICIENT:
|
||||
return 80;
|
||||
case EXPERT:
|
||||
return 100;
|
||||
default:
|
||||
return 0;
|
||||
return skillVector.get(SKILL.JUMP);
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
|
||||
public int getRunSkill() {
|
||||
return skillVector.get(SKILL.RUN);
|
||||
}
|
||||
|
||||
public int getShootSkill() {
|
||||
return skillVector.get(SKILL.SHOOT);
|
||||
}
|
||||
|
||||
public int getStompSkill() {
|
||||
return skillVector.get(SKILL.STOMP);
|
||||
}
|
||||
|
||||
public double getProbability(ChallengeRewardType crt) {
|
||||
// if (!isEnabled(type)) {
|
||||
// return 0.0;
|
||||
// }
|
||||
|
||||
switch (crt) {
|
||||
case GAP:
|
||||
return Math.min(.5, (.5) * (skillVector.get(SKILL.JUMP) / 100.0)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ public class ProfileMatcher {
|
||||
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.
|
||||
@@ -82,11 +81,27 @@ public class ProfileMatcher {
|
||||
TYPE type = null;
|
||||
HashMap<SKILL, Integer> skillVector = new HashMap<SKILL, Integer>();
|
||||
|
||||
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.percentageBlocksDestroyed);
|
||||
skillHolder = (int) (100 * ((playerMetrics.percentageEmptyBlockesDestroyed
|
||||
+ playerMetrics.percentageCoinBlocksDestroyed + playerMetrics.percentagePowerBlockDestroyed) / 3));
|
||||
skillVector.put(SKILL.BUMP, new Integer(skillHolder));
|
||||
|
||||
// Get collect skills.
|
||||
@@ -114,16 +129,15 @@ public class ProfileMatcher {
|
||||
+ 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);
|
||||
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 / 75.0))) / 2));
|
||||
: 1 - (playerMetrics.timesPressedRun / 200))) / 2));
|
||||
skillVector.put(SKILL.RUN, new Integer(skillHolder));
|
||||
|
||||
// Get shoot skills.
|
||||
@@ -167,13 +181,29 @@ public class ProfileMatcher {
|
||||
|
||||
// Determine rote level.
|
||||
skillHolder = 0;
|
||||
Entry<SKILL, Integer> hold;
|
||||
for (Iterator<Entry<SKILL, Integer>> i = skillVector.entrySet()
|
||||
.iterator(); i.hasNext();) {
|
||||
skillHolder += i.next().getValue().intValue();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
\relax
|
||||
\citation{zimmerman}
|
||||
\citation{bartle}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}}
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Adaptive Mario in action}}{1}}
|
||||
\newlabel{img:mario-ex}{{1}{1}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {2}Related Works}{1}}
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Flow State: Game Difficulty vs. Player Skill}}{2}}
|
||||
\newlabel{img:flow-state}{{2}{2}}
|
||||
\bibstyle{unsrt}
|
||||
\bibdata{p3refs}
|
||||
\bibcite{bartle}{1}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {3}Approach}{2}}
|
||||
\citation{forgy}
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces A Simple Overland Level Grammar}}{3}}
|
||||
\newlabel{img:stochastic-grammar}{{3}{3}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {4}Evaluation}{4}}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {5}Conclusion}{4}}
|
||||
\bibstyle{unsrt}
|
||||
\bibdata{p3refs}
|
||||
\bibcite{zimmerman}{1}
|
||||
\bibcite{bartle}{2}
|
||||
\bibcite{forgy}{3}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
\begin{thebibliography}{1}
|
||||
|
||||
\bibitem{zimmerman}
|
||||
K.~Salen and E.~Zimmerman.
|
||||
\newblock {\em Rules of Play}.
|
||||
\newblock The MIT Press, 2004.
|
||||
|
||||
\bibitem{bartle}
|
||||
Richard Bartle.
|
||||
\newblock Hearts, {Clubs}, {Diamonds}, {Spades}: {Players Who} suit {MUDs}.
|
||||
\newblock Hearts, clubs, diamonds, spades: Players who suit {MUDs}.
|
||||
\newblock http://aigamedev.com/open/interviews/mario-ai/, 1996.
|
||||
\newblock [Online; accessed 18-March-2012].
|
||||
|
||||
\bibitem{forgy}
|
||||
C.~Forgy.
|
||||
\newblock Rete: A fast algorithm for the many pattern/many object pattern match
|
||||
problem.
|
||||
\newblock {\em Artificial Intelligence}, 19:17--37, 1982.
|
||||
|
||||
\end{thebibliography}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (MiKTeX 2.9) (preloaded format=pdflatex 2012.1.11) 18 MAR 2012 15:01
|
||||
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (MiKTeX 2.9) (preloaded format=pdflatex 2012.1.11) 18 MAR 2012 23:05
|
||||
entering extended mode
|
||||
**D:/workspace/cs8803p3/writeup/CS8803_P3.tex
|
||||
(D:/workspace/cs8803p3/writeup/CS8803_P3.tex
|
||||
@@ -170,15 +170,23 @@ LaTeX Font Info: External font `cmex10' loaded for size
|
||||
(Font) <8> on input line 16.
|
||||
LaTeX Font Info: External font `cmex10' loaded for size
|
||||
(Font) <6> on input line 16.
|
||||
<mario_example.png, id=1, 642.4pt x 481.8pt>
|
||||
|
||||
|
||||
LaTeX Warning: Reference `mario-ex' on page 1 undefined on input line 23.
|
||||
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
<mario_example.png, id=1, 642.4pt x 481.8pt>
|
||||
File: mario_example.png Graphic file (type png)
|
||||
<use mario_example.png>
|
||||
Package pdftex.def Info: mario_example.png used on input line 23.
|
||||
Package pdftex.def Info: mario_example.png used on input line 27.
|
||||
(pdftex.def) Requested size: 216.80669pt x 162.60416pt.
|
||||
File: mario_example.png Graphic file (type png)
|
||||
|
||||
<use mario_example.png>
|
||||
Package pdftex.def Info: mario_example.png used on input line 24.
|
||||
<mario_underground.png, id=2, 642.4pt x 481.8pt>
|
||||
File: mario_underground.png Graphic file (type png)
|
||||
<use mario_underground.png>
|
||||
Package pdftex.def Info: mario_underground.png used on input line 28.
|
||||
(pdftex.def) Requested size: 216.80669pt x 162.60416pt.
|
||||
Missing character: There is no à in font cmr10!
|
||||
Missing character: There is no ¡ in font cmr10!
|
||||
@@ -186,57 +194,71 @@ Missing character: There is no
|
||||
Missing character: There is no in font cmr10!
|
||||
Missing character: There is no à in font cmr10!
|
||||
Missing character: There is no ¡ in font cmr10!
|
||||
<flow-state.png, id=2, 305.34074pt x 223.43475pt>
|
||||
|
||||
<flow-state.png, id=3, 305.34074pt x 223.43475pt>
|
||||
File: flow-state.png Graphic file (type png)
|
||||
|
||||
<use flow-state.png>
|
||||
Package pdftex.def Info: flow-state.png used on input line 36.
|
||||
(pdftex.def) Requested size: 216.80669pt x 158.65341pt.
|
||||
|
||||
|
||||
LaTeX Warning: `!h' float specifier changed to `!ht'.
|
||||
|
||||
[1
|
||||
<use flow-state.png>
|
||||
Package pdftex.def Info: flow-state.png used on input line 40.
|
||||
(pdftex.def) Requested size: 271.0125pt x 198.33125pt.
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
[1
|
||||
|
||||
{C:/ProgramData/MiKTeX/2.9/pdftex/config/pdftex.map} <D:/workspace/cs8803p3/wri
|
||||
teup/mario_example.png>]
|
||||
teup/mario_example.png> <D:/workspace/cs8803p3/writeup/mario_underground.png (P
|
||||
NG copy)>]
|
||||
LaTeX Font Info: External font `cmex10' loaded for size
|
||||
(Font) <7> on input line 61.
|
||||
(Font) <7> on input line 49.
|
||||
LaTeX Font Info: External font `cmex10' loaded for size
|
||||
(Font) <5> on input line 61.
|
||||
(Font) <5> on input line 49.
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
[2 <D:/workspace/cs8803p3/writeup/flow-state.png>]
|
||||
<StochasticGrammar.png, id=15, 438.438pt x 222.8325pt>
|
||||
<StochasticGrammar.png, id=17, 438.438pt x 222.8325pt>
|
||||
File: StochasticGrammar.png Graphic file (type png)
|
||||
|
||||
<use StochasticGrammar.png>
|
||||
Package pdftex.def Info: StochasticGrammar.png used on input line 83.
|
||||
Package pdftex.def Info: StochasticGrammar.png used on input line 93.
|
||||
(pdftex.def) Requested size: 406.51875pt x 206.62346pt.
|
||||
(D:\workspace\cs8803p3\writeup\CS8803_P3.bbl
|
||||
Underfull \hbox (badness 10000) in paragraph at lines 4--8
|
||||
[]\OT1/cmr/m/n/10 Richard Bar-tle. Hearts, Clubs, Di-a-monds, Spades: Play-ers
|
||||
Who suit MUDs.
|
||||
[]
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
[3 <D:/workspace/cs8803p3/writeup/StochasticGrammar.png>]
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
Missing character: There is no â in font cmr10!
|
||||
Missing character: There is no € in font cmr10!
|
||||
Missing character: There is no ™ in font cmr10!
|
||||
[4] (D:\workspace\cs8803p3\writeup\CS8803_P3.bbl) [5]
|
||||
(D:\workspace\cs8803p3\writeup\CS8803_P3.aux)
|
||||
|
||||
[3 <D:/workspace/cs8803p3/writeup/StochasticGrammar.png>]) [4]
|
||||
(D:\workspace\cs8803p3\writeup\CS8803_P3.aux) )
|
||||
LaTeX Warning: There were undefined references.
|
||||
|
||||
)
|
||||
Here is how much of TeX's memory you used:
|
||||
1905 strings out of 494045
|
||||
26059 string characters out of 3145969
|
||||
87712 words of memory out of 3000000
|
||||
5212 multiletter control sequences out of 15000+200000
|
||||
7458 words of font info for 27 fonts, out of 3000000 for 9000
|
||||
1919 strings out of 494045
|
||||
26282 string characters out of 3145969
|
||||
90718 words of memory out of 3000000
|
||||
5224 multiletter control sequences out of 15000+200000
|
||||
7804 words of font info for 28 fonts, out of 3000000 for 9000
|
||||
715 hyphenation exceptions out of 8191
|
||||
27i,7n,32p,875b,220s stack positions out of 5000i,500n,10000p,200000b,50000s
|
||||
<C:/Program Files (x86)/MiKTeX 2
|
||||
.9/fonts/type1/public/amsfonts/cm/cmbx10.pfb><C:/Program Files (x86)/MiKTeX 2.9
|
||||
/fonts/type1/public/amsfonts/cm/cmbx12.pfb><C:/Program Files (x86)/MiKTeX 2.9/f
|
||||
onts/type1/public/amsfonts/cm/cmr10.pfb><C:/Program Files (x86)/MiKTeX 2.9/font
|
||||
s/type1/public/amsfonts/cm/cmr12.pfb><C:/Program Files (x86)/MiKTeX 2.9/fonts/t
|
||||
ype1/public/amsfonts/cm/cmr17.pfb><C:/Program Files (x86)/MiKTeX 2.9/fonts/type
|
||||
1/public/amsfonts/cm/cmtt10.pfb>
|
||||
Output written on CS8803_P3.pdf (4 pages, 130421 bytes).
|
||||
27i,7n,32p,1715b,220s stack positions out of 5000i,500n,10000p,200000b,50000s
|
||||
<C:/Program Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmbx10.pfb>
|
||||
<C:/Program Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmbx12.pfb><C
|
||||
:/Program Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmr10.pfb><C:/P
|
||||
rogram Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmr12.pfb><C:/Prog
|
||||
ram Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmr17.pfb><C:/Program
|
||||
Files (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmti10.pfb><C:/Program F
|
||||
iles (x86)/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmtt10.pfb>
|
||||
Output written on CS8803_P3.pdf (5 pages, 155888 bytes).
|
||||
PDF statistics:
|
||||
42 PDF objects out of 1000 (max. 8388607)
|
||||
50 PDF objects out of 1000 (max. 8388607)
|
||||
0 named destinations out of 1000 (max. 500000)
|
||||
16 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
21 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -15,66 +15,76 @@
|
||||
|
||||
\maketitle
|
||||
|
||||
\section*{Introduction}
|
||||
The authors of Adaptive Mario use several PCG techniques to develop a simple platform-game engine which adjusts to player preferences and skills to guide the player toward the ideal flow-state. This architecture should ensure that he maximum amount of replayability is delivered, considering the limited multimedia assets and gameplay mechanics of the Infinite Mario engine on which it is based.
|
||||
\section{Introduction}
|
||||
The game mechanics of the Super Mario world are celebrated and well-understood. Indeed, even in this age of super-computer graphics and Hollywood-level production values, few games can match the pure enjoyment delivered by this simple 8-bit Nintendo platform jumper. Here, we endeavor not to change that formula, but to imbue it with a modern, artificial-intelligence driven approach to enhance the game-play experience.
|
||||
|
||||
In creating Adaptive Mario, we use several procedural content generation (PCG) techniques built upon this simple platform-jumper engine. Our algorithms adjust to player preferences and skills, and guide players toward an ideal game-play experience. Our design is intended to ensure that the maximum amount of replay value is delivered, considering the limited multimedia assets and game-play mechanics of the Infinite Mario engine on which it is based.
|
||||
|
||||
Every Mario level has the implicit goal of progressing from left to right until the completion gate appears, all the while keeping Mario safe. A number of more esoteric subgoals also arise, such as destroying enemies, collecting coins, and minimizing time of completion. Our conceptual design goal, however, was a bit different. As shown in Figure \ref{mario-ex} we aim to provide players with different styles of play and skill levels with distinct and customized game-play experience from the moment the level begins. The process of generating a level consists of a number of steps. First, a player profile is drawn based on the player’s performance in an average, non-customized level. That data is fed into our generation engine to guide the creation of subsequent levels. The level generation pipe-line is explained in full detail in \emph{Section 3 Approach}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.4 \textwidth]{mario_example.png}
|
||||
\includegraphics[width=0.4 \textwidth]{mario_example.png}
|
||||
\includegraphics[width=0.4 \textwidth]{mario_underground.png}
|
||||
\caption{Adaptive Mario in action}
|
||||
\label{img:mario-ex}
|
||||
\end{figure}
|
||||
|
||||
As shown in Figure \ref{img:mario-ex}, the conceptual goal is that players with different play styles and skill levels should have a very different gameplay experience from the moment the level begins. Of course, the very first time Adaptive Mario is run in a new environment, a specific level is presented based on an a priori model of an 'average' player.
|
||||
As shown in Figure \ref{img:mario-ex}, the conceptual goal is that players with different play styles and skill levels should have a very different game-play experience from the moment the level begins. The very first time Adaptive Mario is run in a new environment, a random level is presented based on an a priori model of an 'average' player. A high degree of randomness at this point allows the player to freely choose a game-play style rather than being constrained by a prefabricated environment.
|
||||
|
||||
\section*{Related Works}
|
||||
The essential goal of Adaptive Mario is to facilitate flow state by giving the player sufficient challenge and variety without rapidly becoming too difficult. As defined by Mihály Csíkszentmihályi, ``flow'' is the state of total immersion and cocentration in which the player believes he or she is overcoming obstacles by the narrowest of margins. As shown in Figure \ref{img:flow-state}, achieving this state involves a delicate balance between the difficulty of the game and the player's degree of skill. If the game is too hard, the player will become frustrated. On the other hand, most players will become bored if the game is too easy.
|
||||
\section{Related Works}
|
||||
A fundamental concept in the design of Adaptive Mario is \emph{flow}. As defined by Mihály Csíkszentmihályi, according to Salen and Zimmerman \cite{zimmerman}, flow is a state of total immersion and concentration in which the player believes he or she is overcoming obstacles by the narrowest margins. As shown in Figure \ref{img:flow-state}, achieving this state involves a delicate balance between the difficulty of the game and the degree of the player's skill. If the game is too hard, the player will become frustrated. On the other hand, most players will become bored if the game is too easy. Our overarching goal was to facilitate a flow state by giving the player sufficient challenge and variety without rapidly becoming too difficult.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{figure}%[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.4 \textwidth]{flow-state.png}
|
||||
\includegraphics[width=0.5 \textwidth]{flow-state.png}
|
||||
\caption{Flow State: Game Difficulty vs. Player Skill}
|
||||
\label{img:flow-state}
|
||||
\end{figure}
|
||||
|
||||
In order to gauge the requisite level is difficulty, it is first necessary to develop a model of the player's ability. Numerous models of player psychology have been proposted since Richard Bartle's 1996 study of MUD (Multi-User Dungeon) players \cite{bartle}. Some examples are:
|
||||
This theory presents two variables: challenge and skill. Adapting the difficulty of a level is well within our purview as game designers, but the level of difficulty required to generate flow state depends on the player’s skill level. We therefore first developed a model of player psychology and ability. Numerous such models have been proposed since Richard Bartle's 1996 study of MUD (Multi-User Dungeon) players \cite{bartle}, all of which attempt to classify a player into a category or type. In developing our model, we considered some notable examples:
|
||||
|
||||
\begin{description}
|
||||
\item[Richard Bartle's Player Types] Killer, Socializer, Achiever, Explorer
|
||||
\item[Nick Yee's Player Motivations] Relationships, Immersion, Grief, Achievement, Leadership
|
||||
\item[John Radoff's Player Motivations] Immersion, Cooperation, Achievement, Competition
|
||||
\end{description}
|
||||
\begin{table}[ht]
|
||||
\centering
|
||||
\begin{tabular}{ | l | l | }
|
||||
\hline
|
||||
Author & Player Model\\ \hline
|
||||
Richard Bartle & Killer, Socializer, Achiever, Explorer\\ \hline
|
||||
Nick Yee & Relationships, Immersion, Grief, Achievement, Leadership \\ \hline
|
||||
John Radoff & Immersion, Cooperation, Achievement, Competition \\ \hline
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The Adaptive Mario PlayerProfile assigns the player one of five roles, based on past performance: BUMPER, COLLECTOR, JUMPER, RUNNER and SHOOTER. This model is loosely based on Bartle's extended model, with BUMPER corresponding to manipulator, in the sense that the player is observed to manipulate the environment by bumping bricks and throwing shells. This model differs from the examples above primary in that social elements are not evaluated, since Mario is a single-player game.
|
||||
The Adaptive Mario PlayerProfileMatcher class assigns the player one of five roles based on past performance: BUMPER, COLLECTOR, JUMPER, RUNNER or SHOOTER. This model is loosely based on Bartle's extended model, with BUMPER corresponding to Planner, in the sense that the player is observed to interact with the environment by bumping bricks and throwing shells. However, our model necessarily differs somewhat from the above examples, as Adaptive Mario is a single-player game with no social element.
|
||||
|
||||
\section*{Level Generator Design}
|
||||
\section{Approach}
|
||||
The Adaptive Mario level generator functions as a 4-stage pipeline composed of a Profile Matcher, Level Archetype Selector, Level Generator and Challenge Component Creator.
|
||||
|
||||
\subsection*{Profile Matcher}
|
||||
The first step of the level generation process is to evaluate the player's style and skill level. Adaptive Mario uses the output file ``player.txt'' generated by the GamePlay class during the most recent game, if available. In addition, more detailed statistics from the DataRecorder class are preserved as XML and are used by some components of the level generator. The ProfileMatcher scores aspects of gameplay such as number of kills, jumps and coins collected in order to evaluate the player's skill in the areas of running, jumping, collecting, shooting. This vector of skills, rated on a range of 1 - 100, is then compared against a precalculated 'typical' vector for each of the five player types using a simple mean-squared error metric. The player is then given the profile matching the most similar set of skills.
|
||||
|
||||
In addition to assigning a player profile, the ProfileMatcher assesses the player's skill level by comparing specific metric against an ideal score assigning the player one of five skill levels: Novice, Beginner, Competent, Proficient or Expert. These categories are based on the Dreyfus model of skill acquisition with each successive level reperesnting an achievement of 20% of the Profile's key skill maximum score, before penalties. Actions such as dying or making unnecessary jumps negatively impact the skill assessment.
|
||||
The first step of the level generation process is to evaluate the player's style and skill level. Adaptive Mario uses the output file ``player.txt'' generated by the GamePlay class during the most recent game, if available. If no player data is available, as in a first run of the game, a simple, un-weighted level is generated, to assist in collecting baseline player data for future runs. More detailed statistics from the DataRecorder class are preserved as XML and are used by some components of the level generator. The ProfileMatcher scores aspects of game-play such as number of kills, jumps, and coins collected in order to evaluate the player's skill in the five identified areas of competency and generate a vector of skill values, within a range of 1 -- 100. The relative weight of each different skill is maintained by the player profile for reference by later segments of the level creation process. Here, the player is also assigned a type based on his or her most dominant trait, and an overall skill level: expert, proficient, competent, beginner, or novice. A player’s skill level is based on a number of different factors. First of all, his or her average skill level across the skill vector is included. The speed with which the previous level was completed is integrated, as a player who finds a level too easy can complete it quickly and without error. Finally, the number of times their power-up state changed; if a player is not being threatened or hurt will tend to remain in one power-up state through much of a level, with few changes. A weighted overall skill score is produced and mapped to one of the five skill levels as shown below.
|
||||
|
||||
\begin{table}[ht]
|
||||
\centering
|
||||
\begin{tabular}{ | l | l | l | }
|
||||
\hline
|
||||
Score & Skill Level & Attributes\\ \hline
|
||||
20\% & Novice & low situational awareness, reflexive responses\\ \hline
|
||||
40\% & Beginner & uses judgement to react to challenges, limited awareness \\ \hline
|
||||
60\% & Competent & copies with multiple challenges, uses sound strategy \\ \hline
|
||||
80\% & Proficient & makes rapid decisions, prioritzes goals \\ \hline
|
||||
100\% & Expert & intuitively solves challenges, pushes boundaries \\ \hline
|
||||
20\% & Novice & Low situational awareness, Reflexive responses\\ \hline
|
||||
40\% & Beginner & Uses judgment to react to challenges, Limited awareness \\ \hline
|
||||
60\% & Competent & Copes with multiple challenges, Uses sound strategy \\ \hline
|
||||
80\% & Proficient & Makes rapid decisions, Prioritizes goals \\ \hline
|
||||
100\% & Expert & Intuitively solves challenges, Pushes limits \\ \hline
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
These categories are based on the Dreyfus model of skill acquisition with each successive level representing an achievement of 20\% of the Profile's key skill maximum score, before penalties. Actions such as dying or making unnecessary jumps negatively impact the skill assessment.
|
||||
|
||||
\subsection*{Level Archetype Selection}
|
||||
The Level Archetype selector picks an overall macro-form for the level based on the player's profile and skill level. As there are five categories and only three distinct environments in the Inifinite Mario engine (Overland, Underground and Castle), different profiles cause similar Level Archetypes to be generated. However, this does provide the opportunity to indicate to the player what types of challenges lie ahead and may serve to enhance immersion.
|
||||
|
||||
Next, the Level Archetype is further customized for the player by disabling challenges and enabling rewards according to a predined set of rules. In order to allow the rule set to be easily customized and extended, Adaptive Mario uses Drools, an implementation of Charles Forgy's 1982 Rete algorithm. Once the appropriate rules have fired, level generation proceeds to the third stage: macro-structure generation.
|
||||
Next, the Level Archetype is further customized for the player by disabling challenges and enabling rewards according to a predefined set of rules. In order to allow the rule set to be easily customized and extended, Adaptive Mario uses Drools, an implementation of Charles Forgy's 1982 Rete algorithm \cite{forgy}. Once the appropriate rules have fired, level generation proceeds to the third stage: macro-structure generation.
|
||||
|
||||
\subsection*{Level Generation: Macro-structure}
|
||||
The task of the macro-structure generator is to deliver a list of seqeuential Mario level design elements (LevelComponents) based on a high level description similar to ``Overland level with gaps but no pipes, difficulty 5/10''. Several algorithms were considered during the planning phase of this module. A Genetic Algorithm approach could potentially produce novel levels, but judging the relative fitness of candidate levels could be problematic. Hierarchical task networks (HTNs) and rhythm-based approaches have also been used, but tend to generate levels with a very uniform structure in a single pass. For ease of modification and to leverage the authors' design intuition, a context-free grammar (CFG) was implemented instead.
|
||||
The task of the macro-structure generator is to deliver a list of sequential Mario level design elements (LevelComponents) based on a high level description similar to ``Overland level with gaps but no pipes, difficulty 5/10''. Several algorithms were considered during the planning phase of this module. A Genetic Algorithm approach could potentially produce novel levels, but judging the relative fitness of candidate levels could be problematic. Hierarchical task networks (HTNs) and rhythm-based approaches have also been used, but tend to generate levels with a very uniform structure in a single pass. For ease of modification and to leverage the authors' design intuition, a context-free grammar (CFG) was implemented instead.
|
||||
|
||||
Given a suitable array of predefined LevelComponent types, each of which corresponds to one or more Infinite Mario game sprites, the structure of a level archetype can be completely specified in the form of a file-based CFG. As shown in the following example, the OR clauses are assigned probability values, giving a stochastic property to the generated level structure.
|
||||
|
||||
@@ -85,16 +95,42 @@ Given a suitable array of predefined LevelComponent types, each of which corresp
|
||||
\label{img:stochastic-grammar}
|
||||
\end{figure}
|
||||
|
||||
One potential pitfall of using a CFG for this purpose is that the generated level may be over-specified (containing too many elements) and hence too crowded, or under-specified and nearly empty. To mitigate this problem, a fitness evaluation function iteratively invokes the LevelGrammar, rejecting proposed levels with too many or too few LevelComponents.
|
||||
One potential pitfall of using a CFG for this purpose is that the generated level may be over-specified (containing too many elements) and hence too crowded, or under-specified and nearly empty. To mitigate this problem, a fitness evaluation function iteratively invokes the LevelGrammar class's generateRandomTree() method, rejecting proposed levels with too many or too few LevelComponents.
|
||||
|
||||
\subsection*{Challenge Components: Micro-structure}
|
||||
Challenge components are the building blocks of an Adaptive Mario level. Each challenge component represents a small puzzle or level element. Each level consists of a sequence of challenge components. Through only a small number of these components, we have achieved a wide variety of possible levels. In fact, the challenge components induce variety and adapt to the player’s style in the following ways.
|
||||
|
||||
\section*{Evaluation}
|
||||
First, the selection of components plays a large role in defining the character of a level. Many challenges are better suited for different play styles, based on the skills they require to traverse or the rewards they cause a player to incur. This allows a level to shift toward a more engaging landscape for a player of a particular style. Second, each challenge component dynamically adjusts its difficulty based on the player’s skill level. The player’s skill competencies affect the number of enemies they encounter, the number of power-ups they are offered, the number of coins placed on a level, the difficulty of the enemies they face, the size and number of pits in a level, and the types of terrain encountered. Because challenge components are so dynamic, even a single challenge element encountered repeatedly can present and engaging challenge.
|
||||
|
||||
\section*{Conclusion}
|
||||
It seems clear from the results that the framework of Adaptive Mario has the potential to guide the player toward his or her own idealized version of a platform game, while still presenting a reasonable level of challenge. Not only does difficulty scale in proportion to the player's performance (preventing frustration), but care is taken in the design of the level grammar to avoid repeatedly giving the player 'more of the same' (leading to boredom). One potential issue exists, however - it is possible that some players simply are not fans of the platform jumper genre!
|
||||
An additional benefit of challenge components is their modularity. Though we have implemented a strong suite of challenges, innumerable more are possible. With the implementation of a function and slight modification of the level generation grammar, new challenges can be added to Adaptive Mario to make it even more variable and extend its replay value further.
|
||||
|
||||
Fortunately, the architecture of Adaptive Mario (and the externalization of the rule engine and level grammar in particular) means that the designer is not limited to the side-scrolling platform jumper palette. It is easy to imagine that Adaptive Mario could be transformed into a Roguelike or adventure game without substantially altering the core Mario mechanics. Just as the player is freed from the constraints of pregenerated content, the designer (or hobbyist) is given the necessary tools to alter the game world as desired.
|
||||
\begin{table}[ht]
|
||||
\centering
|
||||
\begin{tabular}{ | l | l | l | }
|
||||
\hline
|
||||
Difficulty & Challenge Component & Description \\ \hline
|
||||
1 & Coin Dive & Some empty blocks, coins line the path to the ground.\\ \hline
|
||||
2 & Free power-up & Sets the player up to get a power-up with little or no challenge.\\ \hline
|
||||
3 & Straight & A straight stretch of land with maybe one enemy and maybe some coins or blocks\\ \hline
|
||||
4 & Single Pit & A pit that the user must jump over; rocks on either side.\\ \hline
|
||||
5 & Bowling Alley & A red koopa right before a long line of enemies. Kill them all!\\ \hline
|
||||
6 & Cannon Line & A stack of 2 or 3 cannons.\\ \hline
|
||||
7 & Maze & A maze of indestructible blocks, with enemies.\\ \hline
|
||||
8 & Lemming Trap & A little pit with a few enemies that jump down into it.\\ \hline
|
||||
9 & Platform Jump & A bunch of platforms to jump between.\\ \hline
|
||||
10 & Pipe Jump & A bunch of thin pipes to jump between.\\ \hline
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\section{Evaluation}
|
||||
Developing Adaptive Mario presented several challenges. From a technical standpoint, finding a Java-compatible Rete rule engine became a time-consuming effort, due mostly to the large number of third-party JAR dependencies. Ultimately, we were able to incorporate the Drools engine into our code base. The amount of time, however, that was invested in doing so forced the minimization of role of the rules engine. It therefore makes only basic alterations to random levels based on the player profile.
|
||||
|
||||
Randomized level generation using a stochastic CFG was largely successful, however. Not only can our very simple grammar generate a huge variety of levels, the implementation of a parser for the rule set allows future designers to make meaningful content changes without needing to recompile source code. Such grammars are, of course, limited to some degree. The grammar by definition lacks any contextual awareness. It therefore does nothing to avoid awkward juxtaposition of challenge components. Though challenge components are self-contained such that transition between any two should not present a problem, we nonetheless recognize this stratification as a limitation of our architecture.
|
||||
|
||||
Perhaps the most challenging aspect of our development process was the creation of the PlayerProfile. Each level is generated based on player data from only the single previous level traversal. Thus, the terrain of a previous level may have an inordinate effect on the evaluation of a player’s current tastes. We built our ProfileMatcher to minimize this possibility, but it nonetheless exists. A more complex version of Adaptive Mario would benefit greatly from a more iterative and flexible approach to player modeling, perhaps by employing an artificial neural network or Bayesian inference. These techniques would allow for more accurate and therefore useful scoring values, but would require more involved modification of the game engine.
|
||||
|
||||
\section{Conclusion}
|
||||
It seems clear from these results that the framework of Adaptive Mario has the potential to guide the player toward his or her own idealized version of a platform game, while still presenting a reasonable level of challenge. Not only does difficulty scale in proportion to the player's performance (preventing frustration), but care is taken in the design of the level grammar to avoid repeatedly giving the player 'more of the same' (leading to boredom). We certainly recognize many areas ripe for improvement (such as the limited level grammar), yet Adaptive Mario as currently implemented represents a robust engine. It provides tools for the adaptive content designer and new experiences for the consumer. It certainly adds a novel twist on the genre for any platform gaming enthusiast.
|
||||
|
||||
\section*{Appendix A: Building the Game}
|
||||
Building the Project 3 executable requires a Java SDK version 1.6+ and Apache Ant.
|
||||
@@ -105,10 +141,6 @@ To build the game, execute \texttt{ant clean} followed by \texttt{ant} from the
|
||||
|
||||
To run the game, change to the `dist' subdirectory following a successful build and execute \texttt{java -jar CS8803\_P3.jar}.
|
||||
|
||||
\begin{description}
|
||||
\item[seed] random number generator seed value (\texttt{long})
|
||||
\end{description}
|
||||
|
||||
\bibliographystyle{unsrt}
|
||||
\bibliography{p3refs}
|
||||
|
||||
|
||||
@@ -30,8 +30,24 @@
|
||||
|
||||
@MISC{bartle,
|
||||
AUTHOR = "Richard Bartle",
|
||||
TITLE = "Hearts, {Clubs}, {Diamonds}, {Spades}: {Players Who} suit {MUDs}",
|
||||
TITLE = "Hearts, Clubs, Diamonds, Spades: Players Who suit {MUDs}",
|
||||
HOWPUBLISHED = "http://aigamedev.com/open/interviews/mario-ai/",
|
||||
YEAR = 1996,
|
||||
NOTE = "[Online; accessed 18-March-2012]"
|
||||
}
|
||||
|
||||
@BOOK{zimmerman,
|
||||
title = {Rules of Play},
|
||||
author = {Salen, K. and Zimmerman, E.},
|
||||
publisher = {The MIT Press},
|
||||
year = 2004
|
||||
}
|
||||
|
||||
@ARTICLE{forgy,
|
||||
AUTHOR = "Forgy, C.",
|
||||
TITLE = "RETE: A fast algorithm for the many pattern/many object pattern match problem",
|
||||
YEAR = "1982",
|
||||
JOURNAL = "Artificial Intelligence",
|
||||
VOLUME = "19",
|
||||
PAGES = "17-37"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (MiKTeX 2.9) (preloaded format=pdftex 2012.1.30) 18 MAR 2012 14:06
|
||||
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (MiKTeX 2.9) (preloaded format=pdftex 2012.1.30) 18 MAR 2012 19:56
|
||||
entering extended mode
|
||||
**D:/workspace/cs8803p3/writeup/p3refs.bib
|
||||
(D:/workspace/cs8803p3/writeup/p3refs.bib)
|
||||
|
||||
Reference in New Issue
Block a user