1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

[BTS] Development thread

Discussion in 'Civ4 - Better AI' started by jdog5000, Sep 19, 2008.

  1. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    Did you test your code? :crazyeye: :lol: :cry: Not even ONE settler was founding a city, every settler from every AI player was moving around like crazy (for several rounds!) and killed by animals. :lol:

    Or is there a mistake in my code, something I must delete?

    PHP:
    void CvUnitAI::AI_settleMove()
    {
        
    PROFILE_FUNC();

        if (
    GET_PLAYER(getOwnerINLINE()).getNumCities() == 0)
        {
    /************************************************************************************************/
    /* Afforess & Fuyu                      Start      08/26/10                                       */
    /*                                                                                              */
    /* Check Adjacent Tiles for Better Spot                                                         */
    /************************************************************************************************/
            
    if (canMove())
            {
                
    //Force Recalculation
                
    plot()->setFoundValue(getOwnerINLINE(), -1);
                
    int iCurrentValue plot()->getFoundValue(getOwnerINLINE());
                
    CvPlotpBestPlot NULL;
                for (
    int iPlot 0iPlot NUM_DIRECTION_TYPESiPlot++)
                {
                    
    CvPlotpAdjacentPlot plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iPlot));
                    if (
    pAdjacentPlot != NULL)
                    {
                        
    //Don't give up coast or river
                        
    if ( (plot()->isRiver() && !pAdjacentPlot->isRiver()) || (plot()->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()) && !pAdjacentPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN())) )
                            continue;
                        
                        
    //Force Recalculation
                        
    pAdjacentPlot->setFoundValue(getOwnerINLINE(), -1);
                        
    int iPlotValue pAdjacentPlot->getFoundValue(getOwnerINLINE());

                        
    //Only settle on top of a bonus if it actually makes sense
                        
    if (pAdjacentPlot->getBonusType(NO_TEAM) != NO_BONUS)
                        {
                            if (
    pAdjacentPlot->calculateNatureYield(YIELD_FOODgetTeam(), true) > 0)
                            {
                                
    iPlotValue *= 90;
                                
    iPlotValue /= 100;
                            }
                            else
                            {
                                
    iPlotValue *= 95;
                                
    iPlotValue /= 100;
                            }
                        }

                        if (
    iPlotValue iCurrentValue)
                        {
                            
    //Can this unit reach the plot this turn? (getPathLastNode()->m_iData2 == 1)
                            //Will this unit still have movement points left to found the city the same turn? (getPathLastNode()->m_iData1 > 0))
                            
    if (pAdjacentPlot->movementCost(thisplot()) < movesLeft() || generatePath(pAdjacentPlot) && (getPathLastNode()->m_iData2 == 1) && (getPathLastNode()->m_iData1 0))
                            {
                                
    iCurrentValue iPlotValue;
                                
    pBestPlot pAdjacentPlot;
                            }
                        }
                    }
                }
                if (
    pBestPlot != NULL)
                {
                    if( 
    gUnitLogLevel >= )
                    {
                        
    logBBAI("    Settler not founding in place but moving to the better adjacent tile %d, %d"pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
                    }
                    
    getGroup()->pushMission(MISSION_MOVE_TOpBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), MOVE_SAFE_TERRITORYfalsefalseMISSIONAI_FOUNDpBestPlot);
                    return;
                }
            }
    /************************************************************************************************/
    /* Afforess & Fuyu                         END                                                    */
    /************************************************************************************************/
        
    }
        
        
    int iDanger GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 3);
        
        if (
    iDanger 0)
        {
            if ((
    plot()->getOwnerINLINE() == getOwnerINLINE()) || (iDanger 2))
            {
                
    joinGroup(NULL);
                if (
    AI_retreatToCity())
                {
                    return;
                }
                if (
    AI_safety())
                {
                    return;
                }
                
    getGroup()->pushMission(MISSION_SKIP);
            }
        }
     
  2. Afforess

    Afforess The White Wizard

    Joined:
    Jul 31, 2007
    Messages:
    12,239
    Location:
    Austin, Texas
    IDK about that code, but my current version is working correctly...

    Code:
    /************************************************************************************************/
    /* Afforess & Fuyu	                  Start      08/26/10                                       */
    /*                                                                                              */
    /* Check Adjacent Tiles for Better Spot                                                         */
    /************************************************************************************************/
    		if (!hasMoved() && canMove())
    		{
    			//Force Recalculation
    			plot()->setFoundValue(getOwnerINLINE(), -1);
    			int iCurrentValue = plot()->getFoundValue(getOwnerINLINE());
    			CvPlot* pBestPlot = NULL;
    			for (int iPlot = 0; iPlot < NUM_DIRECTION_TYPES; iPlot++)
    			{
    				CvPlot* pAdjacentPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iPlot));
    				if (pAdjacentPlot != NULL)
    				{
    					//Force Recalculation
    					pAdjacentPlot->setFoundValue(getOwnerINLINE(), -1);
    
    					int iPlotValue = pAdjacentPlot->getFoundValue(getOwnerINLINE());
    					
    					if ( pAdjacentPlot->getBonusType(NO_TEAM) != NO_BONUS)
    					{
    						iPlotValue /= 2;
    					}
    					if ( plot()->isRiver() && !pAdjacentPlot->isRiver())
    					{
    						iPlotValue /= 2;
    					}
    					if (plot()->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()) && !pAdjacentPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
    					{
    						iPlotValue /= 2;
    					}
    					if (iPlotValue > iCurrentValue)
    					{
    						//Can this unit reach the plot this turn? (getPathLastNode()->m_iData2 == 1)
    						//Will this unit still have movement points left to found the city the same turn? (getPathLastNode()->m_iData1 > 0))
    						if (generatePath(pAdjacentPlot) && (getPathLastNode()->m_iData2 == 1) && (getPathLastNode()->m_iData1 > 0))
    						{
    							iCurrentValue = iPlotValue;
    							pBestPlot = pAdjacentPlot;
    						}
    					}
    				}
    			}
    			if (pBestPlot != NULL)
    			{
    				getGroup()->pushMission(MISSION_MOVE_TO, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), MOVE_SAFE_TERRITORY, false, false, MISSIONAI_FOUND, pBestPlot);
    				return;
    			}
    		}
    /************************************************************************************************/
    /* Afforess	                     END                                                            */
    /************************************************************************************************/
    
     
  3. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    No, your code has the same problem. Or should I remove the part below the newly added code?

    This:

    PHP:
        }
        
        
    int iDanger GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 3);
        
        if (
    iDanger 0)
        {
            if ((
    plot()->getOwnerINLINE() == getOwnerINLINE()) || (iDanger 2))
            {
                
    joinGroup(NULL);
                if (
    AI_retreatToCity())
                {
                    return;
                }
                if (
    AI_safety())
                {
                    return;
                }
                
    getGroup()->pushMission(MISSION_SKIP);
            }
        } 

    Edit: just merged some code from AND, I guess, I've replaced too much code when using the code from this forum. will check out your code again.
     
  4. Afforess

    Afforess The White Wizard

    Joined:
    Jul 31, 2007
    Messages:
    12,239
    Location:
    Austin, Texas
    So your code exactly mirrors the code in the AND svn? I have tons of beta testers using the current code, with 0 complaints.
     
  5. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    Now... and works like a charm. :)
     
  6. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    I did not test my code, it was still working though - settler sometimes moves to adjacent tile and founds there same turn.
    If I had tested it however, I would have seen that sometimes this methods leads to very questionable results and would not have added it to SVN like that. I'll try a different method, at a later time.
     
  7. Munch

    Munch Benevolent Despot

    Joined:
    May 25, 2006
    Messages:
    2,081
    When I merged the AI_settleMove() code I had the same problem. Some AI players settled in place, some roamed for several turns from the middle of the continent before settling on the coast, and some never settled at all. The reason for this, I believe, is that I merged in the code AND overwrote the section:

    Code:
    		// RevDCM TODO: What makes sense for rebels here?
    		if (canFound(plot()))
    		{
    /************************************************************************************************/
    /* BETTER_BTS_AI_MOD                      10/02/09                                jdog5000      */
    /*                                                                                              */
    /* AI logging                                                                                   */
    /************************************************************************************************/
    			if( gUnitLogLevel >= 2 )
    			{
    				logBBAI("    Settler founding in place due to no cities");
    			}
    /************************************************************************************************/
    /* BETTER_BTS_AI_MOD                       END                                                  */
    /************************************************************************************************/
    
    			getGroup()->pushMission(MISSION_FOUND);
    			return;
    		}
    That'll teach me not to thoroughly read the stuff I'm merging in ... :lol:
     
  8. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    Right. That was the problem.
     
  9. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    The problem is no setUnitAIType, it starts in CvCityAI::AI_chooseUnit(UnitAITypes eUnitAI, ...) - the city chooses the best unit for a certain unitai, frequently this unit's default ai will not match the unitai it is built with.
    You are right, units stick to their AI mostly. It's not always the default one though, just the one they recieve when they are added to the production queue.

    No, it's not the easiest. I kind of thought you'd like this one since it was similar to what you suggested but it's pretty far from practical. I have no idea how to do it. As I said, it's something you'd have to write from scratch.
    Keeping track of how/for what purpose the human uses the units sounds like a much better idea to me. At least I'd know what to do there. A lot of work but at least the idea is simple.
     
  10. phungus420

    phungus420 Deity

    Joined:
    Mar 1, 2003
    Messages:
    6,296
    That's unfortunate, but that's what I gathered as well. I'm going to try to figure out how to get the hardcoded stuff in RevDCM softcoded for now, so I wol't be looking into this. It would definitely be something worth while to implement though, since AIAutoplay can't really be used now in the lategame to finish up, as the AI freaks out and just starts building tons of units, something I think would be corrected if it ended up with units that were proportional to the UnitAITypes it thinks it needs.

    Adding some logic to allow the AI to easier switch it's UnitAIs in stacks would probably be good to, especially when it is on offense, as that would allow it to easier/more logically leave defenders behind while the attack stack moves on.
     
  11. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    I said I'd try something different for capital placement, and here it is.
    The found value recalculation suggested by Afforess only returns found values where bStartingLoc is true. But I'm not looking for a starting location, I want a place to build a city, a so-called "city site". As a human player you can see them as circled tiles when you have a settler unit selected.

    code is in spoiler
    Spoiler :
    Code:
    [COLOR="Gray"]void CvUnitAI::AI_settleMove()
    {
        PROFILE_FUNC();
    
        if (GET_PLAYER(getOwnerINLINE()).getNumCities() == 0)
        {[/COLOR]
    [COLOR="Green"]/************************************************************************************************/
    /* Afforess & Fuyu                      Start      09/18/10                                       */
    /*                                                                                              */
    /* Check for Good City Sites Near Starting Location                                             */
    /************************************************************************************************/[/COLOR]
            int iGameSpeedPercent = ( (2 * GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getTrainPercent()) + GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getConstructPercent() + GC.getGameSpeedInfo(GC.getGameINLINE().getGameSpeedType()).getResearchPercent() ) / 4;
            int iMaxFoundTurn = (iGameSpeedPercent + 50) / 150; [COLOR="Green"]//quick 0, normal/epic 1, marathon 2[/COLOR]
            if ( canMove() && !GET_PLAYER(getOwnerINLINE()).AI_isPlotCitySite(plot()) && GC.getGameINLINE().getElapsedGameTurns() <= iMaxFoundTurn )
            {
                int iBestValue = 0;
                int iBestFoundTurn = 0;
                CvPlot* pBestPlot = NULL;
    
                for (int iCitySite = 0; iCitySite < GET_PLAYER(getOwnerINLINE()).AI_getNumCitySites(); iCitySite++)
                {
                    CvPlot* pCitySite = GET_PLAYER(getOwnerINLINE()).AI_getCitySite(iCitySite);
                     if (pCitySite->getArea() == getArea() || canMoveAllTerrain())
                    {
                        [COLOR="Green"]//int iPlotValue = GET_PLAYER(getOwnerINLINE()).AI_foundValue(pCitySite->getX_INLINE(), pCitySite->getY_INLINE());[/COLOR]
                        int iPlotValue = pCitySite->getFoundValue(getOwnerINLINE());
                        if (iPlotValue > iBestValue)
                        {
                            if (generatePath(pCitySite))
                            {
                                int iFoundTurn = GC.getGameINLINE().getElapsedGameTurns() + getPathLastNode()->m_iData2 - ((getPathLastNode()->m_iData1 > 0)? 1 : 0);
                                if (iFoundTurn <= iMaxFoundTurn)
                                {
                                    iPlotValue *= 100; [COLOR="Green"]//more precision[/COLOR]
                                    [COLOR="Green"]//the slower the game speed, the less penality the plotvalue gets for long walks towards it. On normal it's -18% per turn[/COLOR]
                                    iPlotValue *= 100 - std::min( 100, ( (1800/iGameSpeedPercent) * iFoundTurn ) );
                                    iPlotValue /= 100;
                                    if (iPlotValue > iBestValue)
                                    {
                                        iBestValue = iPlotValue;
                                        iBestFoundTurn = iFoundTurn;
                                        pBestPlot = pCitySite;
                                    }
                                }
                            }
                        }
                    }
                }
    
                if (pBestPlot != NULL)
                {
                    [COLOR="Green"]//Don't give up coast or river, don't settle on bonus with food[/COLOR]
                    if ( (plot()->isRiver() && !pBestPlot->isRiver())
                        || (plot()->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()) && !pBestPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
                        || (pBestPlot->getBonusType(NO_TEAM) != NO_BONUS && pBestPlot->calculateNatureYield(YIELD_FOOD, getTeam(), true) > 0) )
                    {
                        pBestPlot = NULL;
                    }
                }
    
                if (pBestPlot != NULL)
                {
                    if( gUnitLogLevel >= 2 )
                    {
                        logBBAI("    Settler not founding in place but moving %d, %d to nearby city site at %d, %d (%d turns away) with value %d)", (pBestPlot->getX_INLINE() - plot()->getX_INLINE()), (pBestPlot->getY_INLINE() - plot()->getY_INLINE()), pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), iBestFoundTurn, iBestValue);
                    }
                    getGroup()->pushMission(MISSION_MOVE_TO, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), MOVE_SAFE_TERRITORY, false, false, MISSIONAI_FOUND, pBestPlot);
                    return;
                }
            }
    [COLOR="Green"]/************************************************************************************************/
    /* Afforess & Fuyu                         END                                                    */
    /************************************************************************************************/[/COLOR]
    
            [COLOR="Gray"]// RevDCM TODO: What makes sense for rebels here?
            if (canFound(plot()))
            {[/COLOR]

    Code:
    [I](CvPlayerAI::AI_foundValue)[/I]
    [COLOR="Gray"]    else if (!bStartingLoc)
        {
            for (iI = 0; iI < GC.getNumTraitInfos(); iI++)
            {
                if (hasTrait((TraitTypes)iI))
                {
                    //Greedy founding means getting the best possible sites - fitting maximum
                    //resources into the fat cross.
                    iGreed += (GC.getTraitInfo((TraitTypes)iI).getUpkeepModifier() / 2);
                    iGreed += 20 * (GC.getTraitInfo((TraitTypes)iI).getCommerceChange(COMMERCE_CULTURE));
                }
            }[/COLOR]
    [COLOR="Green"]/********************************************************************************/
    /*     Better City Placement?                        24.07.2010            Fuyu        */
    /********************************************************************************/
    		//Fuyu: be greedier[/COLOR]
    		if (!isFoundedFirstCity())
    		{
    			[COLOR="Green"]//here I'm assuming that the capital gets a palace that has significant culture output, thus popping the final level 2 really quickly[/COLOR]
    			iGreed = std::max(iGreed, 145);
    		}
    		else
    		{
    			for (iI = 0; (iGreed < 140 && iI < GC.getNumProcessInfos()); iI++)
    			{
    				if (canMaintain((ProcessTypes)iI))
    				{
    					iGreed = std::max(iGreed, 100 + 20 * std::min(2, (GC.getProcessInfo((ProcessTypes)iI).getProductionToCommerceModifier(COMMERCE_CULTURE)/100)) );
    				}
    			}
    
    			for (iI = 0; (iGreed < 140 && iI < GC.getNumSpecialistInfos()); iI++)
    			{
    				if (isSpecialistValid((SpecialistTypes)iI))
    				{
    					iGreed = std::max(iGreed, 100 + 10 * std::min(4, (GC.getSpecialistInfo((SpecialistTypes)iI).getCommerceChange(COMMERCE_CULTURE) + getSpecialistExtraCommerce(COMMERCE_CULTURE))) );
    				}
    			}
    
    			if (iGreed < 140 && AI_isAreaAlone(pArea))
    			{
    				[COLOR="Green"]//assuming that the player actually has access to something that can make a city produce culture[/COLOR]
    				iGreed = std::max(iGreed, 125 + (5 * std::min(3, pArea->getCitiesPerPlayer(getID()) )) );
    			}
    		}
    
    		iGreed = range(iGreed, 100, 150);
    [COLOR="Green"]/********************************************************************************/
    /*     Better City Placement?                                        END             */
    /********************[/COLOR]************************************************************/
    [COLOR="Gray"]    }
        //iClaimThreshold is the culture required to pop the 2nd borders.[/COLOR]
    untested; all comments welcome.
     
  12. Afforess

    Afforess The White Wizard

    Joined:
    Jul 31, 2007
    Messages:
    12,239
    Location:
    Austin, Texas
    I'll test it out Fuyu, Nice.
     
  13. Afforess

    Afforess The White Wizard

    Joined:
    Jul 31, 2007
    Messages:
    12,239
    Location:
    Austin, Texas
    Fuyu, your code is giving me a CTD, you forgot to check if pBestPlot was already null, here:

    Code:
    if ( pBestPlot != NULL && ((plot()->isRiver() && !pBestPlot->isRiver())
                    || (plot()->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()) && !pBestPlot->isCoastalLand(GC.getMIN_WATER_SIZE_FOR_OCEAN()))
                    || (pBestPlot->getBonusType(NO_TEAM) != NO_BONUS && pBestPlot->calculateNatureYield(YIELD_FOOD, getTeam(), true) > 0) ) )
    
     
  14. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    Thx for testing. Apart from that obvious fail, does it work as intended? I wasn't sure if not founding first turn can confuse the AI, I'm just hoping it won't.
     
  15. Afforess

    Afforess The White Wizard

    Joined:
    Jul 31, 2007
    Messages:
    12,239
    Location:
    Austin, Texas
    Sure, it seems to work. Question - Does the " else if (!bStartingLoc)" block your code from AI_foundValue from ever working? I'm assuming bStartngLoc = this is the capital.
     
  16. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    I believe bStartingLoc mode is how random maps get starting locations, unless the mapscript uses its own code, it has nothing to do with actual city placement. So no, it doesn't keep my code from being used.
     
  17. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    Something is still wrong with this code... take a look at the screenshots:





    :eek: I don't think the new place is better. :lol:
     
  18. Fuyu

    Fuyu Emperor

    Joined:
    Nov 5, 2009
    Messages:
    1,225
    Location:
    Austria
    I noticed similar behavior, which is why I changed it, see post #191.
     
  19. Cybah

    Cybah Emperor

    Joined:
    Jun 22, 2007
    Messages:
    1,480
    did you test it?

    Nevermind, did not read the following posts. ;)
     
  20. Glovz

    Glovz Chieftain

    Joined:
    Jun 17, 2006
    Messages:
    13
    Just out of curiousity, has anyone taken over from jdog towards maintaining and releasing new versions of batterai?

    There has been a lot of interesting discussion and code snipets since his departure, would be nice to see a new version of betterai released or at least an announcement of who will be taking over.
     

Share This Page