[BUG] Experience review - including subskill recalculation (7-weeks)
Describe the bug I took a deep dive into experience and how it is calculated in HO.
Because hattrick has a page where you can see the drilldown in experience points (click on a player and there is a tab "more information" with historic results of a player including minutes played and on which position and experience gained etc. ). You can almost exactly determine the experience sub-level.
In order to correctly interpreted the results could you (@wsbrenk ) answer a few questions:
- Does HO include the latest match? Is experience subskill recalculated if I played a match and after the match is done I download the HRF file?
- How does HO cope with friendly matches? International friendly matches (0,7 XP) have double the experience of a local match (0,35 XP)? Do you take the lowest, highest or average number? Or does the coding include differentiating between local and international matches?
- What does subskill recalculation (7 weeks) do in relation to experience? What matches are included and which are not? Does it differ from the matches considered for ability subskill recalculation?
Findings:
- I had some unexpected results with regards to experience when I did a comparison between the latest download and the HRF file of the week before. Because I downloaded the file directly after training results were available; the only matches that have been played were the matches corresponding to week 12, but experience seemed to be based not exactly on those matches, but took into consideration also a match prior to week 12?!
- Things became more weird after subskill recalculation 7-weeks. (look at screenshots). It differs completely from what I would expect. Only a few players gain experience (subskill), while almost my whole squad should have got an increase.
By hand I calculated the experience for 7 a 8 weeks in a row. So hope you can look at this as soon as possible so that I can do proper testing and see how we can improve this part.
To Reproduce Steps to reproduce the behavior:
- Download data
- Compare data with last week data (look at experience)
- Do a subskill recalculation (7-weeks)
- Compare data with last week data (look at experience)
Screenshots
Experience increase by comparison (after subskill recalculation 7-weeks)

Matches played between 18th June 2022 10:29 and 24th June 10:57


Platform information:
- OS: Windows10
- Version 6.0.315
Additional context Hattrick change:
- Experience needed for an increase in level was 28,571 and is now 100 ( * 3,5). And the experience gained by matches is also increased by a factor 3,5.
- Experience calculation is thus the same as in the old situation. [Is the code adjusted accordingly so that it is in line with the 'hattrick manual'? I say this for recognisable code purpose for future developers]
Additional info: I downloaded today's HRF file (which included Sunday's league game) I noticed that there is no gain in experience, so the conclusion that I can draw from that, is that experience is not directly updated after a match have been played.
Are there certain moments when experience recalculation is triggered? Is it connected to certain days?
@masterpatje experience is calculated together with trainings subskill calculation. the trigger is the training date. each played minute has experience effect with the weights of the match type as you found in the "docs". for some kinds of match the effect is smaller than 0.01 which is not displayed if you compare only one week.
General comment Today the training results became available, which includes last week matches. Now I can more easily check experience; I first tested my formula with the information on the hattrick website. And then I looked at the experience gained since last week.
Test 1: Experience on HRF download after training update
Results: Experience is most probably correctly calculated on download. The small differences in 5 players that I noticed might be because of decimal precision shown in HO. Q: Is is true that in the background calculation 3 decimals behind the komma are taking into consideration? And that for visual purposes the decimals are rounded to 2 decimals behind the komma? Q: Does HO in the background distinquishes between (international and national) friendlies? In other words is there coding that looks at the nationality in a friendly match and sets the appropiate experience? The experience gain differs and thus I want to know how HO copes with it? Or does it consider always the minimum or maximum or average? Just to be sure what I am looking at.

Test 2: Experience after importing HRF file (hopefully today) Will do this test later, because otherwise I can't do the proper subskill testing.
Test 3: Experience after subskill recalculation (hopefully today) Will do this test later, because I can't redo the action.
Test 4: Experience comparison saturday 24 june 2022 10:57 (includes training week 12) versus friday 18 june 10:29 (includes training week 11) after subskill recalculation
Result: Visual representation of experience gained was not correct, the experience subskill level was correct.
Today I had the HRF file from 1 july 2022 (includes training week 13) and when I compare it with 24 june 2022 (includes training week 12) and do a recalculation the experience is exactly the same also the visual representation of the gain. (good)
Last week when I did a subskill recalculation the (visual!) experience gained changed and afterwards was only visible for only 7 players and was incorrectly in amount of subskill gain, but the experience subskill in total is correct for all players.
Q: Do you understand why this might happen? Please note that in this week comparison I compare friday (1st) to friday (24th) and the week before I had a comparison between saturday (24th) and friday (18th). Maybe it helps understanding why the visual representation of the gain is not shown for all players.



HO calculates with higher precision than displayed in the table column. you can check that with sql editor.
HO considers the different match types when calculating the experience:
public double getExperienceIncrease(int minutes) {
double p;
var _matchType = this.getMatchTypeExtended();
if (_matchType instanceof MatchType){
p = switch ((MatchType) _matchType){
case CUP, QUALIFICATION -> 2d;
case FRIENDLYNORMAL, FRIENDLYCUPRULES -> .1;
case INTFRIENDLYCUPRULES, INTFRIENDLYNORMAL -> .2;
case NATIONALFRIENDLY, LEAGUE -> 1d;
case MASTERS -> 5d;
case NATIONALCOMPCUPRULES -> 16d;
case NATIONALCOMPNORMAL -> 8d;
case TOURNAMENTGROUP -> getTournamentExperienceFactor(TournamentType.getById(this.iMatchContextId), false);
case TOURNAMENTPLAYOFF -> getTournamentExperienceFactor(TournamentType.getById(this.iMatchContextId), true);
default -> 0d;
};
} else{
//case MatchTypeExtended.EMERALDCUP, MatchTypeExtended.RUBYCUP, MatchTypeExtended.SAPPHIRECUP -> .5;
p = 0.5;
}
return minutes * p / 90. / 28.571;
}
just found one of my players showing also missing increments

different subs 0.021000315, 0.21000314 is a minor bug. values were stored as double precision values but load as floats.
lastmatch at 06-28 was loaded before training calculation. after downloading current training the 7th skill increment was calculated. result is 0.024 which is still displayed as .02.
Comparison between last version and current version (332) gives the following result (see screenshot): In general experience is a slighty higher so that is good I think.
I tested on player that stood out: Szogedi had an increase of 0,007 (90 minutes friendly) in match 29-6-2022 . His history in the last 6 training weeks prior to the 7th training week are: 18-5-2022; 21 minutes cup ; 1,633 [training week 7] 22-5-2022; 21 minutes league; 0,817 [training week 8] 25-5-2022; 11 minutes cup; 0,857 [training week 8] 29-5-2022; 11 minutes league; 0,428 [training week 9] 1-6-2022; 45 minutes friendly (local); 0,175 [training week 9] 8-6-2022; 90 minutes friendly (international); 0,7 [training week 10] 15-6-2022; 90 minutes friendly (international); 0,7 [training week 11] 19-6-2022; 27 minutes league; 1,05 [training week 12] 22-6-2022; 90 minutes friendly (international); 0,7 [training week 12] Total 6 training weeks prior to last week; 7,06 (0,0706 = 0,07 rounded) 7th week: 29-06-2022; 90 minutes friendly (international) [training week 13] Total 7 training weeks: 7,76 (0,0776 = 0,08 rounded)
My conclusion: Increase in 7th week should have been 0,01. (comparison training week 13 versus training week 12)
But I noted experience is indicated as 8,10; Which is higher than 8,08, so I think it takes other matches into consideration was well. Q: Could you tell me which matches are included? Or how I can check which matches were included on subskill recalculation (7-weeks)?

Code can be changed to:
public double getExperienceIncrease(int minutes) { double p; var _matchType = this.getMatchTypeExtended(); if (_matchType instanceof MatchType){ p = switch ((MatchType) _matchType){ case CUP, QUALIFICATION -> 7d; case FRIENDLYNORMAL, FRIENDLYCUPRULES -> .350; case INTFRIENDLYCUPRULES, INTFRIENDLYNORMAL -> .700; case NATIONALFRIENDLY, LEAGUE -> 3.5d; case MASTERS -> 17,5d; case NATIONALCOMPCUPRULES -> 56d; case NATIONALCOMPNORMAL -> 28d; case TOURNAMENTGROUP -> getTournamentExperienceFactor(TournamentType.getById(this.iMatchContextId), false); case TOURNAMENTPLAYOFF -> getTournamentExperienceFactor(TournamentType.getById(this.iMatchContextId), true); default -> 0d; }; } else{ //case MatchTypeExtended.EMERALDCUP, MatchTypeExtended.RUBYCUP, MatchTypeExtended.SAPPHIRECUP -> 1.75; p = 0.5; } return minutes * p / 90. / 100; }
Maybe you can check if new categories need to be added, because they specify more categories for national teams matches as it seems.
In general experience calculation is the same, except that everything is multiplied by 3,5.
https://www84.hattrick.org/Help/Rules/PlayerAttributes.aspx

these match types are handled in method getTournamentExperienceFactor:
private double getTournamentExperienceFactor(@Nullable TournamentType tournamentType, boolean isPlayOff) {
if ( tournamentType == null) return 0d;
if ( tournamentType.isWorldCup()){
// World Cup match 28
// World Cup (Semi and Final) 56
if ( isPlayOff){
return 56/3.5;
}
else {
return 28/3.5;
}
}
else if ( tournamentType.isNationsCup()){
// Nations Cup 7
// Nations Cup (Knockout) 14
if ( isPlayOff){
return 14/3.5;
}
else {
return 7/3.5;
}
}
else if ( tournamentType.isContinentalCup()){
// Continental Championships 14
// Continental Championships (Quarter, Semi and Final) 21
if ( isPlayOff){
return 21/3.5;
}
else {
return 14/3.5;
}
}
else if ( tournamentType.isNTFriendly()){
// National Team friendly 3.5
return 1d;
}
return 0d;
}
trainings are examined from latest download (hrf file) which is older than 7 weeks. if fear if there is a gap of several weeks the considers matches could be a bit older than 7 weeks.
trainings are examined from latest download (hrf file) which is older than 7 weeks. if fear if there is a gap of several weeks the considers matches could be a bit older than 7 weeks.
What do you exactly mean with "latest download (hrf file) which is older than 7 weeks?".. do you mean the latest file or te files that is 7 weeks older than the last downloaded hrf file?
In version 7.0.337 (portable) (which works 'experience wise' the same as HO6 most probably) I did a new test. Because it was a clean build I added HRF files one at a time by importing and afterwards recalculating (7-weeks). The experience stays the same?! :







if you only import the hrfs the match details are missing, i fear.
the calculation selects all hrfs from database which are downloaded during the last 7 week.
the oldest entry (7 weeks old) loads the hrf loaded before (some minutes or months before him) this entry will give the start values for the skills (and experience). all trainings in past training list that happened before oldest hrf and after the previous one will be taken into account.
this is continued with the following hrfs
@wsbrenk; back from holiday and did some more testing. I thought I wanted to test in on a stand alone version (so I took 7.0.350) and I only have 2 HRF files in the database, 19th of July and 8th July (for comparison):
TEST 1: HRF download 19th July = CORRECT
On downloading 19th July 2022 HRF file the experience is correctly calculated:
It produces the same results as in the screenshot below and thus includedes the 10th July and 13th July matches:

TEST 2: SUBSKILL RECALCULATION (7-weeks or all) = INCORRECT
As you can see the total experience is higher and the gain is incorrectly displayed.




Reaction other wsbrenk comments:
if you only import the hrfs the match details are missing, i fear.
For me it is important to first fix this issue and later on we can see if a missing HRF file could be tackled with downloadable matches.
the calculation selects all hrfs from database which are downloaded during the last 7 week.
the oldest entry (7 weeks old) loads the hrf loaded before (some minutes or months before him) this entry will give the start values for the skills (and experience). all trainings in past training list that happened before oldest hrf and after the previous one will be taken into account.
this is continued with the following hrfs
Does this mean that the 7 week old HRF is considered as the base reference and the 6 newer weeks are than considered to be the files where the gain of experience is visible?
Today new test:
- imported HRF files 8th July and 19th July
- downloaded HRF file 22th July
- in total 3 HRF files including 6 matches
- Experience is shown for 22th July and is correct (and based on the friendly matches on the 17th and 20th).

- Now I am going to do subskill recalculation (7-weeks)
- The experience is correctly calculated in total (yeah!!) and based on the 6 latest matches from the HRF files present in the database.

Problems are in the (visual) comparison by week and thus the gain is incorrectly shown:
Comparing week 16:
- because I played only friendly and on initial download the maximum gain of experience was 0,01. Orona and van Keeken are incorrect (+0,02) and some players missed a gain in experience (Dieleman 0,00 and Wellinga 0,00 and Szogedi 0,00)

Comparing week 15:

Conclusion:
- experience in total is correctly calculated
- still some odd things in the comparison by week, any ideas @wsbrenk ?

So it should all matches training week 10 till 16 (so last matches included are training week 10 (5th and 8th June matches) :

HO experience after recalculation:

My calculation (last matches included 5th and 8th June)
So there is now a big difference in total experience?! In last test it was correct considering only 3 HRF files that were in the database.

Does this mean that the 7 week old HRF is considered as the base reference and the 6 newer weeks are than considered to be the files where the gain of experience is visible?
not quite, I'm afraid. base of the calculation is the latest download BEFORE the 7 week old hrf file. all trainings and matches after that download will be considered.
@wsbrenk I want to help test this issues because of the excel I made in which I am sure that I correctly calculated the experience week by week, but in order to do so I need some help.
In the above example the total experience of last 7 weeks (see the past training table) was after recalculation no correct in total.
It would be nice if we could fix this for the HO 6.0 release but it is not necessary to do so, but I want to help to make it possible.
Couls you tell how we can move forward on this issue? What weeks do are considered looking at that example?
Sorry for the typos ;-)
Idea for testing could be the following. I can download the HO 7.0 portable version (which is based on HO 6.0) and where the subskill recalculation functionality works the same.
-
I can start by downloading the current HRF file, which includes 24th (wednesday) and 27th july (sunday) matches.
-
Then do a subskill recalculation (7-weeks).
-
Compare total experience before and after.
4a) Add a HRF file with the training week before and 17th (wed) and 20th (sun) matches.
4b) Then do a subskill recalculation (7-weeks).
4c) Compare total experience before and after.
- ...repeat proces for another week for step 4...
Problem with this approach is that the altered total experience after subskill recalculation cannot be made undone and I don't know if that influences the test?
But before I do this, I need you to reply on my other comment that I posted today.
@masterpatje many thanks for your help on this.
- the hrf files do not include the corresponding match information needed for experience calculation. they have to be downloaded separatly if they are not already in the database.
the result of the calculation should not depend on previous runs of experience calculations - only on the start values given in the latest hrf data before 7 week period. the development of each player can be viewed best with the Simple SQL editor, as shown 28 days ago.
if it would help, i can try to add some logging of experience increments during the calculation. what do you think about this?
Yes! This would help a lot. From that I can do debugging and see where the calculations are different and why.
So if you could add this (temporarily) in the newest HO7 version and can continue exploring.
Let me know when it is ready and I will look at it asap.
[Debug] HOVerwaltung: Start full subskill calculation. Sat Jul 30 17:25:34 CEST 2022 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Walter Hasemann: 0.0 new sub value=0.0 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Franziskus Zentsch: 0.0035000525007875117 new sub value=0.017500262480500475 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Daniel-Luca Schlütterf: 0.0 new sub value=0.0035000524949282408 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Manolo Klaus: 0.035000525007875115 new sub value=0.8131787996393753 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Peter Gensberger: 0.0 new sub value=0.0 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Dennis Wölm: 0.0 new sub value=0.0 [Info] Player: Training 17.06.2022, 00:30:00 Experience increment of Georg Hornsteiner: 0.0035000525007875117 new sub value=0.0350005223940046
further information useful for the evaluation?
Add the training week number. The training week corresponding to that week experience gain (if it corresponds to the 'past training' week table). I can deduce from the training week the corresponding matches. So that I am absolutely sure which matches were included.
added minutes - the dev is created
further info has to wait - have to feed my bees now.
Just started debugging, but right after the first player Jarne Clarebout I got an error. This was on a clean build with only the first HRF file downloaded. I directly checked the 7-weeks recalculation. I will
[Info] Player: Training 29 jul. 2022 01:05:00 Minutes= 90 Experience increment of Jarne Clarebout: 0.07000105001575023 new sub value=0.07000105001575023 [Debug] HOVerwaltung: recalcSubskills : [Error] HOVerwaltung: java.lang.NullPointerException: Cannot invoke "core.training.TrainingPoints.getTrainingDuration()" because the return value of "core.training.TrainingPerPlayer.getTrainingPair()" is null [Error] HOVerwaltung: core.model.player.Player.calcSubskills(Player.java:2318) [Error] HOVerwaltung: core.model.HOModel.calcSubskills(HOModel.java:548) [Error] HOVerwaltung: core.model.HOVerwaltung.recalcSubskills(HOVerwaltung.java:165) [Error] HOVerwaltung: core.gui.HOMainFrame.actionPerformed(HOMainFrame.java:326) [Error] HOVerwaltung: java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972) [Error] HOVerwaltung: java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313) [Error] HOVerwaltung: java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405) [Error] HOVerwaltung: java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262) [Error] HOVerwaltung: java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:374) [Error] HOVerwaltung: java.desktop/javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1028) [Error] HOVerwaltung: java.desktop/javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:1072) [Error] HOVerwaltung: java.desktop/java.awt.Component.processMouseEvent(Component.java:6626) [Error] HOVerwaltung: java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389) [Error] HOVerwaltung: java.desktop/java.awt.Component.processEvent(Component.java:6391) [Error] HOVerwaltung: java.desktop/java.awt.Container.processEvent(Container.java:2266) [Error] HOVerwaltung: java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001) [Error] HOVerwaltung: java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324) [Error] HOVerwaltung: java.desktop/java.awt.Component.dispatchEvent(Component.java:4833) [Error] HOVerwaltung: java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948) [Error] HOVerwaltung: java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575) [Error] HOVerwaltung: java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516) [Error] HOVerwaltung: java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310) [Error] HOVerwaltung: java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780) [Error] HOVerwaltung: java.desktop/java.awt.Component.dispatchEvent(Component.java:4833) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) [Error] HOVerwaltung: java.base/java.security.AccessController.doPrivileged(AccessController.java:399) [Error] HOVerwaltung: java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) [Error] HOVerwaltung: java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744) [Error] HOVerwaltung: java.base/java.security.AccessController.doPrivileged(AccessController.java:399) [Error] HOVerwaltung: java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) [Error] HOVerwaltung: java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) [Error] HOVerwaltung: java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) [Debug] TrainingManager: HOVerwaltung model changed => TrainingManager and TrainingWeekManager are reinitialized