<commit_before>package lila.tournament
package arena

import org.joda.time.DateTime

case class Sheet(scores: List[Sheet.Score]) {
  val total  = scores.foldLeft(0)(_ + _.value)
  def onFire = Sheet.isOnFire(scores)
}

object Sheet {

  sealed trait Version
  case object V1 extends Version
  case object V2 extends Version // second draw gives zero point

  sealed abstract class Flag(val id: Int)
  case object Double        extends Flag(3)
  case object StreakStarter extends Flag(2)
  case object Normal        extends Flag(1)
  case object Null          extends Flag(0)

  sealed trait Berserk
  case object NoBerserk      extends Berserk
  case object ValidBerserk   extends Berserk
  case object InvalidBerserk extends Berserk

  sealed trait Result
  case object ResWin  extends Result
  case object ResDraw extends Result
  case object ResLoss extends Result
  case object ResDQ   extends Result

  case class Score(
      res: Result,
      flag: Flag,
      berserk: Berserk
  ) {

    def isBerserk = berserk != NoBerserk

    def isWin = res match {
      case ResWin  => Some(true)
      case ResLoss => Some(false)
      case _       => None
    }

    def isDraw = res == ResDraw

    val value = ((res, flag) match {
      case (ResWin, Double)  => 4
      case (ResWin, _)       => 2
      case (ResDraw, Double) => 2
      case (ResDraw, Null)   => 0
      case (ResDraw, _)      => 1
      case _                 => 0
    }) + {
      if (res == ResWin && berserk == ValidBerserk) 1 else 0
    }
  }

  val emptySheet = Sheet(Nil)

  def apply(userId: String, pairings: Pairings, version: Version): Sheet = Sheet {
    val nexts = (pairings drop 1 map some) :+ None
    pairings.zip(nexts).foldLeft(List.empty[Score]) {
      case (scores, (p, n)) =>
        val berserk = if (p berserkOf userId) {
          if (p.notSoQuickFinish) ValidBerserk else InvalidBerserk
        } else NoBerserk
        (p.winner match {
          case None if p.quickDraw => Score(ResDQ, Normal, berserk)
          case None =>
            Score(
              ResDraw,
              if (version != V1 && scores.headOption.exists(_.isDraw)) Null
              else if (isOnFire(scores)) Double
              else Normal,
              berserk
            )
          case Some(w) if userId == w =>
            Score(
              ResWin,
              if (isOnFire(scores)) Double
              else if (scores.headOption ?? (_.flag == StreakStarter)) StreakStarter
              else
                n match {
                  case None                                 => StreakStarter
                  case Some(s) if s.winner.contains(userId) => StreakStarter
                  case _                                    => Normal
                },
              berserk
            )
          case _ => Score(ResLoss, Normal, berserk)
        }) :: scores
    }
  }

  private val v2date = new DateTime(2020, 4, 21, 0, 0, 0)

  def versionOf(date: DateTime) =
    if (date isBefore v2date) V1 else V2

  private def isOnFire(scores: List[Score]) =
    scores.headOption.exists(_.res == ResWin) &&
      scores.lift(1).exists(_.res == ResWin)
}
<commit_msg>only a win can end a tournament draw streak - closes #6415<commit_after>package lila.tournament
package arena

import org.joda.time.DateTime

case class Sheet(scores: List[Sheet.Score]) {
  val total  = scores.foldLeft(0)(_ + _.value)
  def onFire = Sheet.isOnFire(scores)
}

object Sheet {

  sealed trait Version
  case object V1 extends Version
  case object V2 extends Version // second draw gives zero point

  sealed abstract class Flag(val id: Int)
  case object Double        extends Flag(3)
  case object StreakStarter extends Flag(2)
  case object Normal        extends Flag(1)
  case object Null          extends Flag(0)

  sealed trait Berserk
  case object NoBerserk      extends Berserk
  case object ValidBerserk   extends Berserk
  case object InvalidBerserk extends Berserk

  sealed trait Result
  case object ResWin  extends Result
  case object ResDraw extends Result
  case object ResLoss extends Result
  case object ResDQ   extends Result

  case class Score(
      res: Result,
      flag: Flag,
      berserk: Berserk
  ) {

    def isBerserk = berserk != NoBerserk

    def isWin = res match {
      case ResWin  => Some(true)
      case ResLoss => Some(false)
      case _       => None
    }

    def isDraw = res == ResDraw

    val value = ((res, flag) match {
      case (ResWin, Double)  => 4
      case (ResWin, _)       => 2
      case (ResDraw, Double) => 2
      case (ResDraw, Null)   => 0
      case (ResDraw, _)      => 1
      case _                 => 0
    }) + {
      if (res == ResWin && berserk == ValidBerserk) 1 else 0
    }
  }

  val emptySheet = Sheet(Nil)

  def apply(userId: String, pairings: Pairings, version: Version): Sheet = Sheet {
    val nexts = (pairings drop 1 map some) :+ None
    pairings.zip(nexts).foldLeft(List.empty[Score]) {
      case (scores, (p, n)) =>
        val berserk = if (p berserkOf userId) {
          if (p.notSoQuickFinish) ValidBerserk else InvalidBerserk
        } else NoBerserk
        (p.winner match {
          case None if p.quickDraw => Score(ResDQ, Normal, berserk)
          case None =>
            Score(
              ResDraw,
              if (isOnFire(scores)) Double
              else if (version != V1 && isDrawStreak(scores)) Null
              else Normal,
              berserk
            )
          case Some(w) if userId == w =>
            Score(
              ResWin,
              if (isOnFire(scores)) Double
              else if (scores.headOption ?? (_.flag == StreakStarter)) StreakStarter
              else
                n match {
                  case None                                 => StreakStarter
                  case Some(s) if s.winner.contains(userId) => StreakStarter
                  case _                                    => Normal
                },
              berserk
            )
          case _ => Score(ResLoss, Normal, berserk)
        }) :: scores
    }
  }

  private val v2date = new DateTime(2020, 4, 21, 0, 0, 0)

  def versionOf(date: DateTime) =
    if (date isBefore v2date) V1 else V2

  private def isOnFire(scores: List[Score]) =
    scores.headOption.exists(_.res == ResWin) &&
      scores.lift(1).exists(_.res == ResWin)

  private def isDrawStreak(scores: List[Score]): Boolean = scores match {
    case Nil => false
    case s :: more =>
      s.isWin match {
        case None        => true
        case Some(true)  => false
        case Some(false) => isDrawStreak(more)
      }
  }
}

{'fraction_non_alphanumeric': 0.08859040144883791, 'fraction_numerical': 0.010262601871415635, 'mean_word_length': 1.8651102464332037, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '365910', 'n_tokens_mistral': 2051, 'n_tokens_neox': 1949, 'n_words': 618}