-
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
Initial Civ Personality implementation #10939
Conversation
Some comments since they were asked for: What are the reasons for having a hardcoded preferred policy? If we improve our policy picker to convert each policy branch to its stats, we could maybe use the other personality fields instead. It would take a lot more work, but we need to find a way to rank uniques anyway if there isn't one I think. Note that I have limited knowledge of how A question that we are likely to come across in the future: Is there a way for us to make the Civ personalities changeable during the game? It might be an interesting way for the modders to interact with the AI through fancy building events and policies.
My idea would be that players always have a personality and can customize the basics in the AutoPlay tab or something. We will almost always need to do some work of automation for the player so this may be able to give them more control again. For a disclaimer, I haven't looked into ConstructionAutomation very much yet, but I plan to look into it after working on the Worker AI. |
Just a way of working with what was already there. Currently, it's already hardcoded to work based on the preferred victory type, all this would do is add in the preferred policy alongside it. And I can see a mod creator wanting an AI to focus on a policy branch over working for what's best for those stats
Actually something I was thinking about too. Actually, that also leans into my questions about possible uniques going forwards, but that should probably be in another PR. It could slot into |
After taking a break from this PR, I'll try to at least get it to a ready level. Minimum, this is to split some extra elements of the AI's behavior I want to look at into its own PR. Question, btw: why was the old culture AI adverse to building defensive stuff? I can maybe, possibly, not really understand why it didn't attack, but this has nothing to do with offense Interesting strange thing: |
core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt
Outdated
Show resolved
Hide resolved
} | ||
if (civInfo.getHappiness() > 5 && cityState.cityStateFunctions.canProvideStat(Stat.Food)) { | ||
value += 5 | ||
value += (civInfo.getPersonality()[PersonalityValue.Food]).toInt() - 5 |
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.
Sounds like getPersonalityValue(personality) one liner, you have this 5 time
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.
I'm down for getting rid of these parenthesis, but now that I look at it, wouldn't using .get
be better than that? Actually, on second look, I don't need the parenthesis in the first place
@@ -85,15 +86,23 @@ object Automation { | |||
} else { | |||
if (city.civ.gold < 0 && city.civ.stats.statsForNextTurn.gold <= 0) | |||
yieldStats.gold *= 2 // We have a global problem | |||
yieldStats.gold * city.civ.getPersonality().scaledFocus(PersonalityValue.Gold) |
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.
*=
, I assume, also for the rest of this function
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.
A kotlin quirk that throwing away the results of an expression does not get a warning.
(Is city.civ.getPersonality() expensive? val
it if so - this is automation and runs middlish-often I would expect)
Also - this is rankStatsForCityWork, while worker automation is controlled by Automation.rankStatsValue - see also #11082 - you have considered it I assume?
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.
Most of the cost of city.civ.getPersonality is looking up the ruleset... but, hmm, yeah, probably should be a val
Haven't considered worker automation (I mostly looked at where the existing victory focus code was, none is there to my knowledge. Would rather work on it as a separate PR
core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt
Outdated
Show resolved
Hide resolved
if (cityState.cityStateFunctions.canProvideStat(Stat.Culture)) { | ||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) | ||
value += 10 | ||
value += civPersonality[PersonalityValue.Culture].toInt() - 5 |
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.
Can we change these civPersonality[PersonalityValue.Culture].toInt() - 5
lines to use something like civPersonality.getNormalizedValue(range = 5)
to be more readable. The important thing is the range parameter shows here what values it will give out for readability.
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.
I know Yairm mentioned something similar, but my issue is kinda simple: wouldn't that read as civPersonality.getNormalizedValue(PersonalityValue.Culture, range = 5)
. Which means
- There's no real shortening of lines, nor any real simplification. If anything, it makes lines longer
- It's only better for readability if you know what a "nornalizedValue" should mean in this case. Sure, kdocs, but I'm pretty sure we could address questions here with kdocs as well
- You're suggesting a more expensive version of
.get
or(.scaledFocus * 5).toInt()
... For just this? - Why not just create an overload for
scaledFocus
? I'm all for doing this, but then my question is "why are we using that as a replacement for square brackets/.get"
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.
Why not just create an overload for scaledFocus? I'm all for doing this, but then my question is "why are we using that as a replacement for square brackets/.get"
The main goal is abstraction, encapsulation and lower coupling. If I am accessing a certain civPersonality in some other file, the reader shouldn't have to know about how the actual personality data structure is implemented. (ie: is it stored in a hashmap and what is the range of the personality values?) It doesn't have to be shorter and the performance cost here is not an issue.
Also (range = 5)
and getNormalizedValue
were just a suggestion.
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.
Ok, but I'm not sure where the benefits of encapsulation is here, especially since there already is a getter/setter that already handles things pretty cleanly. Like, the code will always have to read personality.getterFunction(PersonalityValue, whateverArgument)
, with the toInt
likely being ideal as well
Again, I can totally seeing adjusting scaledFocus
to cover what you want. But the only thing your suggestion addresses assuming the scale is 0-10 vs any other set of values... Which shouldn't matter here anyways because it's still going to be communicated to the modder as 0-10, and this usecase in particular is still using specifically that assumption
That
As you can see abstracting the scale of those values didn't even cross my mind. As they are meant to be mostly gleaned from public sources on the original, documentation could be enough. Having a public API that normalizes 0..10 to, e.g., -1..+1 and then have AI code that maps those back to 0..10 weights is academical standard, but also a bit silly, compromises are allowed imho. |
Not at computer, some of this message is academically above my head, especially without a computer to get a feel for exactly what you mean (I'm, like 90% self taught, 10% learned the basics of c++ in highschool, only learned java by tearing apart a friend's project), but this statement I 100% can comment on. On paper this sounds right, in practice... How do you import this?
If the insistence is not having multiple classes/enum s in the same file, then fine (though I don't think it feels particularly worthwhile here besides looking at Studio's interface). But renaming it to that in particular would be pretty awful Better suggestion is
I'd be fine with removing that and making it immutable, though imo seems a bit early to rule out |
Was just ideas. Yes, you got it - import Personality and use Personality.Value. Not a new standard - I do it, Gdx does it. You're right about the naming - Gdx does it like: Label.LabelStyle... And not only unknowing developers might ruin that, Studio's newer aggressive autoimport too with default settings (heck it imports ImageGetter.ruleset unasked if you start to use the name without declaring the parameter first)... As I said, just ideas. But "bit early to rule out a mutable Personality" sounds good, like - changing personality = Education = you can teach Gandhi to be nice and not nuke everybody... 💣 💥 👼 |
Like StateForConditionals.IgnoreConditionals? I guess that works, but Idk. Pretty much everything else here isn't in the primary constructor which seems inconsistent? Idk, maybe I'm worrying about formatting a bit too much here, just seems like it'll look odd
Now that... that is possible. I wonder if I need a |
Oh, huh. Studio's aggressive auto imports actually imports like this. Neat Still not sure addressing a quirk in Studio is reason enough that this is worthwhile, but I guess my import concerns isn't as valid as I thought |
Not quite. StateForConditionals is a data class, immutable, never read from json. I mean class Demo private constructor (val isSpecialDefault: Boolean = false) {
var someField = ""
var anotherField = 0
companion object {
val default = Demo(true)
}
} Now
Look different, which could be the point. Tons of possible permutations, up to using a class hierarchy as "type" marker... I just thought about setting apart visually that some fields are json-controlled others not, and since there's no |
I think there have been a lot of discussions about the future, but I want to know if I can merge this PR :) @SomeTroglodyte - it's hard to separate your "maybe think of" comments from "I think this needs to be different" comments. |
No! If in doubt, always assume my comments are food for thought only. |
What is the purpose of neuteralPersonality? I don't understand yet what Civs this should apply to. It seems to break a few things right now and maybe more in the future, but I want to know if there was a purpose for incorporating it. |
Things being broken is a bug. See #11158. neutralPersonality otherwise is supposed to detect a personality isn't implemented and use assumptions from before this PR |
The is a PR based on combining elements of #10791 and #10717 (sorry @MioBestWaifu, but I kinda got impatient) as well as somewhat closely working with Victory.Focus. There are some obvious questions, notes, and other aspects to consider from this PR
ConstructionAutomation
adds modifiers to buildings based on whether they provide a stat rather rather how much of the stat it provides, which seems odd to meI would definitely like it if I can get help from @tuvus on what should be affected by this PR and where things should split into separate PRs/what should be adjusted regarding what already exist for Victory.Focus