<commit_before>import scala.language.postfixOps
import scala.util.Random
import org.scalatest._
import org.romeo.loveletter.game._

class EngineSpec extends FlatSpec with Matchers {

  behavior of "A game"

  it should "fail to start with too few players" in {
    val tooFewPlayers = Seq("Tyler")
    an [IllegalArgumentException] should be thrownBy Game(tooFewPlayers)
  }
  it should "fail to start with too many players" in {
    val tooManyPlayers = Seq("Tyler", "Kevin", "Morgan", "Trevor", "Jeff")
    an [IllegalArgumentException] should be thrownBy Game(tooManyPlayers)
  }

  behavior of "The first player"

  it should "be the current player" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    Game.currentPlayer.eval(game).name should be (players.head)
  }

  it should "change when a turn is ended" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    Game.endTurn.exec(game).players.head.name should be (players(1))
  }

  it should "be back to its initial state after a full round of turns" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    def FullRoundCycle() = for {
      p1 <- Game.currentPlayer
      p2 <- Game.endTurn
      p3 <- Game.endTurn
      p4 <- Game.endTurn
      _ <- Game.endTurn
    } yield Seq(p1, p2, p3, p4)
    //after 4 turns change the state should be back to where it started
    val (newGame, newPlayers) = FullRoundCycle()(game)
    newGame should be (game)
    newPlayers.map(_.name) should be (players)
  }

  behavior of "A match"

  it should "have 2 cards for the current player" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9191)).exec(Game(players));

    val currentPlayer = Game.currentPlayer.eval(game);
    currentPlayer.hand should have length 2
  }

  it should "have 1 for player whose turn it isn't when starting a game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9192)).exec(Game(players));

    val currentPlayer = Game.currentPlayer.eval(game);
    (game.players.diff(Seq(currentPlayer))).foreach(_.hand should have length 1)
  }

  it should "have the same number of cards in the game as were in the deck (minus burn card)" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9193)).exec(Game(players));

    val cardsInPlay = game.players.foldLeft(0)((acc, p) => acc + p.hand.length) + game.deck.length + game.visibleDiscard.length + game.discard.length
    cardsInPlay + 1 should be (org.romeo.loveletter.game.Deck.cards.length)
  }

  it should "have the same number of cards in the game as were in the deck (minus burn card) for a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game.startMatch(new Random(9194)).exec(Game(players));

    val cardsInPlay = game.players.foldLeft(0)((acc, p) => acc + p.hand.length) + game.deck.length + game.visibleDiscard.length + game.discard.length
    cardsInPlay + 1 should be (org.romeo.loveletter.game.Deck.cards.length)
  }

  it should "have 3 visible discards in a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game.startMatch(new Random(9195)).exec(Game(players));

    game.visibleDiscard should have length (3)
  }

  it should "have no visible discards in a non 2 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9196)).exec(Game(players));

    game.visibleDiscard should have length (0)
  }

  it should "report a winner if a player has 7 points in a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (7)
  }

  it should "report a winner if a player has 5 points in a 3 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (5)
  }

  it should "report a winner if a player has 4 points in a 4 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (4)
  }

//TODO
  it should "have all players not eliminated and not protected, even if they were in the previous match"

  behavior of "The game deck"

  it should "be put in random order when shuffled" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players)
    val r1 = new Random(1337)
    val r2 = new Random(1337)

    val (newGame, shuffledDeck) = Game.shuffle(r1)(game)
    newGame.deck should be (shuffledDeck)
    shuffledDeck should not be (Deck.cards)
    shuffledDeck should contain theSameElementsAs Deck.cards
    shuffledDeck should be (r2.shuffle(Deck.cards))
  }

  it should "lose its top card when burnCard is called" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players)<commit_msg>add test for starting a new match with protected or eliminated players<commit_after>import scala.language.postfixOps
import scala.util.Random
import org.scalatest._
import org.romeo.loveletter.game._

class EngineSpec extends FlatSpec with Matchers {

  behavior of "A game"

  it should "fail to start with too few players" in {
    val tooFewPlayers = Seq("Tyler")
    an [IllegalArgumentException] should be thrownBy Game(tooFewPlayers)
  }
  it should "fail to start with too many players" in {
    val tooManyPlayers = Seq("Tyler", "Kevin", "Morgan", "Trevor", "Jeff")
    an [IllegalArgumentException] should be thrownBy Game(tooManyPlayers)
  }

  behavior of "The first player"

  it should "be the current player" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    Game.currentPlayer.eval(game).name should be (players.head)
  }

  it should "change when a turn is ended" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    Game.endTurn.exec(game).players.head.name should be (players(1))
  }

  it should "be back to its initial state after a full round of turns" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);
    def FullRoundCycle() = for {
      p1 <- Game.currentPlayer
      p2 <- Game.endTurn
      p3 <- Game.endTurn
      p4 <- Game.endTurn
      _ <- Game.endTurn
    } yield Seq(p1, p2, p3, p4)
    //after 4 turns change the state should be back to where it started
    val (newGame, newPlayers) = FullRoundCycle()(game)
    newGame should be (game)
    newPlayers.map(_.name) should be (players)
  }

  behavior of "A match"

  it should "have 2 cards for the current player" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9191)).exec(Game(players));

    val currentPlayer = Game.currentPlayer.eval(game);
    currentPlayer.hand should have length 2
  }

  it should "have 1 for player whose turn it isn't when starting a game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9192)).exec(Game(players));

    val currentPlayer = Game.currentPlayer.eval(game);
    (game.players.diff(Seq(currentPlayer))).foreach(_.hand should have length 1)
  }

  it should "have the same number of cards in the game as were in the deck (minus burn card)" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9193)).exec(Game(players));

    val cardsInPlay = game.players.foldLeft(0)((acc, p) => acc + p.hand.length) + game.deck.length + game.visibleDiscard.length + game.discard.length
    cardsInPlay + 1 should be (org.romeo.loveletter.game.Deck.cards.length)
  }

  it should "have the same number of cards in the game as were in the deck (minus burn card) for a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game.startMatch(new Random(9194)).exec(Game(players));

    val cardsInPlay = game.players.foldLeft(0)((acc, p) => acc + p.hand.length) + game.deck.length + game.visibleDiscard.length + game.discard.length
    cardsInPlay + 1 should be (org.romeo.loveletter.game.Deck.cards.length)
  }

  it should "have 3 visible discards in a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game.startMatch(new Random(9195)).exec(Game(players));

    game.visibleDiscard should have length (3)
  }

  it should "have no visible discards in a non 2 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game.startMatch(new Random(9196)).exec(Game(players));

    game.visibleDiscard should have length (0)
  }

  it should "report a winner if a player has 7 points in a 2 player game" in {
    val players = Seq("Tyler", "Kevin")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (7)
  }

  it should "report a winner if a player has 5 points in a 3 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (5)
  }

  it should "report a winner if a player has 4 points in a 4 player game" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players);

    def giveSomePoints = for {
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      _ <- Game.awardPoint(players(1))
      nonWinner <- Game.findWinner
      _ <- Game.awardPoint(players(1))
      winner <- Game.findWinner
    } yield (nonWinner, winner)

    val (nonWinner, winner) = giveSomePoints.eval(game)
    nonWinner should be (empty)
    winner.get.name should be (players(1))
    winner.get.score should be (4)
  }

  it should "have all players not eliminated and not protected, even if they were in the previous match" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players)

    def modifyPlayersThenRestartMatch = for {
      _ <- Game.startMatch(new Random(8375))
      _ <- Game.protectPlayer(players(0), true)
      _ <- Game.eliminatePlayer(players(0), true)
      _ <- Game.protectPlayer(players(1), true)
      _ <- Game.eliminatePlayer(players(2), true)
      _ <- Game.startMatch(new Random(7482))
    } yield ()

    val newGame = modifyPlayersThenRestartMatch.exec(game)
    newGame.players.foreach { p =>
      p.isProtected should be (false)
      p.isEliminated should be (false)
    }
  }

  behavior of "The game deck"

  it should "be put in random order when shuffled" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players)
    val r1 = new Random(1337)
    val r2 = new Random(1337)

    val (newGame, shuffledDeck) = Game.shuffle(r1)(game)
    newGame.deck should be (shuffledDeck)
    shuffledDeck should not be (Deck.cards)
    shuffledDeck should contain theSameElementsAs Deck.cards
    shuffledDeck should be (r2.shuffle(Deck.cards))
  }

  it should "lose its top card when burnCard is called" in {
    val players = Seq("Tyler", "Kevin", "Morgan", "Trevor")
    val game = Game(players)
{'fraction_non_alphanumeric': 0.12332202389732999, 'fraction_numerical': 0.013866351969317008, 'mean_word_length': 3.259817781966698, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3833899', 'n_tokens_mistral': 4719, 'n_tokens_neox': 4479, 'n_words': 1336}