From: Erik V. <ev...@us...> - 2011-10-13 15:41:56
|
data/GamesList.xml | 6 - rails/game/GameManager.java | 10 ++ rails/game/StartRound_1835.java | 35 +++++----- rails/game/specific/_1835/GameManager_1835.java | 49 +++++++++----- rails/game/specific/_1835/OperatingRound_1835.java | 36 +++++----- rails/game/specific/_1835/PrussianFormationRound.java | 63 ++++++++---------- 6 files changed, 110 insertions(+), 89 deletions(-) New commits: commit d5283097c814b535456a530af604b9640b68d603 Author: Erik Vos <eri...@xs...> Date: Thu Oct 13 17:31:12 2011 +0200 Fixed two 1835 bugs and updated Game Notes. - Crash in final PR formation round. Cause: already closed privates were not excluded from the merge process. - Hangs in first OR if Start Packet not sold. Caused by missing call to setPossibleActions() in StartRound_1835. Also corrected 'operate' flag for this case when creating OR object (unclear if this omission was causing problems). Removed bug description and workaround from the Game Notes. Replaced workaround for the non-closing privates bug. diff --git a/data/GamesList.xml b/data/GamesList.xml index de88eb3..9d9b161 100644 --- a/data/GamesList.xml +++ b/data/GamesList.xml @@ -204,10 +204,8 @@ Three variants have been implemented: - Snake Known bugs: - - Game hangs in OR if Start Packet has not been sold and minors run. - Workaround: save the game. - - OBB and PfB do not always close when required. - Workaround: close minor via the Special menu. + - OBB and PfB do not always close when required. + Workaround: close minor via the Special menu. </Description> <Option name="Variant" values="Standard,Clemens,Snake"/> <Option name="RouteAwareness" values="Highlight,Deactivate" default="Highlight" /> diff --git a/rails/game/specific/_1835/PrussianFormationRound.java b/rails/game/specific/_1835/PrussianFormationRound.java index 79faa66..aa9a3b0 100644 --- a/rails/game/specific/_1835/PrussianFormationRound.java +++ b/rails/game/specific/_1835/PrussianFormationRound.java @@ -2,9 +2,7 @@ package rails.game.specific._1835; import java.util.*; -import rails.common.DisplayBuffer; -import rails.common.GuiDef; -import rails.common.LocalText; +import rails.common.*; import rails.game.*; import rails.game.action.DiscardTrain; import rails.game.action.PossibleAction; @@ -18,10 +16,10 @@ public class PrussianFormationRound extends StockRound { private PublicCompanyI m2; private PhaseI phase; - private boolean startPr; - private boolean forcedStart; - private boolean mergePr; - private boolean forcedMerge; + private boolean startPr; + private boolean forcedStart; + private boolean mergePr; + private boolean forcedMerge; private List<CompanyI> foldablePrePrussians; @@ -33,7 +31,7 @@ public class PrussianFormationRound extends StockRound { Step step; - private static String PR_ID = GameManager_1835.PR_ID; + private static String PR_ID = GameManager_1835.PR_ID; private static String M2_ID = GameManager_1835.M2_ID; public PrussianFormationRound (GameManagerI gameManager) { @@ -44,19 +42,19 @@ public class PrussianFormationRound extends StockRound { } - @Override - public void start() { + @Override + public void start() { prussian = companyManager.getPublicCompany(PR_ID); phase = getCurrentPhase(); - startPr = !prussian.hasStarted(); + startPr = !prussian.hasStarted(); forcedMerge = phase.getName().equals("5"); forcedStart = phase.getName().equals("4+4") || forcedMerge; - mergePr = !prussianIsComplete(gameManager); + mergePr = !prussianIsComplete(gameManager); ReportBuffer.add(LocalText.getText("StartFormationRound", PR_ID)); log.debug("StartPr="+startPr+" forcedStart="+forcedStart - +" mergePr="+mergePr+" forcedMerge="+forcedMerge); + +" mergePr="+mergePr+" forcedMerge="+forcedMerge); step = startPr ? Step.START : Step.MERGE; @@ -72,7 +70,7 @@ public class PrussianFormationRound extends StockRound { if (step == Step.MERGE) { startingPlayer - = ((GameManager_1835)gameManager).getPrussianFormationStartingPlayer(); + = ((GameManager_1835)gameManager).getPrussianFormationStartingPlayer(); log.debug("Original Prussian starting player was "+startingPlayer.getName()); setCurrentPlayer(startingPlayer); if (forcedMerge) { @@ -80,6 +78,7 @@ public class PrussianFormationRound extends StockRound { setFoldablePrePrussians(); List<CompanyI> foldables = new ArrayList<CompanyI> (); for (PrivateCompanyI company : gameManager.getAllPrivateCompanies()) { + if (company.isClosed()) continue; sps = company.getSpecialProperties(); if (sps != null && !sps.isEmpty() && sps.get(0) instanceof ExchangeForShare) { foldables.add(company); @@ -101,7 +100,7 @@ public class PrussianFormationRound extends StockRound { } @Override - public boolean setPossibleActions() { + public boolean setPossibleActions() { if (step == Step.START) { Player m2Owner = m2.getPresident(); @@ -150,7 +149,7 @@ public class PrussianFormationRound extends StockRound { } @Override - protected boolean processGameSpecificAction(PossibleAction action) { + protected boolean processGameSpecificAction(PossibleAction action) { if (action instanceof FoldIntoPrussian) { @@ -256,13 +255,13 @@ public class PrussianFormationRound extends StockRound { if (cash > 0) { new CashMove(bank, prussian, cash); ReportBuffer.add(LocalText.getText("FloatsWithCash", - prussian.getName(), - Bank.format(cash) )); + prussian.getName(), + Bank.format(cash) )); } else { ReportBuffer.add(LocalText.getText("Floats", prussian.getName())); } - + executeExchange (Arrays.asList(new CompanyI[]{m2}), true, false); prussian.setFloated(); } @@ -280,7 +279,7 @@ public class PrussianFormationRound extends StockRound { break; } - // This is now dead code, but won't be when some sensible validations exist + // This is now dead code, but won't be when some sensible validations exist if (errMsg != null) { DisplayBuffer.add(LocalText.getText("CannotMerge", action.getFoldedCompanyNames(), @@ -301,7 +300,7 @@ public class PrussianFormationRound extends StockRound { } private void executeExchange (List<CompanyI> companies, boolean president, - boolean display) { + boolean display) { ExchangeForShare efs; PublicCertificateI cert; @@ -316,7 +315,7 @@ public class PrussianFormationRound extends StockRound { // Shortcut, sp should be checked efs = (ExchangeForShare) company.getSpecialProperties().get(0); cert = unavailable.findCertificate(prussian, efs.getShare()/prussian.getShareUnit(), - president); + president); cert.moveTo(player.getPortfolio()); //company.setClosed(); String message = LocalText.getText("MERGE_MINOR_LOG", @@ -325,8 +324,8 @@ public class PrussianFormationRound extends StockRound { PR_ID, company instanceof PrivateCompanyI ? "no" : Bank.format(((PublicCompanyI)company).getCash()), - company instanceof PrivateCompanyI ? "no" - : ((PublicCompanyI)company).getPortfolio().getTrainList().size()); + company instanceof PrivateCompanyI ? "no" + : ((PublicCompanyI)company).getPortfolio().getTrainList().size()); ReportBuffer.add(message); if (display) DisplayBuffer.add (message); message = LocalText.getText("GetShareForMinor", @@ -352,8 +351,8 @@ public class PrussianFormationRound extends StockRound { message = LocalText.getText("ExchangesBaseToken", PR_ID, minor.getName(), city.getName()); - ReportBuffer.add(message); - if (display) DisplayBuffer.add (message); + ReportBuffer.add(message); + if (display) DisplayBuffer.add (message); prussian.layBaseToken(hex, 0); } @@ -400,9 +399,9 @@ public class PrussianFormationRound extends StockRound { // Does the company own such a train? if (!company.getPortfolio().getTrainList().contains(train)) { errMsg = - LocalText.getText("CompanyDoesNotOwnTrain", - company.getName(), - train.getName() ); + LocalText.getText("CompanyDoesNotOwnTrain", + company.getName(), + train.getName() ); break; } @@ -436,9 +435,9 @@ public class PrussianFormationRound extends StockRound { protected void finishRound() { RoundI interruptedRound = gameManager.getInterruptedRound(); ReportBuffer.add(" "); - if (interruptedRound != null) { - ReportBuffer.add(LocalText.getText("EndOfFormationRound", PR_ID, - interruptedRound.getRoundName())); + if (interruptedRound != null) { + ReportBuffer.add(LocalText.getText("EndOfFormationRound", PR_ID, + interruptedRound.getRoundName())); } else { ReportBuffer.add(LocalText.getText("EndOfFormationRoundNoInterrupt", PR_ID)); } commit cd9efcef8d70134015e96e25a809f49b468800ea Author: Erik Vos <eri...@xs...> Date: Thu Oct 13 17:11:37 2011 +0200 Fixed 1835 bug: Hangs in first OR if Start Packet not sold. Missing call to setPossibleActions() in StartRound_1835. Also corrected 'operate' flag for this case when creating OR object (unclear if this omission was causing problems). diff --git a/rails/game/GameManager.java b/rails/game/GameManager.java index c691147..8507931 100644 --- a/rails/game/GameManager.java +++ b/rails/game/GameManager.java @@ -644,7 +644,7 @@ public class GameManager implements ConfigurableComponentI, GameManagerI { public void nextRound(RoundI round) { if (round instanceof StartRound) { if (startPacket != null && !startPacket.areAllSold()) { - startOperatingRound(false); + startOperatingRound(runIfStartPacketIsNotCompletelySold()); } else if (skipFirstStockRound) { PhaseI currentPhase = phaseManager.getCurrentPhase(); @@ -693,6 +693,14 @@ public class GameManager implements ConfigurableComponentI, GameManagerI { } } + /** Stub, to be overridden if companies can run before the Start Packet has been completely sold + * (as in 1835). + * @return true if companies can run regardless. Default false. + */ + protected boolean runIfStartPacketIsNotCompletelySold() { + return false; + } + protected void startStartRound() { String startRoundClassName = startPacket.getRoundClassName(); Class<? extends StartRound> startRoundClass = null; diff --git a/rails/game/StartRound_1835.java b/rails/game/StartRound_1835.java index e982aad..80710e7 100644 --- a/rails/game/StartRound_1835.java +++ b/rails/game/StartRound_1835.java @@ -18,7 +18,7 @@ public class StartRound_1835 extends StartRound { private static IntegerState turn = new IntegerState("TurnNumber", 0); private static IntegerState startRoundNumber = - new IntegerState("StartRoundNumber", 0); + new IntegerState("StartRoundNumber", 0); /* Additional variants */ public static final String CLEMENS_VARIANT = "Clemens"; @@ -46,7 +46,7 @@ public class StartRound_1835 extends StartRound { setCurrentPlayerIndex (numPlayers-1); } - + if (!setPossibleActions()) { /* * If nobody can do anything, keep executing Operating and Start @@ -124,8 +124,8 @@ public class StartRound_1835 extends StartRound { if (possibleActions.isEmpty()) { String message = - LocalText.getText("CannotBuyAnything", - currentPlayer.getName()); + LocalText.getText("CannotBuyAnything", + currentPlayer.getName()); ReportBuffer.add(message); DisplayBuffer.add(message); numPasses.add(1); @@ -134,12 +134,13 @@ public class StartRound_1835 extends StartRound { * No-one has enough cash left to buy anything, so close the * Start Round. */ - numPasses.set(0); - finishRound(); + numPasses.set(0); + finishRound(); + gameManager.getCurrentRound().setPossibleActions(); - // This code may be called recursively. - // Jump out as soon as we have something to do - if (!possibleActions.isEmpty()) break; + // This code may be called recursively. + // Jump out as soon as we have something to do + if (!possibleActions.isEmpty()) break; return false; } @@ -187,13 +188,13 @@ public class StartRound_1835 extends StartRound { if (variant.equalsIgnoreCase(CLEMENS_VARIANT)) { /* Reverse order in the first cycle only */ newIndex = - cycleNumber == 0 ? numPlayers - 1 - turnIndex - : turnIndex; + cycleNumber == 0 ? numPlayers - 1 - turnIndex + : turnIndex; } else if (variant.equalsIgnoreCase(SNAKE_VARIANT)) { /* Reverse order in the second cycle only */ newIndex = - cycleNumber == 1 ? numPlayers - 1 - turnIndex - : turnIndex; + cycleNumber == 1 ? numPlayers - 1 - turnIndex + : turnIndex; } else { newIndex = turnIndex; } @@ -201,9 +202,9 @@ public class StartRound_1835 extends StartRound { setCurrentPlayerIndex(newIndex); Player newPlayer = getCurrentPlayer(); log.debug("Game turn has moved from " + oldPlayer.getName() - + " to " + newPlayer.getName() + " [startRound=" - + startRoundNumber + " cycle=" + cycleNumber + " turn=" - + turnNumber + " newIndex=" + newIndex + "]"); + + " to " + newPlayer.getName() + " [startRound=" + + startRoundNumber + " cycle=" + cycleNumber + " turn=" + + turnNumber + " newIndex=" + newIndex + "]"); } else { @@ -212,7 +213,7 @@ public class StartRound_1835 extends StartRound { super.setNextPlayer(); Player newPlayer = getCurrentPlayer(); log.debug("Game turn has moved from " + oldPlayer.getName() - + " to " + newPlayer.getName()); + + " to " + newPlayer.getName()); } return; diff --git a/rails/game/specific/_1835/GameManager_1835.java b/rails/game/specific/_1835/GameManager_1835.java index 1d92d44..8a44dca 100644 --- a/rails/game/specific/_1835/GameManager_1835.java +++ b/rails/game/specific/_1835/GameManager_1835.java @@ -1,12 +1,13 @@ /* $Header: /Users/blentz/rails_rcs/cvs/18xx/rails/game/specific/_1835/GameManager_1835.java,v 1.9 2010/05/15 19:05:39 evos Exp $ */ package rails.game.specific._1835; +import rails.common.parser.GameOption; import rails.game.*; public class GameManager_1835 extends GameManager { - private RoundI previousRound = null; - private Player prFormStartingPlayer = null; + private RoundI previousRound = null; + private Player prFormStartingPlayer = null; public static String M2_ID = "M2"; public static String PR_ID = "PR"; @@ -19,7 +20,21 @@ public class GameManager_1835 extends GameManager { public static String BY_ID = "BY"; public GameManager_1835() { - super(); + super(); + } + + /** In standard 1835, minors can run even if the start packet has not been completely sold, + * unless the "MinorsRequireFloatedBY" option is in effect and the Bayerische + * has not yet floated. + * @return true only if minors can run. + */ + @Override + protected boolean runIfStartPacketIsNotCompletelySold() { + if (getGameOption(GameOption.VARIANT).equalsIgnoreCase("Clemens") + || getGameOption("MinorsRequireFloatedBY").equalsIgnoreCase("yes")) { + return companyManager.getPublicCompany(GameManager_1835.BY_ID).hasFloated(); + } + return true; } @Override @@ -35,15 +50,15 @@ public class GameManager_1835 extends GameManager { previousRound = null; } } else { - PhaseI phase = getCurrentPhase(); - if ((phase.getName().equals("4") || phase.getName().equals("4+4") - || phase.getName().equals("5")) - && !PrussianFormationRound.prussianIsComplete(this)) { - previousRound = round; - startPrussianFormationRound (null); - } else { - super.nextRound(round); - } + PhaseI phase = getCurrentPhase(); + if ((phase.getName().equals("4") || phase.getName().equals("4+4") + || phase.getName().equals("5")) + && !PrussianFormationRound.prussianIsComplete(this)) { + previousRound = round; + startPrussianFormationRound (null); + } else { + super.nextRound(round); + } } } @@ -51,15 +66,15 @@ public class GameManager_1835 extends GameManager { public void startPrussianFormationRound(OperatingRound_1835 or) { interruptedRound = or; - createRound (PrussianFormationRound.class).start (); + createRound (PrussianFormationRound.class).start (); } public void setPrussianFormationStartingPlayer(Player prFormStartingPlayer) { - this.prFormStartingPlayer = prFormStartingPlayer; - } + this.prFormStartingPlayer = prFormStartingPlayer; + } - public Player getPrussianFormationStartingPlayer() { - return prFormStartingPlayer; + public Player getPrussianFormationStartingPlayer() { + return prFormStartingPlayer; } @Override diff --git a/rails/game/specific/_1835/OperatingRound_1835.java b/rails/game/specific/_1835/OperatingRound_1835.java index fe5589e..6f4d312 100644 --- a/rails/game/specific/_1835/OperatingRound_1835.java +++ b/rails/game/specific/_1835/OperatingRound_1835.java @@ -16,9 +16,9 @@ import rails.game.state.BooleanState; public class OperatingRound_1835 extends OperatingRound { private BooleanState needPrussianFormationCall - = new BooleanState ("NeedPrussianFormationCall", false); + = new BooleanState ("NeedPrussianFormationCall", false); private BooleanState hasLaidExtraOBBTile - = new BooleanState ("HasLaidExtraOBBTile", false); + = new BooleanState ("HasLaidExtraOBBTile", false); /** * Registry of percentage of PR revenue to be denied per player @@ -122,8 +122,8 @@ public class OperatingRound_1835 extends OperatingRound { return sharesPerRecipient; } - /** - * Register black minors as having operated + /** + * Register black minors as having operated * for the purpose of denying income after conversion to a PR share */ @Override @@ -162,7 +162,7 @@ public class OperatingRound_1835 extends OperatingRound { && !company.isClosed() && company != operatingCompany.get() && company.getCurrentSpace().getPrice() - < prussian.getCurrentSpace().getPrice()) { + < prussian.getCurrentSpace().getPrice()) { log.debug("PR will operate before "+company.getName()); break; } @@ -191,11 +191,11 @@ public class OperatingRound_1835 extends OperatingRound { if (operatingCompany.get().canUseSpecialProperties()) { for (SpecialTileLay stl : getSpecialProperties(SpecialTileLay.class)) { - if (stl.isExtra() - // If the special tile lay is not extra, it is only allowed if - // normal tile lays are also (still) allowed - || stl.getTile() != null - && getCurrentPhase().isTileColourAllowed(stl.getTile().getColourName())) { + if (stl.isExtra() + // If the special tile lay is not extra, it is only allowed if + // normal tile lays are also (still) allowed + || stl.getTile() != null + && getCurrentPhase().isTileColourAllowed(stl.getTile().getColourName())) { // Exclude the second OBB free tile if the first was laid in this round if (stl.getLocationNameString().matches("M1(7|9)") @@ -216,14 +216,14 @@ public class OperatingRound_1835 extends OperatingRound { } } } - + return currentSpecialTileLays; } @Override public boolean layTile(LayTile action) { - + boolean hasJustLaidExtraOBBTile = action.getSpecialProperty() != null - && action.getSpecialProperty().getLocationNameString().matches("M1(5|7)"); + && action.getSpecialProperty().getLocationNameString().matches("M1(5|7)"); // The extra OBB tiles may not both be laid in the same round if (hasJustLaidExtraOBBTile) { @@ -238,7 +238,7 @@ public class OperatingRound_1835 extends OperatingRound { return false; } else { moveStack.start(true); // Duplicate, but we have to - hasLaidExtraOBBTile.set(true); + hasLaidExtraOBBTile.set(true); // Done here to make getSpecialTileLays() return the correct value. // It's provisional, on the assumption that other validations are OK. // TODO To get it really right, we should separate validation and execution. @@ -258,11 +258,11 @@ public class OperatingRound_1835 extends OperatingRound { @Override protected void newPhaseChecks() { PhaseI phase = getCurrentPhase(); - if (phase.getName().equals("4") - || phase.getName().equals("4+4") - && !companyManager.getPublicCompany(GameManager_1835.PR_ID).hasStarted() + if (phase.getName().equals("4") + || phase.getName().equals("4+4") + && !companyManager.getPublicCompany(GameManager_1835.PR_ID).hasStarted() || phase.getName().equals("5") - && !PrussianFormationRound.prussianIsComplete(gameManager)) { + && !PrussianFormationRound.prussianIsComplete(gameManager)) { if (getStep() == GameDef.OrStep.DISCARD_TRAINS) { // Postpone until trains are discarded needPrussianFormationCall.set(true); commit df31d98077ce7d818cae7c671c77a403fdc3eeca Author: Erik Vos <eri...@xs...> Date: Thu Oct 13 13:14:01 2011 +0200 Updated 1835 Game Notes diff --git a/data/GamesList.xml b/data/GamesList.xml index 63b992c..de88eb3 100644 --- a/data/GamesList.xml +++ b/data/GamesList.xml @@ -205,9 +205,9 @@ Three variants have been implemented: Known bugs: - Game hangs in OR if Start Packet has not been sold and minors run. - Workaround: select option "Minors don't run if BY has not floated". + Workaround: save the game. - OBB and PfB do not always close when required. - Workaround: use Correction mode. + Workaround: close minor via the Special menu. </Description> <Option name="Variant" values="Standard,Clemens,Snake"/> <Option name="RouteAwareness" values="Highlight,Deactivate" default="Highlight" /> |