Infinite Loop BTS 1.01

Code:
bool CyPlayer::isTurnActive()
{
	return m_pPlayer ? m_pPlayer->isTurnActive() : false;
}
Code:
            [COLOR="Green"]### ### ### civjo: finish "Infinite Loop" - begin ### ### ###[/COLOR]

            [COLOR="Green"]### Shift + 0: force all units of all players to end turn[/COLOR]
            if( theKey==int(InputTypes.KB_0) and self.customEM.bShift ):
                for iPlayer in range(0, game.countCivPlayersEverAlive()):
                    pPlayer = gc.getPlayer(iPlayer)
                    if not pPlayer.isNone() [B]and pPlayer.isTurnActive()[/B]:
                        iNumUnits = pPlayer.getNumUnits()
                        print(   "[COLOR="Gray"]~~~~~~~~~~~~~~~ %s (Player %s) %s units[/COLOR]" %
                                 ( gc.getCivilizationInfo(pPlayer.getCivilizationType()).getDescription(), iPlayer, iNumUnits )   )

                        iUnit = 0
                        iCountUnit = 0
                        while iCountUnit < iNumUnits:
                            pUnit = pPlayer.getUnit(iUnit)
                            if not pUnit.isNone():
                                pUnit.finishMoves()
                                print(   "[COLOR="Gray"]%s~ [%s,%s] %s %s-%s: finishMoves()[/COLOR]" %
                                         ( iCountUnit, pUnit.getX(), pUnit.getY(), pUnit.getName(), pUnit.getGroupID(), pUnit.getID() )   )
                                iCountUnit += 1
                            iUnit += 1
                        [B]break[/B]

            [COLOR="Green"]### ### ### civjo: finish "Infinite Loop" - end ### ### ###[/COLOR]
This should actually work.
 
Fuyu, could this code be merged into Tester.py and a new version uploaded to this thread? I'd be happy to try it out.

(If not, I'll play around with the original.)

Thanks everyone, I'm looking forward to reinstalling Civ IV on this system and trying to play through this!
 
Not Quite.

The Game Loops through of all of the units several times each turn, trying to determine if they should move (land units move before air units...etc, something like that), but if it get's an infinite loop it keeps looping through units, finding a unit(s) who have NOT used up their moves, and tries to make them move. But the unit fails to move for whatever reason, and it loops again. I just decided to keep track of the last unit that was looped over and updated, and count the number times it passed. If it looped over the unit too many times (my number was arbitrary), it reset the units moves and moved on.

Seemed to fix all the infinite loop issues. Honestly, when I came up with the fix:

A. Everything I knew about programming had been self-taught. (Not the case anymore)
B. I was going to quit modding in a few weeks.
C. I wasn't inclined to learn exactly how SelectionGroups worked, the whole thing seemed a convoluted mess.
D. And my solution (miraculously) worked, and consequences be damned.

If you find a better solution that covers all the cases, let me know.

@Afforess - I have seen something similar with the version of this hack which exists in the original function: CvSelectionGroupAI::AI_update(). This hack checks the function readyToMove() up to 100 times for the group, and if the unit still says it can move, calls finishMoves(). In that case it was triggered by trying to get workers to perform impossible actions - e.g. building improvements they couldn't on their current plot. So, even the original developers didn't come up with a more elegant workaround!

I agree about the selection group and mission code being a convoluted mess. In the mod I am writing, I discovered that workers and the like wake up each turn and instantly delete their mission and ask for a new one, even they have a bunch of missions queued and there is no immediate threat. (The reason I think is so settlers check for a new mission each turn in case their destination needs to change). It took several hours of debugging to figure that one out.
 
@Fuyu
I like the solution because it is only active when it's needed.
I changed my mind about C++/Python and agree now with you, this also makes the solution not only compatible with pure BBAI, but also more easily compatible with MODs using BBAI sources ... (therefore I chose Tester.py and not CvEventManager.py too)

Thank you for providing the function name, isTurnActive. It saved me some time.

Here the new Tester version (complete documentation ;)):

### Alt + [0-5]: force all (remaining) units of Domain # of the currently active player to end turn. # is € of {0...5}

### Alt + 8: show all units of the currently active player in PythonDbg.log
### Alt + 9: force all (remaining) units of the currently active player to end turn

### Shift + [0-2]: force current unit to end turn & increment iUnitIndex to next unit of domain type # (0 naval, 1 air, 2 land)

### Shift + 4: decrement iUnitIndex--
### Shift + 5: reset iUnitIndex
### Shift + 6: increment iUnitIndex++
### Shift + 7: increment iUnitIndex by 10

### Shift + 8: show current unit
### Shift + 9: force current unit to end turn

BTW, bob2.sav can be reanimated by finishing ANY _ONE_ of the following units:
Spoiler :
38~ [40,30] Grenadier (True)24297619-2891814: 0 ----
76~ [40,30] Grenadier (False)18317536-3358796: 0 g ----
86~ [40,30] Knight (False)18317536-1843286: 0 g ----
111~ [40,30] Cannon (False)18317536-4817007: 0 g ----
179~ [40,30] Musketman (False)18317536-4120755: 0 g ----
181~ [40,30] Cannon (True)18317536-4784309: 0 G ----
 

Attachments

  • Tester.zip
    2.4 KB · Views: 266
@Afforess - I have seen something similar with the version of this hack which exists in the original function: CvSelectionGroupAI::AI_update(). This hack checks the function readyToMove() up to 100 times for the group, and if the unit still says it can move, calls finishMoves(). In that case it was triggered by trying to get workers to perform impossible actions - e.g. building improvements they couldn't on their current plot. So, even the original developers didn't come up with a more elegant workaround!

I spent 8 straight hours of one fine saturday figuring out my solution. ;)
 
I had added Afforess' code to my mod, but it was never being hit, so I removed it. I did hit the other loop code mentioned by Carboniferous fairly regularly, but at this point, I seem to have the infinite loop issues handled.

Aside from the grouping and ungrouping cause I mentioned earlier in this thread, another thing that can result in infinite loops is not pushing a MISSION_SKIP mission when you have a unit that isn't doing anything. Making sure that every unit that isn't already in the middle of a mission has some sort of mission every turn (even if it's just a SKIP) will go a long way towards eliminating the infinite loop issue.
 
How do units in the state you describe react to a given finishMoves() command? Does it result in a (temporary) 'skip mission', so that this case is already handled? Or is additional action needed?

when you have a unit that isn't doing anything
is this the status jdog5000 called in his original tester.py "stranded units"?
 
How do units in the state you describe react to a given finishMoves() command? Does it result in a (temporary) 'skip mission', so that this case is already handled? Or is additional action needed?

No idea.

is this the status jdog5000 called in his original tester.py "stranded units"?

No. Stranded is a flag used for units that are either on another landmass or otherwise blocked so that they can't reach any of the player's cities. This is tied in with naval movement so that transport ships will try and pick up units flagged as Stranded.

What I mean by a unit that isn't doing anything, is that sometimes a unit will pass through it's movement function and not meet any of the criteria for finding something useful to do, so they'll just stay where they are doing nothing (city defense units are one common example).

From what I can tell, every unit needs a mission every turn. Even if that mission is 'do nothing'. Otherwise you'll end up with the infinite loops.
 
a unit that isn't doing anything
Do you have a save file with such an unit causing (soon) an infinite loop??
If it is with your MOD, it can be easily tested for my question with the old tester.py: (shift 0) ... and it still loops further (or not!!) ...
If it is plain BBAI, I would be more than happy, if you could post it here.

---> If anybody has a save file with infinite features:D ... I am interested!

[edit: post#33 old tester.py: shift0, post#45 new tester.py: alt9]
 
I've just run into this problem. I'm wondering exactly what I need to do with the "fix". Do I replaced the code in the function with the fix code? Or do I place it before or after the code in the function?

I've found the cpp file with this function. Do I need to use that to recompile a dll? In which case, which dll do I recompile, and how do I do it? I'm not a complete coding noob, so you can point me in the right direction and I will probably be able to figure it out.
 
@Llewen
I can't promise you a 100% solution but you'd definitely increase the chances of getting this fixed if you upload your savegame (preferably short before the infinite loop event).
 
Well the mod I was playing, Fall Further isn't being developed anymore. I've since switched to RiFE which is a mod mod of Fall Further and Fall from Heaven II. As for that specific game, I just went into the world builder mode and deleted groups of countries, then countries, then groups of units, then units, until I found the problem unit. I ended up having to do a similar thing twice more before I got through the game. But even though it took an hour or two each time, it was still better than losing the dozens of hours I had already spent on that particular game entirely. And once I knew what I was looking for, it was a bit easier (in this case it was leaders that were the problem).
 
I suppose, you don't have this savegame anymore ... if you have it still laying around you could go a more convenient way:

1. Download tester.py from post#45
2. Look for the Tester.py file in your Better BTS AI Installation, default would be:
C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Better BTS AI\Assets\Python\Development
3. Make a save copy and then overwrite it with the new downloaded Tester.py version.
4. Alt + [0-2] gives you the Domain (0 naval, 1 air, 2 land) of the conflicting unit(s) of the country in question, reload savegame
5. repeatedly Shift + [0-2] gives you a usable unit (look in PythonDbg.log), reload savegame
6. Shift + 4,5,6,7,9 can be used to just finish _ONE_ single unit

You'll need to reload the savegame 2 times and about 5 minutes (step 4-6).
 
Thanks, I'll try to remember that for next time if it happens again. :)
 
"next time if it happens again"
This is another part of the problem! :D 'It' really happens way too seldom!! ;)

To be clear: "just finish _ONE_ single unit" means not delete unit, but 'Force _ONE_ single unit to End Turn' btw.
(in the one 'infinite savegame' I have, ALL usable units have only 0 moving points left anyway :eek:)

PS
Even MODs which aren't based on BBAI, ie. don't have the Tester.py file, can solve this kind of 'hanging games' by porting the relevant code fragment eg. into the CvEventManager.
 
I'd just say to random folks looking in here: Don't let yourself be discouraged from installing this mod (as I almost was) out of fear that your games may become broken. I have finished a lot of games already and did not yet run into this problem. Even if it should happen eventually, the vastly improved AI is still worth it.
 
True, BBAI is a tremendous improvement, and I never met the beast myself, too. (Thank you, rramstad for your savegame)

Just in case the rare event should happen, it is enough to read posts #45 and #54 ...
Less than a quarter hour later you are surely playing again.
 
@Fuyu

I changed my mind about C++/Python and agree now with you, this also makes the solution not only compatible with pure BBAI, but also more easily compatible with MODs using BBAI sources ... (therefore I chose Tester.py and not CvEventManager.py too)

Thank you for providing the function name, isTurnActive. It saved me some time.

Here the new Tester version (complete documentation ;)):

### Alt + [0-5]: force all (remaining) units of Domain # of the currently active player to end turn. # is € of {0...5}

### Alt + 8: show all units of the currently active player in PythonDbg.log
### Alt + 9: force all (remaining) units of the currently active player to end turn

### Shift + [0-2]: force current unit to end turn & increment iUnitIndex to next unit of domain type # (0 naval, 1 air, 2 land)

### Shift + 4: decrement iUnitIndex--
### Shift + 5: reset iUnitIndex
### Shift + 6: increment iUnitIndex++
### Shift + 7: increment iUnitIndex by 10

### Shift + 8: show current unit
### Shift + 9: force current unit to end turn

I've been playing with this today - thanks a lot to civjo for providing this Python tool :goodjob:

However, it took me awhile to figure out that the key numbers to use should not be the ones from the numeric keypad but those from the main keyboard. Which is a problem when, like me, you're using a French keyboard with all numbers accessible only with the Shift key. In other words, I could not get 'Shift + Shift number' to work etc.

The workaround is to use Ctrl instead of Shift, but still the combination of Ctrl+Shift+number is a bit difficult, so I changed all numbers to F1, F2, etc.

Since civjo is using a formula like iDomain = theKey - 2 (to arrive to Domain = 0 = Sea), I had to change it to iDomain = theKey - 59 (Key F1 is 59, F2 is 60, etc).

Hope this helps somebody someday... :)
 
I suppose, you don't have this savegame anymore ... if you have it still laying around you could go a more convenient way:

1. Download tester.py from post#45
2. Look for the Tester.py file in your Better BTS AI Installation, default would be:
C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Better BTS AI\Assets\Python\Development
3. Make a save copy and then overwrite it with the new downloaded Tester.py version.
4. Alt + [0-2] gives you the Domain (0 naval, 1 air, 2 land) of the conflicting unit(s) of the country in question, reload savegame
5. repeatedly Shift + [0-2] gives you a usable unit (look in PythonDbg.log), reload savegame
6. Shift + 4,5,6,7,9 can be used to just finish _ONE_ single unit

You'll need to reload the savegame 2 times and about 5 minutes (step 4-6).

Anyone still active here? Giving this mod a try and just read up on this. I literally hate it when I have to abandon games halfway due to bugs so I wanna have this option ready. I know nothing about programming though so I understand these steps as far as step 3. The other ones apparently you need to do in Debug mode? Or in world builder when some AI's units get stuck in a decision loop?

If anyone could explain the last steps 3 for non programming dummies I would really appreciate it :D
 
Top Bottom