self_threat = option.centity.getThreatUtility(Compute.getChargeDamageTakenBy(option.entity, option.PhysicalTarget.entity), CEntity.SIDE_FRONT)*(1-Compute.oddsAbove(toHit.getValue())/100);
self_threat = option.centity.getThreatUtility(Compute.getChargeDamageTakenBy(option.entity, option.PhysicalTarget.entity), CEntity.SIDE_FRONT)*(Compute.oddsAbove(toHit.getValue())/100);
  public EntityState[] calculateMove(Entity entity) {    Object[] enemy_array = Compute.vectorToArray(game.getValidTargets(entity));    CEntity self = this.enemies.get(entity);    EntityState current = self.old;    Object[] move_array;    if (entity.isSelectable() && !self.moved) {      move_array = self.getAllMoves().toArray();    } else {      move_array = new Object[] {current};    }    Compute.randomize(move_array); //this helps produce a better mix of things to do    EntityState.Table pass = new EntityState.Table();        Vector fm = new Vector();    for (Iterator i = this.getEntitiesOwned().iterator(); i.hasNext();){      Entity en = (Entity)i.next();      if (en.getId() != entity.getId()) {        fm.add(new EntityState((this.enemies.get(en)).old));      }    }    Object[] friend_move_array = fm.toArray();        /***************************************************     * first pass, filter moves based upon present case     ****************************************************/    for (int i = 0; i < move_array.length + friend_move_array.length; i++) { // for each state (could some prefiltering be done?)      EntityState option = null;      if (i >= move_array.length) {        option = (EntityState)friend_move_array[i - move_array.length];      } else {        option = (EntityState)move_array[i];      }      option.setState();      if (option.damages == null) option.damages = new double[enemy_array.length];      if (option.threats == null) option.threats = new double[enemy_array.length];      if (option.max_threats == null) option.max_threats = new double[enemy_array.length];      if (option.min_damages == null) option.min_damages = new double[enemy_array.length];            for (int e = 0; e < enemy_array.length; e++) { // for each enemy        Entity en = (Entity)enemy_array[e];        CEntity enemy = this.enemies.get(en);        int enemy_hit_arc = Compute.getThreatHitArc(enemy.old.curPos, enemy.old.curFacing, option.curPos);        int self_hit_arc = Compute.getThreatHitArc(option.curPos, option.curFacing, enemy.old.curPos);        int[] modifiers = option.getModifiers(enemy.entity);        if (!enemy.entity.isImmobile() && modifiers[EntityState.DEFENCE_MOD] != ToHitData.IMPOSSIBLE) {          self.engaged = true;          int mod = modifiers[EntityState.DEFENCE_MOD];          double max = option.getMaxModifiedDamage(enemy.old, this.enemies.get(en), mod, modifiers[EntityState.DEFENCE_PC]);          if (en.isSelectable()) { // let him turn a little            enemy.old.curFacing = (enemy.old.curFacing+1)%6;            max = Math.max(option.getMaxModifiedDamage(enemy.old, this.enemies.get(en), mod+1, modifiers[EntityState.DEFENCE_PC]),max);            enemy.old.curFacing = (enemy.old.curFacing+4)%6;            max = Math.max(option.getMaxModifiedDamage(enemy.old, this.enemies.get(en), mod+1, modifiers[EntityState.DEFENCE_PC]),max);            //return to original facing            enemy.old.curFacing = (enemy.old.curFacing+1)%6;          }          max = self.getThreatUtility(max, self_hit_arc);          if (enemy.entity.isProne()) max *= .6;          option.threats[e] = max;          option.max_threats[e] = max;          option.threat += max;          option.tv.add(max+" Threat "+e+"\n");        }                //damage reasoning        /* As a first approximation, take the maximum to a single target */        if (!option.isPhysical) {          if (modifiers[EntityState.ATTACK_MOD] != ToHitData.IMPOSSIBLE) {            self.engaged = true;            double max = enemy.old.getMaxModifiedDamage(option, self, modifiers[0], modifiers[EntityState.ATTACK_PC]);            max = enemy.getThreatUtility(max, enemy_hit_arc);            option.damages[e] = max;            option.min_damages[e] = max;            option.tv.add(max+" Damage "+e+"\n");            option.damage = Math.max(max, option.damage);          }        } else {          try {            if (option.PhysicalTarget.entity.getId() == enemy.entity.getId()) {              if (!option.PhysicalTarget.isPhysicalTarget) {                ToHitData toHit = null;                double self_threat = 0;                double damage = 0;                if (option.isJumping) {                  toHit = Compute.toHitDfa(game, option.entity.getId(), option.PhysicalTarget.entity.getId(), option.curPos);                  damage = 2*Compute.getDfaDamageFor(option.entity);                  self_threat = option.centity.getThreatUtility(Compute.getDfaDamageTakenBy(option.entity), CEntity.SIDE_REAR)*Compute.oddsAbove(toHit.getValue())/100;                  self_threat += option.centity.getThreatUtility(.1*self.entity.getWeight(), CEntity.SIDE_REAR);                  self_threat *= 100/option.centity.entity.getWeight();                } else {                  self.old.setState();                  MovementData md = option.getMovementData();                  toHit = Compute.toHitCharge(game, option.entity.getId(), option.PhysicalTarget.entity.getId(), md);                  damage = Compute.getChargeDamageFor(option.entity, md.getHexesMoved());                  self_threat = option.centity.getThreatUtility(Compute.getChargeDamageTakenBy(option.entity, option.PhysicalTarget.entity), CEntity.SIDE_FRONT)*(1-Compute.oddsAbove(toHit.getValue())/100);                  option.setState();                }                damage = option.PhysicalTarget.getThreatUtility(damage, toHit.getSideTable())*Compute.oddsAbove(toHit.getValue())/100;                //these are always risky...                if (toHit.getValue() > 10) damage = 0;                option.damages[e] = damage;                option.min_damages[e] = damage;                option.damage = damage;                option.self_threat += self_threat;              } else {                option.threat += Integer.MAX_VALUE;              }            }          } catch (Exception e1) {            e1.printStackTrace();            option.threat += Integer.MAX_VALUE;          }        }      } //-- end while of each enemy      self.old.setState();    } //-- end while of first pass        Arrays.sort(move_array);        int filter = 50;    if (friend_move_array.length > 1) {      filter = 100;    }    //top 100 utility, mostly conservative    for (int i = 0; i < filter && i < move_array.length ; i++) {      pass.put((EntityState)move_array[i]);    }        Arrays.sort(move_array, new Comparator() {      public int compare(Object obj, Object obj1) {        if (((EntityState)obj).damage - .5*((EntityState)obj).getUtility() >        ((EntityState)obj1).damage - .5*((EntityState)obj1).getUtility()) {          return -1;        }        return 1;      }    });        //top 100 damage    for (int i = 0; i < filter && i < move_array.length; i++) {      pass.put((EntityState)move_array[i]);    }        //pass now contains 100 ~ 200 moves    /*********************************************************     *  New second pass, combination moves/firing     *    based only on the present case, since only one mech moves     *    at a time     ********************************************************/    if (friend_move_array.length > 1) {      move_array = pass.values().toArray();      pass.clear();      double[] combo = new double[enemy_array.length];      for (int j = 0; j < move_array.length; j++) {        EntityState option = (EntityState)move_array[j];        for (int e = 0; e < enemy_array.length; e++) { // for each enemy          Entity en = (Entity)enemy_array[e];          CEntity enemy = this.enemies.get(en);          if (option.damages[e] > 0) {            for (int f = 0; f < friend_move_array.length; f++) {              EntityState foption = (EntityState)friend_move_array[f];              double threat_divisor = 1;              if (foption.damages[e] > 0) {                option.damage += (enemy.canMove()?.1:.2)*option.damages[e] ;                threat_divisor += foption.centity.canMove()?.4:.6;              }              option.threat -= option.threats[e];                //this is most important              option.threats[e] /= threat_divisor;              option.threat += option.threats[e];            }          }        }      }        Arrays.sort(move_array);            filter = 50;            //top utility, mostly conservative      for (int i = 0; i < filter && i < move_array.length ; i++) {        pass.put((EntityState)move_array[i]);      }            Arrays.sort(move_array, new Comparator() {        public int compare(Object obj, Object obj1) {          if (((EntityState)obj).damage - .5*((EntityState)obj).getUtility() >          ((EntityState)obj1).damage - .5*((EntityState)obj1).getUtility()) {            return -1;          }          return 1;        }      });            //top 50 damage      for (int i = 0; i < filter && i < move_array.length; i++) {        pass.put((EntityState)move_array[i]);      }    }        /**********************************************************     * third pass, (not so bad) oppurtunistic planner     * gives preference to good ranges/defensive positions     * based upon the mech characterization     ***********************************************************/    move_array = pass.values().toArray();    pass.clear();        for (int j = 0; j < move_array.length; j++) {      EntityState option = (EntityState)move_array[j];      option.setState();      double adjustment = 0;      double temp_adjustment = 0;      for (int e = 0; e < enemy_array.length; e++) { // for each enemy        Entity en = (Entity)enemy_array[e];        CEntity enemy = this.enemies.get(en);        int current_range = self.old.curPos.distance(enemy.old.curPos);        int range = option.curPos.distance(enemy.old.curPos);        if (range > self.long_range) {          temp_adjustment += (!(range < enemy.long_range)?.5:1)*(1+self.RangeDamages[self.Range])*(Math.max(range - self.long_range - .5*Math.max(self.jumpMP, .8*self.runMP),0));        }        //this is wrong on larger maps...        //a mech must choose which other mechs to be engaged with or stay in the middle        if ((self.Range == self.RANGE_SHORT && (current_range > 5 || range > 9)) || (self.RangeDamages[self.RANGE_SHORT] < 4 && current_range > 10)) {          temp_adjustment += ((enemy.Range > self.RANGE_SHORT)?.5:1)*(Math.max(1+self.RangeDamages[self.RANGE_SHORT], 5))*Math.max(range -.5*Math.max(self.jumpMP, .8*self.runMP),0);        } else if (self.Range == self.RANGE_MEDIUM) {          temp_adjustment += ((current_range < 6 || current_range > 12)?1:.25)*((enemy.Range > self.RANGE_SHORT)?.5:1)*(1+self.RangeDamages[self.RANGE_MEDIUM])*Math.abs(range - .5*Math.max(self.jumpMP, .8*self.runMP));        } else if (option.damage < .25*self.RangeDamages[self.RANGE_LONG]) {          temp_adjustment += ((range < 10)?.25:1)*(Math.max(1+self.RangeDamages[self.RANGE_LONG],3))*(1/(1+option.threat));        }        adjustment += Math.sqrt(temp_adjustment*enemy.bv/self.bv);        //I would always like to face the opponent (the most open direction)        if (!(enemy.entity.isProne() || enemy.entity.isImmobile()) && Compute.getThreatHitArc(option.curPos, option.curFacing, enemy.entity.getPosition()) != CEntity.SIDE_FRONT) {          int fa = Compute.getFiringAngle(option.curPos, option.curFacing, enemy.entity.getPosition());          if (fa > 90 && fa < 270) {            int distance = option.curPos.distance(enemy.old.curPos);            double mod = 1;            if (fa > 130 && fa < 240) mod = 2;            mod *= (Math.max(self.jumpMP, .8*self.runMP) < 5)?4:2*Math.sqrt(((double)self.bv)/enemy.bv)/((double)distance/6 + 1);            option.self_threat += mod;            option.tv.add(mod + " " + fa + " Back to enemy\n");          }        }      }      adjustment *= self.overall_armor_percent*self.strategy.attack/enemy_array.length;      //fix for hiding in level 2 water      //To a greedy bot, it always seems nice to stay in here...      Hex h = game.board.getHex(option.curPos);      if (h.contains(Terrain.WATER) && h.surface() > (self.entity.elevation() + ((option.isProne)?0:1))) {        double mod = (self.entity.heat + option.getMovementheatBuildup() <= 7)?100:30;        adjustment += self.bv/mod;      }      //add them in now, then re-add them later      if (self.Range > self.RANGE_SHORT) {        int ele_dif = game.board.getHex(option.curPos).getElevation() - game.board.getHex(self.old.curPos).getElevation();        adjustment -= (Math.max(ele_dif, 0) + 1)*((double)Compute.getTargetTerrainModifier(game, option.entity.getId()).getValue() + 1);      }            //close the range if nothing else and healthy      if (option.damage < .25*self.RangeDamages[self.Range] && adjustment < .25*self.RangeDamages[self.Range] && self.overall_armor_percent > .25) {        for (int e = 0; e < enemy_array.length; e++) { // for each enemy          Entity en = (Entity)enemy_array[e];          CEntity enemy = this.enemies.get(en);          int range = option.curPos.distance(enemy.old.curPos);          adjustment += Math.sqrt((double)range*enemy.bv/(double)self.bv)/enemy_array.length;          }      }               if (option.damage < .25*(1+self.RangeDamages[self.Range])) {        option.self_damage = -2*adjustment;      } else if (option.damage < .5*(1+self.RangeDamages[self.Range])) {        option.self_damage = -adjustment;      }      option.tv.add(option.self_damage+" Initial Damage Adjustment " +"\n");    }    Arrays.sort(move_array);        //top 30 utility    for (int j = 0; j < 30 && j < move_array.length; j++) {      pass.put((EntityState)move_array[j]);    }        Arrays.sort(move_array, new Comparator() {      public int compare(Object obj, Object obj1) {        if (((EntityState)obj).damage - ((EntityState)obj).getUtility() >        ((EntityState)obj1).damage - ((EntityState)obj1).getUtility()) {          return -1;        }        return 1;      }    });        //top 30 damage    for (int i = 0; i < 30 && i < move_array.length ; i++) {      pass.put((EntityState)move_array[i]);    }        //reduce self threat, and add bonus for terrain    for (com.sun.java.util.collections.Iterator i = pass.values().iterator(); i.hasNext();) {      EntityState option = (EntityState)i.next();      option.setState();      option.self_damage *= .25;      option.self_threat *= .5;      double terrain = 2*((double)Compute.getTargetTerrainModifier(game, option.entity.getId()).getValue());      option.tv.add(terrain+" Terrain Adjusment " +"\n");        option.self_threat -= terrain;    }        move_array = pass.values().toArray();    pass.clear();        //pass should contains 30 ~ 60    /******************************************     * fourth pass, speculation on top moves     * use averaging to filter     ********************************************/    for (int e = 0; e < enemy_array.length; e++) { // for each enemy      Entity en = (Entity)enemy_array[e];      CEntity enemy = this.enemies.get(en);      //engage in speculation on "best choices" when you have lost the iniative      if (enemy.canMove()) {        Object[] enemy_move_array = enemy.calculateCounterMoves().toArray();        Vector to_check = new Vector();        //check some enemy moves        for (int j = 0; j < move_array.length; j++) {          EntityState option = null;          to_check.clear();          option = (EntityState)move_array[j];          option.setState();          //check for damning hexes specifically          //could also look at intervening defensive          Vector coord = new Vector();          Coords back = option.curPos.translated((option.curFacing + 3) % 6);          coord.add(back);          coord.add(back.translated((option.curFacing + 2) % 6));          coord.add(back.translated((option.curFacing + 4) % 6));          coord.add(option.curPos.translated((option.curFacing)));          coord.add(option.curPos.translated((option.curFacing + 1)%6));          coord.add(option.curPos.translated((option.curFacing + 2)%6));          coord.add(option.curPos.translated((option.curFacing + 4)%6));          coord.add(option.curPos.translated((option.curFacing + 5)%6));          Iterator ci = coord.iterator();          while (ci.hasNext()) {            Coords test = (Coords)ci.next();            Vector c = enemy.findMoves(test);            if (c.size() != 0) to_check.addAll(c);          }          int range = option.curPos.distance(enemy.old.curPos);          int compare=0;          if ((enemy.long_range) > range - Math.max(enemy.jumpMP, enemy.runMP)) {            compare = 30;          } else if (enemy.long_range > range) {            compare = 10;          }          double mod = this.enemies_moved/this.getEnemyEntities().size();          compare *= (1 + mod);          for (int k = 0; k <= compare && k < enemy_move_array.length; k++) {            if (enemy_move_array.length < compare) {              to_check.add(enemy_move_array[k]);            } else {              int value = Compute.random.nextInt(enemy_move_array.length);              if (value%2 == 1) {                to_check.add(enemy_move_array[value]);              } else {                to_check.add(enemy_move_array[k]);              }            }          }          Iterator eo = to_check.iterator();          while (eo.hasNext()) {            EntityState enemy_option = (EntityState)eo.next();            double max_threat = 0;            double max_damage = 0;            enemy_option.setState();            int enemy_hit_arc = Compute.getThreatHitArc(enemy_option.curPos, enemy_option.curFacing, option.curPos);            int self_hit_arc = Compute.getThreatHitArc(enemy_option.curPos, enemy_option.curFacing, option.curPos);            if (enemy_option.isJumping) {              enemy_hit_arc = Compute.ARC_FORWARD; //assume they will choose forward, but really should be the strongest            }            int[] modifiers = option.getModifiers(enemy_option.entity);            if (modifiers[1] != ToHitData.IMPOSSIBLE) {              self.engaged = true;              if (!enemy_option.isJumping) {                max_threat = option.getMaxModifiedDamage(enemy_option, this.enemies.get(en), modifiers[1], modifiers[EntityState.DEFENCE_PC]);              } else {                max_threat = .8*enemy.getModifiedDamage((modifiers[EntityState.DEFENCE_PC]==1)?CEntity.TT:CEntity.SIDE_FRONT, enemy_option.curPos.distance(option.curPos), modifiers[1]);              }              max_threat = self.getThreatUtility(max_threat, self_hit_arc);            }            if (modifiers[0] != ToHitData.IMPOSSIBLE) {              self.engaged = true;              max_damage = enemy_option.getMaxModifiedDamage(option, self, modifiers[0], modifiers[EntityState.ATTACK_PC]);              max_damage = enemy.getThreatUtility(max_damage, enemy_hit_arc);              if (option.isPhysical) {                if (option.PhysicalTarget.entity.getId() == enemy.entity.getId()) {                  max_damage = option.damages[e];                } else {                  max_damage = 0;                }              }            }            option.max_threats[e] = Math.max(max_threat, option.max_threats[e]);            option.min_damages[e] = Math.min(option.min_damages[e], max_damage);            if (max_threat - max_damage > option.threats[e] - option.damages[e]) {              option.threats[e] = max_threat;              option.damages[e] = max_damage;              option.tv.add(max_threat+" Spec Threat "+e+"\n");              option.tv.add(max_damage+" Spec Damage "+e+"\n");            }          }          //update estimates          option.damage = 0;          option.threat = 0;          for (int d = 0; d < option.damages.length; d++) {            //my damage is the average of expected and min            option.damage += (option.min_damages[e] + option.damages[e])/2;            //my threat is average of absolute worst, and expected            option.threat = Math.max(option.threat, option.max_threats[e] + option.threats[e])/2;            option.threats[e] = (option.max_threats[e] + 2*option.threats[e])/3;          }        }        //restore enemy        enemy.old.setState();      }      self.old.setState();    } //--end move speculation        Arrays.sort(move_array);        //top 40 utility    for (int i = 0; i < 20 && i < move_array.length ; i++) {      pass.put((EntityState)move_array[i]);    }        /*Arrays.sort(move_array, new Comparator() {      public int compare(Object obj, Object obj1) {        if (((EntityState)obj).damage*.5 - ((EntityState)obj).getUtility() >        ((EntityState)obj1).damage*.5 - ((EntityState)obj1).getUtility()) {          return -1;        }        return 1;      }    });        //top 20 damage    for (int i = 0; i < 20 && i < move_array.length ; i++) {      pass.put((EntityState)move_array[i]);    }*/        for (com.sun.java.util.collections.Iterator i = pass.values().iterator(); i.hasNext();) {      EntityState option = (EntityState)i.next();      option.self_threat *= .5;      option.self_damage *= .5;    }        move_array = pass.values().toArray();    pass.clear();        //pass should now be 20 ~ 40    /**************************************************     * fourth pass, final damage and threat approximation     *  --prevents moves that from the previous pass would     *    cause the mech to die     ************************************************/    if (self.engaged) {      int remaining = game.getNoOfEntities() - game.getEntitiesOwnedBy(getLocalPlayer());      for (int j = 0; j < move_array.length; j++) {        EntityState option = (EntityState)move_array[j];        option.setState();        //if (this.enemies_moved > 0) {          GAAttack temp = this.bestAttack(option);          double[] damages = null;          if (temp != null) {            //will increase the utility of states that all for overheat cooling, etc.            option.damage = (option.damage + temp.getFittestChromosomesFitness())/2;          } else {            option.damage /= 2;          }          for (int e = 0; e < enemy_array.length; e++) { // for each enemy            Entity en = (Entity)enemy_array[e];            CEntity enemy = this.enemies.get(en);            if (!enemy.canMove()) {              option.threats[e] = (option.threats[e] + this.attackUtility(enemy.old, self))/2;              option.tv.add(option.threats[e]+" Revised Threat "+e+" \n");              if (!option.isPhysical) {                if (temp != null) {                  option.damages[e] = (option.damages[e] + temp.getDamageUtility(enemy))/2;                  option.tv.add(option.damages[e]+" Revised Damage "+e+" \n");                } else {                  //probably zero, but just in case                  option.damages[e] = option.min_damages[e];                  option.tv.add(option.damages[e]+" Revised Damage "+e+" \n");                }                if (option.curPos.distance(enemy.old.curPos) == 1) {                  PhysicalOption p = this.getBestPhysicalAttack(option.entity.getId(), enemy.entity.getId());                  if (p != null) {                    option.damages[e] += p.expectedDmg;                    option.tv.add(p.expectedDmg+" Physical Damage "+e+" \n");                  }                  p = this.getBestPhysicalAttack(enemy.entity.getId(),option.entity.getId());                  if (p != null) {                    option.threats[e] += .25*p.expectedDmg;                    option.tv.add(.5*p.expectedDmg+" Physical Threat "+e+" \n");                  }                }              }            } else if (!option.isPhysical) { //enemy can move (not physical check just in case)              if (temp != null) {                option.damages[e] = (option.damages[e] + temp.getDamageUtility(enemy))/2;              } else {                option.damages[e] = option.min_damages[e];              }            }          }          option.threat = 0;          for (int d = 0; d < option.damages.length; d++) {            option.threat += option.threats[d];          }          option.tv.add(option.threat+" Revised Threat Utility\n");          option.tv.add(option.damage+" Revised Damage Utility\n");        //}      }    }        Arrays.sort(move_array);    self.old.setState(); //just to make sure we are back to where we started        /**********************************************     * Return top twenty moves to the lance algorithm     **********************************************/    EntityState[] result = new EntityState[Math.min(move_array.length,20)];    for (int i = 0; i < Math.min(move_array.length,20); i++) {      result[i] = (EntityState)move_array[i];      //if (i < 10) System.out.println(result[i] + " " + result[i].getUtility());    }    return result;  }