@Override
  public String toString() {
    String s =
        "alpha="
            + brain.getAlpha()
            + " gamma="
            + brain.getGamma()
            + "\nlambda="
            + brain.getLambda()
            + " rand="
            + brain.getRandActions()
            + "\nunip="
            + isUnipolar()
            + "\n";

    TreeMap<Integer, WeaponScore> newRanking = new TreeMap<Integer, WeaponScore>();
    for (Map.Entry<Integer, WeaponScore> e : weaponRanking.entrySet()) {
      newRanking.put(e.getKey(), e.getValue());
    }
    weaponRanking = newRanking;

    for (Map.Entry<Integer, WeaponScore> e : weaponRanking.entrySet()) {
      s += "\n" + CommFun.getGunName(e.getKey()) + " : " + e.getValue().getScore();
    }
    return s;
  }
  public RLCombatModule(RLBot bot) {
    this.bot = bot;
    brain = new Brain(this, actions);

    brain.setAlpha(0.8); // learning rate
    brain.setGamma(0.3); // discounting rate
    brain.setLambda(0.6); // trace forgetting
    //        b.setUseBoltzmann(true);
    //        b.setTemperature(0.001);
    brain.setRandActions(0.1); // exploration

    //        brain.setAlpha(getRandParam(0.1, 0.8, 0.1)); //learning rate
    //        brain.setGamma(getRandParam(0, 0.9, 0.2)); //discounting rate
    //        brain.setLambda(getRandParam(0, 0.9, 0.2)); //trace forgetting
    ////        b.setUseBoltzmann(true);
    ////        b.setTemperature(0.001);
    //        brain.setRandActions(getRandParam(0.01, 0.4, 0.1)); //exploration
    //        unipolar = Rand.b();

    for (int i = 7; i < 18; i++) {
      weaponRanking.put(i, new WeaponScore(0.5, 1));
    }
  }
  /**
   * Returns the firing instructions
   *
   * @param fd firing decision
   * @param bot the bot
   * @return
   */
  public FiringInstructions getFiringInstructions(FiringDecision fd) {
    if (fd == null || fd.enemyInfo == null) {
      return null;
    }

    updateRewards();

    boolean reloading = bot.getWorld().getPlayer().getPlayerGun().isCoolingDown();
    Vector3f noFiringLook =
        fd.enemyInfo.predictedPos == null
            ? fd.enemyInfo.getObjectPosition()
            : fd.enemyInfo.predictedPos;
    if (reloading) {
      return getNoFiringInstructions(noFiringLook);
    }

    shootings.add(
        new Shooting(
            bot.getFrameNumber(),
            bot.getFrameNumber()
                + (long)
                    getTimeToHit(
                        bot.cConfig.getBulletSpeedForGivenGun(bot.getCurrentWeaponIndex()),
                        bot.getBotPosition(),
                        fd.enemyInfo.getObjectPosition(),
                        fd.enemyInfo.predictedPos),
            fd.enemyInfo.ent.getName()));

    if (shootings.size() > maxShootingsCount) {
      shootings.removeFirst();
    }

    if (actionEndFrame == -1) {
      // first time
    }
    if (bot.getFrameNumber() >= actionEndFrame) {
      actionEndFrame = bot.getFrameNumber() + actionTime;
      // count rewards:
      bot.rewardsCount++;
      if (actionReward <= 1 && actionReward >= -1) {
        bot.totalReward += actionReward;
      } else {
        bot.totalReward += (actionReward > 0) ? 1 : -1;
        System.out.println("excessive reward! " + actionReward);
      }

      int rc = 1;
      double rwd = actionReward;
      int cwpi = bot.getCurrentWeaponIndex();
      if (weaponRanking.get(cwpi) != null) {
        rc = weaponRanking.get(cwpi).rewardsCount + 1;
        rwd = weaponRanking.get(cwpi).rewardsTotal + actionReward;
      }
      weaponRanking.put(cwpi, new WeaponScore(rwd, rc));

      BotStatistic.getInstance().addReward(bot.getBotName(), actionReward, bot.getFrameNumber());
      //            Dbg.prn(bot.getBotName()+"@"+bot.getFrameNumber()+" r="+actionReward);
      // choose new action and execute it
      perceive();
      brain.count();
      executeAction(actions[brain.getAction()]);
      actionReward = 0;
    }

    //        return getFiringInstructionsAtHitpoint(fd, 1);

    //        return getNewPredictingFiringInstructions(bot, fd,
    // bot.cConfig.getBulletSpeedForGivenGun(bot.getCurrentWeaponIndex()));

    switch (actionFireMode) {
      case Action.FIRE:
        return getFastFiringInstructions(fd, bot);
      case Action.FIRE_PREDICTED:
        return getNewPredictingFiringInstructions(
            bot, fd, bot.cConfig.getBulletSpeedForGivenGun(bot.getCurrentWeaponIndex()));
      default:
      case Action.NO_FIRE:
        return getNoFiringInstructions(noFiringLook);
    }
  }