package com.hearthsim.util.tree;
import com.hearthsim.card.Card;
import com.hearthsim.card.CharacterIndex;
import com.hearthsim.card.minion.*;
import com.hearthsim.model.BoardModel;
import com.hearthsim.model.PlayerSide;
import com.hearthsim.player.playercontroller.BoardScorer;
import com.hearthsim.util.HearthAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A tree that keeps track of possible game states
*/
public class HearthTreeNode {
byte depth_;
public final BoardModel data_;
HearthAction action;
protected double score_;
private double bestChildScore_;
protected List<HearthTreeNode> children_;
private HearthTreeNode parent;
public HearthTreeNode getParent() {
return parent;
}
private class NodeValPair {
public final HearthTreeNode node_;
public final double value_;
NodeValPair(HearthTreeNode node, double value) {
node_ = node;
value_ = value;
}
}
public HearthTreeNode(BoardModel data) {
this(data, null);
}
public HearthTreeNode(BoardModel data, HearthAction action) {
this(data, action, 0.0);
}
private HearthTreeNode(BoardModel data, HearthAction action, double score) {
this(data, action, score, (byte)0);
}
public HearthTreeNode(BoardModel data, HearthAction action, double score, byte depth) {
this(data, action, score, depth, null);
}
public HearthTreeNode(BoardModel data, HearthAction action, double score, byte depth,
List<HearthTreeNode> children) {
data_ = data;
this.action = action;
score_ = score;
children_ = children;
depth_ = depth;
}
public HearthAction getAction() {
return action;
}
public void setAction(HearthAction action) {
this.action = action;
}
public byte getDepth() {
return depth_;
}
public void setDepth(byte value) {
depth_ = value;
}
public double getScore() {
return score_;
}
public void setScore(double value) {
score_ = value;
}
public double getBestChildScore() {
return bestChildScore_;
}
public void setBestChildScore(double bestChildScore) {
this.bestChildScore_ = bestChildScore;
}
public HearthTreeNode addChild(HearthTreeNode node) {
node.setDepth((byte)(depth_ + 1));
if (children_ == null) {
children_ = new ArrayList<>();
}
children_.add(node);
node.parent = this;
return node;
}
public void addChildren(Collection<HearthTreeNode> nodes) {
for (HearthTreeNode node : nodes) {
this.addChild(node);
}
}
public void clearChildren() {
if (children_ != null)
children_.clear();
}
public List<HearthTreeNode> getChildren() {
return children_;
}
public void setChildren(List<HearthTreeNode> children) {
children_ = children;
for (HearthTreeNode node : children) {
node.parent = this;
}
}
public int numChildren() {
if (children_ == null)
return 0;
else {
return children_.size();
}
}
public boolean isLeaf() {
return children_ == null || children_.size() == 0;
}
/**
* Returns the node below this node that result in the highest value when a given function is applied
*
* @param func Function to apply to each node
* @return
*/
public HearthTreeNode findMaxOfFunc(BoardScorer ai) {
NodeValPair nvp = this.findMaxOfFuncImpl(ai);
return nvp.node_;
}
private NodeValPair findMaxOfFuncImpl(BoardScorer ai) {
if (this.isLeaf())
return new NodeValPair(this, ai.boardScore(this.data_));
NodeValPair maxNode = null;
double maxSoFar = -1.e300;
for (final HearthTreeNode child : children_) {
NodeValPair maxOfChild = child.findMaxOfFuncImpl(ai);
if (maxOfChild.value_ > maxSoFar) {
maxSoFar = maxOfChild.value_;
maxNode = maxOfChild;
}
}
return maxNode;
}
@Override
public String toString() {
String toRet = "{";
toRet = toRet + "\"data\": " + data_ + ", ";
toRet = toRet + "\"children\": [";
if (children_ != null) {
boolean hasContent = false;
for (final HearthTreeNode child : children_) {
toRet = toRet + child + ", ";
hasContent = true;
}
if (hasContent) {
toRet = toRet.substring(0, toRet.length() - 2);
}
}
toRet = toRet + "]";
toRet = toRet + "}";
return toRet;
}
public HearthTreeNode notifyMinionDamaged(PlayerSide targetSide, Minion minion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionDamagedInterface) {
toRet = ((MinionDamagedInterface)character).minionDamagedEvent(characterLocation.getPlayerSide(), targetSide, minion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyMinionDead(PlayerSide deadMinionPlayerSide, Minion deadMinion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionDeadInterface) {
toRet = ((MinionDeadInterface)character).minionDeadEvent(characterLocation.getPlayerSide(), deadMinionPlayerSide, deadMinion, toRet);
}
}
for (Card card : toRet.data_.modelForSide(PlayerSide.CURRENT_PLAYER).getHand()) {
if (card instanceof MinionDeadInterface) {
toRet = ((MinionDeadInterface)card).minionDeadEvent(PlayerSide.CURRENT_PLAYER, deadMinionPlayerSide, deadMinion, toRet);
}
}
for (Card card : toRet.data_.modelForSide(PlayerSide.WAITING_PLAYER).getHand()) {
if (card instanceof MinionDeadInterface) {
toRet = ((MinionDeadInterface)card).minionDeadEvent(PlayerSide.WAITING_PLAYER, deadMinionPlayerSide, deadMinion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyMinionHealed(PlayerSide targetSide, Minion minion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionHealedInterface) {
toRet = ((MinionHealedInterface)character).minionHealedEvent(characterLocation.getPlayerSide(), targetSide, minion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyMinionPlacement(PlayerSide targetSide, Minion minion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionPlacedInterface) {
toRet = ((MinionPlacedInterface)character).minionPlacedEvent(characterLocation.getPlayerSide(), targetSide, minion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyMinionPlayed(PlayerSide targetSide, Minion minion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionPlayedInterface) {
toRet = ((MinionPlayedInterface)character).minionPlayedEvent(characterLocation.getPlayerSide(), targetSide, minion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyMinionSummon(PlayerSide targetSide, Minion minion) {
HearthTreeNode toRet = this;
for (CharacterIndex.CharacterLocation characterLocation : toRet.data_) {
Minion character = toRet.data_.getCharacter(characterLocation);
if (!character.isSilenced() && character instanceof MinionSummonedInterface) {
toRet = ((MinionSummonedInterface)character).minionSummonEvent(characterLocation.getPlayerSide(), targetSide, minion, toRet);
}
}
return toRet;
}
public HearthTreeNode notifyHeroAbilityUsed(PlayerSide targetSide, Minion targetCharacter) {
HearthTreeNode toRet = this;
ArrayList<BoardModel.MinionPlayerPair> inspiredMinions = new ArrayList<>();
for (BoardModel.MinionPlayerPair mp : toRet.data_.getAllMinionsFIFOList()) {
if (mp.getMinion() instanceof MinionWithInspire && !mp.getMinion().isSilenced()) {
inspiredMinions.add(mp);
}
}
for (BoardModel.MinionPlayerPair mp : inspiredMinions) {
toRet = ((MinionWithInspire)mp.getMinion()).heroAbilityUsedEvent(mp.getPlayerSide(), targetSide, targetCharacter, toRet);
}
return toRet;
}
}