Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/main/java/dk/sdu/mmmi/modulemon/BattleAI/BattleAI.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ public class BattleAI implements IBattleAI {


public BattleAI(IBattleSimulation battleSimulation, IBattleParticipant participantToControl, IGameSettings settings) {
knowledgeState = new KnowledgeState();
var enableKnowlegdeStates = (Boolean) settings.getSetting(SettingsRegistry.getInstance().getAIKnowlegdeStateEnabled());
System.out.println(String.format("Minimax AI using knowledge states: %b", enableKnowlegdeStates));
knowledgeState = new KnowledgeState(!enableKnowlegdeStates);
this.participantToControl = participantToControl;
this.opposingParticipant = participantToControl == battleSimulation.getState().getPlayer()
? battleSimulation.getState().getEnemy()
Expand Down Expand Up @@ -249,7 +251,7 @@ private boolean isTerminal(IBattleState battleState) {

// Check if all the opposing participant's (known) monster are dead
boolean allEnemyMonstersDead = enemy.getMonsterTeam().stream()
.filter(x -> knowledgeState.getEnemyMonsters().contains(x)) //only consider monsters we've seen
.filter(x -> knowledgeState.hasSeenMonster(x)) //only consider monsters we've seen
.allMatch(x -> x.getHitPoints()<=0);
if (allEnemyMonstersDead) return true;

Expand All @@ -272,7 +274,7 @@ private float utility(IBattleState battleState) {

int enemyMonsterHPSum = 0;
for(IMonster monster : opposingParticipant.getMonsterTeam()) {
if (knowledgeState.getEnemyMonsters().contains(monster)) {
if (knowledgeState.hasSeenMonster(monster)) {
if (monster.getHitPoints()>0) enemyMonsterHPSum += monster.getHitPoints();
}
}
Expand All @@ -291,8 +293,7 @@ private List<IBattleState> successorFunction(IBattleState battleState) {

for (IMonsterMove move : activeParticipant.getActiveMonster().getMoves()) {
if (!activeParticipant.equals(participantToControl)) {
if (!(knowledgeState.getMonsterMoves().containsKey(activeParticipant.getActiveMonster()) &&
knowledgeState.getMonsterMoves().get(activeParticipant.getActiveMonster()).contains(move))){
if (!(knowledgeState.hasSeenMove(activeParticipant.getActiveMonster(), move))){
continue; // If we have not seen this move, don't consider the option where it is used
}
}
Expand All @@ -306,7 +307,7 @@ private List<IBattleState> successorFunction(IBattleState battleState) {

for (IMonster monster : activeParticipant.getMonsterTeam()) {
if (!activeParticipant.equals(participantToControl)) {
if (!knowledgeState.getEnemyMonsters().contains(monster)){
if (!knowledgeState.hasSeenMonster(monster)){
continue;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ public class BattleResult implements IBattleResult {
private IBattleParticipant winner;
private IBattleParticipant player;
private IBattleParticipant enemy;
private int turnCount;

public BattleResult(IBattleParticipant winner, IBattleParticipant player, IBattleParticipant enemy) {
public BattleResult(IBattleParticipant winner, IBattleParticipant player, IBattleParticipant enemy, int turnCount) {
this.winner = winner;
this.player = player;
this.enemy = enemy;
this.turnCount = turnCount;
}

@Override
Expand All @@ -28,4 +30,9 @@ public IBattleParticipant getPlayer() {
public IBattleParticipant getEnemy() {
return enemy;
}

@Override
public int getTurns() {
return turnCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class BattleView implements IGameViewService, IBattleView {
private boolean _isInitialized;
private boolean _battleStarted;
private IBattleCallback _battleCallback;
private int _numTurns;
private IBattleSimulation _battleSimulation;
private IBattleState _currentBattleState;
private BattleScene _battleScene;
Expand Down Expand Up @@ -131,6 +132,7 @@ public void startBattle(List<IMonster> playerMonsters, List<IMonster> enemyMonst
setBattleAIFactory();
_battleSimulation.StartBattle(player, enemy);
_currentBattleState = _battleSimulation.getState().clone(); // Set an initial battle-state
_numTurns = 0;
_battleCallback = callback;
_battleMusic.play();
_battleMusic.setLooping(true);
Expand Down Expand Up @@ -174,7 +176,7 @@ public void forceBattleEnd() {

public void handleBattleEnd(VictoryBattleEvent victoryBattleEvent) {
if (_battleCallback != null) {
_battleCallback.onBattleEnd(new BattleResult(victoryBattleEvent.getWinner(), _battleSimulation.getState().getPlayer(), _battleSimulation.getState().getEnemy()));
_battleCallback.onBattleEnd(new BattleResult(victoryBattleEvent.getWinner(), _battleSimulation.getState().getPlayer(), _battleSimulation.getState().getEnemy(), _numTurns));
} else {
gameViewManager.setDefaultView();
}
Expand Down Expand Up @@ -314,6 +316,7 @@ public void update(GameData gameData, IGameViewManager gameViewManager) {
if (battleEvent != null) {
IBattleState eventState = battleEvent.getState();
if (battleEvent instanceof MoveBattleEvent) {
this._numTurns++;
_currentBattleState = eventState;
MoveBattleEvent event = (MoveBattleEvent) battleEvent;
if (event.getUsingParticipant().isPlayerControlled()) {
Expand Down Expand Up @@ -342,6 +345,7 @@ public void update(GameData gameData, IGameViewManager gameViewManager) {

this._battleScene.setTextToDisplay(event.getText());
} else if (battleEvent instanceof ChangeMonsterBattleEvent) {
this._numTurns++;
ChangeMonsterBattleEvent event = (ChangeMonsterBattleEvent) battleEvent;
boolean causedByFaintingMonster = event instanceof MonsterFaintChangeBattleEvent;
if (event.getParticipant().isPlayerControlled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ private void doOpponentMove() {
getOpponentAI().doAction();
} catch (Exception ex) {
System.out.println(ex);
ex.printStackTrace();
nextEvent = new AICrashedEvent(String.format("The opponent %s controller has crashed!", this.opponentAIFactory), this.battleState.getActiveParticipant(), ex, battleState.clone());
onNextEvent = this::switchTurns;
}
Expand All @@ -302,6 +303,7 @@ private void doPlayerMove() {
getPlayerAI().doAction();
} catch (Exception ex) {
System.out.println(ex);
ex.printStackTrace();
nextEvent = new AICrashedEvent(String.format("The player %s controller has crashed!", this.playerAIFactory), this.battleState.getActiveParticipant(), ex, battleState.clone());
onNextEvent = this::switchTurns;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public interface IBattleResult {
IBattleParticipant getWinner();
IBattleParticipant getPlayer();
IBattleParticipant getEnemy();
int getTurns();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ public class KnowledgeState {
// A map, mapping each of the enemy's monsters to a list of the moves, the AI has seen it use
private Map<IMonster, List<IMonsterMove>> monsterMoves;

public KnowledgeState() {
private boolean allKnowing = false;

public KnowledgeState(boolean allKnowing) {
this.allKnowing = allKnowing;
enemyMonsters = new ArrayList<>();
monsterMoves = new HashMap<>();
}
Expand All @@ -26,4 +29,22 @@ public List<IMonster> getEnemyMonsters() {
public Map<IMonster, List<IMonsterMove>> getMonsterMoves() {
return monsterMoves;
}

public boolean hasSeenMove(IMonster monster, IMonsterMove move){
if(allKnowing){
return true;
}
if(!this.monsterMoves.containsKey(monster)){
return false;
}

return this.monsterMoves.get(monster).contains(move);
}

public boolean hasSeenMonster(IMonster monster){
if(allKnowing){
return true;
}
return this.enemyMonsters.contains(monster);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public void draw(GameData gameData) {
var topOfResults = resultContainer.getY() + resultContainer.getHeight();
var headerY = topOfResults - 50;
var textY = headerY - 60;
var lineHeight = 25;
var lineHeight = 30;
var textX = resultContainer.getX() + 50;
text.setCoordinateMode(TextUtils.CoordinateMode.CENTER);
text.drawTitleFont(spriteBatch, resultsHeader, Color.BLACK, gameData.getDisplayWidth() / 2f, headerY );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.TimeUtils;
import dk.sdu.mmmi.modulemon.CommonBattleClient.IBattleView;
import dk.sdu.mmmi.modulemon.CommonBattleSimulation.IBattleAIFactory;
import dk.sdu.mmmi.modulemon.CommonBattleSimulation.IBattleSimulation;
Expand Down Expand Up @@ -31,6 +32,8 @@ public class CustomBattleView implements IGameViewService {
private Queue<BaseAnimation> backgroundAnimations;
private CustomBattleScene scene;

private Random random = new Random();

// LibGDX Sound stuff
private Sound selectSound;
private Sound chooseSound;
Expand Down Expand Up @@ -59,17 +62,17 @@ public void init(IGameViewManager gameViewManager) {
private int cursorPosition = 0;
private boolean editingMode = false;
private boolean redrawRoulettes = true;
private boolean showingResults = false;
private boolean showingResults = false;


@Override
public void update(GameData gameData, IGameViewManager gameViewManager) {
cursorPosition = MathUtils.clamp(cursorPosition, 0, 14);
scene.setShowResults(showingResults);
var nextAnimation = backgroundAnimations.peek();
if(nextAnimation != null){
if (nextAnimation != null) {
nextAnimation.update(gameData);
if(nextAnimation.isFinished()){
if (nextAnimation.isFinished()) {
backgroundAnimations.poll();
}
}
Expand Down Expand Up @@ -138,8 +141,27 @@ public void handleInput(GameData gameData, IGameViewManager gameViewManager) {
}
}

if (gameData.getKeys().isDown(GameKeys.K) && cursorPosition < 12) {
// Shuffle random monsters
if (gameData.getKeys().isDown(GameKeys.LEFT_CTRL)) {
// If holding CTRL, randomize both teams
for (int i = 0; i < selectedTeamAIndicies.length; i++) {
selectedTeamAIndicies[i] = random.nextInt(monsterRegistry.getMonsterAmount());
selectedTeamBIndicies[i] = random.nextInt(monsterRegistry.getMonsterAmount());
}
} else {
if (isTeamA(cursorPosition)) {
selectedTeamAIndicies[getGridAdjustedCursor(cursorPosition)] = random.nextInt(monsterRegistry.getMonsterAmount());
} else {
selectedTeamBIndicies[getGridAdjustedCursor(cursorPosition)] = random.nextInt(monsterRegistry.getMonsterAmount());
}
}
redrawRoulettes = true;
chooseSound.play(getSoundVolume());
}

if (gameData.getKeys().isPressed(GameKeys.ACTION)) {
if(showingResults){
if (showingResults) {
showingResults = false;
return;
}
Expand All @@ -148,12 +170,21 @@ public void handleInput(GameData gameData, IGameViewManager gameViewManager) {
chooseSound.play(getSoundVolume());
}

if(showingResults){
if (showingResults) {
return; // Don't allow any movement when showing results
}

if (gameData.getKeys().isPressed(GameKeys.DELETE) && cursorPosition < 13) {
addToSelectedIndicies(null);
if (gameData.getKeys().isDown(GameKeys.LEFT_CTRL)) {
// If holding CTRL, empty both teams
for (int i = 0; i < selectedTeamAIndicies.length; i++) {
selectedTeamAIndicies[i] = null;
selectedTeamBIndicies[i] = null;
}
} else {
addToSelectedIndicies(null);
}
redrawRoulettes = true;
editingMode = false;
chooseSound.play(getSoundVolume());
}
Expand Down Expand Up @@ -206,14 +237,14 @@ public void handleInput(GameData gameData, IGameViewManager gameViewManager) {
}
}

private void startBattle(IGameViewManager gameViewManager){
private void startBattle(IGameViewManager gameViewManager) {
var teamA = Arrays.stream(getMonsterArray(selectedTeamAIndicies)).filter(Objects::nonNull).toList();
var teamB = Arrays.stream(getMonsterArray(selectedTeamBIndicies)).filter(Objects::nonNull).toList();

var teamAAI = getSelectedAI(selectedTeamAAI);
var teamBAI = getSelectedAI(selectedTeamBAI);

if(teamA.isEmpty() || teamB.isEmpty() || teamBAI == null){
if (teamA.isEmpty() || teamB.isEmpty() || teamBAI == null) {
wrongSound.play(1);
var anim = new ErrorTextAnimation(scene, "Add some monsters to both teams you dork!");
anim.start();
Expand All @@ -227,27 +258,35 @@ private void startBattle(IGameViewManager gameViewManager){

gameViewManager.setView(battleView.getGameView(), false); // Do not dispose the map
customBattleMusic.stop();
long startTime = TimeUtils.millis();
battleView.startBattle(teamA, teamB, result -> {
customBattleMusic.play();
gameViewManager.setView(this);
boolean teamAWon = result.getWinner() == result.getPlayer();
var winnerTeamName = teamAWon ?
(teamAAI == null ? "You" : teamAAI.toString())
: (teamBAI.toString());
scene.setResultsHeader("The winner is: " + winnerTeamName +"!");
scene.setResultLines(new String[]{
"TODO: Collect some stats from the battle and display them here",
"On multiple lines",
"Wow! This is amazing!!"
});
(teamAAI == null ? "You" : teamAAI.toString())
: (teamBAI.toString());
scene.setResultsHeader("The winner is: " + winnerTeamName + "!");

var resultLines = new ArrayList<String>() {{
add(String.format("Total turns: %d", result.getTurns()));
add(String.format("Battle time: %.2f seconds", (TimeUtils.timeSinceMillis(startTime) / 1000f)));
add(String.format("The winning team ended up as so:"));
}};

for (var monster : result.getWinner().getMonsterTeam()) {
resultLines.add(String.format(" - %s", monster));
}

scene.setResultLines(resultLines.toArray(new String[resultLines.size()]));
cursorPosition = 0;
showingResults = true;
editingMode = false;
});

// Set the battle AI after the startBattle method to override the configs.
battleSimulation.setOpponentAIFactory(teamBAI);
if(teamAAI != null) {
if (teamAAI != null) {
battleSimulation.setPlayerAIFactory(teamAAI);
}
}
Expand All @@ -257,15 +296,15 @@ private void addToSelectedIndicies(Integer a) {
if (cursorPosition < 12) {
var numMonsters = this.monsterRegistry.getMonsterAmount() - 1;
if (isTeamA(cursorPosition)) {
selectedTeamAIndicies[getGridAdjustedCursor(cursorPosition)] = scrollIndexWithNull(selectedTeamAIndicies[getGridAdjustedCursor(cursorPosition)],a , numMonsters);
selectedTeamAIndicies[getGridAdjustedCursor(cursorPosition)] = scrollIndexWithNull(selectedTeamAIndicies[getGridAdjustedCursor(cursorPosition)], a, numMonsters);
} else {
selectedTeamBIndicies[getGridAdjustedCursor(cursorPosition)] = scrollIndexWithNull(selectedTeamBIndicies[getGridAdjustedCursor(cursorPosition)],a, numMonsters);
selectedTeamBIndicies[getGridAdjustedCursor(cursorPosition)] = scrollIndexWithNull(selectedTeamBIndicies[getGridAdjustedCursor(cursorPosition)], a, numMonsters);
}
} else if (cursorPosition == 12) {
selectedTeamAAI = scrollIndexWithNull(selectedTeamAAI, a, this.battleAIFactoryList.size() - 1);
} else if (cursorPosition == 13) {
var newBValue = scrollIndexWithNull(selectedTeamBAI, a, this.battleAIFactoryList.size() - 1);
if(newBValue == null){
if (newBValue == null) {
newBValue = selectedTeamBAI == 0 ? this.battleAIFactoryList.size() - 1 : 0;
}
selectedTeamBAI = newBValue;
Expand All @@ -278,9 +317,9 @@ private Integer scrollIndexWithNull(Integer input, Integer a, int maxValue) {
}

if (input == null) {
if (a > 0){
if (a > 0) {
return 0;
}else{
} else {
return maxValue;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/dk/sdu/mmmi/modulemon/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ public void setSettingsService(IGameSettings settings) {
if (settings.getSetting(SettingsRegistry.getInstance().getAIAlphaBetaSetting()) == null) {
settings.setSetting(SettingsRegistry.getInstance().getAIAlphaBetaSetting(), true);
}
if (settings.getSetting(SettingsRegistry.getInstance().getAIKnowlegdeStateEnabled()) == null) {
settings.setSetting(SettingsRegistry.getInstance().getAIKnowlegdeStateEnabled(), true);
}
if (settings.getSetting(SettingsRegistry.getInstance().getBattleMusicThemeSetting()) == null) {
settings.setSetting(SettingsRegistry.getInstance().getBattleMusicThemeSetting(), "Original");
}
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/dk/sdu/mmmi/modulemon/MCTSBattleAI/EmptyMove.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dk.sdu.mmmi.modulemon.MCTSBattleAI;

import dk.sdu.mmmi.modulemon.CommonMonster.IMonsterMove;

public class EmptyMove implements IMonsterMove {
@Override
public String getName() {
return "Loaf around";
}

@Override
public String getSoundPath() {
return null;
}

@Override
public String getBattleDescription() {
return "Your monster loafs around. It does nothing.";
}

@Override
public String getSummaryScreenDescription() {
return getBattleDescription();
}
}
Loading