All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.m50d.plusminuszero.PlusMinusZero.scala Maven / Gradle / Ivy

There is a newer version: 0.2
Show newest version
package com.github.m50d.plusminuszero

import cats.instances.double._
import cats.instances.list._
import cats.instances.vector._
import cats.syntax.apply._
import cats.syntax.flatMap._
import cats.syntax.foldable._
import cats.syntax.functor._
import cats.syntax.traverse._
import scala.scalajs.js.annotation._
import us.oyanglul.owlet._
import org.scalajs.dom._
import DOM._
import monix.reactive.Observable
import monix.reactive.subjects.Var
import monix.execution.Scheduler.Implicits.global
import Function.const
import cats.Foldable

case class TournamentResult(weight: Double, points: Int)

object TournamentResult {
  def weight: Owlet[Double] = label(number("weight", 0), "Weight")
  def points: Owlet[Int] = label(number("points", 0), "Points").map(_.toInt)
  def tournamentResult: Owlet[TournamentResult] =
    (weight, points).mapN(apply)

  val Zero = TournamentResult(1, 0)
}

object Rank {
  private def weightedAverage[F[_]: Foldable](results: F[TournamentResult]) =
    (results.foldMap(r => r.points * r.weight)) / results.foldMap(_.weight)

  def partA(results: Vector[TournamentResult]) = {
    val (best5, rest) = results.padTo(5, TournamentResult.Zero).splitAt(5)
    val toDrop = rest.size / 5
    weightedAverage(best5 ++ rest.dropRight(toDrop))
  }

  def partB(results: Vector[TournamentResult]) =
    weightedAverage(results.padTo(4, TournamentResult.Zero).take(4))

  def ranking(results: Vector[TournamentResult]) = {
    val sortedResults = results.sortBy(_.points).reverse
    0.5 * partA(sortedResults) + 0.5 * partB(sortedResults)
  }
}

@JSExportTopLevel("plusMinusZero.PlusMinusZero")
object PlusMinusZero {
  @JSExport
  def main(args: Array[String]): Unit = {
    val toAdd = Var(None): Var[Option[TournamentResult]]
    val added = toAdd.scan(Vector[TournamentResult]())(_ ++ _)
    val toRemove = Var(None): Var[Option[TournamentResult]]
    val removed = toRemove.scan(Vector[TournamentResult]())(_ ++ _)
    val listOfResults = added.combineLatestMap(removed)(_ diff _)

    val explanation = {
      val el: html.Div = document.createElement("div").asInstanceOf[html.Div]
      val text = document.createTextNode("""
In "weight", enter MERS weight for tournaments in the past year and MERS weight/2
for tournaments between 1 and 2 years ago.

In "points", enter EMA points for your place in the tournament - 1000 for first
and around 0 for last.
""")
      el.appendChild(text)
      Owlet(Observable(List(el)), Var(()))
    }
    
    val resultEntry = div(TournamentResult.tournamentResult, Var(Seq.empty))
    val addNewResult = (resultEntry, button("add", false, true)).mapN((
      (entry, pressed) => toAdd := (if (pressed) Some(entry) else None)))

    val rankUi = {
      val sink = Var(())
      val el: html.Div = document.createElement("div").asInstanceOf[html.Div]
      listOfResults foreach { results =>
        while (el.lastChild != null) {
          el.removeChild(el.lastChild)
        }
        results foreach { result =>
          val rel = document.createElement("div").asInstanceOf[html.Div]
          val tn = document.createTextNode(result.toString)
          rel.appendChild(tn)
          val removeButton =
          document.createElement("button").asInstanceOf[html.Button]
          removeButton.appendChild(document.createTextNode("x"))
          removeButton.onclick = { _ =>
            toRemove := Some(result)
          }
          rel.appendChild(removeButton)
          el.appendChild(rel)
        }
        val score = Rank.ranking(results.toVector)
        val scoreTn = document.createTextNode(s"EMA Ranking points: $score")
        el.appendChild(scoreTn)
        sink := (())
      }
      Owlet(Observable(List(el)), sink)
    }
    render(explanation *> addNewResult *> rankUi, "#app")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy