package pl.edu.pw.mini.game.flipit.deterministic.allpoints

import argonaut.Parse
import org.scalactic.TolerantNumerics
import org.scalatest.{ Matchers, WordSpec }
import pl.edu.pw.mini.game.flipit.deterministic.{ NoOp, Flip, FlipItConfig, FlipItMove }
import scala.annotation.tailrec


class AllPointsFlipItGameSpec extends WordSpec with Matchers {

  implicit val doubleEquality = TolerantNumerics.tolerantDoubleEquality(0.001)


  "game 4c-10" when {
    val config =
      Parse.decodeEither[FlipItConfig](
        scala.io.Source.fromInputStream(getClass.getResourceAsStream("flipit-4c-10.apflip")).mkString
      ).right.get
    val game = AllPointsFlipItGameFactory.create(config)

    "playing (NoOp, Flip(0))" should {
      val as = game.makeMove(NoOp, Flip(0)).attackerState.asInstanceOf[AllPointsFlipItAttackerStateInner]
      "succeed" in {
        as.previousMoveSucceded should be(true)
      }
      "should allow attacker move further" in {
        as.possibleMoves should contain only (NoOp, Flip(0), Flip(1), Flip(2))
      }
    }

    def gameTest(defenderMove: List[FlipItMove], attackerMove: List[FlipItMove],
      defenderPayoff: Double, attackerPayoff: Double) = {
      require(defenderMove.length == attackerMove.length)
      @tailrec def checkRound(gameState: AllPointsFlipItGame, defenderMove: List[FlipItMove],
        attackerMove: List[FlipItMove], round: Int): Unit = {
        (attackerMove, defenderMove) match {
          case (am::ams, dm::dms) => {
            s"allow A: ${am} in round ${round}" in {
              gameState.attackerState.possibleMoves should contain(am)
            }
            s"allow D: ${dm} in round ${round}" in {
              gameState.defenderState.possibleMoves should contain(dm)
            }
            val nn = gameState.makeMove(dm, am)

            s"advance to allowable attacker state in roud ${round}" in {
              gameState.attackerState.possibleContinuations(am) should contain(nn.attackerState)
            }

            checkRound(nn, dms, ams, round+1)
          }
          case _ => {
            "be in terminal state after all moves" in {
              gameState.defenderState.possibleMoves should equal(Nil)
              gameState.attackerState.possibleMoves should equal(Nil)
            }
            s"give defender reward of ${defenderPayoff}" in {
              gameState.defenderPayoff should equal(defenderPayoff)
            }
            s"give attcker reward of ${attackerPayoff}" in {
              gameState.attackerPayoff should equal(attackerPayoff)
            }
          }
        }
      }
      s"playing sequence D(${defenderMove}) A(${attackerMove})" should {
        checkRound(game, defenderMove, attackerMove, 1)
      }
    }

    gameTest(List(NoOp, Flip(0), NoOp, NoOp, Flip(1)),
      List(Flip(0), NoOp, Flip(1), Flip(1), NoOp), 93.817, -9.555)

    gameTest(List(Flip(0), Flip(0), Flip(0), Flip(0), Flip(1)),
      List(Flip(0), NoOp, Flip(0), Flip(0), NoOp), 80.526, -17.314)

    gameTest(List(NoOp, NoOp, Flip(0), NoOp, Flip(1)),
      List(Flip(0), NoOp, Flip(1), Flip(3), NoOp), 71.459, 14.583)
    
  }
}
