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

18VA near completed #534

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 46 additions & 4 deletions src/main/java/net/sf/rails/algorithms/NetworkVertex.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public enum StationType {
// only for station objects
private final Stop stop;

private RevenueManager revenueManager;

/** constructor for station on mapHex */
public NetworkVertex(MapHex hex, Station station) {
Expand All @@ -64,6 +65,8 @@ public NetworkVertex(MapHex hex, Station station) {

this.virtual = false;
this.virtualId = null;

this.revenueManager = hex.getRoot().getRevenueManager();
}

/** constructor for side on mapHex */
Expand Down Expand Up @@ -157,6 +160,7 @@ public int getValue() {
return value;
}

/*
public int getValueByTrain(NetworkTrain train) {
int valueByTrain;
if (isMajor()) {
Expand All @@ -174,6 +178,41 @@ public int getValueByTrain(NetworkTrain train) {
valueByTrain = value;
}
return valueByTrain;
}*/
public int getValueByTrain (NetworkTrain train) {
/*
int valueByTrain = 0;
switch (stop.getType()) {
case CITY:
valueByTrain = value * train.getMultiplyMajors();
break;
case TOWN:
if (!train.ignoresMinors()) {
valueByTrain = value * train.getMultiplyMinors();
}
break;
case OFFMAP:
valueByTrain = hex.getCurrentValueForPhase(
hex.getRoot().getPhaseManager().getCurrentPhase());
break;
case MINE:
// For 18VA (see GameManager_18VA). Default return value is 0.
valueByTrain = hex.getValuePerTrain(train.getRailsTrain());
break;
case PASS:
valueByTrain = 0;
break;
default:
valueByTrain = value;
break;
}
return valueByTrain;*/
Train railsTrain= train.getRailsTrain();
PublicCompany company = (PublicCompany)railsTrain.getOwner();
int revenue = revenueManager.getActualAsInteger(stop, railsTrain,
company);
log.debug("+++++ Vertex {} has value {} for train {} of {}", this, revenue, train, company);
return revenue;
}

public NetworkVertex setValue(int value) {
Expand Down Expand Up @@ -244,15 +283,19 @@ public boolean initRailsVertex(PublicCompany company, boolean running) {
// if company == null, then no vertex gets removed
if (company != null && !stop.isRunToAllowedFor(company, running)
&& !stop.isRunThroughAllowedFor(company)) {
log.info("Vertex is removed");
log.debug("Vertex is removed");
return false;
}

// check if it is a major or minor
if (stop.getScoreType() == Access.Score.MAJOR) {
setStationType(StationType.MAJOR);
} else if (stop.getScoreType() == Access.Score.MINOR) {
setStationType(StationType.MINOR);
//if (stop.getAccess() != null && stop.getAccess().getType() == Stop.Type.MINE) {
// setStationType(StationType.COALMINE);
//} else {
setStationType(StationType.MINOR);
//}
} else if (stop.getScoreType() == Access.Score.NO) { // Used in 18EU Alpine variant
setStationType(StationType.PASS); // Not sure if this is sensible for 18EU
}
Expand Down Expand Up @@ -285,8 +328,6 @@ public boolean initRailsVertex(PublicCompany company, boolean running) {
public void setRailsVertexValue(Phase phase) {
// side vertices and virtuals cannot use this function
if (virtual || type == VertexType.SIDE) return;

// define value
value = stop.getValueForPhase(phase);
}

Expand Down Expand Up @@ -524,6 +565,7 @@ public static Rectangle getVertexMapCoverage(HexMap map, Collection<NetworkVerte
* over the vertices in the LinkedList behind DLLGraph.Segment.
*/
public boolean equals (NetworkVertex otherVertex) {

return toString().equals(otherVertex.toString());
}
}
6 changes: 6 additions & 0 deletions src/main/java/net/sf/rails/algorithms/RevenueAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/
public final class RevenueAdapter implements Runnable {

int totalRevenue;
int specialRevenue;

private static final Logger log = LoggerFactory.getLogger(RevenueAdapter.class);
Expand Down Expand Up @@ -655,6 +656,7 @@ public int calculateRevenue(int startTrain, int finalTrain) {
rc.initRuns(startTrain, finalTrain);
rc.executePredictions(startTrain, finalTrain);
int value = rc.calculateRevenue(startTrain, finalTrain);
totalRevenue = value;

return value;
}
Expand All @@ -663,6 +665,10 @@ public int getSpecialRevenue() {
return specialRevenue;
}

public int getTotalRevenue() {
return totalRevenue;
}

public List<RevenueTrainRun> getOptimalRun() {
if (optimalRun == null) {
optimalRun = convertRcRun(rc.getOptimalRun());
Expand Down
97 changes: 89 additions & 8 deletions src/main/java/net/sf/rails/algorithms/RevenueManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@
import net.sf.rails.common.parser.Configurable;
import net.sf.rails.common.parser.ConfigurationException;
import net.sf.rails.common.parser.Tag;
import net.sf.rails.game.PublicCompany;
import net.sf.rails.game.RailsManager;
import net.sf.rails.game.RailsRoot;
import net.sf.rails.game.*;
import net.sf.rails.game.state.ArrayListState;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Coordinates and stores all elements related to revenue calulcation,
* Coordinates and stores all elements related to revenue calculation,
* which are permanent.
* The conversion of Rails elements is in the responsibility of the RevenueAdapter.
* For each GameManager instance only one RevenueManager is created.
*/
public final class RevenueManager extends RailsManager implements Configurable {
public class RevenueManager extends RailsManager implements Configurable {

private int specialRevenue;
protected int specialRevenue;
protected RailsRoot root;
protected PhaseManager phaseManager;

private static final Logger log = LoggerFactory.getLogger(RevenueManager.class);

Expand All @@ -49,6 +49,8 @@ public final class RevenueManager extends RailsManager implements Configurable {
*/
public RevenueManager(RailsRoot parent, String id) {
super(parent, id);
this.root = parent;
this.phaseManager = root.getPhaseManager();
}

public void configureFromXML(Tag tag) throws ConfigurationException {
Expand Down Expand Up @@ -206,7 +208,7 @@ boolean initDynamicModifiers(RevenueAdapter revenueAdapter) {
* @return revenue from active calculator
*/
// FIXME: This does not fully cover all cases that needs the revenue from the calculator
// EV: indeed, it used in a different way in 1837, so beware!
// EV: indeed, it is used in a different way in 1837, so beware!
// See RunToCoalMineModifier.
int revenueFromDynamicCalculator(RevenueAdapter revenueAdapter) {
return calculatorModifier.calculateRevenue(revenueAdapter);
Expand Down Expand Up @@ -271,7 +273,7 @@ int predictionValue(List<RevenueTrainRun> run) {
* @param revenueAdapter
* @return pretty print output from all modifiers (both static and dynamic)
*/
String prettyPrint(RevenueAdapter revenueAdapter) {
protected String prettyPrint(RevenueAdapter revenueAdapter) {
StringBuilder prettyPrint = new StringBuilder();

for (RevenueStaticModifier modifier : activeStaticModifiers) {
Expand All @@ -291,5 +293,84 @@ String prettyPrint(RevenueAdapter revenueAdapter) {
return prettyPrint.toString();
}

/*---------------------------
* The below new section of RevenueManager provides a generic interface
* to obtain the actual revenue values of stops of any kind.
* Game-specific subclasses can provide special cases.
*
* These methods are used by
* - NetworkVertex, to initialise stop values per train
* in getValueByTrain().
* - Dynamic modifiers (currently 18VA only, TBD).
*
* The methods getBaseRevenue() and getExtraRevenue() can be
* overridden in subclasses of RevenueManager (which is no longer final).
* This allows to specify the actual revenue value of stops
* per stop type, train type en company details, as needed.
*
* Method getExtraRevenue() was intended to return any extra values
* that should be returned by predictionValue() in dynamic modifiers.
* However, the flexibility of getBaseRevenue() should allow
* getExtraRevenue to return zero at all times.
*
* Revenues are returned as objects of the new Revenue class,
* which can carry both normal and special revenue values.
* Special values include the direct-to-treasury amounts
* of 1837 and 18VA (revenue from mines).
*
* Created 04/2023 by Erik Vos
*/


/* 'Actual revenue' is the final value of a stop for a given
* train and company. This is the value with which NetworkVertex
* objects can b e initialized.
*
* @param stop The stop for which a revenue value is requested
* @param train A specific train that may affect the revenue,
* or null if the train does not matter
* @param company A specific company that may affect the revenue,
* or null if the company does not matter
* @return A new Revenue object
*/
public final Revenue getActualRevenue(Stop stop, Train train, PublicCompany company) {
return getBaseRevenue (stop, train, company)
.addRevenue (getExtraRevenue(stop, train, company));
}

public final int getActualAsInteger (Stop stop, Train train, PublicCompany company) {
Revenue rev = getActualRevenue(stop, train, company);
return rev.getNormalRevenue() + rev.getSpecialRevenue();
}

/** Same as getRevenue(), but intended to get
* <i>additional</i> revenue vales, as are needed for
* the predictionValue() methods in dynamic modifiers.
*
* This method may not be really needed anymore, as getActualRevenue
* now provides the final value per NetworkVertex, rather than a loosely
* predicted one.
*/
public Revenue getExtraRevenue (Stop stop, Train train, PublicCompany company) {

return new Revenue (0, 0);
}

public final int getExtraAsInteger (Stop stop, Train train, PublicCompany company) {
Revenue rev = getExtraRevenue(stop, train, company);
return rev.getNormalRevenue() + rev.getSpecialRevenue();
}

/** Return the revenue insofar it could be configured.
* This probably represents the former initial NetworkVertex revenue,
* and it still is the default for the new actual revenue.
*
* @param stop
* @param train
* @param company
* @return
*/
protected Revenue getBaseRevenue (Stop stop, Train train, PublicCompany company) {
return new Revenue (stop.getValueForPhase(phaseManager.getCurrentPhase()),0);
}
}
12 changes: 11 additions & 1 deletion src/main/java/net/sf/rails/game/GameManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ public void nextRound(Round round) {
if (currentPhase.getNumberOfOperatingRounds() != numOfORs.value()) {
numOfORs.set(currentPhase.getNumberOfOperatingRounds());
}
log.info("Phase={} ORs={}", currentPhase.toText(), numOfORs);
log.debug("Phase={} ORs={}", currentPhase.toText(), numOfORs);

// Create a new OperatingRound (never more than one Stock Round)
// OperatingRound.resetRelativeORNumber();
Expand Down Expand Up @@ -1935,6 +1935,16 @@ public boolean isTrainBlocked (Train train) {
return blockedTrains.contains(train);
}

/** Stub for train-type dependent stop values.
* Used in 18VA for CMD value (see GameManager_18VA).
* The given default value of 0 is correct for 1837.
* @param train The train
* @return The train-dependent value of a stop
*/
public int getValuePerTrain (Train train) {
return 0;
}


//------------------------------------
// Random generator
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/net/sf/rails/game/MapHex.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
*/
public class MapHex extends RailsModel implements RailsOwner, Configurable {

public enum ValueType {
PERTILE, // Default
PERPHASE, // Default for offmap tiles
PERTRAIN // So far for 18VA CMD only
}

private static final Logger log = LoggerFactory.getLogger(MapHex.class);

public static class Coordinates {
Expand Down Expand Up @@ -166,6 +172,7 @@ public String toString() {
* Values if this is an off-board hex
*/
private List<Integer> valuesPerPhase = null;
private ValueType valueType = ValueType.PERTILE;

/*
* Temporary storage for impassable hexsides. Once neighbours has been set
Expand Down Expand Up @@ -254,6 +261,9 @@ public enum BlockedToken {
private final PortfolioSet<BonusToken> bonusTokens
= PortfolioSet.create(this, "bonusTokens", BonusToken.class);

private final PortfolioSet<BaseToken> offStationBaseTokens
= PortfolioSet.create(this, "offStationBaseTokens", BaseToken.class);

/**
* Parameters for extra text to be printed at a specified position on the hex.
* Added for 1837 to print coal mine names
Expand Down Expand Up @@ -295,7 +305,10 @@ public void configureFromXML(Tag tag) throws ConfigurationException {
label = tag.getAttributeAsString("label", "");

// Off-board revenue values
String valueTypeString = tag.getAttributeAsString("valueType", "perTile");
valueType = ValueType.valueOf(valueTypeString.toUpperCase());
valuesPerPhase = tag.getAttributeAsIntegerList("value");
if (!valuesPerPhase.isEmpty()) valueType = ValueType.PERPHASE;

// Location name
stopName = tag.getAttributeAsString("city", "");
Expand Down Expand Up @@ -829,6 +842,12 @@ public boolean layBaseToken(PublicCompany company, Stop stop) {
}
}

/** Lay an off-station base token, as in 18VA */
public boolean layOffStationBaseToken (BaseToken token) {
offStationBaseTokens.add(token);
return true;
}

/**
* Lay a bonus token.
*
Expand Down Expand Up @@ -856,6 +875,10 @@ public PortfolioSet<BonusToken> getBonusTokens() {
return bonusTokens;
}

public PortfolioSet<BaseToken> getOffStationBaseTokens() {
return offStationBaseTokens;
}

public boolean hasTokenSlotsLeft(Station station) {
// FIXME: Is this still required
// if (station == 0) station = 1; // Temp. fix for old save files
Expand Down Expand Up @@ -1099,6 +1122,10 @@ public List<Integer> getValuesPerPhase() {
return valuesPerPhase;
}

public ValueType getValueType() {
return valueType;
}

public int getCurrentValueForPhase(Phase phase) {
if (hasValuesPerPhase() && phase != null) {
return valuesPerPhase.get(Math.min(valuesPerPhase.size(),
Expand All @@ -1108,6 +1135,10 @@ public int getCurrentValueForPhase(Phase phase) {
}
}

public int getValuePerTrain (Train train) {
return getRoot().getGameManager().getValuePerTrain(train);
}

public String getStopName() {
return stopName;
}
Expand Down
Loading