package com.hearthsim.util.factory;
import com.hearthsim.card.CharacterIndex;
import com.hearthsim.card.Deck;
import com.hearthsim.card.Location;
import com.hearthsim.exception.HSException;
import com.hearthsim.model.BoardModel;
import com.hearthsim.model.PlayerSide;
import com.hearthsim.player.playercontroller.BoardScorer;
import com.hearthsim.util.tree.HearthTreeNode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public abstract class BoardStateFactoryBase {
protected final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());
protected final Deck deckPlayer0_;
private final ChildNodeCreator childNodeCreator;
/**
* Constructor
*
* @param deckPlayer0
* @param deckPlayer1
*/
public BoardStateFactoryBase(Deck deckPlayer0, Deck deckPlayer1) {
this(deckPlayer0, deckPlayer1, new ChildNodeCreatorBase(deckPlayer0, deckPlayer1));
}
/**
* Constructor
*
* @param deckPlayer0
* @param deckPlayer1
*/
public BoardStateFactoryBase(Deck deckPlayer0, Deck deckPlayer1, ChildNodeCreator creator) {
deckPlayer0_ = deckPlayer0;
childNodeCreator = creator;
}
protected ArrayList<HearthTreeNode> createChildren(HearthTreeNode boardStateNode) throws HSException {
ArrayList<HearthTreeNode> nodes = new ArrayList<>();
nodes.addAll(this.childNodeCreator.createHeroAbilityChildren(boardStateNode));
nodes.addAll(this.childNodeCreator.createPlayCardChildren(boardStateNode));
nodes.addAll(this.childNodeCreator.createAttackChildren(boardStateNode));
return nodes;
}
/**
* Recursively generate all possible moves
* This function recursively generates all possible moves that can be done starting from a given BoardState.
* While generating the moves, it applies the scoring function to each BoardState generated, and it will only keep the
* highest scoring branch.
* The results are stored in a tree structure and returned as a tree of BoardState class.
*
* @param boardStateNode The initial BoardState wrapped in a HearthTreeNode.
* @param scoreFunc The scoring function for AI.
* @return boardStateNode manipulated such that all subsequent actions are children of the original boardStateNode input.
*/
public abstract HearthTreeNode doMoves(HearthTreeNode boardStateNode, BoardScorer ai) throws HSException;
/**
* Handles dead minions
* For each dead minion, the function calls its deathrattle in the correct order, and then removes the dead minions from the board.
*
* @return true if there are dead minions left (minions might have died during deathrattle). false otherwise.
* @throws HSException
*/
public static HearthTreeNode handleDeadMinions(HearthTreeNode boardState) {
HearthTreeNode toRet = boardState;
// First, remove all the dead minions. If the dead minions had deathrattles, queue them up.
List<MinionPlayerLocation> deadMinions = new ArrayList<>();
Iterator<BoardModel.MinionPlayerPair> minionIter = toRet.data_.getAllMinionsFIFOList().iterator();
while (minionIter.hasNext()) {
BoardModel.MinionPlayerPair minionIdPair = minionIter.next();
if (minionIdPair.getMinion().getTotalHealth() <= 0) {
// Determine the proper character location
CharacterIndex leftIndex = boardState.data_.modelForSide(minionIdPair.getPlayerSide()).getIndexForCharacter(minionIdPair.getMinion());
while (leftIndex != CharacterIndex.HERO && leftIndex != CharacterIndex.UNKNOWN &&
boardState.data_.modelForSide(minionIdPair.getPlayerSide()).getCharacter(leftIndex.indexToLeft()).getTotalHealth() <= 0) {
leftIndex = leftIndex.indexToLeft();
}
deadMinions.add(new MinionPlayerLocation(minionIdPair, new Location<>(minionIdPair.getPlayerSide(), leftIndex)));
}
}
for (MinionPlayerLocation minionPlayerLocation : deadMinions) {
toRet.data_.removeMinion(minionPlayerLocation.minionPlayerPair);
}
// Next, resolve each minion's death sequence
for (MinionPlayerLocation minionPlayerLocation : deadMinions) {
PlayerSide playerSide = minionPlayerLocation.minionPlayerPair.getPlayerSide();
toRet = minionPlayerLocation.minionPlayerPair.getMinion()
.destroyAndNotify(playerSide, minionPlayerLocation.location.getIndex(), toRet);
}
if (toRet.data_.hasDeadMinions())
return BoardStateFactoryBase.handleDeadMinions(toRet);
else
return toRet;
}
private static class MinionPlayerLocation {
public final BoardModel.MinionPlayerPair minionPlayerPair;
public final Location<CharacterIndex> location;
public MinionPlayerLocation(BoardModel.MinionPlayerPair minionPlayerPair, Location<CharacterIndex> location) {
this.minionPlayerPair = minionPlayerPair;
this.location = location;
}
}
}