Skip to content

Commit

Permalink
#235 Calculate form sub skill (#2082)
Browse files Browse the repository at this point in the history
* #235 Calculation of the tsi sub

* #235 Form sub in statistic panel

* #235 Form sub in statistic panel

* #235 PlayerSubskillOffsetDialog disable not available skills

* #235 PlayerSubskillOffsetDialog disable not available skills

* #235 review

* #235 review
  • Loading branch information
wsbrenk committed Jun 22, 2024
1 parent fb8bbd7 commit 06e13cc
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 94 deletions.
6 changes: 3 additions & 3 deletions src/main/java/core/db/DBManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@
public class DBManager implements PersistenceManager {

/** database versions */
private static final int DBVersion = 800; // HO 8.0 version
private static final int DBVersion = 900; // HO 9.0 version
/**
* Previous db version is used by development versions to ensure that db upgrade will rerun on each
* new installed preliminary version
*/
private static final int previousDBVersion = 701;
private static final double DBConfigVersion = 8d; // HO 8.0 version
private static final int previousDBVersion = 800;
private static final double DBConfigVersion = 9d; // HO 8.0 version

/** 2004-06-14 11:00:00.0 */
public static Timestamp TSIDATE = new Timestamp(1087203600000L);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/core/db/DBUpdater.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ void updateDB(int DBVersion) {
private void updateDBv900(int dbVersion) throws SQLException {
var matchDetailsTable = dbManager.getTable(MatchDetailsTable.TABLENAME);
matchDetailsTable.tryChangeColumn("Matchreport", "VARCHAR(40000)");

var playerTable = dbManager.getTable(SpielerTable.TABLENAME);
playerTable.tryAddColumn("SubForm", "FLOAT DEFAULT 0");

updateDBVersion(dbVersion, 900);
}

Expand Down
5 changes: 1 addition & 4 deletions src/main/java/core/db/HRFTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@ public HRF loadLatestHRFDownloadedBefore(Timestamp fetchDate) {
}

private HRF loadMaxHrf() {
/**
* liefert die Maximal Vergebene Id eines HRF-Files
*/
String loadMaxHrfSql = createSelectStatement("order by HRF_ID desc LIMIT 1");
return loadHRF(loadMaxHrfSql);
}
Expand All @@ -157,7 +154,7 @@ private HRF loadHRF(String query, Object... params) {
}

public List<Integer> getHrfIdPerWeekList(int nWeeks) {
var sql = "select min(hrf_id) as id from " +
var sql = "select max(hrf_id) as id from " +
getTableName() +
" group by unix_timestamp(datum)/7/86400 order by id desc limit " +
nWeeks;
Expand Down
16 changes: 1 addition & 15 deletions src/main/java/core/db/SpielerTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ protected void initColumns() {
ColumnDescriptor.Builder.newInstance().setColumnName("SubPasspiel").setGetter((p) -> ((Player) p).getSub4Skill(PlayerSkill.PASSING)).setSetter((p, v) -> ((Player) p).setSubskill4PlayerSkill(PlayerSkill.PASSING, (float) v)).setType(Types.REAL).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("SubStandards").setGetter((p) -> ((Player) p).getSub4Skill(PlayerSkill.SETPIECES)).setSetter((p, v) -> ((Player) p).setSubskill4PlayerSkill(PlayerSkill.SETPIECES, (float) v)).setType(Types.REAL).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("SubStamina").setGetter((p) -> ((Player) p).getSub4Skill(PlayerSkill.STAMINA)).setSetter((p, v) -> ((Player) p).setSubskill4PlayerSkill(PlayerSkill.STAMINA, (float) v)).setType(Types.REAL).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("SubForm").setGetter((p) -> ((Player) p).getSub4Skill(PlayerSkill.FORM)).setSetter((p, v) -> ((Player) p).setSubskill4PlayerSkill(PlayerSkill.FORM, (float) v)).setType(Types.REAL).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("iSpezialitaet").setGetter((p) -> ((Player) p).getSpecialty()).setSetter((p, v) -> ((Player) p).setSpecialty((int) v)).setType(Types.INTEGER).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("iCharakter").setGetter((p) -> ((Player) p).getGentleness()).setSetter((p, v) -> ((Player) p).setGentleness((int) v)).setType(Types.INTEGER).isNullable(false).build(),
ColumnDescriptor.Builder.newInstance().setColumnName("iAnsehen").setGetter((p) -> ((Player) p).getHonesty()).setSetter((p, v) -> ((Player) p).setHonesty((int) v)).setType(Types.INTEGER).isNullable(false).build(),
Expand Down Expand Up @@ -238,21 +239,6 @@ public Player loadLatestPlayerInfo(int playerId) {
return loadOne(Player.class, connectionManager.executePreparedQuery(loadLatestPlayerInfoSql, playerId));
}

private final String getTrainerTypeSql = createSelectStatement(" WHERE HRF_ID=? AND TrainerTyp >=0 AND Trainer >0 order by Trainer desc");

int getTrainerType(int hrfID) {
try (ResultSet rs = connectionManager.executePreparedQuery(getTrainerTypeSql, hrfID)) {
if (rs != null) {
if (rs.next()) {
return rs.getInt("TrainerTyp");
}
}
} catch (Exception ignored) {
}

return -99;
}

private final String loadPlayerBeforeSql = createSelectStatement(" WHERE SpielerID=? AND Datum<=? ORDER BY Datum DESC LIMIT 1");

public Player loadPlayerBefore(int playerId, Timestamp before) {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/core/db/StatisticQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static double[][] getSpielerDaten4Statistik(int spielerId, int anzahlHRF)
tempwerte[1] = rs.getDouble("Gehalt") / faktor;
tempwerte[2] = rs.getDouble("Fuehrung");
tempwerte[3] = rs.getDouble("Erfahrung") + rs.getDouble("SubExperience");
tempwerte[4] = rs.getDouble("Form");
tempwerte[4] = rs.getDouble("Form") + rs.getDouble("SubForm");
tempwerte[5] = rs.getDouble("Kondition") + rs.getDouble("SubStamina");
tempwerte[6] = rs.getDouble("Torwart") + rs.getDouble("SubTorwart");
tempwerte[7] = rs.getDouble("Verteidigung") + rs.getDouble("SubVerteidigung");
Expand Down Expand Up @@ -249,8 +249,8 @@ public static double[][] getDataForTeamStatisticsPanel(int nbHRF, String group)
while (rs.next()) {
final double[] thisHRFvalues = new double[nbColumnsHRF];
thisHRFvalues[0] = rs.getDouble("Fuehrung"); //Leadership
thisHRFvalues[1] = rs.getDouble("Erfahrung"); //Experience
thisHRFvalues[2] = rs.getDouble("Form");
thisHRFvalues[1] = rs.getDouble("Erfahrung")+ rs.getDouble("SubExperience"); //Experience
thisHRFvalues[2] = rs.getDouble("Form")+ rs.getDouble("SubForm");
thisHRFvalues[3] = rs.getDouble("Kondition"); //Stamina
thisHRFvalues[4] = rs.getDouble("Torwart") + rs.getDouble("SubTorwart"); //Goalkeeper
thisHRFvalues[5] = rs.getDouble("Verteidigung") + rs.getDouble("SubVerteidigung"); //Defence
Expand Down
15 changes: 5 additions & 10 deletions src/main/java/core/gui/model/PlayerSkillColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,15 @@ public IHOTableEntry getTableEntry(Player player, Player comparePlayer){
return new DoubleLabelEntries(getSkillValue(player),getCompareValue(player,comparePlayer));
}

public IHOTableEntry getSkillValue(Player player){
public IHOTableEntry getSkillValue(Player player) {
var value = player.getValue4Skill(skill);
if( skill == PlayerSkill.FORM
// || skill == PlayerSkill.STAMINA
|| skill == PlayerSkill.LEADERSHIP
|| skill == PlayerSkill.LOYALTY){
return new ColorLabelEntry(value,
background,
false, 0);
if (skill == PlayerSkill.LEADERSHIP || skill == PlayerSkill.LOYALTY) {
return new ColorLabelEntry(value, background, false, 0);
}
return new SkillEntry(value + player.getSub4Skill(skill),
ColorLabelEntry.FG_STANDARD,
background);
ColorLabelEntry.FG_STANDARD, background);
}

/**
* Get a value if comparePlayer is not null
* @param player Player
Expand Down
108 changes: 95 additions & 13 deletions src/main/java/core/model/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static java.lang.Integer.MAX_VALUE;
import static java.lang.Integer.min;
import static core.constants.player.PlayerSkill.*;
import static java.lang.Math.*;

public class Player extends AbstractTable.Storable {

Expand Down Expand Up @@ -105,6 +106,7 @@ public void setContractDate(String contractDate) {
*/
private double subDefendingSkill;
private double subStamina;
private double subForm;

/**
* Agressivität
Expand Down Expand Up @@ -383,7 +385,7 @@ public Player(HOProperties properties, HODateTime hrfDate, int hrfId) {
this.hrfDate = hrfDate;

if (hrfDate.isBefore(HODateTime.fromDbTimestamp(DBManager.TSIDATE))) {
tsi /= 1000d;
tsi /= 1000;
}

totalCards = properties.getInt("warnings", 0);
Expand Down Expand Up @@ -1247,6 +1249,20 @@ public double getSkill(PlayerSkill iSkill) {
return getValue4Skill(iSkill) + getSub4Skill(iSkill);
}

/**
* Get the effective skill value from the displayed skill value
* Displayed value has to be reduced by 1, if not already zero
* @param iSkill Skill id
* @return double Effective skill value
*/
public double getEffectiveSkill(PlayerSkill iSkill) {
double ret = getValue4Skill(iSkill);
if ( ret > 0 ){
ret += getSub4Skill(iSkill) - 1.;
}
return ret;
}

/**
* Returns accurate subskill number. If you need subskill for UI
* purpose it is better to use getSubskill4Pos()
Expand All @@ -1265,6 +1281,7 @@ public double getSub4Skill(PlayerSkill skill) {
case SETPIECES -> subSetPiecesSkill;
case EXPERIENCE -> subExperience;
case STAMINA -> subStamina;
case FORM -> subForm;
default -> 0;
};

Expand All @@ -1283,6 +1300,7 @@ public void setSubskill4PlayerSkill(PlayerSkill skill, double value) {
case SETPIECES -> subSetPiecesSkill = value;
case EXPERIENCE -> subExperience = value;
case STAMINA -> subStamina = value;
case FORM -> subForm = value;
}
}

Expand Down Expand Up @@ -2044,21 +2062,10 @@ public void calcSubSkills(int previousID, List<TrainingPerWeek> trainingWeeks) {
var inc = trainingPerPlayer.getExperienceSub();
experienceSub += inc;
if (experienceSub > 0.99) experienceSub = 0.99;

var minutes = 0;
var tp = trainingPerPlayer.getTrainingPair();
if (tp != null) {
minutes = tp.getTrainingDuration().getPlayedMinutes();
} else {
if (tp == null) {
HOLogger.instance().warning(getClass(), "no training info found");
}
// HOLogger.instance().info(getClass(),
// "Training " + training.getTrainingDate().toLocaleDateTime() +
// "; Minutes= " + minutes +
// "; Experience increment of " + this.getFullName() +
// "; increment: " + inc +
// "; new sub value=" + experienceSub
// );
}
}
}
Expand All @@ -2075,6 +2082,7 @@ public void calcSubSkills(int previousID, List<TrainingPerWeek> trainingWeeks) {
this.setSubskill4PlayerSkill(skill, sub);
this.setSubExperience(experienceSub);
}
adjustFormSub();
}

/**
Expand Down Expand Up @@ -2361,4 +2369,78 @@ List<SkillChange> getSkillChanges() {
}
return skillChanges;
}

/**
* Adjustment of the form sub
* is done by approximating the calculated tsi value to the given one.
*/
private void adjustFormSub(){
if ( this.injuryWeeks > -1) return;
var max = .99;
var min = 0.;
while (max-min > 0.01){
var calculateTSI = this.calculateTSI();
var currentSub = this.getSub4Skill(FORM);
if (calculateTSI < this.getTsi()){
min = currentSub;
}
else {
max = currentSub;
}
var newSub = (max + min) / 2;
this.setSubskill4PlayerSkill(FORM, newSub);
}
}

/**
* Calculate the TSI value from player's skills
* The formula from Schum is used: <a href="https://www87.hattrick.org/Forum/Read.aspx?t=17404127&n=5&v=0&mr=0">...</a>
* which is similar but with more precise coefficients than the formula given by the unwritten manual.
* The age coefficients are from the unwritten manual. In case of the goalkeeper tsi the factor is guessed.
* @return long TSI
*/
public long calculateTSI() {

// Formula from unwritten manual:
// TSI = (1.03Def^3 + 1.03PM^3 + 1.03Sc^3 + 1.0Ps^3 + 0.84*Wg^3)^2 * St^0.5 * Fm^0.5 / 1000
//
//TSIgk = 3 * Gk^3.359 * Fm^0.5

double def =getEffectiveSkill(DEFENDING);
double pm = getEffectiveSkill(PLAYMAKING);
double sc = getEffectiveSkill(SCORING);
double ps = getEffectiveSkill(PASSING);
double wg = getEffectiveSkill(WINGER);
double st = getEffectiveSkill(STAMINA);
double fm = getEffectiveSkill(FORM);
double gk = getEffectiveSkill(KEEPER);

double tsi = 0;
int startTsiDrop = 27;
if (gk < def ||
gk < sc ||
gk < ps ||
gk < wg ||
gk < pm) {
tsi += pow(def, 3.) * 1.034;
tsi += pow(pm, 3.) * 1.031;
tsi += pow(sc, 3.) * 1.038;
tsi += pow(ps, 3.) * 1.035;
tsi += pow(wg, 3.) * .826;

tsi = tsi * tsi;
tsi *= sqrt(st);
tsi /= 1000.;
} else {
tsi = 3. * pow(gk, 3.359);
startTsiDrop = 31;
}
tsi *= sqrt(fm);

var ageFactor = Math.min(1, max(1./8., 1. - (this.getAge()-startTsiDrop)/8.));
tsi *= ageFactor;

return round(tsi/10) * 10;
}

}
35 changes: 14 additions & 21 deletions src/main/java/core/option/SliderPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ public final class SliderPanel extends ImagePanel implements ChangeListener {
private JSlider m_jslSlider;
private JFormattedTextField m_jtfTextfield;
private final float m_fFaktor;
private float m_fTextfeldFaktor = 1;
private final float m_fTextfeldFaktor;
private final int m_iTextbreite;
private final int decimals = 0;
private boolean bDeactivateTxtLister = false;


Expand All @@ -45,14 +44,21 @@ public SliderPanel(String text, int max, int min, float faktor, float textfeldfa
initComponents(text, max, min);
}

@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
m_jslSlider.setEnabled(enabled);
m_jtfTextfield.setEnabled(enabled);
}

/**
* Wert mit faktor multiplizieren und auf int casten
*/
public void setValue(float value) {
m_jslSlider.setValue((int) (value * m_fFaktor));
m_jslSlider.setValue((int) (value * m_fFaktor + .5));
}

public final float getValue() {
public float getValue() {
HOLogger.instance().log(getClass(),(float) m_jslSlider.getValue() + " : "
+ (m_jslSlider.getValue() / m_fFaktor));
return m_jslSlider.getValue() / m_fFaktor;
Expand All @@ -68,11 +74,7 @@ public void removeChangeListener(ChangeListener listener) {

public void stateChanged(javax.swing.event.ChangeEvent changeEvent) {
bDeactivateTxtLister = true;
if (decimals != 0){
m_jtfTextfield.setText(core.util.Helper.round(m_jslSlider.getValue() * m_fTextfeldFaktor, decimals) + "");}
else {
m_jtfTextfield.setText((int)(m_jslSlider.getValue() * m_fTextfeldFaktor) + "");
}
m_jtfTextfield.setText((int) (m_jslSlider.getValue() * m_fTextfeldFaktor) + "");
bDeactivateTxtLister = false;
}

Expand Down Expand Up @@ -122,25 +124,16 @@ private void initComponents(String text, int max, int min) {

m_jtfTextfield = new JFormattedTextField(formatter);
m_jtfTextfield.setColumns(4);

if (decimals != 0) {
m_jtfTextfield.setText(core.util.Helper.round(m_jslSlider.getValue() * m_fTextfeldFaktor, decimals) + "");
}
else
{
m_jtfTextfield.setText((int)(m_jslSlider.getValue() * m_fTextfeldFaktor) + "");
}

m_jtfTextfield.setText((int) (m_jslSlider.getValue() * m_fTextfeldFaktor) + "");
m_jtfTextfield.setEditable(true);
m_jtfTextfield.setHorizontalAlignment(SwingConstants.RIGHT);


m_jtfTextfield.getDocument().addDocumentListener(new DocumentListener() {

@Override
public void removeUpdate(DocumentEvent arg0) {
if (!bDeactivateTxtLister) {
if(m_jtfTextfield.getText().equals("")){
if(m_jtfTextfield.getText().isEmpty()){
updateSliderWithoutEvent(0);
}
else {
Expand All @@ -152,7 +145,7 @@ public void removeUpdate(DocumentEvent arg0) {
@Override
public void insertUpdate(DocumentEvent arg0) {
if (!bDeactivateTxtLister) {
if(m_jtfTextfield.getText().equals("")){
if(m_jtfTextfield.getText().isEmpty()){
updateSliderWithoutEvent(0);
}
else {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/core/rating/RatingPredictionModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public double calc(RatingSector sector, Integer roleId, Player player, Byte beha

/**
* Get value from contribution cache
* (Attention: Method is used by AkasolaceRatingPredictionModel.groovy)
* (ATTENTION: Method is used by AkasolaceRatingPredictionModel.groovy)
* @param p Player
* @param roleId Lineup position
* @param behaviour Behaviour
Expand Down Expand Up @@ -1308,7 +1308,7 @@ protected double calcStrength(@NotNull Player player, PlayerSkill playerSkill) {
* @return Double
*/
protected double calcForm(@NotNull Player player) {
var form = min(7., calcSkillRating(.5 + player.getSkill(PlayerSkill.FORM)));
var form = min(7., calcSkillRating(player.getSkill(PlayerSkill.FORM)));
return 0.378 * sqrt(form); // form 0.5 .. 7.0
}

Expand Down
Loading

0 comments on commit 06e13cc

Please sign in to comment.