package controllers; import java.math.RoundingMode; import java.net.URL; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.ResourceBundle; import data.GuiMysql; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.DatePicker; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.MapValueFactory; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.TextFieldTableCell; import javafx.util.Callback; import javafx.util.converter.FloatStringConverter; import objects.League; import objects.SoccerMatch; import objects.SoccerMatchAnalysis; import objects.bets.Bet; import objects.bets.Bet.Status; @SuppressWarnings("rawtypes") public class AnalysisTestController implements Initializable { private static final float MIN_COVER_ODDS = 1.25f; private static final float MAX_COVER_ODDS = 2.25f; private static final DecimalFormat FLOAT_FORMATTER = new DecimalFormat("0.00"); @FXML TableView gameViewTable; TableColumn homeTeamNameColumn = new TableColumn<>("Home Team"); TableColumn awayTeamNameColumn = new TableColumn<>("Away Team"); TableColumn odds1Column = new TableColumn<>("1"); TableColumn oddsXColumn = new TableColumn<>("X"); TableColumn odds2Column = new TableColumn<>("2"); TableColumn leagueNameColumn = new TableColumn<>("Country"); TableColumn countryNameColumn = new TableColumn<>("League"); TableColumn analyzeValueColumn = new TableColumn<>("Value"); TableColumn matchBetAmountColumn = new TableColumn<>("Bet Amount"); TableColumn addBetColumn = new TableColumn<>("Add Bet"); @FXML TableView activeBetsTable; TableColumn matchupColumn = new TableColumn<>("Matchup"); TableColumn oddsColumn = new TableColumn<>("Odds"); TableColumn betAmountColumn = new TableColumn<>("BetAmount"); TableColumn statusColumn = new TableColumn<>("Status"); TableColumn homeScoreColumn = new TableColumn<>("Home Score"); TableColumn awayScoreColumn = new TableColumn<>("Away Score"); TableColumn betCoveredNumberColumn = new TableColumn<>("Bet Covered Number"); @FXML TableView> leagueBetStatsTable; @FXML TableColumn statsLeagueNameColumn = new TableColumn<>("League"); @FXML TableColumn statsHomeWinColumn = new TableColumn<>("Home Win"); @FXML TableColumn statsHomeLossColumn = new TableColumn<>("Home Loss"); @FXML TableColumn statsHomePercentColumn = new TableColumn<>("Home Percent"); @FXML TableColumn statsAwayWinColumn = new TableColumn<>("Away Win"); @FXML TableColumn statsAwayLossColumn = new TableColumn<>("Away Loss"); @FXML TableColumn statsAwayPercentColumn = new TableColumn<>("Away Percent"); @FXML TableColumn statsTotalPercentColumn = new TableColumn<>("Total Percent"); @FXML Button checkBetsButton; @FXML Button getMatchesButton; @FXML DatePicker date; @FXML TextField bankTextField; @FXML TextField bettingPercentTextField; private float currentBetAmount = 10f; @Override public void initialize(URL location, ResourceBundle resources) { FLOAT_FORMATTER.setRoundingMode(RoundingMode.UP); DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.ENGLISH); dfs.setDecimalSeparator('.'); dfs.setGroupingSeparator(','); FLOAT_FORMATTER.setDecimalFormatSymbols(dfs); bettingPercentTextField.textProperty().addListener((observable, oldValue, newValue) -> { if (newValue.length() > 0) { checkValidBetAmount(); } }); date.valueProperty().addListener((observable, oldValue, newValue) -> { if (oldValue != newValue) { gameViewTable.getItems().clear(); } }); gameViewTable.getSelectionModel().selectedItemProperty().addListener((obj, oldValue, newValue) -> { if (oldValue != null) { oldValue.setBetAmount(currentBetAmount); } if (newValue != null) { newValue.setBetAmount(getNewBetValue(newValue)); } gameViewTable.refresh(); activeBetsTable.refresh(); }); homeTeamNameColumn.setCellValueFactory(new PropertyValueFactory<>("homeTeamName")); awayTeamNameColumn.setCellValueFactory(new PropertyValueFactory<>("awayTeamName")); odds1Column.setCellValueFactory(new PropertyValueFactory<>("odds1")); oddsXColumn.setCellValueFactory(new PropertyValueFactory<>("oddsX")); odds2Column.setCellValueFactory(new PropertyValueFactory<>("odds2")); leagueNameColumn.setCellValueFactory(new PropertyValueFactory<>("leagueName")); countryNameColumn.setCellValueFactory(new PropertyValueFactory<>("countryName")); analyzeValueColumn.setCellValueFactory(new PropertyValueFactory<>("analysisValue")); matchBetAmountColumn.setCellValueFactory(new PropertyValueFactory<>("betAmount")); Callback, TableCell> cellFactory = new Callback, TableCell>() { @Override public TableCell call(final TableColumn param) { return new TableCell() { private final Button btn = new Button("Submit"); { btn.setOnAction((ActionEvent event) -> { SoccerMatch data = getTableView().getItems().get(getIndex()); addToBets(data); }); } @Override public void updateItem(Void item, boolean empty) { super.updateItem(item, empty); if (empty) { setGraphic(null); } else { setGraphic(btn); } } }; } }; addBetColumn.setCellFactory(cellFactory); gameViewTable.getColumns().addAll(homeTeamNameColumn, awayTeamNameColumn, odds1Column, oddsXColumn, odds2Column, countryNameColumn, leagueNameColumn, analyzeValueColumn, matchBetAmountColumn, addBetColumn); gameViewTable.setEditable(true); odds1Column.setCellFactory(TextFieldTableCell.forTableColumn(new FloatStringConverter())); odds1Column.setOnEditCommit( e -> e.getTableView().getItems().get(e.getTablePosition().getRow()).setOdds1(e.getNewValue())); oddsXColumn.setCellFactory(TextFieldTableCell.forTableColumn(new FloatStringConverter())); oddsXColumn.setOnEditCommit( e -> e.getTableView().getItems().get(e.getTablePosition().getRow()).setOddsX(e.getNewValue())); odds2Column.setCellFactory(TextFieldTableCell.forTableColumn(new FloatStringConverter())); odds2Column.setOnEditCommit( e -> e.getTableView().getItems().get(e.getTablePosition().getRow()).setOdds2(e.getNewValue())); matchupColumn.setCellValueFactory(new PropertyValueFactory<>("matchup")); matchupColumn.setMinWidth(100d); oddsColumn.setCellValueFactory(new PropertyValueFactory<>("betOdds")); betAmountColumn.setCellValueFactory(new PropertyValueFactory<>("betAmount")); homeScoreColumn.setCellValueFactory(new PropertyValueFactory<>("homeScore")); awayScoreColumn.setCellValueFactory(new PropertyValueFactory<>("awayScore")); betCoveredNumberColumn.setCellValueFactory(new PropertyValueFactory<>("betCoveredNumber")); statusColumn.setCellValueFactory(new PropertyValueFactory<>("status")); activeBetsTable.getColumns().addAll(matchupColumn, oddsColumn, betAmountColumn, statusColumn, homeScoreColumn, awayScoreColumn, betCoveredNumberColumn); getActiveBets(); statsLeagueNameColumn.setCellValueFactory(new MapValueFactory<>("statsLeagueName")); statsHomeWinColumn.setCellValueFactory(new MapValueFactory<>("statsHomeWin")); statsHomeLossColumn.setCellValueFactory(new MapValueFactory<>("statsHomeLoss")); statsAwayWinColumn.setCellValueFactory(new MapValueFactory<>("statsAwayWin")); statsAwayLossColumn.setCellValueFactory(new MapValueFactory<>("statsAwayLoss")); statsHomePercentColumn.setCellValueFactory(new MapValueFactory<>("statsHomePercent")); statsAwayPercentColumn.setCellValueFactory(new MapValueFactory<>("statsAwayPercent")); statsTotalPercentColumn.setCellValueFactory(new MapValueFactory<>("statsTotalPercent")); initializeStatsPanel(); } private void addMatchesToTable(List matches) { ObservableList currentMatches = FXCollections.observableArrayList(); for (SoccerMatch soccerMatch : matches) { int analyzeValue = getAnalyzeValue(soccerMatch); soccerMatch.setAnalysisValue(analyzeValue); if (currentBetAmount > 0) { soccerMatch.setBetAmount(currentBetAmount); } currentMatches.add(soccerMatch); } gameViewTable.getItems().addAll(currentMatches); } private void addToBets(SoccerMatch data) { if (data.getAnalysisValueInt() > 0) { setNewBet(data, true); } else if (data.getAnalysisValueInt() < 0f) { setNewBet(data, false); } else { System.out.println("Can't add bets with analysts value " + data.getAnalysisValueInt()); } } private void checkBet(Bet b) { if (b.getHomeScore() >= 0 && b.getAwayScore() >= 0 && (b.getBet().equals("1") && b.getHomeScore() > b.getAwayScore()) || (b.getBet().equals("2") && b.getHomeScore() < b.getAwayScore())) { // Win b.setStatus(Bet.Status.DONE); Float win = (b.getBetOdds() * b.getBetAmount()); updateBank(win); } else if (b.getHomeScore() >= 0 && b.getAwayScore() >= 0) { // Loss b.setStatus(Bet.Status.LOST); } GuiMysql.getInstance().updateBetStatus(b.getId(), b.getStatus()); updateStatsPanel(b); } @FXML private void CheckBetsAction() { activeBetsTable.getItems().stream().filter(p -> p.getStatus() == Bet.Status.OPEN).forEach(this::checkBet); activeBetsTable.refresh(); updateBetAmount(); } private void checkValidBetAmount() { try { float bankValue = Float.parseFloat(bankTextField.getText()); float betPercent = Float.parseFloat(bettingPercentTextField.getText()); final float betAmount = Float.parseFloat(FLOAT_FORMATTER.format(bankValue * (betPercent / 100f))); currentBetAmount = betAmount; gameViewTable.getItems().forEach(i -> { i.setBetAmount(betAmount); }); gameViewTable.refresh(); } catch (NumberFormatException e) { // Ignore } } private void getActiveBets() { List analysisBets = GuiMysql.getInstance().getAnalysisBets(); activeBetsTable.getItems().addAll(analysisBets); activeBetsTable.refresh(); } private Integer getAnalyzeValue(SoccerMatch soccerMatch) { int result = 0; SoccerMatchAnalysis analysis = new SoccerMatchAnalysis(soccerMatch); League leagueInfo = GuiMysql.getInstance().getLeagueInfo(soccerMatch.getHomeTeam().getTeamLeagueId()); if (leagueInfo != null) { int homeWinsCount = analysis.winLossRatio(leagueInfo.getWinLossRatio(), true); int awayWinsCount = analysis.winLossRatio(leagueInfo.getWinLossRatio(), false); int homeWinLossRatioCount = analysis.winLossRationHomeAndAway(true, leagueInfo.getWinLossRatioHomeAndAway()); int awayWinLossRatioCount = analysis.winLossRationHomeAndAway(false, leagueInfo.getWinLossRatioHomeAndAway()); int homeScoringTotal = analysis.scoringTotal(leagueInfo.getScoringTotal(), true); int awayScoringTotal = analysis.scoringTotal(leagueInfo.getScoringTotal(), false); int scoringDiffLastGames = analysis.getScoringDiffLastGames(leagueInfo.getScoringDiffLastGame()); int winsCountDiff = homeWinsCount - awayWinsCount; int winLossRatioDiff = homeWinLossRatioCount - awayWinLossRatioCount; int scoringTotalDiff = homeScoringTotal - awayScoringTotal; if (scoringDiffLastGames < 0 && winsCountDiff < 0 && winLossRatioDiff < 0 && scoringTotalDiff < 0) { result = (scoringDiffLastGames + winsCountDiff + winLossRatioDiff + scoringTotalDiff) / 4; } else if (scoringDiffLastGames > 0 && winsCountDiff > 0 && winLossRatioDiff > 0 && scoringTotalDiff > 0) { result = (scoringDiffLastGames + winsCountDiff + winLossRatioDiff + scoringTotalDiff) / 4; } } return result; } private float getBank() { float bank = 0; try { bank = Float.parseFloat(bankTextField.getText()); } catch (NumberFormatException e) { } return bank; } @FXML private void GetMatchesAction() { List matches = GuiMysql.getInstance().getMatches(1, date.getValue().toString(), "ASC", true); addMatchesToTable(matches); } private Float getNewBetValue(SoccerMatch newValue) { float odds = 0f; float currentDebt = 0f; float result = currentBetAmount; if (((newValue.getAnalysisValueInt() > 0 && newValue.getOdds1() > MIN_COVER_ODDS && newValue.getOdds1() < MAX_COVER_ODDS) || (newValue.getAnalysisValueInt() < 0 && newValue.getOdds2() > MIN_COVER_ODDS && newValue.getOdds2() < MAX_COVER_ODDS)) && activeBetsTable.getItems().stream().filter(p -> p.getStatus() == Bet.Status.LOST).count() > 0) { Bet bet = activeBetsTable.getItems().stream().filter(p -> p.getStatus() == Bet.Status.LOST).findFirst() .get(); Bet tempBet = bet; int betCoverNumber = 0; while (tempBet.getCoveredBetId() > 0) { tempBet = GuiMysql.getInstance().getAnalysisBet(tempBet.getCoveredBetId()); currentDebt += tempBet.getBetAmount(); betCoverNumber++; } bet.setBetCoveredNumber(betCoverNumber); if (betCoverNumber > 3 && newValue.getAnalysisValueInt() < 10 && newValue.getAnalysisValueInt() > -10) { return Float.parseFloat(FLOAT_FORMATTER.format(result)); } currentDebt += bet.getBetAmount(); newValue.setPreviousBet(bet); if (newValue.getAnalysisValueInt() > 0f) { odds = newValue.getOdds1(); } else if (newValue.getAnalysisValueInt() < 0f) { odds = newValue.getOdds2(); } if (currentDebt > 0f && odds > 0f) { result = currentDebt / (odds - 1f); // + currentBetAmount; } else if (newValue.getBetAmount() != null) { result = newValue.getBetAmount(); } } return Float.parseFloat(FLOAT_FORMATTER.format(result)); } private void initializeStatsPanel() { List bets = GuiMysql.getInstance().getAnalysisBetStatistics(); for (Bet bet : bets) { updateStatsPanel(bet); } } @FXML private void RemoveDoneBetsAction() { List doneBets = activeBetsTable.getItems().stream() .filter(p -> p.getStatus() == Status.DONE || p.getStatus() == Status.COVERED) .toList(); for (Bet bet : doneBets) { if (bet.getStatus().equals(Status.COVERED)) { GuiMysql.getInstance().updateBetStatus(bet.getId(), Status.DONE); } activeBetsTable.getItems().remove(bet); } activeBetsTable.getItems().sort((b1, b2) -> Float.compare(b2.getBetAmount(), b1.getBetAmount())); activeBetsTable.refresh(); } private void setNewBet(SoccerMatch data, boolean isHomeBet) { int betId = GuiMysql.getInstance() .addAnalysisBet(new Bet(-1, data, isHomeBet ? "1" : "2", data.getBetAmount(), isHomeBet ? data.getOdds1() : data.getOdds2())); Bet bet = GuiMysql.getInstance().getAnalysisBet(betId); if (data.getPreviousBet() != null) { data.getPreviousBet().setStatus(Status.COVERED); bet.setCoveredBetId(data.getPreviousBet().getId()); GuiMysql.getInstance().setBetCovered(bet.getId(), bet.getCoveredBetId()); try { activeBetsTable.getItems().set(activeBetsTable.getItems().indexOf(data.getPreviousBet()), data.getPreviousBet()); } catch (IndexOutOfBoundsException e) { data.setPreviousBet(null); // TODO, ingen bra lösning kanske.. Men enkel att göra :) } finally { GuiMysql.getInstance().updateBetStatus(data.getPreviousBet().getId(), Status.COVERED); } } activeBetsTable.getItems().add(bet); updateBank(-bet.getBetAmount()); } private void updateBank(float value) { float bank; try { bank = Float.parseFloat(bankTextField.getText()); bank += value; bankTextField.setText(String.valueOf(bank)); } catch (NumberFormatException e) { } } private void updateBetAmount() { try { float betLevel = Float.parseFloat(bettingPercentTextField.getText()); currentBetAmount = Float.parseFloat(FLOAT_FORMATTER.format(getBank() * (betLevel / 100f))); } catch (NumberFormatException e) { } } private void updateStatsPanel(Bet b) { String leagueName = b.getMatch().getLeagueName(); Optional> leagueStatsRow = leagueBetStatsTable.getItems().stream() .filter(p -> p.get("statsLeagueName") != null && leagueName.equals(p.get("statsLeagueName"))) .findFirst(); if (leagueStatsRow.isEmpty()) { Map newItem = new HashMap<>(); newItem.put("statsLeagueName", leagueName); newItem.put("statsHomeWin", b.isBetOnHomeTeam() && b.getHomeScore() > b.getAwayScore() ? 1 : 0); newItem.put("statsHomeLoss", b.isBetOnHomeTeam() && (b.getHomeScore() < b.getAwayScore() || b.getHomeScore() == b.getAwayScore()) ? 1 : 0); int statsHomeWin = (int) newItem.get("statsHomeWin"); int statsHomeLoss = (int) newItem.get("statsHomeLoss"); if (statsHomeWin > 0 || statsHomeLoss > 0) { newItem.put("statsHomePercent", (statsHomeWin / (float) (statsHomeWin + statsHomeLoss)) * 100); } newItem.put("statsAwayWin", b.isBetOnAwayTeam() && b.getHomeScore() < b.getAwayScore() ? 1 : 0); newItem.put("statsAwayLoss", b.isBetOnAwayTeam() && (b.getHomeScore() > b.getAwayScore() || b.getHomeScore() == b.getAwayScore()) ? 1 : 0); int statsAwayWin = (int) newItem.get("statsAwayWin"); int statsAwayLoss = (int) newItem.get("statsAwayLoss"); if (statsAwayWin > 0 || statsAwayLoss > 0) { newItem.put("statsAwayPercent", (statsAwayWin / (float) (statsAwayWin + statsAwayLoss)) * 100); } if (statsHomeWin + statsHomeLoss + statsAwayWin + statsAwayLoss > 0) { newItem.put("statsTotalPercent", ((statsHomeWin + statsAwayWin) / (float) (statsHomeWin + statsHomeLoss + statsAwayWin + statsAwayLoss)) * 100); } ObservableList> items = leagueBetStatsTable.getItems(); items.add(newItem); } else { Map map = leagueStatsRow.get(); if (b.isBetOnHomeTeam() && b.getHomeScore() > b.getAwayScore()) { map.put("statsHomeWin", (int) map.get("statsHomeWin") + 1); } else if (b.isBetOnHomeTeam() && (b.getHomeScore() < b.getAwayScore() || b.getHomeScore() == b.getAwayScore())) { map.put("statsHomeLoss", (int) map.get("statsHomeLoss") + 1); } else if (b.isBetOnAwayTeam() && b.getHomeScore() < b.getAwayScore()) { map.put("statsAwayWin", (int) map.get("statsAwayWin") + 1); } else if (b.isBetOnAwayTeam() && (b.getHomeScore() > b.getAwayScore() || b.getHomeScore() == b.getAwayScore())) { map.put("statsAwayLoss", (int) map.get("statsAwayLoss") + 1); } int statsHomeWin = (int) map.get("statsHomeWin"); int statsHomeLoss = (int) map.get("statsHomeLoss"); if (statsHomeWin > 0 || statsHomeLoss > 0) { map.put("statsHomePercent", (statsHomeWin / (float) (statsHomeWin + statsHomeLoss)) * 100); } int statsAwayWin = (int) map.get("statsAwayWin"); int statsAwayLoss = (int) map.get("statsAwayLoss"); if (statsAwayWin > 0 || statsAwayLoss > 0) { map.put("statsAwayPercent", (statsAwayWin / (float) (statsAwayWin + statsAwayLoss)) * 100); } if (statsHomeWin + statsHomeLoss + statsAwayWin + statsAwayLoss > 0) { map.put("statsTotalPercent", ((statsHomeWin + statsAwayWin) / (float) (statsHomeWin + statsHomeLoss + statsAwayWin + statsAwayLoss)) * 100); } leagueBetStatsTable.getItems().set(leagueBetStatsTable.getItems().indexOf(map), map); } leagueBetStatsTable.refresh(); } }