public JSONObject toJSON() {
    JSONObject json = new JSONObject();

    json.put("name", name);
    json.put("playerId", playerId);
    json.put("hero", hero.toJSON());
    if (mana != maxMana) json.put("mana", mana);
    if (maxMana > 0) json.put("maxMana", maxMana);
    json.put("deckPos", deckPos);
    if (overload > 0) json.put("overload", overload);
    if (fatigueDamage > 0) json.put("fatigueDamage", fatigueDamage);

    if (minions.size() > 0) {
      JSONArray array = new JSONArray();
      for (Minion minion : minions) {
        array.put(minion.toJSON());
      }
      json.put("minions", array);
    }

    if (hand.size() > 0) {
      JSONArray array = new JSONArray();
      for (Card card : hand) {
        array.put(card.toJSON());
      }
      json.put("hand", array);
    }

    if (numCardsUsed > 0) {
      json.put("numCardsUsed", numCardsUsed);
    }

    return json;
  }
 @Override
 public int hashCode() {
   int hs = p0_hand_.size();
   if (hs < 0) hs = 0;
   int res = hs + p0_minions_.size() * 10 + p1_minions_.size() * 100;
   res += (p0_mana_ <= 0 ? 0 : (p0_mana_ - 1) * 1000);
   res += ((p0_hero_.getHealth() + p1_hero_.getHealth()) % 100) * 10000;
   int th = 0;
   if (hs > 0) {
     Card cc = p0_hand_.get(0);
     try {
       Minion mm = (Minion) cc;
       th += (cc.hasBeenUsed() ? 1 : 0) + mm.getAttack() + mm.getHealth() + cc.getMana();
     } catch (ClassCastException e) {
       th += (cc.hasBeenUsed() ? 1 : 0) + cc.getMana();
     }
   }
   if (hs > 1) {
     Card cc = p0_hand_.get(1);
     try {
       Minion mm = (Minion) cc;
       th += (cc.hasBeenUsed() ? 1 : 0) + mm.getAttack() + mm.getHealth() + cc.getMana();
     } catch (ClassCastException e) {
       th += (cc.hasBeenUsed() ? 1 : 0) + cc.getMana();
     }
   }
   res += (th % 10) * 1000000;
   int mh0 = 0;
   if (p0_minions_.size() > 0) {
     mh0 += p0_minions_.get(0).getHealth();
   }
   if (p0_minions_.size() > 1) {
     mh0 += p0_minions_.get(1).getHealth();
   }
   res += (mh0 % 100) * 10000000;
   int mh1 = 0;
   if (p1_minions_.size() > 0) {
     mh1 += p1_minions_.get(0).getHealth();
   }
   if (p1_minions_.size() > 1) {
     mh1 += p1_minions_.get(1).getHealth();
   }
   res += (mh1 % 20) * 100000000;
   return res;
 }
 @Override
 public int hashCode() {
   int hash = 1;
   hash = hash * 31 + (null == name ? 0 : name.hashCode());
   hash = hash * 31 + playerId;
   hash = hash * 31 + (null == hero ? 0 : hero.hashCode());
   hash = hash * 31 + (null == deck ? 0 : deck.hashCode());
   hash = hash * 31 + mana;
   hash = hash * 31 + maxMana;
   hash = hash * 31 + deckPos;
   hash = hash * 31 + fatigueDamage;
   hash = hash * 31 + (null == minions ? 0 : minions.hashCode());
   hash = hash * 31 + (null == hand ? 0 : hand.hashCode());
   hash = hash * 31 + overload;
   hash = hash * 31 + numCardsUsed;
   return hash;
 }
  @Override
  public boolean equals(Object other) {

    if (other == null) return false;

    if (this.getClass() != other.getClass()) return false;

    PlayerModel otherPlayer = (PlayerModel) other;

    if (playerId != otherPlayer.playerId) return false;
    if (mana != otherPlayer.mana) return false;
    if (maxMana != otherPlayer.maxMana) return false;
    if (overload != otherPlayer.overload) return false;
    if (deckPos != otherPlayer.deckPos) return false;
    if (fatigueDamage != otherPlayer.fatigueDamage) return false;

    if (!name.equals(otherPlayer.name)) return false;
    if (!hero.equals(otherPlayer.hero)) return false;
    if (deck != null && !deck.equals(otherPlayer.deck)) return false;
    if (!minions.equals(otherPlayer.minions)) return false;
    if (!hand.equals(otherPlayer.hand)) return false;
    if (numCardsUsed != otherPlayer.numCardsUsed) return false;
    return true;
  }
  public boolean equals(Object other) {
    if (other == null) {
      return false;
    }

    if (this.getClass() != other.getClass()) {
      return false;
    }

    if (p0_mana_ != ((BoardState) other).p0_mana_) return false;
    if (p1_mana_ != ((BoardState) other).p1_mana_) return false;
    if (p0_maxMana_ != ((BoardState) other).p0_maxMana_) return false;
    if (p1_maxMana_ != ((BoardState) other).p1_maxMana_) return false;

    if (!p0_hero_.equals(((BoardState) other).p0_hero_)) {
      return false;
    }

    if (!p1_hero_.equals(((BoardState) other).p1_hero_)) {
      return false;
    }

    if (p0_deckPos_ != ((BoardState) other).p0_deckPos_) return false;

    if (p1_deckPos_ != ((BoardState) other).p1_deckPos_) return false;

    if (p0_fatigueDamage_ != ((BoardState) other).p0_fatigueDamage_) return false;

    if (p1_fatigueDamage_ != ((BoardState) other).p1_fatigueDamage_) return false;

    if (p0_spellDamage_ != ((BoardState) other).p0_spellDamage_) return false;

    if (p1_spellDamage_ != ((BoardState) other).p1_spellDamage_) return false;

    if (p0_minions_.size() != ((BoardState) other).p0_minions_.size()) return false;
    if (p1_minions_.size() != ((BoardState) other).p1_minions_.size()) return false;
    if (p0_hand_.size() != ((BoardState) other).p0_hand_.size()) return false;

    for (int i = 0; i < p0_minions_.size(); ++i) {
      if (!p0_minions_.get(i).equals(((BoardState) other).p0_minions_.get(i))) {
        return false;
      }
    }

    for (int i = 0; i < p1_minions_.size(); ++i) {
      if (!p1_minions_.get(i).equals(((BoardState) other).p1_minions_.get(i))) {
        return false;
      }
    }

    for (int i = 0; i < p0_hand_.size(); ++i) {
      if (!p0_hand_.get(i).equals(((BoardState) other).p0_hand_.get(i))) {
        return false;
      }
    }

    for (int i = 0; i < p1_hand_.size(); ++i) {
      if (!p1_hand_.get(i).equals(((BoardState) other).p1_hand_.get(i))) {
        return false;
      }
    }

    // More logic here to be discuss below...
    return true;
  }
 public int getNumCards_hand_p1() {
   return p1_hand_.size();
 }
 public int getNumCards_hand_p0() {
   return p0_hand_.size();
 }
 public Card getCard_hand_p0(int index) {
   return p0_hand_.get(index);
 }
 public void removeCard_hand(Card card) {
   p0_hand_.remove(card);
 }
 public void placeCard_hand_p1(Card card) {
   card.isInHand(true);
   p1_hand_.add(card);
 }
 public void placeCard_hand(int playerIndex, Card card) throws HSInvalidPlayerIndexException {
   card.isInHand(true);
   if (playerIndex == 0) p0_hand_.add(card);
   else if (playerIndex == 1) p1_hand_.add(card);
   else throw new HSInvalidPlayerIndexException();
 }
 public Iterator<Card> getIterator_hand() {
   return p0_hand_.iterator();
 }
 public void setCard_hand(Card card, int index) {
   p0_hand_.set(index, card);
 }
 public Card getCard_hand_p1(int index) {
   return p1_hand_.get(index);
 }
 public int getNumCards_hand(int playerIndex) throws HSInvalidPlayerIndexException {
   if (playerIndex == 0) return p0_hand_.size();
   else if (playerIndex == 1) return p1_hand_.size();
   else throw new HSInvalidPlayerIndexException();
 }
 public int getNumMinions() {
   if (minions == null) return 0;
   return minions.size();
 }
 public Card getCard_hand(int playerIndex, int index) throws HSInvalidPlayerIndexException {
   if (playerIndex == 0) return p0_hand_.get(index);
   else if (playerIndex == 1) return p1_hand_.get(index);
   else throw new HSInvalidPlayerIndexException();
 }