package pl.edu.pw.mini.sg.mixed

import pl.edu.pw.mini.sg.game.boundedrationality.DistortedPayoff
import pl.edu.pw.mini.sg.mixed.tree.{ CalculateResults, LeafNode }
import pl.edu.pw.mini.sg.mixed.response.AttackerResponseFinder
import pl.edu.pw.mini.sg.util.stop.{ NoImprovementStop, StopCriterion }
import scala.annotation.tailrec

import pl.edu.pw.mini.sg.rng.RandomGenerator
import org.log4s._
import pl.edu.pw.mini.sg.game.Payoff
import pl.edu.pw.mini.sg.game.deterministic.{AdvanceablePlayerVisibleState, DeterministicGame, PlayerVisibleState, PureStrategy}
import pl.edu.pw.mini.sg.game.deterministic.single.{DefenderGameView, GameWithPayoff, SinglePlayerGame}
import pl.edu.pw.mini.sg.mixed.tree.{InnerResultNode, MixedStrategyNode}



object MixedMethod {
  private val logger = getLogger


  def feasibilityLoop[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](mixedMethodConfig : MixedMethodConfig)(
    gameRoot: G,
    defenderStrategy: MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    desiredAttacker: PureStrategy[AM, AS],
    attackerOracle: AttackerResponseFinder,
    afState: StableAttackerFinderState[AM, AS],
    defenderImprovementIteration: Int
  )(
    implicit rng: RandomGenerator
  ): Either[MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    (MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
      DistortedPayoff, StableAttackerFinderState[AM, AS])] = {
    import mixedMethodConfig.strategyAdjuster.AdjusterState
    @tailrec
    def feasibilityLoopRec(iteration: Int, oaState: StableAttackerFinderState[AM, AS], stopCritetion: StopCriterion,
      previousAttacker: Option[(PureStrategy[AM, AS], InnerResultNode[DM, DS])]) : Either[
      MixedStrategyNode[DM, DS, AdjusterState],
        (MixedStrategyNode[DM, DS, AdjusterState], DistortedPayoff, StableAttackerFinderState[AM, AS])
    ] =
    {
      implicit val ii : IterationInfo = IterationInfo(iteration, defenderImprovementIteration)
      logger.trace(s"FL ${iteration}")
      val ((optimalAttackerResponse, optimalAttackerPayoff), state) = stableAttackerIfNeeded(defenderStrategy, previousAttacker, desiredAttackerPayoff, afState, gameRoot, mixedMethodConfig, attackerOracle)
      logger.trace(s"OA difference ${optimalAttackerPayoff.payoff.distortedAttacker.attacker - desiredAttackerPayoff.payoff.distortedAttacker.attacker}")

      if(isStrategyFeasible(optimalAttackerPayoff, desiredAttackerPayoff)) {
        logger.trace(s"Feasibility x iterations ${iteration}")
        Right((defenderStrategy.deepClonePreserveOS(), desiredAttackerPayoff.payoff, state))
      } else {
        logger.debug(s"Optimal attacker: ${optimalAttackerResponse}")
        makeDefenderStrategyFeasible(mixedMethodConfig)(gameRoot, desiredAttacker, optimalAttackerResponse, defenderStrategy, desiredAttackerPayoff, optimalAttackerPayoff)
        val ((attackerResponse, payoff), state2) = stableAttackerIfNeeded(defenderStrategy, Some((optimalAttackerResponse, optimalAttackerPayoff)), desiredAttackerPayoff, state, gameRoot, mixedMethodConfig, attackerOracle)
        stopCritetion.iterationResultS(desiredAttackerPayoff.payoff.distortedAttacker.attacker - payoff.payoff.distortedAttacker.attacker) match {
          case _ if  isStrategyFeasible(payoff, desiredAttackerPayoff) => 
            logger.trace(s"Feasibility iterations ${iteration}")

            Right((defenderStrategy.deepClonePreserveOS(), desiredAttackerPayoff.payoff, state2))
          case (_, true) => {logger.debug("Feasibility pass failure"); Left(defenderStrategy.deepClonePreserveOS()) }
          case (nextSC, false) => feasibilityLoopRec(iteration+1, state2, nextSC, Some((attackerResponse, payoff)))
        }        
      }
    }

    feasibilityLoopRec(1, afState, NoImprovementStop(mixedMethodConfig.feasibilityTryIterations, 200, 1e-6), None)
  }


  def strategyImprovementIteration[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    mixedMethodConfig: MixedMethodConfig
  )(
    gameRoot: G,
    desiredAttacker: PureStrategy[AM, AS],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    defenderStrategy: MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    afState: StableAttackerFinderState[AM, AS],
    attackerOracle : AttackerResponseFinder,
    iterationNumber: Int
  )(implicit rng: RandomGenerator
  ): Either[
    (MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState]),
    (MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState], DistortedPayoff, StableAttackerFinderState[AM, AS])
  ] = {
    implicit val ii : IterationInfo = IterationInfo(-1, iterationNumber)
    logger.trace("Defender improvement pass")
    improveDefenderPayoff[DM, DS, DefenderGameView[DM, AM, DS, AS, G]](mixedMethodConfig)(new DefenderGameView[DM, AM, DS, AS, G](gameRoot, desiredAttacker), defenderStrategy, desiredAttackerPayoff, false)
    logger.trace(s"Payoff after improvement ${desiredAttackerPayoff.payoff}")

    // Tu dodać możliwość nieprecyzyjnego wyniku. Wtedy zwracam jeden z trzech wariantów:
    // na pewno feasible, może feasible (z oszacowaniem górnym), infeasible
    val ret = feasibilityLoop(mixedMethodConfig)(gameRoot, defenderStrategy, desiredAttackerPayoff, desiredAttacker, attackerOracle, afState, iterationNumber)
    defenderStrategy.pruneMoves(mixedMethodConfig.treePruneDecision)
    desiredAttackerPayoff.prune()
    defenderStrategy.afterIteration()
    ret
  }

  def stableAttackerIfNeeded[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: AdvanceablePlayerVisibleState[AM, AS],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    defenderStrategy: MixedStrategyNode[DM, DS, _],
    previousAttacker: Option[(PureStrategy[AM, AS], InnerResultNode[DM, DS])],
    desiredPayoff: InnerResultNode[DM, DS],
    afState: StableAttackerFinderState[AM, AS],
    gameRoot: G,
    mixedMethodConfig: MixedMethodConfig,
    attackerOracle : AttackerResponseFinder
  )(
    implicit rng: RandomGenerator
  ) : ((PureStrategy[AM, AS], InnerResultNode[DM, DS]), StableAttackerFinderState[AM, AS]) = {
    implicit val br = mixedMethodConfig.boundedRationalityStrategy
    previousAttacker match {
      case Some((oAR, oAP)) if !isStrategyFeasible(oAP, desiredPayoff) => ((oAR, oAP), afState)
      case _ => {
        val ((str, irn), sta) = StableAttackerFinder.findStableAttacker[DM, AM, DS, AS, G](
          gameRoot, defenderStrategy, desiredPayoff,
          mixedMethodConfig.expansionDecision.negativeExpander,afState, attackerOracle)
        CalculateResults.calculate(gameRoot, defenderStrategy, str, irn, mixedMethodConfig.expansionDecision.negativeExpander, true)
        ((str, irn), sta)
      }
    }
  }


  def isStrategyFeasible(optimalAttacker: InnerResultNode[_, _], desiredAttacker: InnerResultNode[_, _], eps: Double = 1e-6) : Boolean =
    desiredAttacker.payoff.distortedAttacker.attacker - optimalAttacker.payoff.distortedAttacker.attacker > -eps

  private def improveDefenderPayoff[
    DM, 
    DS <: PlayerVisibleState[DM],
    G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff
  ](
    mixedMethodConfig: MixedMethodConfig
  )(
    gameRoot: G,
    defenderStrategy: MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    alreadyExpanded: Boolean
  )(implicit
    rng: RandomGenerator,
    iterationInfo: IterationInfo
  ) : Boolean = {
    improveDefenderPayoff2(mixedMethodConfig)(List(PositiveWalkDown(gameRoot, defenderStrategy, desiredAttackerPayoff, alreadyExpanded)), List(false))
  }

  @tailrec
  private final def improveDefenderPayoff2[
    DM,
    DS <: PlayerVisibleState[DM],
    G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff
  ](
    mixedMethodConfig: MixedMethodConfig
  )(
    queue: List[PositiveWalkStep[DM, DS, G, mixedMethodConfig.strategyAdjuster.AdjusterState]],
    expanded: List[Boolean]
  )(implicit
    rng: RandomGenerator,
    iterationInfo: IterationInfo
  ): Boolean = {
    queue match {
      case PositiveWalkUp(defenderStrategy, desiredAttackerPayoff, upperExpanded) :: rest => {
        desiredAttackerPayoff.updateValues()
        mixedMethodConfig.strategyAdjuster.positiveAdjust(defenderStrategy, desiredAttackerPayoff)
        desiredAttackerPayoff.updateValues()
        improveDefenderPayoff2(mixedMethodConfig)(rest, expanded.tail)
      }
      case PositiveWalkDown(gameRoot, defenderStrategy, desiredAttackerPayoff, upperExpanded) :: rest => {
        val ee = mixedMethodConfig.expansionDecision.positivePassExpansion(gameRoot, defenderStrategy,
          desiredAttackerPayoff, upperExpanded)

        val newTail = mixedMethodConfig.subtreeChooser
          .choosePositivePhaseSubtrees(desiredAttackerPayoff, defenderStrategy).view.flatMap(dm => {
            val gameSucc = gameRoot.makeMove(dm)
            desiredAttackerPayoff.successors(dm) match {
              case inr: InnerResultNode[DM, DS] =>
                List(PositiveWalkDown(gameSucc, defenderStrategy.nextStateInTree(dm, gameSucc.playerState), inr, upperExpanded|| ee))
              case _ => Nil
            }
          }).foldLeft(PositiveWalkUp[DM,DS,G, mixedMethodConfig.strategyAdjuster.AdjusterState](defenderStrategy, desiredAttackerPayoff, upperExpanded)::rest)((list, el) => el :: list)

        improveDefenderPayoff2[DM,DS,G](mixedMethodConfig)(newTail, ee::expanded)
      }
      case Nil => require(expanded.length == 1);expanded.head
    }
  }

  private final def improveNode[DM, DS <: PlayerVisibleState[DM]](
    mixedMethodConfig: MixedMethodConfig
  )(
    defenderStrategy: MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    optimalAttackerPayoff: InnerResultNode[DM, DS]
  )(
    implicit rng: RandomGenerator,
    iterationInfo: IterationInfo
  ): Boolean = {
    val oldOAP = optimalAttackerPayoff.payoff.distortedAttacker.attacker
    val oldDAP = desiredAttackerPayoff.payoff.distortedAttacker.attacker
    if(mixedMethodConfig.improveDecision.inTreeImproveDecision(optimalAttackerPayoff.payoff, desiredAttackerPayoff.payoff)) {
      mixedMethodConfig.strategyAdjuster.inTreeAdjust[DM, DS](
        defenderStrategy, desiredAttackerPayoff, optimalAttackerPayoff
      )
      optimalAttackerPayoff.updateValues()
      desiredAttackerPayoff.updateValues()
    }
    val newOAP = optimalAttackerPayoff.payoff.distortedAttacker.attacker
    val newDAP = desiredAttackerPayoff.payoff.distortedAttacker.attacker
    return oldOAP > newOAP || oldDAP < newDAP
  }


  // Zwraca: czy była poprawa 
  // Modyfikuje: strategię i wypłaty
  // Czy przekazywać wiedzę rodzeństwa o rozszerzeniu drzewa??
  def  makeDefenderStrategyFeasible[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: PlayerVisibleState[AM],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    mixedMethodConfig: MixedMethodConfig
  )(
    gameRoot: G, 
    desiredAttacker: PureStrategy[AM, AS],
    optimalAttacker: PureStrategy[AM, AS],
    defenderStrategy: MixedStrategyNode[DM, DS, mixedMethodConfig.strategyAdjuster.AdjusterState],
    desiredAttackerPayoff: InnerResultNode[DM, DS],
    optimalAttackerPayoff: InnerResultNode[DM, DS],
  )(implicit
    rng: RandomGenerator,
    iterationInfo: IterationInfo
  ) : Boolean = {
    feasibilityTreeWalk[DM, AM, DS, AS, G](mixedMethodConfig)(List(
      FeasibilityTreeWalkDown(new BothTreesPosition(
      new DefenderGameView(gameRoot, desiredAttacker),
      new DefenderGameView(gameRoot, optimalAttacker)),
      defenderStrategy, desiredAttackerPayoff, optimalAttackerPayoff,
      false)
    ), List(false))
  }


  @tailrec
  def feasibilityTreeWalk[
    DM, AM,
    DS <: PlayerVisibleState[DM],
    AS <: PlayerVisibleState[AM],
    G <: DeterministicGame[DM, AM, DS, AS, G]
  ](
    mixedMethodConfig: MixedMethodConfig
  )(
    arg: List[FeasibilityTreeWalkArg[DM, AM, DS, AS, G, mixedMethodConfig.strategyAdjuster.AdjusterState]],
    expanded: List[Boolean]
  )(
    implicit rng: RandomGenerator,
    iterationInfo: IterationInfo
  ) : Boolean = {
    implicit val br = mixedMethodConfig.boundedRationalityStrategy
    arg match {
      case OutOfTreeWalkArgDown(optimalAttackerNode, defenderStrategy, optimalAttackerPayoff, upperExpanded) :: tail => {
        val newTail = mixedMethodConfig.subtreeChooser.chooseOutSubtrees[DM, DS](optimalAttackerPayoff, defenderStrategy).view
          .map(dm => {
            val oanS = optimalAttackerNode.makeMove(dm)
            val dsS = defenderStrategy.nextState(
              dm, oanS.gameState.defenderState,
              mixedMethodConfig.expansionDecision.negativeExpander,
              oanS)()
            (oanS, dsS, optimalAttackerPayoff.nextNode(dm))
          }).collect{case (oanS, dsS, rn: InnerResultNode[DM, DS]) => (oanS, dsS, rn)}.foldLeft(
            OutOfTreeWalkArgUp(optimalAttackerNode, defenderStrategy, optimalAttackerPayoff, upperExpanded)::tail
          ){
            case (list, (oanS, dsS, rn)) => OutOfTreeWalkArgDown(oanS, dsS._1, rn, expanded.head)::list
          }
        feasibilityTreeWalk(mixedMethodConfig)(newTail, false::expanded)
      }
      case OutOfTreeWalkArgUp(optimalAttackerNode, defenderStrategy, optimalAttackerPayoff, upperExpanded) :: tail => {
        val subtreeExp :: myLevelExp :: upperL = expanded

        val expandedThisLevel = mixedMethodConfig.expansionDecision
          .outTreeExpansion(optimalAttackerNode, defenderStrategy, optimalAttackerPayoff, subtreeExp || upperExpanded)
        val newExpanded = (subtreeExp || myLevelExp || expandedThisLevel) :: upperL

        optimalAttackerPayoff.updateValues()
        mixedMethodConfig.strategyAdjuster.outOfTreeAdjust(defenderStrategy, optimalAttackerPayoff)
        optimalAttackerPayoff.updateValues()
        feasibilityTreeWalk(mixedMethodConfig)(tail, newExpanded)
      }
      case FeasibilityTreeWalkUp(treePosition, defenderStrategy, desiredAttackerPayoff,
        optimalAttackerPayoff, upperExpanded) :: tail => {
        optimalAttackerPayoff.updateValues()
        desiredAttackerPayoff.updateValues()
        val (expandedLower :: expandedThisLevel :: rest) = expanded
        val ee = mixedMethodConfig.expansionDecision.inTreeExpansion(
          treePosition, defenderStrategy, desiredAttackerPayoff,
          optimalAttackerPayoff, expandedLower || upperExpanded
        )
        optimalAttackerPayoff.updateValues()
        desiredAttackerPayoff.updateValues()

        improveNode(mixedMethodConfig)(defenderStrategy, desiredAttackerPayoff, optimalAttackerPayoff)
        feasibilityTreeWalk(mixedMethodConfig)(tail, (expandedThisLevel||expandedLower||ee)::rest )
      }
      case FeasibilityTreeWalkDown(treePosition, defenderStrategy, desiredAttackerPayoff,
        optimalAttackerPayoff, upperExpanded) :: tail => {
        val newArgs = mixedMethodConfig.subtreeChooser
          .chooseInSubtrees(desiredAttackerPayoff, optimalAttackerPayoff, defenderStrategy).view
          .flatMap(dm => { treePosition.next(dm) match {
            case both: BothTreesPosition[DM, AM, DS, AS, G] => {
                val dms = defenderStrategy.nextState(dm, both.optimalState.defenderState, mixedMethodConfig.expansionDecision.positiveExpander, treePosition.desiredAttacker)()
                val (dapS, oapS) = (desiredAttackerPayoff.nextNode(dm), optimalAttackerPayoff.nextNode(dm))

                (dapS, oapS) match {
                  case (dapIN: InnerResultNode[DM, DS], oapIN: InnerResultNode[DM, DS]) => 
                    List(FeasibilityTreeWalkDown(both, dms._1, dapIN, oapIN, upperExpanded))
                  case (_, oapIN: InnerResultNode[DM, DS]) =>
                    List(OutOfTreeWalkArgDown(both.optimalAttacker, dms._1, oapIN, upperExpanded))
                  case (dapIN: InnerResultNode[DM, DS] ,_) => List(FeasibilityTreeWalkJumpPositive[DM, AM, DS, AS, G, mixedMethodConfig.strategyAdjuster.AdjusterState](both.desiredAttacker, dms._1, dapIN, upperExpanded))
                  case _ => List()
                }
            }
            case optimal: OptimalTreePosition[DM, AM, DS, AS, G] => {
                val dms = defenderStrategy.nextState(dm, optimal.optimalAttacker.gameState.defenderState,
                  mixedMethodConfig.expansionDecision.negativeExpander, optimal.optimalAttacker)()

                optimalAttackerPayoff.nextNode(dm) match {
                  case irn : InnerResultNode[DM, DS] => List(OutOfTreeWalkArgDown(optimal.optimalAttacker, dms._1, irn, upperExpanded))
                  case _ => List()
                }
              }
            }
          }).foldLeft((FeasibilityTreeWalkUp(treePosition, defenderStrategy, desiredAttackerPayoff, optimalAttackerPayoff, upperExpanded))::tail)((list, el) => el :: list)
        feasibilityTreeWalk(mixedMethodConfig)(newArgs, false::expanded)
      }
      case FeasibilityTreeWalkJumpPositive(spg, ds, dap, exp) :: tail => {
        improveDefenderPayoff[DM, DS, DefenderGameView[DM, AM, DS, AS, G]](mixedMethodConfig)(spg, ds, dap, exp)
      }
      case Nil => require(expanded.length == 1);expanded.head
    }
  }

}



sealed trait FeasibilityTreeWalkArg[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
]

final case class FeasibilityTreeWalkJumpPositive[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
](
  gameRoot: DefenderGameView[DM, AM, DS, AS, G],
  defenderStrategy: MixedStrategyNode[DM, DS, AdjSt],
  desiredAttackerPayoff: InnerResultNode[DM, DS],
  alreadyExpanded: Boolean
) extends FeasibilityTreeWalkArg[DM, AM, DS, AS, G, AdjSt]

final case class OutOfTreeWalkArgDown[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
  ](
  optimalAttackerNode: DefenderGameView[DM, AM, DS, AS, G],
  defenderStrategy: MixedStrategyNode[DM, DS, AdjSt],
  optimalAttackerPayoff: InnerResultNode[DM, DS],
  upperExpanded: Boolean
) extends FeasibilityTreeWalkArg[DM, AM, DS, AS, G, AdjSt]

final case class OutOfTreeWalkArgUp[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
](
  optimalAttackerNode: DefenderGameView[DM, AM, DS, AS, G],
  defenderStrategy: MixedStrategyNode[DM, DS, AdjSt],
  optimalAttackerPayoff: InnerResultNode[DM, DS],
  upperExpanded: Boolean
) extends FeasibilityTreeWalkArg[DM, AM, DS, AS, G, AdjSt]

final case class FeasibilityTreeWalkDown[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
](
  treePosition: BothTreesPosition[DM, AM, DS, AS, G],
  defenderStrategy: MixedStrategyNode[DM, DS, AdjSt],
  desiredAttackerPayoff: InnerResultNode[DM, DS],
  optimalAttackerPayoff: InnerResultNode[DM, DS],
  upperExpanded: Boolean
) extends FeasibilityTreeWalkArg[DM, AM, DS, AS, G, AdjSt]

final case class FeasibilityTreeWalkUp[
  DM, AM,
  DS <: PlayerVisibleState[DM],
  AS <: PlayerVisibleState[AM],
  G <: DeterministicGame[DM, AM, DS, AS, G],
  AdjSt
](
  treePosition: BothTreesPosition[DM, AM, DS, AS, G],
  defenderStrategy: MixedStrategyNode[DM, DS, AdjSt],
  desiredAttackerPayoff: InnerResultNode[DM, DS],
  optimalAttackerPayoff: InnerResultNode[DM, DS],
  upperExpanded: Boolean
) extends FeasibilityTreeWalkArg[DM, AM, DS, AS, G, AdjSt]


sealed trait PositiveWalkStep[
  DM, DS <: PlayerVisibleState[DM],
  G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff,
  StAdj
]

final case class PositiveWalkDown[
  DM, DS <: PlayerVisibleState[DM],
  G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff,
  StAdj
](
  gameRoot: G,
  defenderStrategy: MixedStrategyNode[DM, DS, StAdj],
  desiredAttackerPayoff: InnerResultNode[DM, DS],
  alreadyExpanded: Boolean
) extends PositiveWalkStep[DM, DS, G, StAdj]

final case class PositiveWalkUp[
  DM, DS <: PlayerVisibleState[DM],
  G <: SinglePlayerGame[DM, DS, G] with GameWithPayoff,
  StAdj
](
  defenderStrategy: MixedStrategyNode[DM, DS, StAdj],
  desiredAttackerPayoff: InnerResultNode[DM, DS],
  alreadyExpanded: Boolean
) extends PositiveWalkStep[DM, DS, G, StAdj]
