Non-infinite loops freezing the program.

Hi,

I know that if you set the Boolean condition for any kind of loop or any method of recursion (aren't loops and recursion the same thing?) to true, always, then it will cause an infinite loop, and thus a runtime error. It is a logic error that causes the loop condition to remain true, and I fully understand that.

However, what I don't understand is why if I have more than 10 nested loops (each doing a specific thing that is checked in a specific order) and it produces more than 100 messages on the screen, that the entire program freezes, much like if I had created an infinite loop. I have a specific gameplay purpose in mind for creating these 100+ messages (and I use it for debugging purposes to find "edge cases" where my logic is failing in spectacular ways), which is why I'm asking this here question. I've found that if I reduce the number of loops OR reduce the number of messages outputted onto the screen, I can use the program without it freezing, but I kind of need those large numbers of messages in order to produce the correct experience for the player.

I'm just wondering why it is that, I have a 6-core 3.5ghz machine, with 16 gb ddr4 ram, a 512 ssd, and a titan x 12gb graphics card, how it is that these calculations are freezing up my machine. With my understanding of C++, each character should be using between 1 byte to 8 bytes... and so I'd expect that my game logic is using up maybe a couple 100 megabytes of ram, but surely not several gb of ram to perform these calculations.

The odd thing is that these game logics are able to function correctly when they produce fewer messages and the exact same code then fails when its over 100 messages; what I mean is that you have an attribute that gets increased by +1 for certain activities and this attribute is what determines how many messages will be produced. So when this attribute is in the range of 400-500, and this code is producing less than 100 messages, everything is fine, but the moment this attribute gets above 500, any time the code is run it freezes the program, attempting to produce more than 100 messages.

Why is it doing this?


loops and recursion are not the same thing:

loops:

is merely jumping from one line to another line (as a 'loop' you're jumping from the end line of something back to its beginning line)

recursion:

is actually doing something, getting interupted with having to do something else and which you go do that thing (but also remembering/storing/saving what you still have to finish up doing before you got interupted with doing this other task), and then you come back to finish up what you were doing before you had gotten interupted... and now imagine that you're getting interupted with having to do another task before you can do the interupted task... it quickly builds up... the amount of unfinished and thus remembered/stored/saved tasks you still got to go back to and do... (recursion IS very powerful/useful, but it comes at a cost, in that it requires a lot of resources to remember to still do all those unfinished tasks...), for example:

  1. make your bed!
  2. you start to make your bed... when you get another order...
  3. clean up your room!
  4. you stop making your bed and start to clean your room... when you get yet another order...
  5. put your dirty clothes in the washer/laundry!
  6. you stop cleaning your room and start to take your dirty clothes to the washer/laundry.... when you get yet another order...
  7. take out the trash!
  8. you stop putting your dirty clothes in the laundry, and go to take out the trash... (and since I think you get the idea now), and thankfully there's no more orders, and you're able to finish taking out the trash.
  9. you then go back to finishing up putting your dirty clothes in the laundry.
  10. you then go back to finishing cleaning your room
  11. you then go back to finishing making your bed... finally you're done... and can go play now!

https://www.cs.utah.edu/~germain/PPS/Topics/recursion.html

http://www.geeksforgeeks.org/recursion/ (scroll down to the 'four boxes with the dashed blue path lines' picture, to see what is going on, the steps, with recursion)

https://en.wikipedia.org/wiki/Tower_of_Hanoi


the most likely issue is that you do have a coding mistake/typo/logic_issue/infinite_looping

is only removing a certain part of code, making it work without error? if yes, then you likely got a mistake in that code part

if there is indeed no specfiic part of code that is causing the error, and thus removing any part of code, now allows it to work, then it does look like you hit a crash from it using up to much resources


I'm not good at math... but having 10 recursive loops would at least generate:

10! (10 factorial): 10 x 9 x 8 x 7 x 6 x 5 x 4 x 3 x 2 x 1 = 3,628,800 activation records

if not more... if you got each of those 10 nested loops being their own recursion loops...

we could be talking about maybe (again, I suck at math) ....

10! x 10! = 3,628,800 x 3,628,800 = HUGE (lol)
or maybe it's even more HUGE: (3,628,800 x 3,628,799 x 3,628,798 x ... x 2) x (3,628,800) = ... YIKES!...


the quest software can have limits on itself too, such as restricting/limiting how many activation records (or for example your 100 messages) it creates...


another issue is the order of the scripts being run, could be causing conflicts


another issue with quest:

if you loop/recursion something using a popup window or the get input... it'll crash/freeze, as it'll be still waiting for an input for these (the first one) by the person playing, when it creates another popup window or get input (much faster than the human can input for the prior one), which causes the crash, as you can't have two popup windows or 'get inputs' running at the same time.

so, unfortunately, you got to be a lot more creative or better in your design of the scripting... which isn't always easy...


if you post your code, we can scour through it and see if we can find any issues with it


Well, when the controlling attribute is a number below 500, then the code works completely fine, but above 500, it doesn't. When under 500, the code executes flawlessly, doing precisely what I coded it to do. When above 500, it freezes the entire game, forcing me to CTRL+ALT+DEL the program with Task Manager. The order of the scripts being run weren't the issue. I wasn't using a popup window OR the get input function; though on an unrelated note I have had two get inputs occur at the same time before so I know that error quite well.

Well, if each record was say using up say 64 bytes (which is a lot for strings or character variable types) and it had 3,628,800 records, the total number of bytes would be 232,243,200 bytes... as there is 1024 in a kilo byte, that would be 226,800 kb... as there is 1024 in a mega byte that would be 221.4844 mb. I doubt its making 3.63 million records, and even if it was, I have 16 gb of ram or 16384 mb of ram available to me. Even if the program was 32bit and restricted memory usage to ~3.2 to ~3.3 GB, I'd still have sufficient RAM to create and store all those records. So, unless there is a way for me to output to a log file at the time of program freeze / crash to find out how many records are being produced, I'd say that it is a different issue causing the problem.

Yah, you are probably right; I'll have to get more creative on how to do what I want to do.


I have never used it, but you can do this to log text:

request(Log, "This is a message")

Note the Log has no quotes and starts with a capital.

It is possible that Quest has a limit on the number of nested loops it supports; I have never done more than three (and I am curious why ten might be required).


Well, the way I had it setup, is that while the loop is active, turn scripts outside of it do not fire... which is logical as the while loop is its own contained system with its own local variable, but this creates a problem if the player expects those turn scripts to fire. So, if you have a turn script that say levels the player's skill up, and then adds +1 skill increase and that skill increases would help you out within that loop, then you have to nest that bit of code within this larger loop. So what kind of system would require nesting of loops?

An auto-attack option. The player activates the auto-attack command, and then the loop ends if the creature is dead, or if the player reaches 25% or less HP, and keeps looping until one of those conditions stop being true. So, if you have several skills, such as Sword skill, Armor skill, and Block skill, each of them is a separate loop nested within the larger loop of auto-attack. Whenever you get enough sword skill xp, you get a skill up, and as that skill up would improve your odds of success within the auto-attack, you'd want to receive that skill up while auto-attack is looping. So, as a result, you have several loops and nested if/elses, all within one loop, and so the auto-attack loop is fairly big with a lot of logic that gets checked in a specific order before the auto-attack loop condition is checked. It may be possible to reduce the loops or if/elses in some way, but as I've only been doing Quest for the past couple weeks, the system works and so I've not taken the time to refactor it.


That should just be one loop. In each iteration:

  • player attacks
  • determine if skills increase
  • determine if level up
  • run turn scripts
  • test to see if you should continue

The RunTurnScripts function will run the turn scripts. Enemies attacking the player should be handled there so they get an attack if the player does something else (and that would be another loop going through each enemy in tirn).


there's two ways of doing 'constantly checking' operations:

  1. Turnscripts/Timers:

http://docs.textadventures.co.uk/quest/elements/turnscript.html
http://docs.textadventures.co.uk/quest/elements/timer.html

  • controlable for when/where it does the constant checking (enabled:true/false and local/global)
  • managed/controlled timing as the scripting is within the Turnscript/Timer and thus you're able to control/set the 'order of operations'
  • however, working with Turnscripts/Timers (and especially a lot of them) can be quite a hassle too...
  1. the special 'changedNAME_OF_ATTRIBUTE' change Script Attribute

http://docs.textadventures.co.uk/quest/change_scripts.html
http://docs.textadventures.co.uk/quest/guides/running_a_script_when_an_attribute_changes.html

  • it activates immediately upon the specified Attribute's Value changing, so you can have timing issues/conflicts...
  • however, it's also nice because it does immediately activate when the specified Attribute's Value changes, which can avoid the hassle of the issues with using Turnscripts/Timers

Well, clearly I have some refactoring to do then. Thanks for the advice/input.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums