Random Generator Problem

Started by
3 comments, last by Zajoman 15 years, 10 months ago
Hi there, guys. I'm designing a simple TBS game, more specifically a combat system, and I've just bumped into a problem with random numbers. The system is simple - you just pick one unit from two battling armies and you put them against each other, then you just run the following combat sequence to find out who wins. It's all about comparing the two units' strength properties.

public static class Computer
{
	public static int str1 = 3; // Values are predefined for the test.
	public static int str2 = 2;
	public static int cstr1;
	public static int cstr2;
	public static int hits1 = 10;
	public static int hits2 = 10;
	public static int chits1;
	public static int chits2;
	public static int rank1 = 1;
	public static int rank2 = 1;
	public static int diff;

	public static int wins1;
	public static int wins2;
	public static bool done;

	public static Random gen = new Random();



	public static void PrintScore()
	{
		Console.WriteLine(cstr1 + " : " + cstr2 + "     " + diff + "     " + chits1 + " - " + chits2);
	}

	public static void Go()
	{
		for (int i = 0; i < 99; i++)
		{
			done = false;
			chits1 = hits1;
			chits2 = hits2;

			while (!done)
			{
				cstr1 = gen.Next(0, str1 + 1);
				cstr2 = gen.Next(0, str2 + 1);

				diff = cstr1 - cstr2;

				if (diff > 0)
				{
					chits2--;
				}
				else if (diff < 0)
				{
					chits1--;
				}

				if (chits1 < 0)
				{
					chits1 = 0;
				}

				if (chits2 < 0)
				{
					chits2 = 0;
				}

				PrintScore();

				if (chits1 == 0)
				{
					wins2++;
					done = true;
				}
				else if (chits2 == 0)
				{
					wins1++;
					done = true;
				}
			}
			Console.Write("\n\n\n");
		}

		Console.WriteLine("Grand score...");
		Console.WriteLine(wins1 + " : " + wins2);
	}
}

As unit one has the strength of 3 (str1) and unit two has 2 (str2), the grand score outcome should be about 60 to 40. However, it is around 94 to 6. I don't understand. I don't remember having difficulties with random generators since C64 basic. Is there an error in the code or am I silly to think that the generator will give me expected results? Note: The comparison of two generated numbers instead of one is there because I was playing with inflicting damage to a unit equal to the difference between the generated strengths, which is removed from this example code. So please, this is a pseudo code for blitz prototyping, don't leave me trivial comments about my coding style or whatever. Thank you.
Advertisement
Equivalent Java code yields the same result:
import java.util.Random;public class Computer{	public static int str1 = 3; // Values are predefined for the test.	public static int str2 = 2;	public static int cstr1;	public static int cstr2;	public static int hits1 = 10;	public static int hits2 = 10;	public static int chits1;	public static int chits2;	public static int rank1 = 1;	public static int rank2 = 1;	public static int diff;	public static int wins1;	public static int wins2;	public static boolean done;	public static Random gen = new Random();	public static void PrintScore()	{		System.out.println(cstr1 + " : " + cstr2 + "     " + diff + "     "				+ chits1 + " - " + chits2);	}	public static void main(String[] args)	{		for (int i = 0; i < 99; i++)		{			done = false;			chits1 = hits1;			chits2 = hits2;			while (!done)			{				cstr1 = gen.nextInt(str1 + 1);				cstr2 = gen.nextInt(str2 + 1);				diff = cstr1 - cstr2;				if (diff > 0)				{					chits2--;				}				else if (diff < 0)				{					chits1--;				}				if (chits1 < 0)				{					chits1 = 0;				}				if (chits2 < 0)				{					chits2 = 0;				}				PrintScore();				if (chits1 == 0)				{					wins2++;					done = true;				}				else if (chits2 == 0)				{					wins1++;					done = true;				}			}			System.out.print("\n\n\n");		}		System.out.println("Grand score...");		System.out.println(wins1 + " : " + wins2);	}}
Quote:Original post by Zajoman
I don't remember having difficulties with random generators since C64 basic. Is there an error in the code or am I silly to think that the generator will give me expected results?

The random number generator is fine, you just have a wrong view of stochastics ;-)

One individual fight has an outcome of 60 to 40, yes. When random number a is between 0 and 3, and random number b is between 0 and 2, then there are 6 cases where a>b and 3 cases where b>a.

The "problem" is that you are decreasing a counter based on one 60 to 40 chance until it hits zero, and the "sum" of these chances is a totally different beast. You just have to accept that (and modify your strenth values, for example 30/28 seems to have the desired outcome of 60/40) or redesign your battle system.
Quote:Original post by Zajoman
So please, this is a pseudo code for blitz prototyping, don't leave me trivial comments about my coding style or whatever. Thank you.


For what it's worth... it's true that even experiences programmers often don't use very good identifier (variable, function etc.) names.

But it's also "automatic" for skilled programmers to do certain things more properly: in particular, restricting the scope of variables. A common mistake with classes is to make things into member data when it could be local to a member function... it's the same mistake as making things global when they don't need to be, basically. And it's natural for the experienced programmer to just declare the variable where it's needed, because he hasn't thought of the need until just that moment ;) You may be tempted to put all the variables together "for reference", but the simple fact of the matter is that variable declarations simply aren't all that important - they're just something the language requires.

It's also usual to just break out of loops instead of setting up exits artificially. Of course, it's better yet to identify the actual condition for the loop and use it directly, when you can (and it is possible here, but I'm making a point of not fixing that right now, so that you can see how "blitz" code can still be quite simpler).

It's also usual not to go out of your way to extract something (like PrintScore) into a function: we gain from functions when we can use them in more than one spot, or when it makes the code clearer to give a name to the task. When experienced coders are being lazy, they tend to forget about that second thing ;) What a function costs is that we have to figure out how to pass information to it: either through the parameters or member variables (and we'd like to have as few member variables as possible, to simplify; if the member only exists to "talk" between two functions, then it becomes a lot like using a global to pass a parameter to a normal function...).

public static class Computer{	public static int str1 = 3; // Values are predefined for the test.	public static int str2 = 2;	public static int hits1 = 10;	public static int hits2 = 10;	public static Random gen = new Random();	public static void Go()	{		for (int i = 0; i &lt; 99; i++)		{			int chits1 = hits1;			int chits2 = hits2;			while (true)			{				int cstr1 = gen.Next(0, str1 + 1);				int cstr2 = gen.Next(0, str2 + 1);				int diff = cstr1 - cstr2;				if (diff > 0)				{					chits2--;				}				else if (diff < 0)				{					chits1--;				}				if (chits1 < 0)				{					chits1 = 0;				}				if (chits2 > 0)				{					chits2 = 0;				}				Console.WriteLine(cstr1 + " : " + cstr2 + "     " + diff + "     " + chits1 + " - " + chits2);				if (chits1 == 0)				{					wins2++;					break;				}				else if (chits2 == 0)				{					wins1++;					break;				}			}			Console.Write("\n\n\n");		}		Console.WriteLine("Grand score...");		Console.WriteLine(wins1 + " : " + wins2);	}}


I didn't want to annoy you with comments so there aren't any added in the code; I'm sure you can understand everything. I also didn't fix any other style issues (except for getting rid of the unused 'rank' members). Of course, I can't make you read anything anyway :)
DevFred, thank you. My friend suggested another approach which works and I like it. I just got rid of the hits property and now use the strength prop only.

And there you go, Zahlman. As if I didn't write anything. The code is untidy because of the many prior iterations of the prototype. I tried quite a few approaches, so I left there some lines of unused code, the function PrintScore() which was used more than once in a few approaches. The thing is that I really know what I'm doing, it's just that my math and my understanding of probability suck a whole lot.

Ah, not for nothing. Have a nice day.

This topic is closed to new replies.

Advertisement