Random filling a map in C#

Everything about the various Boulder Dash tools, and other stuff created by the fans.

Moderator: Admin

Post Reply
User avatar
rjm
Member
Posts: 16
Joined: Sun Mar 29, 2009 2:57 pm
Location: United Kingdom
Contact:

Random filling a map in C#

Post by rjm »

Hello,

I've been working on a Boulder Dash based game for my kids to play when I came across some references to the BDCFF. After reading through that, I recoded my protoype to import and use caves in this format (0.32 by Tim Stridmann). This works pretty well, and so far I'm happy with the progress.

However, there's one area where it falls over flat which is the RandomFill option to randomly fill a map with various contents. My implementation of this is pretty dreadful and while it gave me pleasing results for what I wanted prior to working from the BDCFF spec, its very very wrong at producing levels imported from it. Line, Raster, etc all fine. RandomFill... horrible :).

Checking another post on this forum about RandomFill (BDCFF - RandSeed and RandomFill), I checked the references to a random number generator and also a random fill implementation in Pascal.

The only problem is I can't program in C or Pascal - nor can I read German :)

Has anyone tried writing the random number generator and fill function in C# and if so can any one offer any advice?

I have downloaded the GDash source to poke about it, but this is written in C and I don't have the foggiest where to even look for the random functionality, let alone how to convert it.

Any help or advice would be greatfully received!

Thanks;
Richard Moss
User avatar
Arno
Site Admin
Posts: 2826
Joined: Sat Mar 17, 2007 2:26 pm
Location: netherlands
Contact:

Post by Arno »

Hi rjm,

All I know is that there exists a website where you can view the procedure code, as it is written in C. I've used almost the same procedure in my BDCFF-->MAPS converter, which is C++.

Just wondering: isn't it possible for you to translate this code to C#, if you know what the C code does? I'm not an advanced programmer, but I understood that most "higher level" programming languages are very similar.

Probably, you have already found the C procedure, but if it may help, I can share the C++ procedure that I used in my conversion tool.
User avatar
rjm
Member
Posts: 16
Joined: Sun Mar 29, 2009 2:57 pm
Location: United Kingdom
Contact:

Post by rjm »

Arno,

Thanks for the response. I haven't found the code you refer to, unless I've missed it in the various pages I've been looking in, so if you can link it then that would be a great help.

I used C quite a few years back so although I'm not even remotely competent in it, I can more or less read it. Hopefully it'll give me a clue at the very least :)

Thanks!;
Richard moss
User avatar
Arno
Site Admin
Posts: 2826
Joined: Sat Mar 17, 2007 2:26 pm
Location: netherlands
Contact:

Post by Arno »

Hi Richard,

I include here the C++ code that I'm using in my BDCFF-->MAPS converter. It uses 2 functions, of which the second fills a cave completely with the random items, specified in the BDCFF file.

I've added a few comments to explain what certain parts of the code actually do. Hope this helps you out!

Code: Select all

void NextRandom(int *RandSeed1, int *RandSeed2) // Random generator used on C64
{  short TempRand1;
   short TempRand2;
   short carry;
   short result;
   assert(((*RandSeed1 >= 0x00) && (*RandSeed1 <= 0xFF)));
   assert(((*RandSeed2 >= 0x00) && (*RandSeed2 <= 0xFF)));
   TempRand1 = (*RandSeed1 & 0x0001) * 0x0080;
   TempRand2 = (*RandSeed2 >> 1) & 0x007F;
   result = (*RandSeed2) + (*RandSeed2 & 0x0001) * 0x0080;
   carry = (result > 0x00FF);
   result = result & 0x00FF;
   result = result + carry + 0x13;
   carry = (result > 0x00FF);
   *RandSeed2 = result & 0x00FF;
   result = *RandSeed1 + carry + TempRand1;
   carry = (result > 0x00FF);
   result = result & 0x00FF;
   result = result + carry + TempRand2;
   *RandSeed1 = result & 0x00FF;
   assert(((*RandSeed1 >= 0x00) && (*RandSeed1 <= 0xFF)));
   assert(((*RandSeed2 >= 0x00) && (*RandSeed2 <= 0xFF)));
}

void random_fill(int cave_nr, int level_nr)
{  int item;
   int number_rand_items=0;
   
   // Count the number of random items (max 4):
   for (int k=0; k<4; k++)
   {  if (random_fill_probs[k] <= 255) number_rand_items++;
   }
   
   // Initialize the values of both random seeds:
   RandSeed1 = 0;
   RandSeed2 = rand_seed[level_nr]; // This is the seed-value from the BDCFF file!
   
   // Fill the top line with steelwalls:
   for (int j=0; j<40; j++)
      put_item(cave_nr, level_nr, item_from_name("STEELWALL"), 0, j);
   // The function put_item places a certain item at a certain place. It has 5 parameters:
   // cave number, level number, item id, vertical position (0 to 21), horizontal position (0 to 39).

   // Fill the rest of the cave (start with the 2nd line):
   for (int i=1; i<22; i++)
   {  for (int j=0; j<40; j++)
      {  item = item_from_name("DIRT"); // Default item is dirt!
         NextRandom(&RandSeed1, &RandSeed2); // Draw a random number between 0 and 255.
         // Determine which item should be placed:
         for (int k=0; k<number_rand_items; k++)
         {  if (RandSeed1 < random_fill_probs[k])
            {  item = random_fill_items[k];
            }
         }
         // Place the item, but make an exception when we are on the border,
         // (both for caves and intermissions) then a steelwall must be placed.
         if ( (!intermission[cave_nr] && i>=1 && i<=20 && j>=1 && j<=38) ||
              ( intermission[cave_nr] && i>=1 && i<=10 && j>=1 && j<=18)    )
            put_item(cave_nr, level_nr, item, i, j);
         else put_item(cave_nr, level_nr, item_from_name("STEELWALL"), i, j);
      }
   }
}
User avatar
rjm
Member
Posts: 16
Joined: Sun Mar 29, 2009 2:57 pm
Location: United Kingdom
Contact:

Post by rjm »

Arno,

Thanks for posting this. It's almost identical to C# syntax (the only difference so far is you say "*RandSeed1" and I say "ref RandSeed1") and so very readable.

Predictably, the problems are in the operator usage and casting in the NextRandom function which should keep me busy for a while trying to figure out :)

Regards;
Richard Moss
User avatar
rjm
Member
Posts: 16
Joined: Sun Mar 29, 2009 2:57 pm
Location: United Kingdom
Contact:

Post by rjm »

Arno,

Well, it took less than than I thought - I'm not sure if my logic is entirely correct in regards to the > but... it works! Now the object coded bdcff maps I'm loading precisely match your screenshots :)

Thanks very much for your help - I'm pretty pleased, as the implementation I had come up with made many BD1 caves unsolvable as areas were blocked etc :shock:

Just in case anyone else needs it, this is the C# version. Obviously your fill map routines will differ according to how you implement your own level types.

Code: Select all

    public static void NextRandom(ref int RandSeed1, ref int RandSeed2) // Random generator used on C64 
    {
      short TempRand1;
      short TempRand2;
      short carry;
      short result;
      TempRand1 = (short)((RandSeed1 & 0x0001) * 0x0080);
      TempRand2 = (short)((RandSeed2 >> 1) & 0x007F);
      result = (short)((RandSeed2) + (RandSeed2 & 0x0001) * 0x0080);
      carry = (short)((result > 0x00FF) ? 1 : 0);
      result = (short)(result & 0x00FF);
      result = (short)(result + carry + 0x13);
      carry = (short)((result > 0x00FF) ? 1 : 0);
      RandSeed2 = result & 0x00FF;
      result = (short)(RandSeed1 + carry + TempRand1);
      carry = (short)((result > 0x00FF) ? 1 : 0);
      result = (short)(result & 0x00FF);
      result = (short)(result + carry + TempRand2);
      RandSeed1 = result & 0x00FF;
    }

    public static void random_fill(Level level, int[] probability, TileType[] items)
    {
      TileType item;
      int RandSeed1;
      int RandSeed2;

      // Initialize the values of both random seeds: 
      RandSeed1 = 0;
      RandSeed2 = level.RandomSeed; // This is the seed-value from the BDCFF file! 

      // Fill the top line with steelwalls: 
      for (int j = 0; j < 40; j++)
        level.Tiles[j, 0].Type = TileType.SteelWall;
      
      // Fill the rest of the cave 
      for (int i = 1; i < level.Height; i++)
      {
        for (int j = 0; j < level.Width; j++)
        {
          item = TileType.Dirt; // Default item is dirt! 
          NextRandom(ref RandSeed1, ref RandSeed2); // Draw a random number between 0 and 255. 

          // Determine which item should be placed: 
          for (int k = 0; k < items.Length; k++)
          {
            if (RandSeed1 < probability[k])
              item = items[k];
          }

          // Place the item, but make an exception when we are on the border, then a steelwall must be placed. 
          if (i > 0 && i < level.Height - 1 && j > 0 && j < level.Width - 1)
            level.Tiles[j, i].Type = item;
          else
            level.Tiles[j, i].Type = TileType.SteelWall;
        }
      }
    }
Once again, thanks for the help!

Regards;
Richard Moss
User avatar
Arno
Site Admin
Posts: 2826
Joined: Sat Mar 17, 2007 2:26 pm
Location: netherlands
Contact:

Post by Arno »

rjm wrote:Well, it took less than than I thought - I'm not sure if my logic is entirely correct in regards to the > but... it works! Now the object coded bdcff maps I'm loading precisely match your screenshots :)
Congratulations! :D And when you've finished your remake, please release it for us to try! ;)
User avatar
rjm
Member
Posts: 16
Joined: Sun Mar 29, 2009 2:57 pm
Location: United Kingdom
Contact:

Post by rjm »

Arno wrote:Congratulations! :D And when you've finished your remake, please release it for us to try! ;)
It'll be a while yet - currently all the elements are in place and working but some bits need more polish :) Plus it's currently only in GDI, next task is to learn DirectX and have the engine run in that.

But when it's ready I'll certainly drop a line :)
Antonio
Member
Posts: 20
Joined: Fri Aug 06, 2010 2:12 pm

Post by Antonio »

Thanks Arno and Richard for posting that source-code, it was a big help for me.
I've 'translated' it to Java, (because my upcoming BD-remake will be in Java :-),
and I shall post it here in case someone is interested :

Code: Select all

  private static int globalRandSeed1;
  private static int globalRandSeed2;
  
  public static void randomFill(String[][] level, int seed, ArrayList<Integer> probabilities, ArrayList<String> items)
  {
    String item;
    int randSeed1 = 0;
    int randSeed2 = seed; // This is the seed-value from the BDCFF file.

    // Fill the top line with walls:
    for (int i = 0; i < 40; i++)
      level[0][i] = "W";
   
    // Fill the rest of the cave.
    for (int r = 1; r < level.length; r++)
    {
      for (int c = 0; c < level[0].length; c++)
      {
        item = "E"; // Default item ('earth').
        NextRandom(randSeed1, randSeed2); // Draw a random number between 0 and 255.
        randSeed1 = globalRandSeed1;        
        randSeed2 = globalRandSeed2;        
        
        // Determine which item should be placed :
        for (int k = 0 ; k < items.size() ; k++)
        {
          if (globalRandSeed1 < probabilities.get(k))
            item = items.get(k);
        }
        
        // Place the item, but make an exception when we are on the border, then a wall must be placed.
        if (r > 0 && r < level.length-1    &&   c > 0 && c < level[0].length-1)
          level[r][c] = item;
        else
          level[r][c] = "W";
      }
    }
  } 
  
  private static void NextRandom(int randSeed1, int randSeed2) // Random generator used on C64
  {
    short carry;
    short result;
    
    short tempRand1 = (short)((randSeed1 & 0x0001) * 0x0080);
    short tempRand2 = (short)((randSeed2 >> 1) & 0x007F);
    
    result = (short)((randSeed2) + (randSeed2 & 0x0001) * 0x0080);
    carry = (short)((result > 0x00FF) ? 1 : 0);
    result = (short)(result & 0x00FF);
    result = (short)(result + carry + 0x13);
    carry = (short)((result > 0x00FF) ? 1 : 0);    
    globalRandSeed2 = result & 0x00FF;
    result = (short)(randSeed1 + carry + tempRand1);
    carry = (short)((result > 0x00FF) ? 1 : 0);
    result = (short)(result & 0x00FF);
    result = (short)(result + carry + tempRand2);
    globalRandSeed1 = result & 0x00FF;
  }
Post Reply