mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
AI: improved usage of attachments:
* AI can play equipment/aura cards more frequent (computer can see and analyse all attached effects now); * AI can attach permanents with bad effects correctly (bad for opponents, good for itself);
This commit is contained in:
parent
89394ffe0a
commit
6cbf94bad6
6 changed files with 149 additions and 64 deletions
|
@ -118,7 +118,12 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
|
||||
Player player = game.getPlayer(playerId);
|
||||
logger.info(new StringBuilder("[").append(game.getPlayer(playerId).getName()).append("], life = ").append(player.getLife()).toString());
|
||||
GameStateEvaluator2.PlayerEvaluateScore score = GameStateEvaluator2.evaluate(playerId, game);
|
||||
logger.info(new StringBuilder("[").append(game.getPlayer(playerId).getName()).append("]")
|
||||
.append(", life = ").append(player.getLife())
|
||||
.append(", score = ").append(score.getTotalScore())
|
||||
.append(" (").append(score.getPlayerInfoFull()).append(")")
|
||||
.toString());
|
||||
StringBuilder sb = new StringBuilder("-> Hand: [");
|
||||
for (Card card : player.getHand().getCards(game)) {
|
||||
sb.append(card.getName()).append(';');
|
||||
|
@ -135,6 +140,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
if (permanent.isAttacking()) {
|
||||
sb.append("(attacking)");
|
||||
}
|
||||
sb.append(':' + String.valueOf(GameStateEvaluator2.evaluatePermanent(permanent, game)));
|
||||
sb.append(';');
|
||||
}
|
||||
}
|
||||
|
@ -200,13 +206,13 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
&& Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.debug("interrupted");
|
||||
return GameStateEvaluator2.evaluate(playerId, game);
|
||||
return GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
}
|
||||
// Condition to stop deeper simulation
|
||||
if (depth <= 0
|
||||
|| SimulationNode2.nodeCount > maxNodes
|
||||
|| game.checkIfGameIsOver()) {
|
||||
val = GameStateEvaluator2.evaluate(playerId, game);
|
||||
val = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
if (logger.isTraceEnabled()) {
|
||||
StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append('>');
|
||||
SimulationNode2 logNode = node;
|
||||
|
@ -240,20 +246,20 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
|
||||
if (game.checkIfGameIsOver()) {
|
||||
val = GameStateEvaluator2.evaluate(playerId, game);
|
||||
val = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
} else if (stepFinished) {
|
||||
logger.debug("Step finished");
|
||||
int testScore = GameStateEvaluator2.evaluate(playerId, game);
|
||||
int testScore = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
if (game.isActivePlayer(playerId)) {
|
||||
if (testScore < currentScore) {
|
||||
// if score at end of step is worse than original score don't check further
|
||||
//logger.debug("Add Action -- abandoning check, no immediate benefit");
|
||||
val = testScore;
|
||||
} else {
|
||||
val = GameStateEvaluator2.evaluate(playerId, game);
|
||||
val = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
}
|
||||
} else {
|
||||
val = GameStateEvaluator2.evaluate(playerId, game);
|
||||
val = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
}
|
||||
} else if (!node.getChildren().isEmpty()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -445,7 +451,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
&& Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.info("interrupted");
|
||||
return GameStateEvaluator2.evaluate(playerId, game);
|
||||
return GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
}
|
||||
node.setGameValue(game.getState().getValue(true).hashCode());
|
||||
SimulatedPlayer2 currentPlayer = (SimulatedPlayer2) game.getPlayer(game.getPlayerList().get());
|
||||
|
@ -490,7 +496,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
int val;
|
||||
if (action instanceof PassAbility && sim.getStack().isEmpty()) {
|
||||
// Stop to simulate deeper if PassAbility and stack is empty
|
||||
val = GameStateEvaluator2.evaluate(this.getId(), sim);
|
||||
val = GameStateEvaluator2.evaluate(this.getId(), sim).getTotalScore();
|
||||
} else {
|
||||
val = addActions(newNode, depth - 1, alpha, beta);
|
||||
}
|
||||
|
@ -533,7 +539,9 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
|
||||
}
|
||||
if (depth == maxDepth) {
|
||||
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + "> " + bestNode.getAbilities().toString());
|
||||
GameStateEvaluator2.PlayerEvaluateScore score = GameStateEvaluator2.evaluate(this.getId(), bestNode.game);
|
||||
String scoreInfo = " [" + score.getPlayerInfoShort() + "-" + score.getOpponentInfoShort() + "]";
|
||||
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + scoreInfo + "> " + bestNode.getAbilities().toString());
|
||||
node.children.clear();
|
||||
node.children.add(bestNode);
|
||||
node.setScore(bestNode.getScore());
|
||||
|
@ -933,7 +941,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
if (action instanceof PassAbility || action instanceof SpellAbility || action.getAbilityType() == AbilityType.MANA) {
|
||||
return false;
|
||||
}
|
||||
int newVal = GameStateEvaluator2.evaluate(playerId, sim);
|
||||
int newVal = GameStateEvaluator2.evaluate(playerId, sim).getTotalScore();
|
||||
SimulationNode2 test = node.getParent();
|
||||
while (test != null) {
|
||||
if (test.getPlayerId().equals(playerId)) {
|
||||
|
@ -942,7 +950,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
if (test.getParent() != null) {
|
||||
Game prevGame = node.getGame();
|
||||
if (prevGame != null) {
|
||||
int oldVal = GameStateEvaluator2.evaluate(playerId, prevGame);
|
||||
int oldVal = GameStateEvaluator2.evaluate(playerId, prevGame).getTotalScore();
|
||||
if (oldVal >= newVal) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
|
|||
protected void calculateActions(Game game) {
|
||||
if (!getNextAction(game)) {
|
||||
Date startTime = new Date();
|
||||
currentScore = GameStateEvaluator2.evaluate(playerId, game);
|
||||
currentScore = GameStateEvaluator2.evaluate(playerId, game).getTotalScore();
|
||||
Game sim = createSimulation(game);
|
||||
SimulationNode2.resetCount();
|
||||
root = new SimulationNode2(null, sim, maxDepth, playerId);
|
||||
|
|
|
@ -4,19 +4,18 @@
|
|||
*/
|
||||
package mage.player.ai;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.player.ai.ma.ArtificialScoringSystem;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author nantuko
|
||||
*
|
||||
* <p>
|
||||
* This evaluator is only good for two player games
|
||||
*
|
||||
*/
|
||||
public final class GameStateEvaluator2 {
|
||||
|
||||
|
@ -25,43 +24,47 @@ public final class GameStateEvaluator2 {
|
|||
public static final int WIN_GAME_SCORE = 100000000;
|
||||
public static final int LOSE_GAME_SCORE = -WIN_GAME_SCORE;
|
||||
|
||||
public static int evaluate(UUID playerId, Game game) {
|
||||
public static PlayerEvaluateScore evaluate(UUID playerId, Game game) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next());
|
||||
Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next()); // TODO: add multi opponent support?
|
||||
if (game.checkIfGameIsOver()) {
|
||||
if (player.hasLost()
|
||||
if (player.hasLost()
|
||||
|| opponent.hasWon()) {
|
||||
return LOSE_GAME_SCORE;
|
||||
return new PlayerEvaluateScore(LOSE_GAME_SCORE);
|
||||
}
|
||||
if (opponent.hasLost()
|
||||
if (opponent.hasLost()
|
||||
|| player.hasWon()) {
|
||||
return WIN_GAME_SCORE;
|
||||
return new PlayerEvaluateScore(WIN_GAME_SCORE);
|
||||
}
|
||||
}
|
||||
int lifeScore = 0;
|
||||
|
||||
int playerLifeScore = 0;
|
||||
int opponentLifeScore = 0;
|
||||
if (player.getLife() <= 0) { // we don't want a tie
|
||||
lifeScore = ArtificialScoringSystem.LOSE_GAME_SCORE;
|
||||
playerLifeScore = ArtificialScoringSystem.LOSE_GAME_SCORE;
|
||||
} else if (opponent.getLife() <= 0) {
|
||||
lifeScore = ArtificialScoringSystem.WIN_GAME_SCORE;
|
||||
playerLifeScore = ArtificialScoringSystem.WIN_GAME_SCORE;
|
||||
} else {
|
||||
lifeScore = ArtificialScoringSystem.getLifeScore(player.getLife()) - ArtificialScoringSystem.getLifeScore(opponent.getLife());
|
||||
playerLifeScore = ArtificialScoringSystem.getLifeScore(player.getLife());
|
||||
opponentLifeScore = ArtificialScoringSystem.getLifeScore(opponent.getLife()); // TODO: minus
|
||||
}
|
||||
int permanentScore = 0;
|
||||
int playerScore = 0;
|
||||
int opponentScore = 0;
|
||||
|
||||
int playerPermanentsScore = 0;
|
||||
int opponentPermanentsScore = 0;
|
||||
try {
|
||||
StringBuilder sbPlayer = new StringBuilder();
|
||||
StringBuilder sbOpponent = new StringBuilder();
|
||||
|
||||
// add values of player
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
playerScore += onePermScore;
|
||||
playerPermanentsScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbPlayer.append(permanent.getName()).append('[').append(onePermScore).append("] ");
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbPlayer.insert(0, playerScore + " - ");
|
||||
sbPlayer.insert(0, playerPermanentsScore + " - ");
|
||||
sbPlayer.insert(0, "Player..: ");
|
||||
logger.debug(sbPlayer);
|
||||
}
|
||||
|
@ -69,27 +72,32 @@ public final class GameStateEvaluator2 {
|
|||
// add values of opponent
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(opponent.getId())) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
opponentScore += onePermScore;
|
||||
opponentPermanentsScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbOpponent.append(permanent.getName()).append('[').append(onePermScore).append("] ");
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbOpponent.insert(0, opponentScore + " - ");
|
||||
sbOpponent.insert(0, opponentPermanentsScore + " - ");
|
||||
sbOpponent.insert(0, "Opponent: ");
|
||||
|
||||
logger.debug(sbOpponent);
|
||||
}
|
||||
permanentScore = playerScore - opponentScore;
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
int handScore;
|
||||
handScore = player.getHand().size() - opponent.getHand().size();
|
||||
handScore *= 5;
|
||||
|
||||
int score = lifeScore + permanentScore + handScore;
|
||||
logger.debug(score + " total Score (life:" + lifeScore + " permanents:" + permanentScore + " hand:" + handScore + ')');
|
||||
return score;
|
||||
int playerHandScore = player.getHand().size() * 5;
|
||||
int opponentHandScore = opponent.getHand().size() * 5;
|
||||
|
||||
int score = (playerLifeScore - opponentLifeScore)
|
||||
+ (playerPermanentsScore - opponentPermanentsScore)
|
||||
+ (playerHandScore - opponentHandScore);
|
||||
logger.debug(score
|
||||
+ " total Score (life:" + (playerLifeScore - opponentLifeScore)
|
||||
+ " permanents:" + (playerPermanentsScore - opponentPermanentsScore)
|
||||
+ " hand:" + (playerHandScore - opponentHandScore) + ')');
|
||||
return new PlayerEvaluateScore(
|
||||
playerLifeScore, playerHandScore, playerPermanentsScore,
|
||||
opponentLifeScore, opponentHandScore, opponentPermanentsScore);
|
||||
}
|
||||
|
||||
public static int evaluatePermanent(Permanent permanent, Game game) {
|
||||
|
@ -104,4 +112,81 @@ public final class GameStateEvaluator2 {
|
|||
return value;
|
||||
}
|
||||
|
||||
public static class PlayerEvaluateScore {
|
||||
private int playerLifeScore = 0;
|
||||
private int playerHandScore = 0;
|
||||
private int playerPermanentsScore = 0;
|
||||
|
||||
private int opponentLifeScore = 0;
|
||||
private int opponentHandScore = 0;
|
||||
private int opponentPermanentsScore = 0;
|
||||
|
||||
private int specialScore = 0; // special score (ignore all other)
|
||||
|
||||
public PlayerEvaluateScore(int specialScore) {
|
||||
this.specialScore = specialScore;
|
||||
}
|
||||
|
||||
public PlayerEvaluateScore(int playerLifeScore, int playerHandScore, int playerPermanentsScore,
|
||||
int opponentLifeScore, int opponentHandScore, int opponentPermanentsScore) {
|
||||
this.playerLifeScore = playerLifeScore;
|
||||
this.playerHandScore = playerHandScore;
|
||||
this.playerPermanentsScore = playerPermanentsScore;
|
||||
this.opponentLifeScore = opponentLifeScore;
|
||||
this.opponentHandScore = opponentHandScore;
|
||||
this.opponentPermanentsScore = opponentPermanentsScore;
|
||||
}
|
||||
|
||||
public int getPlayerScore() {
|
||||
return playerLifeScore + playerHandScore + playerPermanentsScore;
|
||||
}
|
||||
|
||||
public int getOpponentScore() {
|
||||
return opponentLifeScore + opponentHandScore + opponentPermanentsScore;
|
||||
}
|
||||
|
||||
public int getTotalScore() {
|
||||
if (specialScore != 0) {
|
||||
return specialScore;
|
||||
} else {
|
||||
return getPlayerScore() - getOpponentScore();
|
||||
}
|
||||
}
|
||||
|
||||
public int getPlayerLifeScore() {
|
||||
return playerLifeScore;
|
||||
}
|
||||
|
||||
public int getPlayerHandScore() {
|
||||
return playerHandScore;
|
||||
}
|
||||
|
||||
public int getPlayerPermanentsScore() {
|
||||
return playerPermanentsScore;
|
||||
}
|
||||
|
||||
public String getPlayerInfoFull() {
|
||||
return "Life:" + playerLifeScore +
|
||||
", Hand:" + playerHandScore +
|
||||
", Perm:" + playerPermanentsScore;
|
||||
}
|
||||
|
||||
public String getPlayerInfoShort() {
|
||||
return "L:" + playerLifeScore +
|
||||
",H:" + playerHandScore +
|
||||
",P:" + playerPermanentsScore;
|
||||
}
|
||||
|
||||
public String getOpponentInfoFull() {
|
||||
return "Life:" + opponentLifeScore +
|
||||
", Hand:" + opponentHandScore +
|
||||
", Perm:" + opponentPermanentsScore;
|
||||
}
|
||||
|
||||
public String getOpponentInfoShort() {
|
||||
return "L:" + opponentLifeScore +
|
||||
",H:" + opponentHandScore +
|
||||
",P:" + opponentPermanentsScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package mage.player.ai.ma;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author ubeefx, nantuko
|
||||
*/
|
||||
|
@ -20,7 +20,7 @@ public final class ArtificialScoringSystem {
|
|||
public static final int WIN_GAME_SCORE = 100000000;
|
||||
public static final int LOSE_GAME_SCORE = -WIN_GAME_SCORE;
|
||||
|
||||
private static final int LIFE_SCORES[] = {0, 1000, 2000, 3000, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7400, 7800, 8200, 8600, 9000, 9200, 9400, 9600, 9800, 10000};
|
||||
private static final int[] LIFE_SCORES = {0, 1000, 2000, 3000, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7400, 7800, 8200, 8600, 9000, 9200, 9400, 9600, 9800, 10000};
|
||||
private static final int MAX_LIFE = LIFE_SCORES.length - 1;
|
||||
private static final int UNKNOWN_CARD_SCORE = 300;
|
||||
private static final int PERMANENT_SCORE = 300;
|
||||
|
@ -79,29 +79,21 @@ public final class ArtificialScoringSystem {
|
|||
abilityScore += MagicAbility.getAbilityScore(ability);
|
||||
}
|
||||
score += power * 300 + getPositive(toughness) * 200 + abilityScore * (getPositive(power) + 1) / 2;
|
||||
//TODO: it can be improved
|
||||
//score += permanent.getEquipmentPermanents().size() * 50 + permanent.getAuraPermanents().size() * 100;
|
||||
int enchantments = 0;
|
||||
int equipments = 0;
|
||||
for (UUID uuid : permanent.getAttachments()) {
|
||||
Card card = game.getCard(uuid);
|
||||
if (card != null) {
|
||||
MageObject object = game.getObject(uuid);
|
||||
if (object instanceof Card) {
|
||||
Card card = (Card) object;
|
||||
int outcomeScore = object.getAbilities().getOutcomeTotal();
|
||||
if (card.getCardType().contains(CardType.ENCHANTMENT)) {
|
||||
Effect effect = card.getSpellAbility().getEffects().get(0);
|
||||
if (effect != null) {
|
||||
Outcome outcome = effect.getOutcome();
|
||||
if (outcome.isGood()) {
|
||||
enchantments++;
|
||||
} else if (outcome != Outcome.Detriment) {
|
||||
enchantments--;
|
||||
}
|
||||
}
|
||||
enchantments = enchantments + outcomeScore * 100;
|
||||
} else {
|
||||
equipments++;
|
||||
equipments = equipments + outcomeScore * 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
score += equipments * 50 + enchantments * 100;
|
||||
score += equipments + enchantments;
|
||||
|
||||
if (!permanent.canAttack(null, game)) {
|
||||
score -= 100;
|
||||
|
|
|
@ -38,13 +38,13 @@ public final class MagicAbility {
|
|||
// gatecrash
|
||||
put(new EvolveAbility().getRule(), 50);
|
||||
put(new ExtortAbility().getRule(), 30);
|
||||
|
||||
|
||||
|
||||
}};
|
||||
|
||||
public static int getAbilityScore(Ability ability) {
|
||||
if (!scores.containsKey(ability.getRule())) {
|
||||
//System.err.println("Couldn't find ability score: " + ability.getRule());
|
||||
//System.err.println("Couldn't find ability score: " + ability.getClass().getSimpleName() + " - " + ability.toString());
|
||||
//TODO: add handling protection from ..., levelup, kicker, etc. abilities
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public final class MiresGrasp extends CardImpl {
|
|||
// Enchant creature
|
||||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature));
|
||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
Loading…
Reference in a new issue