package pl.edu.pw.mini.jk.sg.mixeduct.attackerresponse
import org.scalatest.WordSpec
import org.scalatest.Matchers
import pl.edu.pw.mini.jk.sg.game.configuration.StaticGameConfigurationReader
import pl.edu.pw.mini.jk.sg.game.graph.GraphGameVariantRunner
import pl.edu.pw.mini.jk.sg.game.graph.NormalizableAdvanceableStateGraphGameFactory
import com.google.common.cache.CacheBuilder
import com.google.common.collect.ImmutableList
import pl.edu.pw.mini.jk.sg.game.graph.moves.single.DesiredConfigurationMove
import pl.edu.pw.mini.jk.sg.gteval.GameMatrices
import pl.edu.pw.mini.jk.sg.opt.util.GameUtil
import pl.edu.pw.mini.jk.sg.game.graph.advanceable.AttackerAdvaceableGraphState
import pl.edu.pw.mini.jk.sg.game.graph.advanceable.AdvanceableDefenderGraphState
import pl.edu.pw.mini.jk.sg.game.graph.advanceable.AdvanceableStateGraphGame
import com.google.common.collect.ImmutableMap
import org.scalactic.TolerantNumerics
import scala.collection.JavaConverters._

final class OptimalAttackerSpec extends WordSpec with Matchers {
  private def toDcm(input: List[Int]) : ImmutableList[DesiredConfigurationMove] = ImmutableList.copyOf(input.map(x => new DesiredConfigurationMove(Array(x))).asJavaCollection.iterator())

  implicit val doubleEquality = TolerantNumerics.tolerantDoubleEquality(0.003)

  "optimal strategy calculator" should {
    "calculate correct result" when {
      "playing against strategy from zssmall-188" in {
        val gameConfig = StaticGameConfigurationReader.readFromStream(getClass().getResourceAsStream("zssmallbuilding-188.json"))
        val gvr = new GraphGameVariantRunner(gameConfig, 5)
        val factory = new NormalizableAdvanceableStateGraphGameFactory(gameConfig,
          Some(CacheBuilder.newBuilder().build[Object, ImmutableList[DesiredConfigurationMove]]()), true)
        val gameMatrices = GameUtil.calculateGameMatrices[
          DesiredConfigurationMove,DesiredConfigurationMove,
          AttackerAdvaceableGraphState,
          AdvanceableDefenderGraphState,
          AdvanceableStateGraphGame
        ](factory, 5)

        val strategy = {
          import java.util.{HashMap => JMap}
          val m = new JMap[ImmutableList[DesiredConfigurationMove], java.lang.Double]()
          m.put(toDcm(List(1,10,9,5,6)), 0.366001)
          m.put(toDcm(List(1,10,9,5,5)), 0.203397)
          m.put(toDcm(List(1, 10, 9, 9, 5)), 0.117104)
          m.put(toDcm(List(1, 10, 9, 5, 4)), 0.087732)
          m.put(toDcm(List(1, 10, 9, 8, 4)), 0.052790)
          m.put(toDcm(List(1, 10, 9, 9, 9)), 0.045523)
          m.put(toDcm(List(1, 10, 9, 9, 8)), 0.036931)
          m.put(toDcm(List(1, 10, 10, 9, 5)), 0.035379)
          m.put(toDcm(List(1, 10, 9, 8, 8)), 0.033630)
          m.put(toDcm(List(1, 10, 9, 8, 9)), 0.021512)
          ImmutableMap.copyOf[ImmutableList[DesiredConfigurationMove], java.lang.Double](m)
        }

        val response = OptimalAttacker.solveAttackerOptimalStrategy(gameMatrices, strategy)
        println(response)
        val payoff = gameMatrices.getExpectedPayoff(strategy, response)
        strategy.values().asScala.map(x => x.toDouble).sum should equal (1.0)
        payoff.defenderPayoff should equal(0.0)
        payoff.attackerPayoff should equal(0.0)
      }

      "playing against another strategy from zssmall-188" in {
        val gameConfig = StaticGameConfigurationReader.readFromStream(getClass().getResourceAsStream("zssmallbuilding-188.json"))
        val gvr = new GraphGameVariantRunner(gameConfig, 5)
        val factory = new NormalizableAdvanceableStateGraphGameFactory(gameConfig,
          Some(CacheBuilder.newBuilder().build[Object, ImmutableList[DesiredConfigurationMove]]()), true)
        val gameMatrices = GameUtil.calculateGameMatrices[
          DesiredConfigurationMove,DesiredConfigurationMove,
          AttackerAdvaceableGraphState,
          AdvanceableDefenderGraphState,
          AdvanceableStateGraphGame
        ](factory, 5)

        val strategy = {
          import java.util.{HashMap => JMap}
          val m = new JMap[ImmutableList[DesiredConfigurationMove], java.lang.Double]()

          m.put(toDcm(List(1, 10, 9, 5, 6)),0.34778888)
          m.put(toDcm(List(1, 10, 9, 5, 4)),0.24046703)
          m.put(toDcm(List(1, 10, 9, 5, 5)),0.23528639)
          m.put(toDcm(List(1, 10, 9, 9, 5)),0.05789850)
          m.put(toDcm(List(1, 10, 9, 9, 8)),0.05198399)
          m.put(toDcm(List(1, 10, 9, 8, 4)),0.03792434)
          m.put(toDcm(List(1, 10, 9, 8, 8)),0.02865087)

          ImmutableMap.copyOf[ImmutableList[DesiredConfigurationMove], java.lang.Double](m)
        }
        strategy.values().asScala.map(x => x.toDouble).sum should equal (1.0)

        val response = OptimalAttacker.solveAttackerOptimalStrategy(gameMatrices, strategy)
        println(response)
        val payoff = gameMatrices.getExpectedPayoff(strategy, response)
        payoff.defenderPayoff should equal(0.0)
        payoff.attackerPayoff should equal(0.0)
      }
    }
  }

}
