Log in

View Full Version : Randomising missions


salmo
12-02-2011, 05:11 AM
I'm trying the write a script to randomise mission parameters in the "MAIN" section of the mission file; time, weather, cloud height, wind etc. Here's what I have, but it does not work, can anyone help out please?

code removed by author

41Sqn_Banks
12-02-2011, 06:09 AM
Hi, first of all the "GamePlay.gpLoadSectionFile(fileName);" doesn't load the mission file into the game engine. It only creates a ISectionFile object that allows to read/write a mission file with the script and do some changes (like you are already do).

To post load a mission file into the running battle you need to call "GamePlay.gpPostMissionLoad(fileName);"

However I don't think it's possible to change the MAIN parameters by a mission file that is post loaded into a running battle.

Anyhow your script should look something like this:


using System;
using System.Collections.Generic;
using maddox.game;
using maddox.game.world;
using maddox.GP;
using System.Security.Cryptography;


public class Mission : AMission
{

public override void OnBattleInit()
{
RandomiseMission();
}

internal ISectionFile RandomiseMission()
{

//time of day
bool RandomiseTime = true;
int minTime = 6;
int maxTime = 17;
// WeatherIndex
bool RandomiseWeatherIndex = true;
int minWeatherIndex = 0;
int maxWeatherIndex = 1;
// cloud height
bool RandomiseClouds = true;
int minClouds = 1000;
int maxClouds = 2000;
// BreezeActivity
bool RandomiseBreezeActivity = true;
int minBreezeActivity = 0;
int maxBreezeActivity = 3;
// ThermalActivity
bool RandomiseThermalActivity = true;
int minThermalActivity = 0;
int maxThermalActivity = 2;

ISectionFile f = GamePlay.gpCreateSectionFile();
string sect;
string key;
string value;
// time of day
if (RandomiseTime == true)
{
sect = "MAIN";
key = "TIME";
value = NextInt(minTime, maxTime).ToString();
f.add(sect, key, value);
GamePlay.gpHUDLogCenter("Time: " + value); // for testing
}

// WeatherIndex
if (RandomiseWeatherIndex == true)
{
sect = "MAIN";
key = "CloudsHeight";
value = NextInt(minWeatherIndex, maxWeatherIndex).ToString();
f.add(sect, key, value);
}

// clouds
if (RandomiseClouds == true)
{
sect = "MAIN";
key = "CloudsHeight";
value = NextInt(minClouds, maxClouds).ToString();
f.add(sect, key, value);
}

// minBreezeActivity
if (RandomiseBreezeActivity == true)
{
sect = "MAIN";
key = "BreezeActivity";
value = NextInt(minBreezeActivity, maxBreezeActivity).ToString();
f.add(sect, key, value);
}

// ThermalActivity
if (RandomiseThermalActivity == true)
{
sect = "MAIN";
key = "ThermalActivity";
value = NextInt(minThermalActivity, maxThermalActivity).ToString();
f.add(sect, key, value);
}

// This post loads the section file f into the current battle.
// It's also possible to save the section file f first by f.save("filename") and then
// call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
// on your hard disk.
GamePlay.gpPostMissionLoad(f);

return f;

}

private static int NextInt(int min, int max)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];

rng.GetBytes(buffer);
int result = BitConverter.ToInt32(buffer, 0);

return new Random(result).Next(min, max);
}

}

salmo
12-02-2011, 07:19 AM
The script compiles without errors in the FMB compiler. When I run the mission, I get 2 outcomes in about 50/50 proportion:
(1) mission load & runs OK & time/weather is random :) :) :)
(2) mission load fails & I get an "object reference not set to an instance of an object" error dialog window.

salmo
12-02-2011, 07:57 AM
Here's a working (beta) of a script to randomise mission time/cloud height/weather/breeze & thermals. Feedback is very welcome.

code removed by author

Octocat
12-08-2011, 09:32 AM
More concise and economical way to generate random numbers:

internal ISectionFile RandomiseMission()
{
var random = new Random(Environment.TickCount);

...
value = random.Next(minBreezeActivity, maxBreezeActivity).ToString();

...
value = random.Next(minThermalActivity, maxThermalActivity).ToString();

...
}

salmo
12-08-2011, 09:53 AM
More concise and economical way to generate random numbers:

code removed by author

It's a moot point. The random method certainly uses less resources & therefore is "more efficient", but many argue about the "randomness" of the numbers generated. The Cryptography system method uses more resources, but provides a "better" (more unpredictable) random number set. Of course, to get true randon numbers you would have to seed the generator with something truely random such as cosmic radiation or background noise.

Octocat
12-08-2011, 10:24 AM
Here the main idea: do not create an object of Random anew for each use. One would be enough.

salmo
12-08-2011, 12:16 PM
Here the main idea: do not create an object of Random anew for each use. One would be enough.

So are you saying that the NextInt routine would be better if the highlighted line were used?

code removed by author

Octocat
12-08-2011, 12:37 PM
No, code "return Random(result).Next(min, max);" is syntactic error for C# compiler, because keyword new required.

I saying, you can remove NextInt function completely. Then create single instance of Random class in the RandomizeWeather function, and then use it as many times as you like, as in my example above.

Octocat
12-08-2011, 12:51 PM
The random method certainly uses less resources...

Random in not the method, but class.

Line below creates instance of Random class, and assigns the value to variable named random.

var random = new Random(Environment.TickCount);

Now, we can call method "Next" any number of times:

int randomNumber = random.Next(min, max);

salmo
12-08-2011, 01:23 PM
Thankyou Octocat. I known how random.next works, but I chose to go with the RNGCryptoServiceProvider rather than the Random class because I feel it gives me "better" random numbers. Anyone using the script is welcome to replace the NextInt routine with the random class as suggested.

Octocat
12-08-2011, 02:28 PM
My variant with RNGCryptoServiceProvider :)

- Code slightly reduced and refactored
- Added minutes randomization
- Fixed maxValue problem (random.Next always return value by 1 less then the specified maximum)


using System;
using maddox.game;
using System.Security.Cryptography;

public class Mission : AMission
{
public override void OnBattleInit()
{
RandomiseMission();
}

internal ISectionFile RandomiseMission()
{
// initialize a new instance of the Random class
var provider = new RNGCryptoServiceProvider();
var buffer = new byte[4];
provider.GetBytes(buffer);
var random = new Random(BitConverter.ToInt32(buffer, 0));

//time of day
bool RandomiseTime = true;
int minTime = 6; // 6am
int maxTime = 17; // 5pm
// WeatherIndex
bool RandomiseWeatherIndex = true;
int minWeatherIndex = 0; // clear
int maxWeatherIndex = 2; // medium clouds
// cloud height
bool RandomiseClouds = true;
int minClouds = 500; // 500m
int maxClouds = 1500; // 1500m
// BreezeActivity
bool RandomiseBreezeActivity = true;
int minBreezeActivity = 0; // zero breeze
int maxBreezeActivity = 10; // max breeze
// ThermalActivity
bool RandomiseThermalActivity = true;
int minThermalActivity = 0; // zero thermals
int maxThermalActivity = 10; // max thermals

ISectionFile secfile = GamePlay.gpCreateSectionFile();

// time of day
if (RandomiseTime == true)
{
string value = (random.Next(minTime * 100, maxTime * 100) / 100f).ToString();
secfile.add("MAIN", "TIME", value);
}

// WeatherIndex
if (RandomiseWeatherIndex == true)
{
string value = random.Next(minWeatherIndex, maxWeatherIndex + 1).ToString();
secfile.add("MAIN", "WeatherIndex", value);
}

// clouds
if (RandomiseClouds == true)
{
string value = random.Next(minClouds, maxClouds + 1).ToString();
secfile.add("MAIN", "CloudsHeight", value);
}

// minBreezeActivity
if (RandomiseBreezeActivity == true)
{
string value = random.Next(minBreezeActivity, maxBreezeActivity + 1).ToString();
secfile.add("MAIN", "BreezeActivity", value);
}

// ThermalActivity
if (RandomiseThermalActivity == true)
{
string value = random.Next(minThermalActivity, maxThermalActivity + 1).ToString();
secfile.add("MAIN", "ThermalActivity", value);
}

// This post loads the section file secfile into the current battle.
// It's also possible to save the section file secfile first by secfile.save("filename") and then
// call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
// on your hard disk.
GamePlay.gpPostMissionLoad(secfile);

return secfile;
}
}

salmo
12-09-2011, 12:12 AM
OK, I see what you've done. One declaration of var provider = new RNGCryptoServiceProvider(); Much better Thankyou.

1. I've moved the Random Class declaration outside the randomisemission routine so it can also be used in the randomisation of mission selection etc. (see below). But this construct produces an error CS1519: Invalid token '(' in class, struct, or interface member declaration on the provider.GetBytes(buffer); line. pehaps you can help?

code removed by author

2. Multiplayer implimentation

This script randomised the [MAIN] section of the mission file OK in single-player mode, but fails in multi-player(server) mode. I have in mind some pseudo code such as: Randomise [MAIN] on server battle start; save [MAIN] section file on the server side; as players connect reload the saved [MAIN] section file from server side so they get the same mission parameters in multi-player mode.

Octocat
12-09-2011, 03:45 PM
Yes, because dynamic type resolution "var x = " works in functions only, and calls like "provider.GetBytes(buffer)" too.

In your case better to use constructor, to bury inside all temporary instances like "buffer" and "provider", when they perform their task. For example:


using System;
using maddox.game;
using System.Security.Cryptography;

public class Mission : AMission
{
// allowed to change only in constructor
private readonly Random m_random;

// Constructor (do not call manually)
public Mission()
{
// initialize a new instance of the Random class
var provider = new RNGCryptoServiceProvider();
var buffer = new byte[4];
provider.GetBytes(buffer);
m_random = new Random(BitConverter.ToInt32(buffer, 0));
}

public override void OnBattleInit()
{
RandomiseMission();
}

internal ISectionFile RandomiseMission()
{
//time of day
bool RandomiseTime = true;
int minTime = 6; // 6am
int maxTime = 17; // 5pm
// WeatherIndex
bool RandomiseWeatherIndex = true;
int minWeatherIndex = 0; // clear
int maxWeatherIndex = 2; // medium clouds
// cloud height
bool RandomiseClouds = true;
int minClouds = 500; // 500m
int maxClouds = 1500; // 1500m
// BreezeActivity
bool RandomiseBreezeActivity = true;
int minBreezeActivity = 0; // zero breeze
int maxBreezeActivity = 10; // max breeze
// ThermalActivity
bool RandomiseThermalActivity = true;
int minThermalActivity = 0; // zero thermals
int maxThermalActivity = 10; // max thermals

ISectionFile secfile = GamePlay.gpCreateSectionFile();

// time of day
if (RandomiseTime == true)
{
string value = (m_random.Next(minTime * 100, (maxTime) * 100) / 100f).ToString();
secfile.add("MAIN", "TIME", value);
}

// WeatherIndex
if (RandomiseWeatherIndex == true)
{
string value = m_random.Next(minWeatherIndex, maxWeatherIndex + 1).ToString();
secfile.add("MAIN", "WeatherIndex", value);
}

// clouds
if (RandomiseClouds == true)
{
string value = m_random.Next(minClouds, maxClouds + 1).ToString();
secfile.add("MAIN", "CloudsHeight", value);
}

// minBreezeActivity
if (RandomiseBreezeActivity == true)
{
string value = m_random.Next(minBreezeActivity, maxBreezeActivity + 1).ToString();
secfile.add("MAIN", "BreezeActivity", value);
}

// ThermalActivity
if (RandomiseThermalActivity == true)
{
string value = m_random.Next(minThermalActivity, maxThermalActivity + 1).ToString();
secfile.add("MAIN", "ThermalActivity", value);
}

// This post loads the section file secfile into the current battle.
// It's also possible to save the section file secfile first by secfile.save("filename") and then
// call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
// on your hard disk.
GamePlay.gpPostMissionLoad(secfile);

return secfile;
}
}