0

I have such a simple code to generate a character in a game.

string[] sex = new string[] { "Man", "Woman" };
string[] age = new string[] { "Child", "Teen", "Young", "Middle-aged", "Elderly", "Old" };
string[] beauty = new string[] { "Beautiful", "Pretty", "Normal", "Plain", "Ugly" };

Random i = new Random();
string sexR = sex[i.Next(0, sex.Length)];
string ageR = age[i.Next(0, age.Length)];
string beautyR = beauty[i.Next(0, beauty.Length)];
Console.WriteLine("{0} {1} {2}", sexR, ageR, beautyR);

While I have just three lines it's Ok. But I want to have many more character traits in the future. How can I make a function that will generate random values from ANY number of arrays I create in my program?

3
  • there'd be little point in making a function for this. you'd have to call it mygetrandom(array) anyways, so you'd be saving nothing in lines-of-code terms. Commented May 19, 2015 at 19:46
  • Is it possible to generate list of random strings without mentioning the names of arrays/lists again? Commented May 19, 2015 at 19:48
  • well, don't know about c#, but if it was a dynamic scripting language, you could make an array of array names, and loop on that. e.g. in php, $names = array('sex', 'age', 'beauty');, then use variable-variables to access each array in turn: foreach($names as $name) { $str = array_rand($$name); } Commented May 19, 2015 at 19:49

3 Answers 3

1

You can create a list of your arrays and loop through those arrays, storing each attribute separately and using a stringbuilder to output the result.

List<string[]> traits = new List<string[]> 
{
    new string[] { "Man", "Woman" },
    new string[] { "Child", "Teen", "Young", "Middle-aged", "Elderly", "Old" },
    new string[] { "Beautiful", "Pretty", "Normal", "Plain", "Ugly" }
};

Random i = new Random();
StringBuilder traitOutput = new StringBuilder();

foreach (string[] trait in traits)
{
    traitOutput.AppendFormat("{0} ", trait[i.Next(0, trait.Length)]);
}

Console.WriteLine(traitOutput);
Sign up to request clarification or add additional context in comments.

Comments

1

If you want to be able to get the value of any number of lists, instead of just a single one, then write a method that accepts a collection of lists, and picks a random item of each one:

public static IEnumerable<T> ChooseFromAll<T>(
    IEnumerable<IList<T>> lists,
    Random generator)
{
    foreach (var list in lists)
        yield return list[generator.Next(list.Count)];
}

Comments

1

I think we can first talk about clearer code for your use case. You clearly are creating some kind of person, so lets represent that with a person class. We've got quite a complex creation scenario, we could put it in the constructor, but I think the best thing to do is create a factory class for this.

Given that, we can use tricks like GetRandomElement to ease the syntax. However, I don't think we need to generalise further. I'm not keen on foreach-ing through the properties or anything like that, I think holding the arrays of your possible values distinct is more declarative.

public class Person{

    public string Sex { get;set;}
    public string Age {get;set;}
    public string Beauty { get;set;}    
}

public class RandomPersonFactory{

    private Random random;
    private string[] SexChoices = new[] { "Man", "Woman" };
    private string[] AgeChoices = new[] { "Child", "Teen", "Young", "Middle-aged", "Elderly", "Old" };
    private string[] BeautyChoices = new[] { "Beautiful", "Pretty", "Normal", "Plain", "Ugly" };

    public RandomPersonFactory(Random random){
        this.random = random;
    }

    public Person CreatePerson(){
        return new Person {
            Sex = GetRandomElement(this.SexChoices),
            Age = GetRandomElement(this.AgeChoices),
            Beauty = GetRandomElement(this.BeautyChoices)
        };
    }

    private T GetRandomElement<T>(T[] array){
        return array[this.random.Next(array.Length)];
    }
}

You could organize your code like this; But if you aren't genuinely changing the quantity and type of traits on individual persons at run time it's needless complexity and misdirection. Ask yourself whether you can have a person without a sex trait, or age trait etc. In this case I would write the factory in a similar way, but I would build up an collection of traits one trait at a time in the CreatePerson method and then create the person with that collection.

public class Person2{
   public IList<Trait> Traits {get;set;}
}

public class Trait{
    public string Name {get;set;}
    public string Value {get;set;}
}

5 Comments

Thank you very much, Nathan. I will think about that.
Actually you are right. I am creating a person. And now I am starting to think how to avoid stuff like a fat and old but beautiful woman..)
@SergZ My concern is that marshaling all your data and lumping it into objects is an approach for c, not c#. In c# the behavior should be in the objects. Your future problem is a bit more complex, with two seconds thought, unless you only have a few silly combinations you could hack out, you'll probably want to end up randomly navigating down a tree like structure.
Yes, I indeed think of a tree. So that if I generate an "ugly woman", it leads to "wrinkled face" etc. And If I generate a pretty girl it can't lead to "fat". So I am thinking what tree structures are available for C sharp.
@SergZ I think you have to roll your own.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.