5
0
mirror of https://github.com/AJMicke/KickerELO.git synced 2026-03-11 05:21:07 +01:00

Show player elo change in match history

This commit is contained in:
Sebastian Beckmann
2025-05-30 16:18:05 +02:00
committed by AJMicke
parent 4ed29fb20e
commit 0e64e0fd02
6 changed files with 122 additions and 12 deletions

View File

@@ -12,13 +12,19 @@ public class EloCalculationService {
private final float initialElo1vs1 = 1500;
private final float initialElo2vs2 = 1500;
private final EloChangeService eloChangeService;
EloCalculationService(EloChangeService eloChangeService) {
this.eloChangeService = eloChangeService;
}
/**
* Updates the 1 vs 1 ELOs of the players according to the result of the game.
* @param gewinner The entity representing the winning player
* @param verlierer The entity representing the losing player
* @param toreVerlierer The number of goals of the losing player
*/
public void updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) {
public void updateElo1vs1(long gameId, Spieler gewinner, Spieler verlierer, short toreVerlierer) {
final float baseK = 50;
final float reductionPerGoal = 0.1f * baseK;
final float finalK = baseK - (reductionPerGoal * toreVerlierer);
@@ -28,17 +34,20 @@ public class EloCalculationService {
gewinner.setElo1vs1(gewinner.getElo1vs1() + eloChange);
verlierer.setElo1vs1(verlierer.getElo1vs1() - eloChange);
eloChangeService.put1vs1Result(gameId, eloChange, -eloChange);
}
/**
* Updates the 2 vs 2 ELOs of the players according to the result of the game
* @param gameId ID of the game
* @param gewinnerVorn The winning offensive player
* @param gewinnerHinten The winning defensive player
* @param verliererVorn The losing offensive player
* @param verliererHinten The losing defensive player
* @param toreVerlierer The number of goals of the losing teams
*/
public void updateElo2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
public void updateElo2vs2(long gameId, Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
final float baseK = 100;
final double adjustedK = baseK * (1 - (0.1 * toreVerlierer));
var totalWinnerElo = gewinnerVorn.getElo2vs2() + gewinnerHinten.getElo2vs2();
@@ -60,6 +69,8 @@ public class EloCalculationService {
gewinnerHinten.setElo2vs2((float) (gewinnerHinten.getElo2vs2() + winner2EloChange));
verliererVorn.setElo2vs2((float) (verliererVorn.getElo2vs2() + loser1EloChange));
verliererHinten.setElo2vs2((float) (verliererHinten.getElo2vs2() + loser2EloChange));
eloChangeService.put2vs2Result(gameId, winner1EloChange, winner2EloChange, loser1EloChange, loser2EloChange);
}
public float getInitialElo1vs1() {

View File

@@ -0,0 +1,37 @@
package org.kickerelo.kickerelo.service;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class EloChangeService {
public record UpdateElo1vs1Change(double winnerEloChange, double loserEloChange) {
}
public record UpdateElo2vs2Change(double winnerFrontEloChange, double winnerBackEloChange, double loserFrontEloChange, double loserBackEloChange) {
}
Map<Long, UpdateElo1vs1Change> changes1vs1 = new HashMap<>();
Map<Long, UpdateElo2vs2Change> changes2vs2 = new HashMap<>();
public void put1vs1Result(long gameId, double winnerEloChange, double loserEloChange) {
changes1vs1.put(gameId, new UpdateElo1vs1Change(winnerEloChange, loserEloChange));
}
public void put2vs2Result(long gameId, double winnerFrontEloChange, double winnerBackEloChange, double loserFrontEloChange, double loserBackEloChange) {
changes2vs2.put(gameId, new UpdateElo2vs2Change(winnerFrontEloChange, winnerBackEloChange, loserFrontEloChange, loserBackEloChange));
}
public UpdateElo1vs1Change get1vs1Result(long gameId) {
var result = changes1vs1.get(gameId);
assert result != null;
return result;
}
public UpdateElo2vs2Change get2vs2Result(long gameId) {
var result = changes2vs2.get(gameId);
assert result != null;
return result;
}
}

View File

@@ -24,15 +24,23 @@ import java.util.stream.Stream;
*/
@Service
public class KickerEloService {
@Autowired
private Ergebnis1vs1Repository ergebnis1vs1Repository;
@Autowired
private Ergebnis2vs2Repository ergebnis2vs2Repository;
@Autowired
private SpielerRepository spielerRepository;
@Autowired
private EloCalculationService eloCalculationService;
public KickerEloService(Ergebnis1vs1Repository ergebnis1vs1Repository,
Ergebnis2vs2Repository ergebnis2vs2Repository,
SpielerRepository spielerRepository,
EloCalculationService eloCalculationService) {
this.ergebnis1vs1Repository = ergebnis1vs1Repository;
this.ergebnis2vs2Repository = ergebnis2vs2Repository;
this.spielerRepository = spielerRepository;
this.eloCalculationService = eloCalculationService;
recalculateAll1vs1();
recalculateAll2vs2();
}
/**
* @return List of all player names sorted alphabetically
*/
@@ -70,7 +78,7 @@ public class KickerEloService {
Ergebnis1vs1 ergebnis = new Ergebnis1vs1(gewinner, verlierer, toreVerlierer);
ergebnis1vs1Repository.save(ergebnis);
eloCalculationService.updateElo1vs1(gewinner, verlierer, toreVerlierer);
eloCalculationService.updateElo1vs1(ergebnis.getId(), gewinner, verlierer, toreVerlierer);
spielerRepository.save(gewinner);
spielerRepository.save(verlierer);
}
@@ -108,7 +116,7 @@ public class KickerEloService {
Ergebnis2vs2 ergebnis = new Ergebnis2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
ergebnis2vs2Repository.save(ergebnis);
eloCalculationService.updateElo2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
eloCalculationService.updateElo2vs2(ergebnis.getId(), gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
spielerRepository.save(gewinnerVorn);
spielerRepository.save(gewinnerHinten);
spielerRepository.save(verliererVorn);
@@ -148,7 +156,8 @@ public class KickerEloService {
}
Stream<Ergebnis1vs1> results = ergebnis1vs1Repository.findAll().stream().sorted(new Ergebnis1vs1TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
eloCalculationService.updateElo1vs1(r.getId(),
players.get(r.getGewinner().getId()),
players.get(r.getVerlierer().getId()),
r.getToreVerlierer());
});
@@ -166,7 +175,8 @@ public class KickerEloService {
}
Stream<Ergebnis2vs2> results = ergebnis2vs2Repository.findAll().stream().sorted(new Ergebnis2vs2TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
eloCalculationService.updateElo2vs2(r.getId(),
players.get(r.getGewinnerVorn().getId()),
players.get(r.getGewinnerHinten().getId()),
players.get(r.getVerliererVorn().getId()),
players.get(r.getVerliererHinten().getId()),

View File

@@ -10,18 +10,30 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.service.EloChangeService;
import java.util.List;
import static org.kickerelo.kickerelo.views.HistoryCommonView.formatEloChange;
@Route("history1vs1")
public class History1vs1View extends VerticalLayout {
private final Ergebnis1vs1Repository repo;
private final EloChangeService eloChangeService;
List<Ergebnis1vs1> res;
public History1vs1View(Ergebnis1vs1Repository repo) {
public History1vs1View(Ergebnis1vs1Repository repo, EloChangeService eloChangeService) {
this.repo = repo;
this.eloChangeService = eloChangeService;
setSizeFull();
H2 subheading = new H2("Spiele 1 vs 1");
res = repo.findAll();
@@ -49,8 +61,10 @@ public class History1vs1View extends VerticalLayout {
grid.removeColumnByKey("id");
Grid.Column<Ergebnis1vs1> winnerColumn = grid.getColumnByKey("gewinner");
winnerColumn.setHeader("Gewinner");
winnerColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getGewinner(), eloChangeService.get1vs1Result(ergebnis.getId()).winnerEloChange())));
Grid.Column<Ergebnis1vs1> loserColumn = grid.getColumnByKey("verlierer");
loserColumn.setHeader("Verlierer");
loserColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerlierer(), eloChangeService.get1vs1Result(ergebnis.getId()).loserEloChange())));
Grid.Column<Ergebnis1vs1> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis1vs1> timestamp = grid.getColumnByKey("timestamp");

View File

@@ -11,17 +11,27 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.service.EloChangeService;
import java.util.List;
import static org.kickerelo.kickerelo.views.HistoryCommonView.formatEloChange;
@Route("history2vs2")
public class History2vs2View extends VerticalLayout {
public History2vs2View(Ergebnis2vs2Repository repo) {
private final Ergebnis2vs2Repository repo;
private final EloChangeService eloChangeService;
public History2vs2View(Ergebnis2vs2Repository repo, EloChangeService eloChangeService) {
this.repo = repo;
this.eloChangeService = eloChangeService;
setSizeFull();
H2 subheading = new H2("Spiele 2 vs 2");
List<Ergebnis2vs2> res = repo.findAll();
@@ -63,12 +73,16 @@ public class History2vs2View extends VerticalLayout {
grid.removeColumnByKey("id");
Grid.Column<Ergebnis2vs2> winnerFront = grid.getColumnByKey("gewinnerVorn");
winnerFront.setHeader("Gewinner vorne");
winnerFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getGewinnerVorn(), eloChangeService.get2vs2Result(ergebnis.getId()).winnerFrontEloChange())));
Grid.Column<Ergebnis2vs2> winnerBack = grid.getColumnByKey("gewinnerHinten");
winnerBack.setHeader("Gewinner hinten");
winnerBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getGewinnerHinten(), eloChangeService.get2vs2Result(ergebnis.getId()).winnerBackEloChange())));
Grid.Column<Ergebnis2vs2> loserFront = grid.getColumnByKey("verliererVorn");
loserFront.setHeader("Verlierer vorne");
loserFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerliererVorn(), eloChangeService.get2vs2Result(ergebnis.getId()).loserFrontEloChange())));
Grid.Column<Ergebnis2vs2> loserBack = grid.getColumnByKey("verliererHinten");
loserBack.setHeader("Verlierer hinten");
loserBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerliererHinten(), eloChangeService.get2vs2Result(ergebnis.getId()).loserBackEloChange())));
Grid.Column<Ergebnis2vs2> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis2vs2> timestamp = grid.getColumnByKey("timestamp");

View File

@@ -0,0 +1,24 @@
package org.kickerelo.kickerelo.views;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import org.kickerelo.kickerelo.data.Spieler;
public class HistoryCommonView {
private HistoryCommonView() {}
static Div formatEloChange(Spieler spieler, double change) {
Div div = new Div();
Span nameSpan = new Span(spieler.getName() + " ");
String formattedChange = String.format("%+.2f", change);
Span changeSpan = new Span(formattedChange);
changeSpan.getStyle().set("color", "var(--lumo-secondary-text-color)");
changeSpan.getStyle().set("font-style", "italic");
div.add(nameSpan, changeSpan);
return div;
}
}