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

General Refactoring

This commit is contained in:
Anton Micke
2025-05-30 20:35:28 +02:00
committed by AJMicke
parent 0e64e0fd02
commit 0286ed7b56
17 changed files with 149 additions and 123 deletions

View File

@@ -1,6 +1,8 @@
package org.kickerelo.kickerelo.service;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
import org.springframework.stereotype.Service;
@@ -12,19 +14,13 @@ 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(long gameId, Spieler gewinner, Spieler verlierer, short toreVerlierer) {
public EloChange1vs1 updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) {
final float baseK = 50;
final float reductionPerGoal = 0.1f * baseK;
final float finalK = baseK - (reductionPerGoal * toreVerlierer);
@@ -35,19 +31,18 @@ public class EloCalculationService {
gewinner.setElo1vs1(gewinner.getElo1vs1() + eloChange);
verlierer.setElo1vs1(verlierer.getElo1vs1() - eloChange);
eloChangeService.put1vs1Result(gameId, eloChange, -eloChange);
return new EloChange1vs1(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(long gameId, Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
public EloChange2vs2 updateElo2vs2(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();
@@ -70,7 +65,7 @@ public class EloCalculationService {
verliererVorn.setElo2vs2((float) (verliererVorn.getElo2vs2() + loser1EloChange));
verliererHinten.setElo2vs2((float) (verliererHinten.getElo2vs2() + loser2EloChange));
eloChangeService.put2vs2Result(gameId, winner1EloChange, winner2EloChange, loser1EloChange, loser2EloChange);
return new EloChange2vs2(winner1EloChange, winner2EloChange, loser1EloChange, loser2EloChange);
}
public float getInitialElo1vs1() {

View File

@@ -1,37 +0,0 @@
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

@@ -0,0 +1,32 @@
package org.kickerelo.kickerelo.service;
import org.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
import java.util.HashMap;
import java.util.Map;
public class EloChangeTracker {
static Map<Long, EloChange1vs1> changes1vs1 = new HashMap<>();
static Map<Long, EloChange2vs2> changes2vs2 = new HashMap<>();
public static void put1vs1Result(Long gameId, EloChange1vs1 change) {
changes1vs1.put(gameId, change);
}
public static void put2vs2Result(long gameId, EloChange2vs2 change) {
changes2vs2.put(gameId, change);
}
public static EloChange1vs1 get1vs1Result(long gameId) {
var result = changes1vs1.get(gameId);
assert result != null;
return result;
}
public static EloChange2vs2 get2vs2Result(long gameId) {
var result = changes2vs2.get(gameId);
assert result != null;
return result;
}
}

View File

@@ -9,10 +9,11 @@ import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.Ergebnis1vs1TimeComparator;
import org.kickerelo.kickerelo.util.Ergebnis2vs2TimeComparator;
import org.kickerelo.kickerelo.util.SpielerNameComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
import org.kickerelo.kickerelo.util.comparator.Ergebnis1vs1TimeComparator;
import org.kickerelo.kickerelo.util.comparator.Ergebnis2vs2TimeComparator;
import org.kickerelo.kickerelo.util.comparator.SpielerNameComparator;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@@ -24,10 +25,10 @@ import java.util.stream.Stream;
*/
@Service
public class KickerEloService {
private Ergebnis1vs1Repository ergebnis1vs1Repository;
private Ergebnis2vs2Repository ergebnis2vs2Repository;
private SpielerRepository spielerRepository;
private EloCalculationService eloCalculationService;
private final Ergebnis1vs1Repository ergebnis1vs1Repository;
private final Ergebnis2vs2Repository ergebnis2vs2Repository;
private final SpielerRepository spielerRepository;
private final EloCalculationService eloCalculationService;
public KickerEloService(Ergebnis1vs1Repository ergebnis1vs1Repository,
Ergebnis2vs2Repository ergebnis2vs2Repository,
@@ -76,9 +77,10 @@ public class KickerEloService {
}
Ergebnis1vs1 ergebnis = new Ergebnis1vs1(gewinner, verlierer, toreVerlierer);
ergebnis1vs1Repository.save(ergebnis);
ergebnis = ergebnis1vs1Repository.save(ergebnis);
eloCalculationService.updateElo1vs1(ergebnis.getId(), gewinner, verlierer, toreVerlierer);
EloChange1vs1 change = eloCalculationService.updateElo1vs1(gewinner, verlierer, toreVerlierer);
EloChangeTracker.put1vs1Result(ergebnis.getId(), change);
spielerRepository.save(gewinner);
spielerRepository.save(verlierer);
}
@@ -114,9 +116,10 @@ public class KickerEloService {
}
Ergebnis2vs2 ergebnis = new Ergebnis2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
ergebnis2vs2Repository.save(ergebnis);
ergebnis = ergebnis2vs2Repository.save(ergebnis);
eloCalculationService.updateElo2vs2(ergebnis.getId(), gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
EloChange2vs2 change = eloCalculationService.updateElo2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
EloChangeTracker.put2vs2Result(ergebnis.getId(), change);
spielerRepository.save(gewinnerVorn);
spielerRepository.save(gewinnerHinten);
spielerRepository.save(verliererVorn);
@@ -156,10 +159,10 @@ public class KickerEloService {
}
Stream<Ergebnis1vs1> results = ergebnis1vs1Repository.findAll().stream().sorted(new Ergebnis1vs1TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo1vs1(r.getId(),
players.get(r.getGewinner().getId()),
players.get(r.getVerlierer().getId()),
r.getToreVerlierer());
EloChange1vs1 c = eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
players.get(r.getVerlierer().getId()),
r.getToreVerlierer());
EloChangeTracker.put1vs1Result(r.getId(), c);
});
spielerRepository.saveAll(players.values());
}
@@ -175,12 +178,12 @@ public class KickerEloService {
}
Stream<Ergebnis2vs2> results = ergebnis2vs2Repository.findAll().stream().sorted(new Ergebnis2vs2TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo2vs2(r.getId(),
players.get(r.getGewinnerVorn().getId()),
players.get(r.getGewinnerHinten().getId()),
players.get(r.getVerliererVorn().getId()),
players.get(r.getVerliererHinten().getId()),
r.getToreVerlierer());
EloChange2vs2 c = eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
players.get(r.getGewinnerHinten().getId()),
players.get(r.getVerliererVorn().getId()),
players.get(r.getVerliererHinten().getId()),
r.getToreVerlierer());
EloChangeTracker.put2vs2Result(r.getId(), c);
});
spielerRepository.saveAll(players.values());
}

View File

@@ -0,0 +1,5 @@
package org.kickerelo.kickerelo.util;
public record EloChange1vs1(double winnerEloChange,
double loserEloChange) {
}

View File

@@ -0,0 +1,7 @@
package org.kickerelo.kickerelo.util;
public record EloChange2vs2(double winnerFrontEloChange,
double winnerBackEloChange,
double loserFrontEloChange,
double loserBackEloChange) {
}

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Spieler;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Spieler;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Spieler;

View File

@@ -8,7 +8,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.Spieler1vs1EloComparator;
import org.kickerelo.kickerelo.util.comparator.Spieler1vs1EloComparator;
@Route("graph1vs1")
public class Graph1vs1View extends VerticalLayout {

View File

@@ -8,7 +8,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.Spieler2vs2EloComparator;
import org.kickerelo.kickerelo.util.comparator.Spieler2vs2EloComparator;
@Route("graph2vs2")
public class Graph2vs2View extends VerticalLayout {

View File

@@ -7,7 +7,6 @@ import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
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;
@@ -16,23 +15,17 @@ 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 {
public class History1vs1View extends HistoryView {
private final Ergebnis1vs1Repository repo;
private final EloChangeService eloChangeService;
List<Ergebnis1vs1> res;
public History1vs1View(Ergebnis1vs1Repository repo, EloChangeService eloChangeService) {
public History1vs1View(Ergebnis1vs1Repository repo) {
this.repo = repo;
this.eloChangeService = eloChangeService;
setSizeFull();
H2 subheading = new H2("Spiele 1 vs 1");
@@ -61,10 +54,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())));
winnerColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.GEWINNER)));
Grid.Column<Ergebnis1vs1> loserColumn = grid.getColumnByKey("verlierer");
loserColumn.setHeader("Verlierer");
loserColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerlierer(), eloChangeService.get1vs1Result(ergebnis.getId()).loserEloChange())));
loserColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.VERLIERER)));
Grid.Column<Ergebnis1vs1> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis1vs1> timestamp = grid.getColumnByKey("timestamp");

View File

@@ -8,7 +8,6 @@ import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
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;
@@ -17,21 +16,17 @@ 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 class History2vs2View extends HistoryView {
private final Ergebnis2vs2Repository repo;
private final EloChangeService eloChangeService;
public History2vs2View(Ergebnis2vs2Repository repo, EloChangeService eloChangeService) {
public History2vs2View(Ergebnis2vs2Repository repo) {
this.repo = repo;
this.eloChangeService = eloChangeService;
setSizeFull();
H2 subheading = new H2("Spiele 2 vs 2");
List<Ergebnis2vs2> res = repo.findAll();
@@ -73,16 +68,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())));
winnerFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_VORN)));
Grid.Column<Ergebnis2vs2> winnerBack = grid.getColumnByKey("gewinnerHinten");
winnerBack.setHeader("Gewinner hinten");
winnerBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getGewinnerHinten(), eloChangeService.get2vs2Result(ergebnis.getId()).winnerBackEloChange())));
winnerBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_HINTEN)));
Grid.Column<Ergebnis2vs2> loserFront = grid.getColumnByKey("verliererVorn");
loserFront.setHeader("Verlierer vorne");
loserFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerliererVorn(), eloChangeService.get2vs2Result(ergebnis.getId()).loserFrontEloChange())));
loserFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_VORN)));
Grid.Column<Ergebnis2vs2> loserBack = grid.getColumnByKey("verliererHinten");
loserBack.setHeader("Verlierer hinten");
loserBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis.getVerliererHinten(), eloChangeService.get2vs2Result(ergebnis.getId()).loserBackEloChange())));
loserBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_HINTEN)));
Grid.Column<Ergebnis2vs2> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis2vs2> timestamp = grid.getColumnByKey("timestamp");

View File

@@ -1,24 +0,0 @@
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;
}
}

View File

@@ -0,0 +1,57 @@
package org.kickerelo.kickerelo.views;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.theme.lumo.LumoUtility;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.service.EloChangeTracker;
import org.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
public class HistoryView extends VerticalLayout {
enum PlayerType1vs1 {
GEWINNER,
VERLIERER
}
enum PlayerType2vs2 {
GEWINNER_VORN,
GEWINNER_HINTEN,
VERLIERER_VORN,
VERLIERER_HINTEN
}
static Div formatEloChange(Ergebnis1vs1 ergebnis, PlayerType1vs1 type) {
EloChange1vs1 change = EloChangeTracker.get1vs1Result(ergebnis.getId());
return switch (type) {
case GEWINNER -> formatEloChange(ergebnis.getGewinner(), change.winnerEloChange());
case VERLIERER -> formatEloChange(ergebnis.getVerlierer(), change.loserEloChange());
};
}
static Div formatEloChange(Ergebnis2vs2 ergebnis, PlayerType2vs2 type) {
EloChange2vs2 change = EloChangeTracker.get2vs2Result(ergebnis.getId());
return switch (type) {
case GEWINNER_VORN -> formatEloChange(ergebnis.getGewinnerVorn(), change.winnerFrontEloChange());
case GEWINNER_HINTEN -> formatEloChange(ergebnis.getGewinnerHinten(), change.winnerBackEloChange());
case VERLIERER_VORN -> formatEloChange(ergebnis.getVerliererVorn(), change.loserFrontEloChange());
case VERLIERER_HINTEN -> formatEloChange(ergebnis.getVerliererHinten(), change.loserBackEloChange());
};
}
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.addClassNames(LumoUtility.FontSize.SMALL, LumoUtility.TextColor.SECONDARY, LumoUtility.FontWeight.LIGHT);
div.add(nameSpan, changeSpan);
return div;
}
}