[BTS] Development thread

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());
			CvPlot* pBestPlot = NULL;
			for (int iPlot = 0; iPlot < NUM_DIRECTION_TYPES; iPlot++)
			{
				CvPlot* pAdjacentPlot = 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_FOOD, getTeam(), 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(this, plot()) < movesLeft() || generatePath(pAdjacentPlot) && (getPathLastNode()->m_iData2 == 1) && (getPathLastNode()->m_iData1 > 0))
						{
							iCurrentValue = iPlotValue;
							pBestPlot = pAdjacentPlot;
						}
					}
				}
			}
			if (pBestPlot != NULL)
			{
				if( gUnitLogLevel >= 2 )
				{
					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_TO, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE(), MOVE_SAFE_TERRITORY, false, false, MISSIONAI_FOUND, pBestPlot);
				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);
		}
	}
 
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                                                            */
/************************************************************************************************/
 
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.
 
So your code exactly mirrors the code in the AND svn? I have tons of beta testers using the current code, with 0 complaints.
 
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.
 
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:
 
Units almost always stick with their default unit AI. I found the only units that do regularly change are for MISSIONARY_SEA, SETTLER_SEA, and the other SEA unit AI's. Assuming your not in the midst of a full scale naval battle, the AI should perform admirably.

Feel free to prove this to yourself, and check were AI_setUnitAIType is called. It's called only 14 times in the SDK. ;)
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.

This seems like the easiest. I glanced through CvUnitAI and CvPlayer, and did a little bit of searching, and didn't come up with anything obvious. Do you know where a function is that performs some logic on a unit and decides the proper UNITAI for that unit?
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.
 
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.
 
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.
 
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) ) )
 
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.
 
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.
 
Something is still wrong with this code... take a look at the screenshots:

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                                                            */
/************************************************************************************************/





:eek: I don't think the new place is better. :lol:
 
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.
 
Top Bottom