Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force generator math updates #6244

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5afc3ee
Basic cleanup for readability. Adjust ratings adjustment math when de…
Nov 28, 2024
cccd446
Readability improvements
Nov 28, 2024
2074199
Generate model list using basic filters. Create new method for gettin…
Nov 29, 2024
92afe42
Clean up dynamic availability adjustment for intro year, adding prope…
Nov 29, 2024
6a58e58
Fix math for +/- dynamic availability calculation
Nov 29, 2024
a71ea5f
Add interpolation for when game year is between era years
Nov 30, 2024
56bc56d
Cache individual model weights so they don't have to be calculated twice
Nov 30, 2024
d3e64c2
Add preliminary chassis weight class filter and weight class filter t…
Nov 30, 2024
148943d
Replace model weight calculations with new process
Nov 30, 2024
3a91cf6
Readability improvements
Nov 30, 2024
a8232df
Improve chassis processing, skipping units not introduced yet plus be…
Dec 1, 2024
a5c5ec0
Improve model interpolation, including phase out/model not present in…
Dec 1, 2024
e29077a
Change original total model weight calculation to natively work with …
Dec 1, 2024
95aca30
Null protection for weight class redistribution section check
Dec 1, 2024
2255135
Readability improvements to faction weight distribution adjustments
Dec 1, 2024
21711b1
Readability improvements to salvage percentage calculations. Fix appl…
Dec 1, 2024
6bda293
Readability improvements to percentage adjustments
Dec 1, 2024
24e98d4
Add unit type and role filtering to C/SL/O adjustment call
Dec 1, 2024
019a85d
Integrate chassis total weight into individual weights. Update math f…
Dec 3, 2024
ce87b8b
Update calculations for Clan/SL proportions. Not entirely successful …
Dec 3, 2024
286f8d2
Rebuild C/SL/O calculations for better results. Include logging for p…
Dec 4, 2024
0d7f212
Minor updates
Dec 5, 2024
1dde064
Improve handling of base IS mixed tech units. Some minor readability …
Dec 5, 2024
d29e18e
Merge branch 'refs/heads/master' into Forcegen-chassis-availability-u…
Dec 5, 2024
4533fd0
Add unit type to C/SL/O info log entries
Dec 5, 2024
def6259
Fix typos in CJF C/SL/R values found through testing
Dec 5, 2024
cae45e0
Updates from review comments
Dec 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions megamek/data/forcegenerator/3058.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@
</faction>
<faction key='CJF'>
<pctOmni>25,25,53,90,100</pctOmni>
<pctClan>53,53,80,46,100</pctClan>
<pctSL>47,47,20,54,0</pctSL>
<pctClan>53,53,80,96,100</pctClan>
<pctSL>47,47,20,4,0</pctSL>
<pctClan unitType='Vehicle'>0,0,20,20,20</pctClan>
<pctSL unitType='Vehicle'>100,100,80,80,80</pctSL>
<salvage pct='17'>CHH:0,CSR:0,CIH:0,CI:0,CSV:2,CFM:0,CCO:0,CGS:0,CSA:0,CDS:0,CW:6,LA:5,CNC:0,CSJ:0,CGB:0</salvage>
Expand Down
4 changes: 2 additions & 2 deletions megamek/data/forcegenerator/3060.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@
</faction>
<faction key='CJF'>
<pctOmni>25,25,55,90,100</pctOmni>
<pctClan>55,55,80,10,100</pctClan>
<pctSL>45,45,20,90,0</pctSL>
<pctClan>55,55,80,90,100</pctClan>
<pctSL>45,45,20,10,0</pctSL>
<pctClan unitType='Vehicle'>0,0,20,20,20</pctClan>
<pctSL unitType='Vehicle'>100,100,80,80,80</pctSL>
<salvage pct='15'>CW:10,LA:2,CSV:4</salvage>
Expand Down
23 changes: 15 additions & 8 deletions megamek/src/megamek/client/ratgenerator/AbstractUnitRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,30 @@ public AbstractUnitRecord(String chassis) {
}

/**
* Adjusts availability rating for the first couple years after introduction.
* Adjusts availability rating for +/- dynamic. Also reduces availability by
* introduction year, with 1 year before heavily reduced for pre-production
* prototypes and first year slightly reduced for working out initial
* production.
*
* @param ar The AvailabilityRecord for the chassis or model.
* @param rating The force equipment rating.
* @param avRating The AvailabilityRecord for the chassis or model.
* @param equipRating The force equipment rating.
* @param ratingLevels The number of equipment rating levels used by the faction.
* @param year The game year
* @param year The game year
* @return The adjusted availability rating.
*/
public int calcAvailability(AvailabilityRating ar, int rating, int ratingLevels, int year) {
int retVal = ar.adjustForRating(rating, ratingLevels);
public int calcAvailability(AvailabilityRating avRating, int equipRating, int ratingLevels, int year) {
int retVal = avRating.adjustForRating(equipRating, ratingLevels);

if (introYear == year) {
// Pre-production prototypes are heavily reduced
if (year == introYear - 1) {
retVal -= 2;
}
if (introYear == year + 1) {

// Initial production year is slightly reduced
if (year == introYear) {
retVal -= 1;
}

return Math.max(retVal, 0);
}

Expand Down
23 changes: 18 additions & 5 deletions megamek/src/megamek/client/ratgenerator/AvailabilityRating.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,26 @@ public int getAvailability() {
return availability;
}

public int adjustForRating(int rating, int numLevels) {
if (rating < 0 || ratingAdjustment == 0) {
/**
* Adjust availability rating for the dynamic +/- value, which is based on
* equipment quality rating. The (+) will reduce availability for commands
* with a lower rating, while the (-) will reduce availability for commands
* with a higher rating.
* @param equipRating zero-based index based on {@code numLevels} of rating to check
* @param numLevels number of equipment levels available, typically 5 (A/B/C/D/F)
* @return integer, may be negative
*/
public int adjustForRating(int equipRating, int numLevels) {
if (ratingAdjustment == 0 || equipRating < 0) {
return availability;
} else if (ratingAdjustment < 0) {
return availability - rating;
}

if (ratingAdjustment > 0) {
// (+) adjustment, reduce availability as equipment rating decreases
return availability - (numLevels - 1 - equipRating);
} else {
return availability - (numLevels - rating);
// (-) adjustment, reduce availability as equipment rating increases
return availability - equipRating;
}
}

Expand Down
170 changes: 157 additions & 13 deletions megamek/src/megamek/client/ratgenerator/ChassisRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
*/
package megamek.client.ratgenerator;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.*;

import megamek.common.EntityMovementMode;
import megamek.logging.MMLogger;

/**
Expand Down Expand Up @@ -55,7 +53,153 @@ public List<ModelRecord> getSortedModels() {

}

public int totalModelWeight(int era, String fKey) {
/**
* Generate a list of models for this chassis based on certain criteria.
* Early prototypes may be available one year before official introduction.
* @param exactYear game year
* @param validWeightClasses restrict weight class to one or more classes
* @param movementModes movement mode types, may be null or empty
* @param networkMask specific C3 network equipment
* @return set of models which pass the filter requirements, may be empty
*/
public HashSet<ModelRecord> getFilteredModels(int exactYear,
Collection<Integer> validWeightClasses,
Collection<EntityMovementMode> movementModes,
int networkMask) {

HashSet<ModelRecord> filteredModels = new HashSet<>();

for (ModelRecord curModel : models) {
// Introduction date should be at most 1 year away for pre-production prototypes
if (curModel.introYear > exactYear + 1) {
continue;
}

// Weight class check
if (validWeightClasses != null && !validWeightClasses.isEmpty() && !validWeightClasses.contains(curModel.getWeightClass())) {
continue;
}

// Movement mode check
if (movementModes != null && !movementModes.isEmpty()) {
if (!movementModes.contains(curModel.getMovementMode())) {
continue;
}
}

// C3 network equipment check
if ((networkMask & curModel.getNetworkMask()) != networkMask) {
continue;
}

filteredModels.add(curModel);
}

return filteredModels;
}

/**
* Total the weights of all models for this chassis, including modifiers for
* +/- dynamic adjustment, intro year adjustment, interpolation, and role
* modifications.
* @param validModels models to add up
* @param currentEra year for current era
* @param exactYear current year in game
* @param nextEra start date of next era after the current one
* @param fRec faction data
* @param roles roles selected for generation, may be null or empty
* @param roleStrictness positive number, higher applies heavier role adjustments
* @param equipRating equipment rating to generate for
* @param numRatingLevels how many rating levels are present
* @return sum of calculated weights of all models of this chassis
*/
public double totalModelWeight(HashSet<ModelRecord> validModels,
int currentEra,
int exactYear,
int nextEra,
FactionRecord fRec,
Collection<MissionRole> roles,
int roleStrictness,
int equipRating,
int numRatingLevels,
HashMap<String,Double> weightData) {

RATGenerator ratGen = RATGenerator.getInstance();
AvailabilityRating avRating, nextAvRating;
double retVal = 0;
double adjRating;
double nextRating;
Number roleRating;

// Clear any pre-existing weighting data - this should only cover
// the current set of models
weightData.clear();

// For each model
for (ModelRecord curModel : validModels) {

if (curModel.factionIsExcluded(fRec)) {
continue;
}

// Get the availability rating for the provided faction and year,
// skip processing if not available
avRating = ratGen.findModelAvailabilityRecord(currentEra, curModel.getKey(), fRec);
if (avRating == null || avRating.getAvailability() <= 0) {
continue;
}

// If required, interpolate availability between era start or intro date
// (whichever is later), and start of next era
if (exactYear > currentEra && currentEra != nextEra) {
nextAvRating = ratGen.findModelAvailabilityRecord(nextEra,
curModel.getKey(), fRec);

int interpolationStart = Math.max(currentEra, Math.min(exactYear, curModel.introYear));

adjRating = curModel.calcAvailability(avRating,
equipRating, numRatingLevels, interpolationStart);

nextRating = 0.0;
if (nextAvRating != null) {
nextRating = curModel.calcAvailability(nextAvRating,
equipRating, numRatingLevels, nextEra);
}

if (adjRating != nextRating) {
adjRating = adjRating +
(nextRating - adjRating) * (exactYear - interpolationStart) / (nextEra - interpolationStart);
}

} else {
// Adjust availability for +/- dynamic and intro year
adjRating = curModel.calcAvailability(avRating, equipRating, numRatingLevels, exactYear);
}

if (adjRating <= 0) {
continue;
}

// Adjust availability for roles. Method may return null as a filtering mechanism.
roleRating = MissionRole.adjustAvailabilityByRole(adjRating,
roles, curModel, exactYear, roleStrictness);

if (roleRating == null || roleRating.doubleValue() <= 0) {
continue;
}

// Calculate the weight and add it to the total
adjRating = AvailabilityRating.calcWeight(roleRating.doubleValue());
retVal += adjRating;

// Cache the final availability rating
weightData.put(curModel.getKey(), adjRating);
}

return retVal;
}

public double totalModelWeight(int era, String fKey) {
FactionRecord fRec = RATGenerator.getInstance().getFaction(fKey);
if (fRec == null) {
logger.warn("Attempt to find totalModelWeight for non-existent faction " + fKey);
Expand All @@ -64,15 +208,15 @@ public int totalModelWeight(int era, String fKey) {
return totalModelWeight(era, fRec);
}

public int totalModelWeight(int era, FactionRecord fRec) {
int retVal = 0;
RATGenerator rg = RATGenerator.getInstance();
public double totalModelWeight(int era, FactionRecord fRec) {
double retVal = 0;
RATGenerator ratGen = RATGenerator.getInstance();

for (ModelRecord mr : models) {
AvailabilityRating ar = rg.findModelAvailabilityRecord(era,
mr.getKey(), fRec);
if (ar != null) {
retVal += AvailabilityRating.calcWeight(ar.getAvailability());
for (ModelRecord curModel : models) {
AvailabilityRating avRating = ratGen.findModelAvailabilityRecord(era,
curModel.getKey(), fRec);
if (avRating != null) {
retVal += AvailabilityRating.calcWeight(avRating.getAvailability());
}
}

Expand Down
Loading
Loading