diff --git a/src/main/java/org/kickerelo/kickerelo/layout/KickerAppLayout.java b/src/main/java/org/kickerelo/kickerelo/layout/KickerAppLayout.java index 9e8f39c..617b7f0 100644 --- a/src/main/java/org/kickerelo/kickerelo/layout/KickerAppLayout.java +++ b/src/main/java/org/kickerelo/kickerelo/layout/KickerAppLayout.java @@ -64,7 +64,8 @@ public class KickerAppLayout extends AppLayout { nav2.setCollapsible(true); nav2.addItem(new SideNavItem("Ergebnis eintragen", Enter2vs2View.class, VaadinIcon.EDIT.create()), new SideNavItem("ELO-Graph", Graph2vs2View.class, VaadinIcon.BAR_CHART.create()), - new SideNavItem("Historie", History2vs2View.class, VaadinIcon.RECORDS.create())); + new SideNavItem("Historie", History2vs2View.class, VaadinIcon.RECORDS.create()), + new SideNavItem("Statistik", Stat2vs2View.class, VaadinIcon.ABACUS.create())); Image githubLogo = new Image("github-mark.png", "Github"); diff --git a/src/main/java/org/kickerelo/kickerelo/repository/Ergebnis2vs2Repository.java b/src/main/java/org/kickerelo/kickerelo/repository/Ergebnis2vs2Repository.java index 3e9c3f9..e3163dc 100644 --- a/src/main/java/org/kickerelo/kickerelo/repository/Ergebnis2vs2Repository.java +++ b/src/main/java/org/kickerelo/kickerelo/repository/Ergebnis2vs2Repository.java @@ -1,9 +1,18 @@ package org.kickerelo.kickerelo.repository; import org.kickerelo.kickerelo.data.Ergebnis2vs2; +import org.kickerelo.kickerelo.data.Spieler; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface Ergebnis2vs2Repository extends JpaRepository { + int countByGewinnerHinten(Spieler gewinnerHinten); + int countByGewinnerVorn(Spieler gewinnerVon); + int countByVerliererHinten(Spieler verliererHinten); + int countByVerliererVorn(Spieler verlierVon); + + int countByGewinnerVornOrGewinnerHinten(Spieler gewinnerVon, Spieler gewinnerHinten); + int countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(Spieler gewinnerVon, Spieler gewinnerHinten, + Spieler verliererVon, Spieler verliererHinten); } diff --git a/src/main/java/org/kickerelo/kickerelo/service/EloCalculationService.java b/src/main/java/org/kickerelo/kickerelo/service/EloCalculationService.java index 90d530c..d462a0d 100644 --- a/src/main/java/org/kickerelo/kickerelo/service/EloCalculationService.java +++ b/src/main/java/org/kickerelo/kickerelo/service/EloCalculationService.java @@ -21,13 +21,13 @@ public class EloCalculationService { public void updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) { final float baseK = 50; final float reductionPerGoal = 0.1f * baseK; - final float finalK = baseK - (reductionPerGoal * toreVerlierer); - float expectedScoreWinner = (float) (1 / (1 + Math.pow(10, (verlierer.getElo1vs1() - gewinner.getElo1vs1()) / 400))); - float expectedScoreLoser = (float) (1 / (1 + Math.pow(10, (gewinner.getElo1vs1() - verlierer.getElo1vs1()) / 400))); - gewinner.setElo1vs1(gewinner.getElo1vs1() + finalK * (1-expectedScoreWinner)); - verlierer.setElo1vs1(verlierer.getElo1vs1() - finalK * expectedScoreLoser); + double x = Math.pow(10, (verlierer.getElo1vs1() - gewinner.getElo1vs1()) / 400); + float eloChange = (float) (finalK * x / (x + 1)); + + gewinner.setElo1vs1(gewinner.getElo1vs1() + eloChange); + verlierer.setElo1vs1(verlierer.getElo1vs1() - eloChange); } /** diff --git a/src/main/java/org/kickerelo/kickerelo/service/Stat2vs2Service.java b/src/main/java/org/kickerelo/kickerelo/service/Stat2vs2Service.java new file mode 100644 index 0000000..7d73e62 --- /dev/null +++ b/src/main/java/org/kickerelo/kickerelo/service/Stat2vs2Service.java @@ -0,0 +1,42 @@ +package org.kickerelo.kickerelo.service; + +import org.kickerelo.kickerelo.data.Spieler; +import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository; +import org.kickerelo.kickerelo.repository.SpielerRepository; +import org.kickerelo.kickerelo.util.Position; +import org.springframework.stereotype.Service; + +@Service +public class Stat2vs2Service { + Ergebnis2vs2Repository ergebnis2vs2Repository; + SpielerRepository spielerRepository; + + public Stat2vs2Service(Ergebnis2vs2Repository ergebnis2vs2Repository, SpielerRepository spielerRepository) { + this.ergebnis2vs2Repository = ergebnis2vs2Repository; + this.spielerRepository = spielerRepository; + } + + public Float getWinrate(Spieler s, Position p) { + int wins, losses; + switch (p) { + case BACK: + wins = ergebnis2vs2Repository.countByGewinnerHinten(s); + losses = ergebnis2vs2Repository.countByVerliererHinten(s); + break; + case FRONT: + wins = ergebnis2vs2Repository.countByGewinnerVorn(s); + losses = ergebnis2vs2Repository.countByVerliererVorn(s); + break; + default: + wins = 0; + losses = 0; + } + return (float) wins / (wins+losses); + } + + public Float getFrontRate(Spieler s) { + int numFront = ergebnis2vs2Repository.countByGewinnerVornOrGewinnerHinten(s, s); + int numAll = ergebnis2vs2Repository.countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(s, s, s, s); + return (float) numFront / numAll; + } +} diff --git a/src/main/java/org/kickerelo/kickerelo/util/Position.java b/src/main/java/org/kickerelo/kickerelo/util/Position.java new file mode 100644 index 0000000..7fc835e --- /dev/null +++ b/src/main/java/org/kickerelo/kickerelo/util/Position.java @@ -0,0 +1,5 @@ +package org.kickerelo.kickerelo.util; + +public enum Position { + FRONT, BACK +} diff --git a/src/main/java/org/kickerelo/kickerelo/views/Stat2vs2View.java b/src/main/java/org/kickerelo/kickerelo/views/Stat2vs2View.java new file mode 100644 index 0000000..7bfa90e --- /dev/null +++ b/src/main/java/org/kickerelo/kickerelo/views/Stat2vs2View.java @@ -0,0 +1,68 @@ +package org.kickerelo.kickerelo.views; + +import com.vaadin.flow.component.combobox.ComboBox; +import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.html.NativeLabel; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.progressbar.ProgressBar; +import com.vaadin.flow.component.progressbar.ProgressBarVariant; +import com.vaadin.flow.router.Route; +import org.kickerelo.kickerelo.data.Spieler; +import org.kickerelo.kickerelo.service.KickerEloService; +import org.kickerelo.kickerelo.service.Stat2vs2Service; +import org.kickerelo.kickerelo.util.Position; + +@Route("stat2vs2") +public class Stat2vs2View extends VerticalLayout { + Stat2vs2Service stat2vs2Service; + KickerEloService kickerEloService; + H2 subheading; + ComboBox selector; + ProgressBar frontRate = new ProgressBar(); + NativeLabel frontRateText = new NativeLabel(); + ProgressBar winRateFront = new ProgressBar(); + NativeLabel winRateFrontText = new NativeLabel(); + ProgressBar winRateBack = new ProgressBar(); + NativeLabel winRateBackText = new NativeLabel(); + + public Stat2vs2View(Stat2vs2Service service, KickerEloService kickerService) { + this.stat2vs2Service = service; + this.kickerEloService = kickerService; + subheading = new H2("2 vs 2 Ergebnis"); + selector = new ComboBox<>("Spieler"); + selector.setItems(kickerService.getSpielerEntities()); + selector.addValueChangeListener(event -> updateData(selector.getValue())); + add(subheading, selector, frontRateText, frontRate, winRateFrontText, winRateFront, winRateBackText, winRateBack); + } + + private void updateData(Spieler s) { + updateFrontRate(s); + updateFrontWinrate(s); + updateBackWinrate(s); + } + + private void updateFrontRate(Spieler s) { + String text = "Anteil Spiele vorne: "; + Float rateFrontGames = stat2vs2Service.getFrontRate(s); + frontRate.setValue(rateFrontGames); + frontRateText.setText(rateFrontGames.isNaN() ? text + "-" : text + String.format("%.2f", rateFrontGames * 100) + "%"); + } + + private void updateFrontWinrate(Spieler s) { + String text = "Winrate vorne: "; + Float winRate = stat2vs2Service.getWinrate(s, Position.FRONT); + winRateFront.setValue(winRate); + winRateFront.removeThemeVariants(ProgressBarVariant.LUMO_SUCCESS, ProgressBarVariant.LUMO_ERROR); + winRateFront.addThemeVariants((winRate > 0.5f ? ProgressBarVariant.LUMO_SUCCESS : ProgressBarVariant.LUMO_ERROR)); + winRateFrontText.setText(winRate.isNaN() ? text + "-" : text + String.format("%.2f", winRate * 100) + "%"); + } + + private void updateBackWinrate(Spieler s) { + String text = "Winrate hinten: "; + Float winRate = stat2vs2Service.getWinrate(s, Position.BACK); + winRateBack.setValue(winRate); + winRateBack.removeThemeVariants(ProgressBarVariant.LUMO_SUCCESS, ProgressBarVariant.LUMO_ERROR); + winRateBack.addThemeVariants((winRate > 0.5f ? ProgressBarVariant.LUMO_SUCCESS : ProgressBarVariant.LUMO_ERROR)); + winRateBackText.setText(winRate.isNaN() ? text + "-" : text + String.format("%.2f", winRate * 100) + "%"); + } +}