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

Add fuel to scrambling #3292

Merged
merged 13 commits into from
Mar 20, 2018
Merged

Add fuel to scrambling #3292

merged 13 commits into from
Mar 20, 2018

Conversation

ron-murhammer
Copy link
Member

@ron-murhammer ron-murhammer commented Mar 17, 2018

Address point 6 on https://forums.triplea-game.org/topic/558/fuel-enhancements

Functional Changes

  • Scramble dialog now shows estimated fuel costs for any units selected to move there and back
  • Fuel costs are treated just like movement during a player's turn so 'fuelFlatCost' is charged once if unit is scrambled, 'fuelCost' is charged for each move
  • Fuel validation and game data changes for selected scramble units added
  • Fixed scramble window to have proper icon and close if game is exited to main menu
  • Updated Actions and Operations window to remove remaining resources since its duplicate information of the bottom bar:
    image

Testing
Manually edited Global 40 2nd to add this to fighters:

	  <option name="fuelFlatCost" value="PUs" count="4"/>
	  <option name="fuelCost" value="PUs" count="1"/>

And this property:

    <property name="Use Fuel Cost" value="true" editable="false">
      <boolean/>
    </property>

Result:
image

More examples: https://forums.triplea-game.org/topic/558/fuel-enhancements/227

@codecov-io
Copy link

codecov-io commented Mar 17, 2018

Codecov Report

Merging #3292 into master will decrease coverage by 0.01%.
The diff coverage is 10.71%.

Impacted file tree graph

@@             Coverage Diff             @@
##             master   #3292      +/-   ##
===========================================
- Coverage     21.91%   21.9%   -0.02%     
- Complexity     5872    5875       +3     
===========================================
  Files           837     837              
  Lines         72620   72696      +76     
  Branches      11852   11865      +13     
===========================================
+ Hits          15916   15921       +5     
- Misses        54598   54671      +73     
+ Partials       2106    2104       -2
Impacted Files Coverage Δ Complexity Δ
...ames/strategy/triplea/delegate/BattleDelegate.java 22.31% <0%> (-0.4%) 53 <0> (ø)
...ava/games/strategy/triplea/ui/ProductionPanel.java 0% <0%> (ø) 0 <0> (ø) ⬇️
...n/java/games/strategy/triplea/ui/TripleAFrame.java 0% <0%> (ø) 0 <0> (ø) ⬇️
...in/java/games/strategy/triplea/ui/UnitChooser.java 0% <0%> (ø) 0 <0> (ø) ⬇️
...ava/games/strategy/triplea/ui/UserActionPanel.java 1.76% <0%> (+0.06%) 4 <0> (ø) ⬇️
...s/strategy/triplea/image/ResourceImageFactory.java 0% <0%> (ø) 0 <0> (ø) ⬇️
...games/strategy/triplea/delegate/MovePerformer.java 72.83% <100%> (-0.11%) 44 <0> (ø)
...rc/main/java/games/strategy/engine/data/Route.java 57.36% <40.74%> (-4.62%) 59 <6> (+2)
...rc/main/java/games/strategy/net/nio/NioReader.java 72.63% <0%> (-2.11%) 17% <0%> (ø)
.../src/main/java/games/strategy/net/nio/Decoder.java 72.82% <0%> (-1.09%) 24% <0%> (-1%)
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e73be10...91b0f52. Read the comment docs.

Copy link
Member

@DanVanAtta DanVanAtta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks generally okay. Some of the code is a bit verbose, a refactoring pass could potentially help.

I am a bit concerned that the updates are all in-line for the most part without unit test. Given the existing code it's not too unreasonable the decisions made; I suspect we will often be able to say that as a reason for not unit testing.

I've took a few passes at this now; again looks pretty good overall. Just need to do some additional play testing and I would give this an okay. Anyone else is welcome to approve in the meantime.

final ResourceCollection cost = new ResourceCollection(data);
cost.add(getMovementFuelCostCharge(Collections.singleton(unit), toRoute, player, data));
cost.add(getFuelCostsAndUnitsChargedFlatFuelCost(
Collections.singleton(unit), returnRoute, player, data, true).getFirst());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting the APIs, it looks like we could improve it a bit and have more work be done by the 'callee' methods, noting:

getFuelCostsAndUnitsChargedFlatFuelCost(Collections.singleton(unit), returnRoute, player, data, true).getFirst()

In this example we could have the first arg take a unit directly, avoid the true parameter by creating a method overload, and could avoid the getFirst by having the method overload return that directly for us.

That would give something like:

I think there is also a method like computeIfAbsent that could replace the if/else if structure for the map update. Going that route, if the body of the for loop were extracted to a helper method I think you could break this up into 2 or 3 pretty clean private methods.

Copy link
Member Author

@ron-murhammer ron-murhammer Mar 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually originally created a method that I think was:

getFuelCostsIgnoringFlatFuel(Unit, Route, PlayerID, GameData)

I ended up not seeing much value in slightly cleaner parameters at the cost of adding misdirection given that the call is to a private method pretty much right below this one. If the call was to a public method then I'd completely agree. I'm open to adding an overloaded method back in if there is a strong consensus/opinion that it improves readability/maintainability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will leave it up to you.

I like the cleaner and reduced parameters as it can make the method a bit more self-descriptive. If you can understand a method by it's name and args, then a person does not need to start hopping down the indirection tree. On the flip side, the boolean parameter being non-indirected requires reverse engineering of its usage to know what it really does.

Second comment/note I'll make, I tend to favor cleaner client code over server ("callee") code. If the client is not clean, then I'll iterate until it is. Then I drop down to the "callee" code and clean that up and it's dependent API's until that is also clean. This way down the whole stack there is no code you can't just read and understand.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third and final comment here:
Normally unit tests would be the driving factor in this decision. The biggest benefit of unit testing is for decoupling design, not for catching bugs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm inclined to leave as is for now since I'm not really strongly convinced one way is much better than the other. I agree with most of you approach and tend to do something fairly similar. And I think the public API calls of Route involving fuel are pretty decent. Unless you have any more specific questions/asks then I'd like to move forward with it and we can always revisit.

Copy link
Member

@DanVanAtta DanVanAtta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished reviewing 100%
Found 3 nitpicky comments, they're low level. If we disagree in theory it would be an interesting conversation, but otherwise not a blocker - looks good otherwise, approving 🥂

@@ -1268,6 +1309,16 @@ public Component getListCellRendererComponent(final JList<? extends Unit> list,
return selection;
}

private JOptionPane getOptionPane(final JComponent parent) {
JOptionPane pane = null;
Copy link
Member

@DanVanAtta DanVanAtta Mar 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A ternary IMO would clean this up quite a bit:

return !(parent instanceof JOptionPane)
     ? getOptionPane((JComponent) parent.getParent())
     : (JOptionPane) parent;

If going with the old pattern, not setting the variable to null and then marking it final to ensure (compiler-enforced) that ti would get set in the if/else block is a good practice IMO. It's a signal to reader that the variable would be set, eg: final JOptionPane pane;.

@@ -302,6 +302,10 @@ private void addToCollection(final Collection<Unit> addTo, final ChooserEntry en
});
}

public void addChangeListener(final ScrollableTextFieldListener listener) {
entries.stream().forEach(entry -> entry.addChangeListener(listener));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List has a convenience forEach method attached to it. FYI can simplify a little bit with it:

entries.forEach(entry -> entry.addChangeListener(listener));

@@ -450,6 +454,10 @@ boolean hasMultipleHitPoints() {
return hasMultipleHits;
}

void addChangeListener(final ScrollableTextFieldListener listener) {
hitTexts.stream().forEach(field -> field.addChangeListener(listener));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto on simplification to remove stream() and call forEach directly on the list.

@ron-murhammer
Copy link
Member Author

@DanVanAtta Yep, good catch on those 3. Updated. Not sure that even I could come up with a reasonable argument against those :)

@ron-murhammer
Copy link
Member Author

Merging based on all feedback being addressed.

@ron-murhammer ron-murhammer merged commit d7eb4bc into master Mar 20, 2018
@RoiEXLab RoiEXLab deleted the Add_Fuel_To_Scrambling branch March 20, 2018 15:40
@asvitkine
Copy link
Contributor

I think this change introduced this error when scrambling on the WW2 Pacific Map:

java.lang.ClassCastException: java.lang.String cannot be cast to javax.swing.JButton
at games.strategy.triplea.ui.TripleAFrame.lambda$scrambleUnitsQuery$39(TripleAFrame.java:1261)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at java.awt.Component.firePropertyChange(Component.java:8428)
at javax.swing.JComponent.putClientProperty(JComponent.java:4087)
at com.apple.laf.AquaRootPaneUI.updateComponentTreeUIActivation(AquaRootPaneUI.java:305)
at com.apple.laf.AquaRootPaneUI.updateComponentTreeUIActivation(AquaRootPaneUI.java:319)
at com.apple.laf.AquaRootPaneUI.updateComponentTreeUIActivation(AquaRootPaneUI.java:319)
at com.apple.laf.AquaRootPaneUI.updateComponentTreeUIActivation(AquaRootPaneUI.java:319)
at com.apple.laf.AquaRootPaneUI.windowDeactivated(AquaRootPaneUI.java:281)
at java.awt.Window.processWindowEvent(Window.java:2069)
at javax.swing.JDialog.processWindowEvent(JDialog.java:683)
at java.awt.Window.processEvent(Window.java:2013)

@ron-murhammer
Copy link
Member Author

@asvitkine Is this a older save game or a lobby game? Do you have a save game that reproduces this?

@asvitkine
Copy link
Contributor

This is caused by JOptionPane.UNINITIALIZED_VALUE being returned when a choice hasn't been made yet, which is a String.

I'll send a PR to fix.

@asvitkine
Copy link
Contributor

#3493

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants