Random Demigod bug and how to fix it.

Okay... First of all everything I will say is ONLY based on what I read in the forums. And the things I am based on in this post are:

1. Demigod is written in Lua (programming language).

2. Random Demigod option always randoms the same Demigod for everyone (Regulus I guess).

 

Please take your time to read everything. 

 

So I know what might have been the problem and I will describe it here:

Here's how random works in most programming languages (and Lua):

There is a thing called Random Seed which is a number on which the Random function is based on. If this number is always the same then the random will follow a certain predictable path. An example of what I mean:

You start your program. You random 10 numbers and they are 1-5-6-7-4....  You close the program and start it again and random 10 numbers. And they will actually be the same! 1-5-6-7-4...

 

To fix this problem in programming languages the Random Seed number must be constantly changed to some number that changes very often. For example - the current time in seconds.

In programming languages like pascal you don't have to know what time is it or how to find it. There is a function Randomize that changes the Random Seed for you (constantly) which makes the random trully random.

 

However in Lua you have to do it more "manualy". You have to use the function:

math.randomseed(os.clock())

os.clock() - is the number on which the random will be based. It's not really seconds, but it's something related to time and something that constantly changes.

 

So to always get trully random you will have to use a function like this:

Every second do:   math.randomseed(os.clock())

 

So it seems like we will get a trully random random. Just what we wanted, right? Wel... This is only partially correct.

You see... In Lua the first number that you random after using the   math.randomseed(os.clock()) function will always be the same.

 

What do I mean? Here's an example:

Every second do:

  math.randomseed(os.clock())

  x = math.random(8)

 

So it seems that X will always be a random number between 1 and 8. But, no. X will ALWAYS! EVERY SECOND be the same number. For example 1 or maybe 4. Like on the first second it's 4, on the 2nd second it's 4, on the third it's 4 and so on.

 

How to fix this problem? Simply random 1 number after using the math.randomseed() function. Like this:

Every second do:

  math.randomseed(os.clock())

  math.random(8)

 

The second number you random with math.random will be REALLY random. You will never guess what it is. Just what we need.

 

 

 

So now... Why have I been explaining all of this to you? Because this is what may have happened:

GPG forgot to random 1 number after using the math.randomseed function.

 

So in a p2p system everyone is the host, right? So everyone randoms 1 number for themselves which, since it's the first number randomed after math.randomseed, turns out to be the same number for everyone! Which then turns into everyone having the same demigod (Regulus?)!

 

 

So you'll ask: How could they have missed this? They have tested it before releasing for sure!

 

Well, since it's hard to test it online (you have to release a special patch just for a closed group of people which is kind of problematic) they tested it in singleplayer with bots.

These are the results they might have got:

In a 5v5 match against 9 AI's you are the only host. So you random the same demigod every match, as beeing the first player to get the random, while the other players (AI's) will get different demiods every match since their random will be a true random.

 

So they are testing this like:

Game 1:

They got Regulus. All other demigods (the AI's) got some random demigods.

Game 2:

They got Regulus. All other demigods (the AI's) got some other random demigods, different from game 1.

 

What GPG are thinking at this point:
"Well... Technically it IS possible that we can get the same hero twise in arow. It is random, isn't it? The other demigods were random, weren't they? That means that the random is working!"

 

Game 3:

They got Regulus. All other demigods (AI's) got some other random demigods, different from games 1 and 2.

 

What GPG are thinking at this point:
"Lol. Regulus again. That's random luck. Getting the same demigod 3 times in a row is VERY RARE but POSSIBLE! Other demigods are random so it's working. Ok guys, take 5. Let's put this patch up for DL."

 

But what they didn't know was that they didn't get Regulus randomly. And if games 4-10 were tested they would always get Regulus.

 

 

So how fast can this be fixed? Well I don't know. How fast does it take you to press ENTER and type math.random(8)? Took me about 3 seconds. But then you have to prepare the new patch for download which can take a lot more time.

 

 

If I could decompile the game and look into the code like Bman did, I would've checked if THIS is the problem causing "not random" random. But I can't.

 

Hope they fix it soon and I even more I hope that I helped.

Discuss if you want to ^^ This may pretty much have been the problem.

If you didn't understand the whole randomseed stuff then take your time to read it again, more careful.

13,758 views 28 replies
Reply #1 Top

As far as i know the problem is that you can't even start a game if someone has chosen random.

Reply #2 Top

Sorry but you have no idea what you're talking about.

First of all, Demigod is NOT WRITTEN in Lua.  It USES Lua for some of the game functionality (mostly scripting events, ai and similar).  The scriping of the game in Lua still mostly relies on libraries provided by the core game which are not written in Lua.

Second, Randomizing with the same seed produces same sequence of random numbers, not the same random number.

Third, Random option makes games impossible to start.  Same demigod on random some are reporting may be just some side effect of the underlaying problem

Reply #3 Top

But, I saw a guy post that he and his friends started a 4v4 game with everyone random and all got regulus.

 

Maybe it sometimes crashes and sometimes doesn't?

 

Here's the link:

http://forums.demigodthegame.com/357389

 

And he was saying:

Other than that, my housemates and i played a game lsat night, whilst most things seem better, the Ai was frustrating, and the game we played all as random, we all got regulus...all 3 games.
End of quote

Reply #4 Top

Quoting Misfortune, reply 2
Sorry but you have no idea what you're talking about.

First of all, Demigod is NOT WRITTEN in Lua.  It USES Lua for some of the game functionality (mostly scripting events, ai and similar).  The scriping of the game in Lua still mostly relies on libraries provided by the core game which are not written in Lua.

Second, Randomizing with the same seed produces same sequence of random numbers, not the same random number.
End of Misfortune's quote

 

First. Random option may have been written in Lua.

 

Second. I've never sayd that. Stop ragin please.

 

Edit: Don't talk about things you don't know.

Here, I'll quote myself:



If you didn't understand the whole randomseed stuff then take your time to read it again, more careful.

End of quote

 

Seriosly reread, you didn't understand correctly what I was describing as the problem. I will repeat again: In Lua the FIRST (not the second) number you random after using math.randomseed is not random and is always the same. Any further numbers will do what you described in your "second".

Reply #5 Top

Hehe, if this is what you do, might as well do (os.clock()//8) + 1.

There's no point in using random() calls if you reseed EVERY TIME.

Reseeding all random clients at start of match with os.clock() (which has a 1 sec resolution in lua), it just plain dumb, and WILL lead to everyone getting the same demigod, assuming their clocks are somewhat synched.

I suggest whoever gets the task of fixing this takes a good look at

http://lua-users.org/wiki/MathLibraryTutorial , math.random section :)

Reply #6 Top

Quoting Rydier, reply 5
Hehe, if this is what you do, might as well do (os.clock()//8) + 1.

There's no point in using random() calls if you reseed EVERY TIME.

Reseeding all random clients at start of match with os.clock() (which has a 1 sec resolution in lua), it just plain dumb, and WILL lead to everyone getting the same demigod, assuming their clocks are somewhat synched.

I suggest whoever gets the task of fixing this takes a good look at

http://lua-users.org/wiki/MathLibraryTutorial , math.random section
End of Rydier's quote

 

Umm... No :D ? Seriosly man you have a mistake in your own calculations. First of all. Clocks sinched? That is nearly impossible. Even with the same PC specs loading times will differ at least by miliseconds. And that matters because of:

Second: I think YOU should read the Lua manual.

os.clock is not seconds. It is based on seconds, but not a number of seconds. It's seconds and milliseconds. It's a number like 56,495 which is  56,495 seconds which is 56495 milliseconds. The Random Seed will be based on 56,495 and not 56 or 57. Which will give you a good random seed. Different for every player.

Reply #8 Top

First. Random option may have been written in Lua.

Second. I've never sayd that. Stop ragin please.

Edit: Don't talk about things you don't know.

End of quote

You based a whole slew of conclusions based on one assumption that you didn't even bother to check.  You can gain full access to the lua scripts from Demigod if you wish so.  Doubt you will find anything there related to game creation and start.

Second, can't blaim me for not following your post 100% with all the mistakes, inacuracies and bottom line terrible writing.

os.clock is not seconds. It is based on seconds, but not a number of seconds. It's seconds and milliseconds. It's a number like 56,495 which is  56,495 seconds which is 56495 milliseconds. The Random Seed will be based on 56,495 and not 56 or 57. Which will give you a good random seed. Different for every player.
End of quote

Actually, to quote the documentation: "If Lua could get milliseconds from os.time() the init could be better done."  Or as rydier already said: "os.clock() (which has a 1 sec resolution in lua)"

So yes, you have no idea what you're talking about.

P.S. Oh and BTW, the first value of math.random after randomizing is not always the same, but rather could be same between two math.randoms if you randomize twice in a very short short interval before the two.

Reply #9 Top

Quoting Misfortune, reply 8
First. Random option may have been written in Lua.

Second. I've never sayd that. Stop ragin please.

Edit: Don't talk about things you don't know.

You based a whole slew of conclusions based on one assumption that you didn't even bother to check.  You can gain full access to the lua scripts from Demigod if you wish so.  Doubt you will find anything there related to game creation and start.

End of Misfortune's quote

In dgdata.zip/common/HeroUtil.lua, you'll find function GetRandomHero(gameInfo), so yes, the random demigod selection is implemented in lua.

Uses a non-standard table.random call though, no idea where they've implemented that... ;)

Reply #10 Top

/bow to you for bothering to check :)

Reply #11 Top

Umm... Seriosly guys you CAN get miliseconds from os.clock().

And take that  Misfortune! Random WAS written in lua, ha! :D

P.S. Oh and BTW, the first value of math.random after randomizing is not always the same
End of quote

In lua it is, sorry. You can google it. Many people ask why it has to be like that, but it's not hard just to random 1 number so people don't complain too much.

Reply #12 Top

And take that  Misfortune! Random WAS written in lua, ha!
End of quote

How about checking how (and where) table.random is implemented :D

Reply #13 Top

It just struck me! Why are you flaming me anyway? I was trying to help.

Reply #14 Top

In which way was I exactly flaming?  It wasn't like I called you names or anything and most of what I said was correct.  Ok I was wrong thinking the game lobby is not very likely to use Lua scripts in some form, but still it wasn't you that provided that info (which was actually something useful regarding the subject).

I only asked that you check the facts before making assumptions... So if you're really serious about helping how about really tracking down where table.random is implemented and if it's a Lua function, analyzing there on the spot what may be wrong.  That's what I would have done if I had access to the game files now.

Reply #15 Top

He's right about the seeding though.  You seed it once and then pull the numbers via random calls.  There's no need to be constantly seeding it, just do it once with the current time milliseconds from epoch.

However, if it's true that the math.random() function always returns the same number after a seed....something is broken.

Reply #16 Top

Quoting Misfortune, reply 12

How about checking how (and where) table.random is implemented
End of Misfortune's quote

 

In utils.lua:

# Return a random element in the table. Uses Sim's random stream if IsSim()
function table.random(t)
if IsSim() then
return t[Random(table.getn(t))]
else
return t[math.random(table.getn(t))]
end
end

Let's just say I can think of lots of things that could go wrong with this code just by looking at it, depending on when and by whom GetRandomHero() is called, as well as when seeds are initialized.

I think I'll rather wait for GPG to fix it than try and wade through the specifics myself :)

 

Reply #17 Top

Quoting Somaz, reply 11
Umm... Seriosly guys you CAN get miliseconds from os.clock().
End of Somaz's quote

My bad then, never actually coded in lua...

I guess you shouldn't trust documentation for 5.1 and 5.0 (syntax in files suggest that's what GPG uses) from lua.org, which both states

"os.clock ()

Returns an approximation of the amount of CPU time used by the program, in seconds." (same for os.time() )

 

Reply #18 Top

Returns an approximation of the amount of CPU time used by the program, in seconds." (same for os.time() )
End of quote

Wait, so it's not the current date?  Then you probably shouldn't use it.

Reply #19 Top

Quoting InfiniteVengeance, reply 18
Returns an approximation of the amount of CPU time used by the program, in seconds." (same for os.time() )

Wait, so it's not the current date?  Then you probably shouldn't use it.
End of InfiniteVengeance's quote

Eh, was talking of their accuracy (both are listed as being in seconds).

os.time() is the current time.

os.clock() is processor time spent.

Reply #20 Top

You are forgetting the fact that this game relies on the fact that all the peers in the game use the same random seed.

My guess is that the random seed code goes something like this: math.randomseed(4) // frogboy rolled a 1d6

Obviously selection of demigods should be performed before the seeds are set to 4 ;)

Reply #21 Top

Confirmed, when random actually works, it is always regulus.....

Reply #22 Top

But what they didn't know was that they didn't get Regulus randomly. And if games 4-10 were tested they would always get Regulus.
End of quote

No, the host/first player does not always get Regulus (or even the same Demigod in a given session). Just tested myself and got Erebus, Rook, Oak, Rook. While your desire to help is appreciated, it'd be a good idea to test things yourself before going to such lengths. That one user's report is certainly strange (and there's likely some bug causing it), but if it were an absolute case everyone would be reporting the same result.

 

Reseeding all random clients at start of match with os.clock() (which has a 1 sec resolution in lua), it just plain dumb, and WILL lead to everyone getting the same demigod, assuming their clocks are somewhat synched.
End of quote

While this statement assumes that each client is responsible for choosing *only* the player's own demigod (which is not an assumption I'd make without seeing it in the actual code), it's much more likely that all players choose all demigods via Deterministic RNG. Determinism (where all players are intentionally using the same seed) is a vital part of a P2P game--if random values were not determined based on a pre-set sequence which is the same for all players in the match, syncing would be impossible.

 

 

Reply #23 Top

Quoting torbeindallas, reply 20
You are forgetting the fact that this game relies on the fact that all the peers in the game use the same random seed.
 
End of torbeindallas's quote

Not always, that's the isSim() call in table.random.

Which of course means, if isSim() is true when GetRandomDemigod() is called, and a player only determines which random demigod he will be, it:

a ) Only works if all players selected to be a random demigod. (Otherwise you get a desync and game stops right at the start)

b ) IF it works, all random players will be the same random demigod. (as their calls all returned the same random value :P ).

If this is the case, you can probably get around everyone being regulus by selecting random, then selecting someone else, then selecting random again. Everyone will still be the same demigod of course, but not Regulus anymore :)

Reply #24 Top

I know you're just trying to help, but there's a certain amount of hubris when an amateur tells a professional how to fix their problems.

You haven't seen the code.  Often times complex problems masquerade as trivial bugs/missing features.

It's best to just let GPG fix the problems.  I'd hate it if someone started spouting "solutions" for bugs in my game code when they haven't even seen the source code.

 

Reply #25 Top

Not always, that's the isSim() call in table.random.

Which of course means, if isSim() is true when GetRandomDemigod() is called, and a player only determines which random demigod he will be, it:
End of quote

The random demigod selection appears to be performed at player brain creation, which is done as part of the Sim init. Thus isSim() will always be true in the context of random demigod selection, and all players do appear to deterministically set the selection for all randoms.