From 7c5eae6e42a12e767c0109f36de8eb5556aefd05 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Tue, 21 May 2013 08:26:27 +0000 Subject: [PATCH] Added support for AI vs AI matches --- src/main/java/forge/game/GameAction.java | 2 + src/main/java/forge/game/GameOutcome.java | 2 + src/main/java/forge/game/MatchController.java | 2 - .../forge/game/ai/AiAttackController.java | 40 +++++++++++------ .../home/sanctioned/CSubmenuConstructed.java | 8 ++-- .../home/sanctioned/VSubmenuConstructed.java | 19 +++++--- src/main/java/forge/gui/match/CMatchUI.java | 44 ++++++++++++------- .../forge/gui/match/nonsingleton/CHand.java | 3 +- .../forge/gui/match/nonsingleton/VHand.java | 3 +- 9 files changed, 80 insertions(+), 43 deletions(-) diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index ffca22a5315..991ad38bfcd 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -840,9 +840,11 @@ public class GameAction { } // ai's cannot finish their game without human yet - so terminate a game if human has left. + /* if (reason == null && !Iterables.any(game.getPlayers(), Predicates.and(Player.Predicates.NOT_LOST, Player.Predicates.isType(PlayerType.HUMAN)))) { reason = GameEndReason.AllHumansLost; } + */ return reason; } diff --git a/src/main/java/forge/game/GameOutcome.java b/src/main/java/forge/game/GameOutcome.java index ee2ef0ed039..ae658a76615 100644 --- a/src/main/java/forge/game/GameOutcome.java +++ b/src/main/java/forge/game/GameOutcome.java @@ -95,6 +95,8 @@ public final class GameOutcome implements Iterable * Getter for the field attackers. @@ -522,7 +525,8 @@ public class AiAttackController { return combat; } if (bAssault) { - System.out.println("Assault"); + if ( LOG_AI_ATTACKS ) + System.out.println("Assault"); CardLists.sortByPowerDesc(attackersLeft); for (Card attacker : attackersLeft) { if (CombatUtil.canAttack(attacker, defender, combat) && this.isEffectiveAttacker(ai, attacker, combat)) { @@ -556,7 +560,8 @@ public class AiAttackController { } if (exalted) { CardLists.sortByPowerDesc(this.attackers); - System.out.println("Exalted"); + if ( LOG_AI_ATTACKS ) + System.out.println("Exalted"); this.aiAggression = 6; for (Card attacker : this.attackers) { if (CombatUtil.canAttack(attacker, defender, combat) && this.shouldAttack(ai, attacker, this.blockers, combat)) { @@ -752,13 +757,16 @@ public class AiAttackController { } else { this.aiAggression = 0; } // stay at home to block - System.out.println(String.valueOf(this.aiAggression) + " = ai aggression"); + + if ( LOG_AI_ATTACKS ) + System.out.println(String.valueOf(this.aiAggression) + " = ai aggression"); // **************** // Evaluation the end // **************** - System.out.println("Normal attack"); + if ( LOG_AI_ATTACKS ) + System.out.println("Normal attack"); attackersLeft = this.notNeededAsBlockers(ai, attackersLeft); attackersLeft = this.sortAttackers(attackersLeft); @@ -858,6 +866,7 @@ public class AiAttackController { boolean isWorthLessThanAllKillers = true; boolean canBeBlocked = false; int numberOfPossibleBlockers = 0; + if (!this.isEffectiveAttacker(ai, attacker, combat)) { return false; @@ -921,8 +930,8 @@ public class AiAttackController { // if the creature cannot block and can kill all opponents they might as // well attack, they do nothing staying back if (canKillAll && isWorthLessThanAllKillers && !CombatUtil.canBlock(attacker)) { - System.out.println(attacker.getName() - + " = attacking because they can't block, expecting to kill or damage player"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking because they can't block, expecting to kill or damage player"); return true; } @@ -938,16 +947,19 @@ public class AiAttackController { switch (this.aiAggression) { case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked if ((canKillAll && isWorthLessThanAllKillers) || !canBeBlocked) { - System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable"); return true; } break; case 5: // all out attacking - System.out.println(attacker.getName() + " = all out attacking"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = all out attacking"); return true; case 4: // expecting to at least trade with something if (canKillAll || (canKillAllDangerous && !canBeKilledByOne) || !canBeBlocked) { - System.out.println(attacker.getName() + " = attacking expecting to at least trade with something"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking expecting to at least trade with something"); return true; } break; @@ -956,21 +968,23 @@ public class AiAttackController { if ((canKillAll && isWorthLessThanAllKillers) || ((canKillAllDangerous || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne) || !canBeBlocked) { - System.out.println(attacker.getName() - + " = attacking expecting to kill creature or cause damage, or is unblockable"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking expecting to kill creature or cause damage, or is unblockable"); return true; } break; case 2: // attack expecting to attract a group block or destroying a // single blocker and surviving if (((canKillAll || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne) || !canBeBlocked) { - System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block"); return true; } break; case 1: // unblockable creatures only if (!canBeBlocked || (numberOfPossibleBlockers == 1 && canKillAll && !canBeKilledByOne)) { - System.out.println(attacker.getName() + " = attacking expecting not to be blocked"); + if ( LOG_AI_ATTACKS ) + System.out.println(attacker.getName() + " = attacking expecting not to be blocked"); return true; } break; diff --git a/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java b/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java index 6d1221ae46a..fed5f2756ac 100644 --- a/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java +++ b/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java @@ -13,6 +13,7 @@ import forge.game.GameType; import forge.game.MatchController; import forge.game.MatchStartHelper; import forge.game.PlayerStartConditions; +import forge.game.player.LobbyPlayer; import forge.gui.SOverlayUtils; import forge.gui.framework.ICDoc; import forge.properties.ForgePreferences; @@ -96,14 +97,14 @@ public enum CSubmenuConstructed implements ICDoc { * @param gameType */ private void startGame(final GameType gameType) { - PlayerStartConditions humanPsc = VSubmenuConstructed.SINGLETON_INSTANCE.getDcHuman().getDeck(); + PlayerStartConditions humanPsc = view.getDcHuman().getDeck(); String humanDeckErrorMessage = gameType.getDecksFormat().getDeckConformanceProblem(humanPsc.getOriginalDeck()); if (null != humanDeckErrorMessage) { JOptionPane.showMessageDialog(null, "Your deck " + humanDeckErrorMessage, "Invalid deck", JOptionPane.ERROR_MESSAGE); return; } - PlayerStartConditions aiDeck = VSubmenuConstructed.SINGLETON_INSTANCE.getDcAi().getDeck(); + PlayerStartConditions aiDeck = view.getDcAi().getDeck(); String aiDeckErrorMessage = gameType.getDecksFormat().getDeckConformanceProblem(aiDeck.getOriginalDeck()); if (null != aiDeckErrorMessage) { JOptionPane.showMessageDialog(null, "AI deck " + aiDeckErrorMessage, "Invalid deck", JOptionPane.ERROR_MESSAGE); @@ -115,7 +116,8 @@ public enum CSubmenuConstructed implements ICDoc { final MatchStartHelper starter = new MatchStartHelper(); Lobby lobby = Singletons.getControl().getLobby(); - starter.addPlayer(lobby.getGuiPlayer(), humanPsc); + LobbyPlayer firstPlayer = view.getCbSpectate().isSelected() ? lobby.getAiPlayer() : lobby.getGuiPlayer(); + starter.addPlayer(firstPlayer, humanPsc); starter.addPlayer(lobby.getAiPlayer(), aiDeck); final MatchController mc = new MatchController(gameType, starter.getPlayerMap()); diff --git a/src/main/java/forge/gui/home/sanctioned/VSubmenuConstructed.java b/src/main/java/forge/gui/home/sanctioned/VSubmenuConstructed.java index 85529443409..c8738a1e16d 100644 --- a/src/main/java/forge/gui/home/sanctioned/VSubmenuConstructed.java +++ b/src/main/java/forge/gui/home/sanctioned/VSubmenuConstructed.java @@ -35,13 +35,14 @@ public enum VSubmenuConstructed implements IVSubmenu { /** */ private final LblHeader lblTitle = new LblHeader("Sanctioned Format: Constructed"); - private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); + private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 3")); private final StartButton btnStart = new StartButton(); private final JCheckBox cbSingletons = new FCheckBox("Singleton Mode"); private final JCheckBox cbArtifacts = new FCheckBox("Remove Artifacts"); private final JCheckBox cbRemoveSmall = new FCheckBox("Remove Small Creatures"); + private final JCheckBox cbAiVsAi = new FCheckBox("Spectate AI vs AI match"); private final FDeckChooser dcHuman = new FDeckChooser("Select your deck:", PlayerType.HUMAN); private final FDeckChooser dcAi = new FDeckChooser("Select AI deck:", PlayerType.COMPUTER); @@ -51,12 +52,15 @@ public enum VSubmenuConstructed implements IVSubmenu { lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); - final String strCheckboxConstraints = "h 30px!, gap 0 20px 0 0"; + final String strCheckboxConstraints = "pushy, gap 0 20px 0 0"; + final String strCheckboxConstraintsTop = "pushy, gap 0 20px 20px 0"; pnlStart.setOpaque(false); - pnlStart.add(cbSingletons, strCheckboxConstraints); - pnlStart.add(btnStart, "span 1 3, growx, pushx, align center"); + pnlStart.add(cbSingletons, strCheckboxConstraintsTop); + pnlStart.add(cbAiVsAi, strCheckboxConstraintsTop); + pnlStart.add(btnStart, "span 1 2, growx, pushx, align center"); pnlStart.add(cbArtifacts, strCheckboxConstraints); pnlStart.add(cbRemoveSmall, strCheckboxConstraints); + } /* (non-Javadoc) @@ -106,7 +110,7 @@ public enum VSubmenuConstructed implements IVSubmenu { dcHuman.populate(); VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(dcAi, "w 44%!, gap 0 0 20px 20px, growy, pushy"); VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(dcHuman, "w 44%!, gap 4% 4% 20px 20px, growy, pushy"); - VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(pnlStart, "span 2, gap 0 0 3.5%! 3.5%!, ax center"); + VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(pnlStart, "span 2, gap 0 0 2.5%! 3.5%!, ax center"); VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().revalidate(); VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().repaintSelf(); @@ -134,6 +138,11 @@ public enum VSubmenuConstructed implements IVSubmenu { return cbRemoveSmall; } + /** @return {@link javax.swing.JCheckBox} */ + public JCheckBox getCbSpectate() { + return cbAiVsAi; + } + //========== Overridden from IVDoc /* (non-Javadoc) diff --git a/src/main/java/forge/gui/match/CMatchUI.java b/src/main/java/forge/gui/match/CMatchUI.java index 2d7fe95873f..a3e01c5c358 100644 --- a/src/main/java/forge/gui/match/CMatchUI.java +++ b/src/main/java/forge/gui/match/CMatchUI.java @@ -18,11 +18,15 @@ package forge.gui.match; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.ImageIcon; + +import com.google.common.collect.Lists; + import forge.Card; import forge.FThreads; import forge.GameEntity; @@ -86,29 +90,35 @@ public enum CMatchUI { // Instantiate all required field slots (user at 0) <-- that's not guaranteed final List fields = new ArrayList(); final List commands = new ArrayList(); - - VField humanField = new VField(EDocID.valueOf("FIELD_0"), localPlayer, localPlayer); - VCommand humanCommand = new VCommand(EDocID.COMMAND_0, localPlayer); - fields.add(0, humanField); - commands.add(0, humanCommand); - setAvatar(humanField, new ImageIcon(FSkin.getAvatars().get(Integer.parseInt(indices[0])))); - humanField.getLayoutControl().initialize(); - humanCommand.getLayoutControl().initialize(); - - int i = 1; - for (Player p : players) { - if (p.equals(localPlayer)) { - continue; + + List sortedPlayers = Lists.newArrayList(players); + + int ixLocal = -1; + for(int i = 0; i < players.size(); i++) { + if( sortedPlayers.get(i) == localPlayer ) { + ixLocal = i; + break; } + } + if( ixLocal > 0 ) { + Player p0 = sortedPlayers.remove(ixLocal); + sortedPlayers.add(0, p0); + } + + + int i = 0; + for (Player p : sortedPlayers) { // A field must be initialized after it's instantiated, to update player info. // No player, no init. VField f = new VField(EDocID.valueOf("FIELD_" + i), p, localPlayer); - setAvatar(f, getPlayerAvatar(p, Integer.parseInt(indices[1]))); - f.getLayoutControl().initialize(); - fields.add(f); VCommand c = new VCommand(EDocID.valueOf("COMMAND_" + i), p); - c.getLayoutControl().initialize(); + fields.add(f); commands.add(c); + + //setAvatar(f, new ImageIcon(FSkin.getAvatars().get())); + setAvatar(f, getPlayerAvatar(p, Integer.parseInt(indices[i > 2 ? 1 : 0]))); + f.getLayoutControl().initialize(); + c.getLayoutControl().initialize(); i++; } diff --git a/src/main/java/forge/gui/match/nonsingleton/CHand.java b/src/main/java/forge/gui/match/nonsingleton/CHand.java index d4629a2f7b6..4329b96aa16 100644 --- a/src/main/java/forge/gui/match/nonsingleton/CHand.java +++ b/src/main/java/forge/gui/match/nonsingleton/CHand.java @@ -78,7 +78,8 @@ public class CHand implements ICDoc { if (initializedAlready) { return; } initializedAlready = true; - player.getZone(ZoneType.Hand).addObserver(o1); + if ( player != null) + player.getZone(ZoneType.Hand).addObserver(o1); view.getHandArea().addMouseListener(madCardClick); } diff --git a/src/main/java/forge/gui/match/nonsingleton/VHand.java b/src/main/java/forge/gui/match/nonsingleton/VHand.java index 365600616b8..baa01fe0e87 100644 --- a/src/main/java/forge/gui/match/nonsingleton/VHand.java +++ b/src/main/java/forge/gui/match/nonsingleton/VHand.java @@ -58,8 +58,7 @@ public class VHand implements IVDoc { if (player0 == null) { tab.setText("NO PLAYER Hand"); - } - else { + } else { tab.setText(player0.getName() + " Hand"); }