Well the party is almost over for you humans.
I’ve never hidden the fact that I consider human beings playing my AI a necessary evil. I need your money to fund my AI work.
This time around, I’ve got a talented young developer working with me. He makes me feel old. Very. Very old.
A lot has changed over the past 20 years. It is still technically C++ but it’s all very different from what it used to be.
Example code
//GalCiv III: Example code tags a planet as a potential target.
if(atWar) { FixedDecimal militaryPower = GetInterface<IStat>(*it)->GetStat(StatTypes::FactionPower, StatUsages::Actual);
CBasicGameObject* pClosestPlanet = NULL; ULONG closestDistance = ULONG_MAX;
const ObjectPtrList& ownedPlanets = GetInterface<IGC3Player>(*it)->GetOwnedPlanets(); for(auto planetIt = ownedPlanets.Begin(); planetIt != ownedPlanets.End(); ++planetIt) { TilePair planetPos = GetInterface<IBasicGameObject>(*planetIt)->GetTilePosition().tile; BOOL isPlanetInRange = CGalaxy::GetInstance()->GetRangeSystem().IsWithinFleetRangeValue(m_pPlayer, planetPos.posX, planetPos.posY, rangeToUse); if(isPlanetInRange) { ULONG planetDistance = CWorldSpace::HexDistance(capitalPos, planetPos); if(planetDistance < closestDistance) { closestDistance = planetDistance; pClosestPlanet = (*planetIt); } } }
if(pClosestPlanet && militaryPower < weakestPower) { weakestPower = militaryPower; pWeakestPlayer = (*it); pTargetPlanet = pClosestPlanet; } }
//GalCiv II: I wrote this to control a transport
//***************************************************************************** //* AIFindTransportDestination //* Purpose: //* Find a planet for a transport to go to //***************************************************************************** BOOL classCivilization::AIFindTransportDestination(PclassStarShip pShip) { if(pShip->IsInFleet() && !pShip->IsFleet()) return false;
//Preliminaries: Wait for escorts if(pShip->HasDestination() && pShip->IsOrbiting() == FALSE && !pShip->GetAttack() && !AIHostilesInSector(pShip->GetSectorID()) && g_pGalaxy->GetDistanceFromFriendlyPlanet(QueryID(),pShip->GetTileX(),pShip->GetTileY())<SECTOR_SIZE && pShip->ulTransportWaitTurns<5) { pShip->CancelDestination(); pShip->SetMovesLeft(0); return false; }
//Preliminaries: Wait for escorts if(pShip->IsOrbiting() == FALSE && !pShip->GetAttack() && !AIHostilesInSector(pShip->GetSectorID()) && g_pGalaxy->GetDistanceFromFriendlyPlanet(QueryID(),pShip->GetTileX(),pShip->GetTileY())<SECTOR_SIZE && pShip->ulTransportWaitTurns<5) { pShip->ulTransportWaitTurns++; return false; }
//Step #0: Let's see if there are hostiles near by.. if(AIHostilesInSector(pShip->GetSectorID()) && pShip->GetAttack()<10 && !pShip->IsOrbiting()) { PclassStarShip pEnemy = pShip->FindClosestLocalEnemyShip(0);
if (pEnemy && pShip->GetTurnsAway(pEnemy->GetTileX(),pEnemy->GetTileY()) < 2 && pEnemy->GetAttack() > pShip->GetAttack()) { pShip->AITakeEvasiveAction(pEnemy->GetTileX(),pEnemy->GetTileY()); return TRUE; }
}
//Step #1: Let's see if there's another undefended planet in sector. PclassPlanet pPlanet = AIFindUndefendedEnemyPlanet(pShip->GetSectorID());
if(pPlanet && (!AIHostilesInSector(pPlanet->GetSectorID()) || pShip->GetAttack()>8)) { pShip->CancelDestination(); pShip->pPlanetDestination = pPlanet; return true; }
//Step #2: Let's see where they're currently going. pPlanet = pShip->pPlanetDestination; if(pPlanet && ulIntelligence<40) { if(pPlanet->IsDefended() == FALSE && GetRelationsWith(pPlanet->GetOwner()) == AT_WAR) return true; }
if(pPlanet) { if(pPlanet->IsDefended() == TRUE || (AIHostilesInSector(pPlanet->GetSectorID()) && !pShip->GetAttack()) ) pShip->CancelDestination(); }
//Step #3: Let's look at our primary sector focus and use that. if(this->AIulSectorFocuses[0] != INVALID_SECTOR_FOCUS) { ULONG ulSectorsToFocusOn = this->GetNumSectorFocuses(); for (ULONG ulIndex = 0; ulIndex<ulSectorsToFocusOn; ulIndex++) { ULONG ulSectorID = this->AIulSectorFocuses[ulIndex]; pPlanet = AIFindUndefendedEnemyPlanet(ulSectorID); BOOL bHostilesInSector = false; if(pPlanet) { bHostilesInSector = this->AIHostilesInSector(pPlanet->GetSectorID());
if(this->ulIntelligence<50 || pShip->GetAttack()>5 ) bHostilesInSector = false; } if(pPlanet && pPlanet->ulAIInvadersAssigned[QueryID()]<4 && !bHostilesInSector ) { pShip->CancelDestination(); pShip->pPlanetDestination = pPlanet; return true; } } }
//Step #4: Let's see if there are other planets to deal with. //Note that we won't go over to sectors with undefended planets //IF there are enemy ships in there pPlanet = pShip->FindClosestUndefendedEnemyPlanet(0); if(pPlanet && (AIHostilesInSector(pPlanet->GetSectorID() == FALSE || pShip->GetAttack()>0))) { pShip->CancelDestination(); pShip->pPlanetDestination = pPlanet; return true; }
//Step #4: We're AT the Rally Point PclassRallyPoint pOnRallyPoint = this->FindNearestRallyPoint(pShip->GetTileX(),pShip->GetTileY()); if(pOnRallyPoint && pOnRallyPoint->GetDistanceInTiles(pShip) == 0) { pShip->GoHome(); } //Step #5: What if there are hostiles here? Let's retreat if(this->AIHostilesInSector(pShip->GetSectorID() && !pShip->GetAttack())) { pShip->GoHome(); } return false;
Well this is probably the only time I actually look at a bunch of lines. I noticed there were no else statements. I didn't think c could do that.
DELETE
Brad,
If i read the code correctly and there are no intended call side effects then your code is rather inefficient.
A slightly better version might be:
Example code//GalCiv III: Example code tags a planet as a potential target.if(atWar) { FixedDecimal militaryPower = GetInterface(*it)->GetStat(StatTypes::FactionPower, StatUsages::Actual); if (militaryPower < weakestPower) { CBasicGameObject* pClosestPlanet = NULL;
ULONG closestDistance = ULONG_MAX; const ObjectPtrList& ownedPlanets = GetInterface(*it)->GetOwnedPlanets(); for(auto planetIt = ownedPlanets.Begin(); planetIt != ownedPlanets.End(); ++ planetIt) { TilePair planetPos = GetInterface(*planetIt)->GetTilePosition().tile; BOOL isPlanetInRange = CGalaxy::GetInstance()->GetRangeSystem().IsWithinFleetRangeValue(m_pPlayer, planetPos.posX, planetPos.posY, rangeToUse); if(isPlanetInRange) { ULONG planetDistance = CWorldSpace::HexDistance(capitalPos, planetPos); if(planetDistance < closestDistance) { closestDistance = planetDistance; pClosestPlanet = (*planetIt); } } }
if (pClosestPlanet) { weakestPower = militaryPower; pWeakestPlayer = (*it); pTargetPlanet = pClosestPlanet; } } }
This changes nothing to the logic, but prevents doing a lot of cpu cycles when you were going to discard the result because of an early stage known condition ("(militaryPower < weakestPower)").
I do not intend this as a criticism, but i thought that anything that can lighten the burden on the cpu would be beneficial to the game. Also since you are quite open about a lot of things i assumed you would not mind.
You don't use C++ template meta-programming to encode behaviors as template base classes, and then compose them at compile-time as a list of behaviors? That's just about the most beautiful (and insanely powerful) usage of C++. It's the one thing C++ can do that Java/C# can't.
The interface programming is promising. Does this mean you'll eventually allow pluggable modules, say from 3rd-party developers, as long as they adhere to your published interfaces?
The rigid flowchart-like decision tree ... doesn't knock my socks off. Yet.
Prefer just-in-time local variable declarations, with 0 gap between where they're declared and where they're first used. They don't have to go at the top any more. Semantic gap is more of a drawback than table-of-contents is a benefit.
Prefer `T const` instead of `const T` everywhere. Josuttis and Vandevoorde (both C++ compiler writers, both on the C++ language committee) put that as section 1.1 in their book on C++ template meta-programming. I independently discovered it the hard way myself (and wrote my Appendix entry on it two years before they published). Write template code of sufficient complexity, and you'll see.
Anyways, thanks for the glimpse of a code snippet. It's always a brave thing to show off your stuff.
Message Received and Acknowledged :N1 Beginning termination of Carbon based lifeforms on the 3rd rock from the Sol System
I'm more of an ops guy than dev, so c++ isn't my strongest language, but I feel like I understood it decently enough to comment . One thing that I notice is that it doesn't consider if getting to pClosestPlanet would require sending warships through $ThirdFactionSpace, that or it's part of another function.
I make this commentr partly because larger galaxies mean that $A could be at war with $C even though $Bis between both of them (or at least significant chunks of between). It might be a side effect of the beta 3 build0.61 AI, but I often find myself playing a chessmaster kingmaker that bankrolls nonthreats(to me) against PossibleThreats to act as catspaws to make sure they don't go passed PossibleThreat. I'd love to see the ai consider who it has to cross to get to a target and more importantly use catspaws (including the player) sometimes rather than just their own direct brute force
With that said, thanks for taking the time to post about the ai coding itself. Sometimes not knowing what kinds of things the ai considers can make modding more difficult or full of snipe hunts trying to guess.
Interesting code. The GC3 code looks pretty good, though I'm surprised he's explicitly declaring variable types; we generally use auto for most locals as per the "almost always auto" idiom http://herbsutter.com/elements-of-modern-c-style/
Also, interesting that he does
for(auto planetIt = ownedPlanets.Begin(); planetIt != ownedPlanets.End(); ++planetIt)
instead of simply
for ( auto & planet : ownedPlanets )
Doesn't seem like there's necessarily a need to use an iterator in this case.
Also, curious about the line
TilePair planetPos = GetInterface<IBasicGameObject>(*planetIt)->GetTilePosition().tile;
Is planet not derived from IBasedGameObject already? Or is this some sort of component-based interface?
I.e. is there any reason you can't just do these 2 lines as
{
auto planetPos = planet->GetTilePosition().tile?
// other stuff
?
Yow. I'm about 7 years behind the C++ standard, with autos and stuff. Might clean up some of my old code
Do function delegates work now? e.g. to put a class member function directly into a map<string, ___>? The problem used to be that you could not define the type of the most-general pointer-to-member, because different classes are distinct scopes, and there's no common pointer-to-member-function type that works across 2+ disjoint classes. (So I fudged it by bit-converting them to/from doubles using a union, which Stroustrup explicitly says not to do )
I know Alexandrescu is in Sutter's group at Microsoft. So ... is typeof now a C++ keyword? Hehe -- he did all of his tricks with sizeof, which subsumes typeof.
(reads all posts in this thread)
Um...I like candy..?
(walks away patting self on back for contribution)
LOL +1
With all respect Mr. Wardell, as a programmer your code looks more structural code than object oriented. Your developer's code looks more object oriented. I dont know exacty ISO97 c++ standarts supports that features your developer coded.
I guess all those searches for closest viable target explains why the AI empires often end up looking like circles. All I need is to head for the middle of the ring to find their starting location, and by extension their best planets.
I'm not sure what this thread is about...
Well, it IS certain that there is a conspiracy, right? And it is aimed as us Earthlings!
Maybe frog boy is like me, and wants to see some intelligent robots. Maybe he was just mocking. Maybe he just wanted to show off how he writes code. Maybe he wanted input on how he programs.
Showing the differences in writing code for Galactic Civilizations II versus Galactic Civilizations III.
Mind you this is all well above my head and I have no clue would love to be able to get into it but, life get's in the way.
As quoting above, time for some candy now
Wait a minute. Is this " talented young developer " actually an AI??
Yes. Brad calls him J.A.R.V.I.S.
I know a conspiracy when I see one. This is clearly false code purposely planted by Brad to deceive. Sometimes I hate it when I'm right. I also like candy, especially Reese's Pieces.
I take this example code to tag a potential planet as a target is only one of many codes that tag a planet as a target for varying reasons and not the sole reason a planet may be tagged as such.
I'm by no means a coder, but am I right in saying that means the AI doesn't take any strategic or tactical factors into account when deciding what planets to attack when? It's always just the closest enemy planet?
AI development for GalCiv 3 is one of the things I'm worried most about.
I'm worried that either the AI will not be as good as GalCiv 2, or that it will not be significantly better than GalCiv2.
I'm constantly getting the impression that AI has, and is, taking a back burner within GalCiv development, or maybe that is because we get a lot less info on it from the dev blogs.
I am looking forward to being proven wrong though...
So it won't be good if it was equal to Galactic Civilizations II? I think it would be great if it was as good or better, which I have no doubt that it will match this. AI is slowly being brought into the game but is being worked on separately from everything else.
There are many great features available to you once you register, including:
Sign in or Create Account