lila/modules/pool/src/main/MatchMaking.scala

61 lines
1.8 KiB
Scala

package lila.pool
import lila.common.WMMatching
object MatchMaking {
case class Couple(p1: PoolMember, p2: PoolMember) {
def members = Vector(p1, p2)
def userIds = members.map(_.userId)
def ratingDiff = p1 ratingDiff p2
}
def apply(members: Vector[PoolMember]): Vector[Couple] = members.partition(_.engine) match {
case (engines, fairs) => naive(engines) ++ (wmMatching(fairs) | naive(fairs))
}
private def naive(members: Vector[PoolMember]): Vector[Couple] =
members.sortBy(-_.rating) grouped 2 collect {
case Vector(p1, p2) => Couple(p1, p2)
} toVector
private object wmMatching {
// above that, no pairing is allowed
private val MaxScore = 300
// quality of a potential pairing. Lower is better.
private def pairScore(a: PoolMember, b: PoolMember) =
a.ratingDiff(b) - {
missBonus(a) + missBonus(b)
} + {
rangeMalus(a, b) + rangeMalus(b, a)
} + {
blockMalus(a, b) + blockMalus(b, a)
}
// score bonus based on how many waves the member missed
private def missBonus(p: PoolMember) = (p.misses * 30) atMost 1000
// big malus if players have conflicting rating ranges
private def rangeMalus(a: PoolMember, b: PoolMember) =
if (a.ratingRange.exists(!_.contains(b.rating))) 2000 else 0
// huge malus if players block each other
private def blockMalus(a: PoolMember, b: PoolMember) =
if (a.blocking.ids contains b.userId) 5000 else 0
def apply(members: Vector[PoolMember]): Option[Vector[Couple]] = {
WMMatching(members.toArray, pairScore).fold(
err => {
logger.error("WMMatching", err)
none
},
_.collect {
case (a, b) if pairScore(a, b) < MaxScore => Couple(a, b)
}.toVector.some
)
}
}
}