开发者

Random weighting in C#

开发者 https://www.devze.com 2023-04-07 11:02 出处:网络
I\'m creating a game in C#, and a crucial part of the game is randomness. It\'s essentially a wrestling simulator, where \'moves\' are selected on 开发者_JS百科a table depending on a number of factors

I'm creating a game in C#, and a crucial part of the game is randomness. It's essentially a wrestling simulator, where 'moves' are selected on 开发者_JS百科a table depending on a number of factors, such as wrestlers attributes and current momentum.

Then out of all the moves that that match this criteria, a random one is selected to be executed using the Random object and Skip/Take in LINQ But this is really not enough. What I want to do is weight moves probability of being chosen (I already have a column on the moves table for this, for an integer between 1 and 100). How would I implement this weighting into my random row selection?


this is not too difficult. I have no code to work with so I assume you have objects Move with an attribute Weight inside a array and all weights sum up to 100.0 (don't really matter). Now you sort the array by weights decending, pick a random number between 0 and 99 and iterate through all this decreasing your random number. As soon as it not positive anymore you stop and pick the current index/move

var value = rnd.NextDouble()*100.0;
foreach(var move in moves.OrderByDescending(m => m.Weight))
{
   value -= move.Weight;
   if (value <= 0) return move;
}

of course you can cache the ordering or even the picks into a big array and use a random-index into this (for performance) but the priciple should be clear I hope.

As George suggested - here is a version where you can drop to assume that the weights sum up to 100:

double _weightSum;
...
// initialize the Sum somewhere
_weightSum = moves.SumBy(m => m.Weight);

Move GetRandomMove()
{
    var value = rnd.NextDouble()*weightSum;
    foreach(var move in moves.OrderByDescending(m => m.Weight))
    {
       value -= move.Weight;
       if (value <= 0) return move;
    }
}


Some code would be helpful for me to understand exactly what you need, but I suggest using the Random .NET library function. Some documentation can be found here:

http://msdn.microsoft.com/en-us/library/system.random.aspx

This example generates 5 random integers

Random rand = new Random();

Console.WriteLine("Five random integer values:");
for (int ctr = 0; ctr <= 4; ctr++)
   Console.Write("{0,15:N0}", rand.Next());
Console.WriteLine();

This will seed it with the current time to make it "more random". If you want reproducible tests, you can seed it with a constant while testing.


Add up the total weight of each possible move.

Divide each one by that total, so you've normalized the range to 0..1.

Get a random number in that range. Choose a consistent order for each move and pick the one that the random number is within.


I looked at doing basically the same thing as Carsten, but with Linq.

given moves is a collection of Moves each with an integer property of Weight

public Move PickRandomMove()
{
    var allMovesWeight = moves.Sum(m => m.Weight);
    // pick a unit of weight at random, then shift it along by the weight of the 
    // first move so that there will always be an element in the TakeWhile results
    var randomPick = new Random().Next(0, allMovesWeight) +  moves.First().Weight;

    return moves.TakeWhile(move => (randomPick -= move.Weight) > 0).Last(); 
}

I suspect there's a clearer way of expressing how the TakeWhile is working, but hopefully you get the idea

0

精彩评论

暂无评论...
验证码 换一张
取 消