-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Implement a waiting command (#6884) #6896
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there :) welcome to the project, glad to have you!
This commit should address all issues in the review. I will squash them later if necessary. I resolved clear-cut conversation, but left open those where I didn't follow the advises exactly. |
Since I failed to properly separate with my main account anyway, I deleted the secondary and will just use the main instead. |
Hey, welcome! I'll need time to really look at this, just two notes from reading mail:
|
I don't think these words in this context are really appropriate anymore ;) |
You're only allowed to throw stones if you're sitting inside the glass house yourself. But if you are, it's fun! |
How about using |
Look it up in blame - that was the original implementation way back until someone with a cyrillic keyboard convinced us otherwise. Of course, back then with the lwjgl2 backend, keyDown was still layout-mapped. Now, I don't even see a way to reflect down into Gdx to get access to the mapping stuff directly... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I looked at this again, more in-depth...
The problem I have is the initialization of the dueUnits
list... doing getDueUnits()
just to get the side effect of adding all units to the list seems like a clutch.
And I noticed one thing: the dueUnits
list contains exactly the same units as the units
field.
So how about, instead of duplicating the list, remove
from beginning, add
at the end, we instead change dueUnits
from a MutableList<MapUnit>
to just an Int
with default -1
- a pointer into the unit
list? (and rename to curDueUnitIdx
or some such)
Then the cycleThroughDueUnits
function would look something like this:
fun cycleThroughDueUnits(unitToSkip: MapUnit?): MapUnit? {
if (units.none() || getDueUnits().none()) return null
var curUnit: MapUnit
do {
if (curDueUnitIdx == -1 || curDueUnitIdx >= (units.size - 1)) {
curDueUnitIdx = 0
} else {
curDueUnitIdx++
}
curUnit = units[curDueUnitIdx]
} while (curUnit == unitToSkip || !curUnit.due || !curUnit.isIdle())
return curUnit
}
and the only thing we need to do to keep the pointer in sync is a if (curDueUnitIdx != -1 && removedIdx < curDueUnitIdx) curDueUnit -= 1
instead of dueUnits.remove(mapUnit)
.
getDueUnits
would then be side-effect-free, since you can just do
fun getDueUnits(): List<MapUnit> {
return units.filter { it.due && it.isIdle() }
}
It changes some work/complexity/performance from keeping the lists in sync & initialized to a bit of array index logic. I think that's a fine trade-off.
It's fine either way, but it'd be cool if you could get rid of the "getDueUnits
for side-effects" clutch if you keep the second list... maybe use a small helper class with add
+remove
helpers.
addWaitAction(unit, actionList, worldScreen); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be above addToggleActionsAction
(what a name...) because that is basically our "Next Page" button, and a "Next Page" button does not make sense in the middle of a page, but should be at the bottom.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Ah, yes, LIFO or Cursor or... How about a virtual ZIndex on the unit itself? Could be made to work... The problem with SQL cursors is they basically need a strict isolation level meaning lock preventing access or be completely immune against fetch fails, against the same record coming along repeatedly, or against missing rows. Translated - a pointer can get out of sync with the structure it's pointing into unless it's part of that structure's implementation... Some of that you have addressed. More would be 'we're using knowledge about how that collection is done now' - fine but someone thinking "Optimization! Let's use a Set instead!" would then break our cursor... Don't get me wrong - I'm not advocating a specific approach, just throwing bones to the dogs. I'll leave the choice to "ghost", and if his solution works... Ah, while I'm playing devil's advocate: Is the LIFO holding on to references longer than before a potential problem? I didn't really look. |
It would, but not without the compiler warning that you can't access a |
And just so you know how it works in this project, these are all suggestions unless you get a red "Changes Requested" review. If you say "nah I don't have time for this/I want to leave it like this" we can also get this merged. This small thing is not a dealbreaker. I just at least want to get a response to my suggestions before I approve :D And another thing: you might've noticed we don't always keep it on-topic. Just ignore irrelevant comments. |
Reimplemented without separate list. However, now From personal observations: would be nice if "Next unit" also would be trigerred by key |
Sorry for the continued teasing, but - getDueUnits() was OK, getDueUnits is bad design anyway. May not have been historically, but at the moment there's only two callers, and both only iterate to the first hit : first and any. (( And the 9-liner Look at what getDueUnit is: units.asSequence().filter { it.due && it.isIdle() }. Now we only need firstOrNull. Which amounts to an Iterable scan (units.asSequence uses Iterable.asSequence - can't do better than to iterate on a List), stop if the predicate is fulfilled, and recognize the Iterable ending to return null. Using your nextPotentiallyDueAt, how could we "just" iterate from there to the end, roll over start from beginning, and give up where we started? Sequence.drop(n) + Sequence.take(n) would probably do it, but is drop efficient on a List? Don't know. take is. drop can be as efficient as [n]... Which for a List is? Depends on which implementation of List is used. Which is actually controlled by addUnit and removeUnit. And it's an ArrayList, promising efficient indexing, hopefully. So that scan could just be written as for loop, which would effectively do the same as the sequence terminator in the drop+take idea and run just as efficient - but you save the overhead of sequence creation. Sorry for rambling. Not even sure I'm right. But your solution creating at least two clones where before there was none, that's true. Maybe Azzurite can explain better. "Z"="N" if no selection: Yup, had the same idea when I played your initial version! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good :) unfortunately I still have comments :D
I'm also not sure why it creates new lists to begin with
The only thing I can imagine is that there were some concurrent list modifications or some such, and the solution was to make copies instead of making it a synchronizedList
? Not sure. I'd be fine with you changing it to direct modification. /shrug
// Callers should consider if cycleThroughDueUnits() is not a better choice. | ||
fun getNextDueUnit() = getDueUnits().firstOrNull() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is unused now, can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kept because it was used before. But OK, removed in the latest commit.
@@ -448,10 +454,18 @@ class CivilizationInfo { | |||
fun getCivUnits(): Sequence<MapUnit> = units.asSequence() | |||
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() } | |||
|
|||
// Similar to getCivUnits(), but the returned list is rotated so that the | |||
// 'nextPotentiallyDueAt' unit is first here. | |||
private fun getCivUnitsStartingAtNexDue() = units.subList(nextPotentiallyDueAt, units.size) + units.subList(0, nextPotentiallyDueAt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sequenceOf(sublist1, sublist2).flatten()
(or sublist1.asSequence() + sublist2.asSequence()
, whatever you prefer :D) should make this a sequence again and not materialize a new list. Otherwise this is unfortunately worse than your previous approach, as getDueUnits()
would be creating a new list each time it is called :D
But if it is a sequence, all getDueUnits().any()
and such calls should short-circuit nicely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also NexDue -> NextDue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed.
1 clone, as |
As commented on the
Wasn't counting the sublist - cuz then a filter gets called on the plus result... right? |
I mean then in total it's actually 4, because |
I think you guys are busy with wrong things. Who cares about performance of a max-50 items (in reality even fewer) list in user interface? It would matter if it was 1000+ items, or called used thousands of times per second. The only maybe performance-relevant place are Your "if it is a sequence, all getDueUnits().any() and such calls should short-circuit nicely" etc. made it necessary for
Actually, no, it also updates Anyway, I think I'm done with this. Do with it what you want: merge as-is, improve, refactor or throw away. I'm willing to do only functionality fixes/improvements and fixes for real problems from now on, not these peanuts. |
I tend to agree with that. Sometimes me + SomeTroglodyte lose ourselves in "pointless" discussion, but we're having fun doing it. But why do you only say that now? From what you wrote here, it seems like you thought that from the very beginning, but didn't say it, and let yourself get annoyed having to "fix" things that you didn't think were necessary. If you thought that, you should've just said so and we could have gotten this merged, because do you remember this comment of mine:
This was always an option. None of the things we said were ever requirements to get this merged. The requirement to get this merged is that it works, which it did from the very beginning. We just had some suggestions that all were optional and you could've said "no, I don't think this is worth it" to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a medium-sized game, 2 turns amounted to 709 calls of the cloning code (the one that was a straight copy before and now splices and resorts) averaging 26.5µs per call.
Same turns, master, tried to do the same steps. 713 / 7.0µs. 14ms penalty distributed over a few minutes. And most of that could be shaved off by using the old code if !isPlayer().
My apologies for my role in how the communications degenerated from constructive to emotionally charged.
This is for the feature request #6884. It's a proof-of-concept for the first alternative outlined there. You can now "postpone" decisions for the current unit using key
Z
(sinceW
is taken, I usedZ
for "zzz..."). Unit for which decisions are postponed this way will reappear on this turn later when you use "Next unit".Thoughts: maybe better to move it from unit action area to the place just below "Next unit".