package kickr.service;
import java.time.Duration;
import java.util.List;
import kickr.config.RatingConfiguration;
import kickr.db.dao.MatchDAO;
import kickr.db.dao.ScoreDAO;
import kickr.db.entity.Match;
import kickr.db.entity.Player;
import kickr.db.entity.Score;
import kickr.db.entity.ScoreChange;
import kickr.db.entity.ScoreType;
import kickr.db.entity.Team;
import kickr.util.MatchResultDetails;
import kickr.util.ScoreUpdates;
import kickr.util.Side;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author nikku
*/
public class RatingService {
private static final Logger LOG = LoggerFactory.getLogger(RatingService.class);
private final MatchDAO matchDao;
private final ScoreDAO scoreDao;
private final Duration ratingDelay;
public RatingService(MatchDAO matchDao, ScoreDAO scoreDao, RatingConfiguration configuration) {
this.matchDao = matchDao;
this.scoreDao = scoreDao;
this.ratingDelay = configuration.getDelay();
}
public void calculateNewRatings() {
List<Match> unratedMatches = matchDao.getUnratedMatches(ratingDelay);
LOG.info("rating {} matches", unratedMatches.size());
try {
unratedMatches.stream()
.map(MatchResultDetails::compute)
.map(this::createChanges)
.flatMap(changes -> changes.stream())
.forEach(change -> {
scoreDao.createChanges(change);
change.apply();
});
} catch (Throwable t) {
LOG.error("rating failed", t);
throw t;
}
}
public List<ScoreChange> createChanges(MatchResultDetails result) {
Match match = result.getMatch();
ScoreUpdates updates = new ScoreUpdates(match, player -> getScore(match, player));
if (result.isTied()) {
// add tie updates
updates.add(Side.TEAM1, ScoreType.TIE, 2);
updates.add(Side.TEAM2, ScoreType.TIE, 2);
for (Side side : Side.values()) {
if (result.getCloseLosses(side) > 0) {
updates.add(side, ScoreType.CLOSE_LOSS, 1);
}
if (result.getStomps(side) > 0) {
updates.add(side, ScoreType.STOMP, 2);
}
}
}
for (Side side : Side.values()) {
Team team = match.getTeam(side);
boolean shortHanded = team.isSingle() && !match.getTeam(side.opposite()).isSingle();
// add win updates
if (result.isWinner(side)) {
updates.add(side, ScoreType.WON, 5);
if (result.getStomps(side) > 0) {
updates.add(side, ScoreType.STOMP, 1);
}
}
// add loss updates
else {
updates.add(side, ScoreType.LOST, -1);
if (result.getCloseLosses(side) > 0) {
updates.add(side, ScoreType.CLOSE_LOSS, 1);
}
if (result.getStomps(side) > 0) {
updates.add(side, ScoreType.STOMP, 2);
}
}
if (shortHanded) {
updates.add(side, ScoreType.SHORT_HANDED, 2);
}
}
match.setRated(true);
LOG.info("rated Match#{}", match.getId());
return updates.asList();
}
public Score getScore(Match match, Player player) {
return scoreDao.getOrCreateScore(player);
}
}