7 Replies - 481 Views - Last Post: 30 August 2019 - 10:30 AM Rate Topic: -----

#1 Layec   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 28-October 13

RNG in the wrong spot

Posted 29 August 2019 - 11:54 AM

Hello All:

I'm making a console-based character generator for the Tabletop RPG (Houseruled BRP for the interested). The first part rolls the attributes for x characters (currently 20) from which the player chooses. Right now I'm just working on generating the rolls for each character. I've run into an obvious problem - each character this program rolls has the same stats because each instance of the character class uses a random number generator with the same seed. I admit I'm kind of at a loss as to how to structure the program now to remove that bug. I'd appreciate any help.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BRP_CharGen
{
    class Character
    {
        //Random Number base
        Random rand = new Random();

        //Attributes
        public int att_Str;
        public string att_StrRolls;

        private int[] Roll(int number, int sides)
        {
            //rolls any number of any sided dice, and collects all the rolls in an array
            int[] DiceRolls = new int[number];
            for (int i = 0; i < number; i++)
            {
                DiceRolls[i] = rand.Next(1, sides + 1);
            }
            return DiceRolls;
        }



        public void RollAttributes(int number, int sides, ref string attRolls, ref int att)
        {
            //makes the roll and puts it in a local array
            int[] Rolls = Roll(number, sides);

            //takes the numbers from the array and makes a string showing the rolls, to post on the console
            attRolls = Rolls[0].ToString();
            for (int i = 1; i < number; i++)
            {
                attRolls += ", " + Rolls[i].ToString();
            }

            //calculates the value of the attribute
            att = Rolls.Sum();
        }
    }
    class Program
    {
        public void OpeningText()
        {
            Console.WriteLine("BRP Character Generator 1.0");
            Console.WriteLine("");
            Console.WriteLine("This program will automate the process of generating your characters and export it to a text file.");
            Console.WriteLine("Warning - Your character can die during character creation.  You will have to start over if this happens.");
            Console.WriteLine("Press Enter to begin.");
            Console.ReadLine();
        }
        static void Main()
        {
            //True will continue the loop, false will break out of it.
            bool Loop_Continue = true;
            
            //Sets the first state.  Hereafter will be changed within the loop.
            string state = "Begin";

            //The while loop, encompassing the switch statement.  
            while (Loop_Continue == true)
            {
                switch (state)
                {
                    //Case Begin - Opening Text and RollAttributes.  Player will select which character to play and will transition to career phase.
                    case "Begin":
                        //Set number of Characters to Generate
                        int HowMany = 20;

                        //Display Opening Text
                        new Program().OpeningText();

                        //Instantiate Each Temporary Character
                        Character[] TempCharacter = new Character[HowMany];
                        for (int i = 0; i < HowMany; i++)
                        {
                            TempCharacter[i] = new Character();
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_StrRolls, ref TempCharacter[i].att_Str);
                        }
                        Console.WriteLine("Breakpoint");

                        break;

                    //Case Career - 
                    //case "Career":

                    //    break;




Is This A Good Question/Topic? 0
  • +

Replies To: RNG in the wrong spot

#2 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 15268
  • View blog
  • Posts: 61,211
  • Joined: 12-June 08

Re: RNG in the wrong spot

Posted 29 August 2019 - 12:01 PM

The easy solution would be to have the class's constructor take in a Random object. The driver program would provide this Random object each time a new class instance is created.
Was This Post Helpful? 0
  • +
  • -

#3 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: RNG in the wrong spot

Posted 29 August 2019 - 12:34 PM

Since he is creating his characters in an array (see line 80), passing in that random obxject instance through constructor dependency injection is going to be much harder. :)/>
Was This Post Helpful? 0
  • +
  • -

#4 Layec   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 28-October 13

Re: RNG in the wrong spot

Posted 30 August 2019 - 09:08 AM

The solution, once found, will be obvious.

So I slept on it, and 15 minutes after I started working on it I found an answer. I had to put the "Character" class in the Program class, and move the Random instance to the Program class and make it static. Nothing else required.
Was This Post Helpful? 0
  • +
  • -

#5 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 15268
  • View blog
  • Posts: 61,211
  • Joined: 12-June 08

Re: RNG in the wrong spot

Posted 30 August 2019 - 09:11 AM

Ah.. what do you mean? For the most part you should not be nesting classes.
Was This Post Helpful? 0
  • +
  • -

#6 Layec   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 28-October 13

Re: RNG in the wrong spot

Posted 30 August 2019 - 09:59 AM

Is there a way to do it without nesting it?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BRP_CharGen
{
    
    class Program
    {
        //Random Number base
        static Random rand = new Random();

        class Character
        {
            public int att_Str, att_Dex, att_Con, att_Siz, att_Int, att_Wil, att_App, att_Soc, att_Wea, att_Edu, att_Psi;
            public string att_StrRolls, att_DexRolls, att_ConRolls, att_SizRolls, att_IntRolls, att_WilRolls, att_AppRolls, att_SocRolls, att_WeaRolls, att_PsiRolls;

            private int[] Roll(int number, int sides)
            {
                //rolls any number of any sided dice, and collects all the rolls in an array
                int[] DiceRolls = new int[number];
                for (int i = 0; i < number; i++)
                {
                    DiceRolls[i] = rand.Next(1, sides + 1);
                }
                return DiceRolls;
            }



            public void RollAttributes(int number, int sides, ref string attRolls, ref int att)
            {
                //makes the roll and puts it in a local array
                int[] Rolls = Roll(number, sides);

                //takes the numbers from the array and makes a string showing the rolls, to post on the console
                attRolls = Rolls[0].ToString();
                for (int i = 1; i < number; i++)
                {
                    attRolls += ", " + Rolls[i].ToString();
                }

                //calculates the value of the attribute
                att = Rolls.Sum();
            }
        }
        private static int CharacterSelect(string prompt, int HowMany)
        {
            while (true)
            {
                Console.WriteLine(prompt);
                string input = Console.ReadLine();
                int value;
                if (int.TryParse(input, out value))
                {
                    if (value < 1)
                        Console.WriteLine("Please enter a number between 1 and {0}.", HowMany);
                    else if (value > HowMany)
                        Console.WriteLine("Please enter a number between 1 and {0}.", HowMany);
                    else
                        return value;
                }
                else
                {
                    Console.WriteLine("Please enter a number between 1 and {0}.", HowMany);
                }
            }
        }
            public void OpeningText()
        {
            Console.WriteLine("BRP Character Generator 1.0");
            Console.WriteLine("");
            Console.WriteLine("This program will automate the process of generating your characters and export it to a text file.");
            Console.WriteLine("Warning - Your character can die during character creation.  You will have to start over if this happens.");
            Console.WriteLine("Press Enter to begin.");
            Console.ReadLine();
        }
        static void Main()
        {
            //True will continue the loop, false will break out of it.
            bool Loop_Continue = true;
            
            //Sets the first state.  Hereafter will be changed within the loop.
            string state = "Begin";

            //The while loop, encompassing the switch statement.  
            while (Loop_Continue == true)
            {
                switch (state)
                {
                    //Case Begin - Opening Text and RollAttributes.  Player will select which character to play and will transition to career phase.
                    case "Begin":
                        //Set number of Characters to Generate
                        int HowMany = 20;
                        

                        //Display Opening Text
                        new Program().OpeningText();

                        //Instantiate Each Temporary Character and Roll Attributes
                        Character[] TempCharacter = new Character[HowMany];
                        for (int i = 0; i < HowMany; i++)
                        {
                            TempCharacter[i] = new Character();
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_StrRolls, ref TempCharacter[i].att_Str);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_DexRolls, ref TempCharacter[i].att_Dex);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_ConRolls, ref TempCharacter[i].att_Con);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_SizRolls, ref TempCharacter[i].att_Siz);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_IntRolls, ref TempCharacter[i].att_Int);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_WilRolls, ref TempCharacter[i].att_Wil);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_AppRolls, ref TempCharacter[i].att_App);
                            TempCharacter[i].RollAttributes(3, 6, ref TempCharacter[i].att_SocRolls, ref TempCharacter[i].att_Soc);
                            TempCharacter[i].RollAttributes(1, 6, ref TempCharacter[i].att_WeaRolls, ref TempCharacter[i].att_Wea);
                            TempCharacter[i].RollAttributes(4, 10, ref TempCharacter[i].att_PsiRolls, ref TempCharacter[i].att_Psi);
                            TempCharacter[i].att_Wea += TempCharacter[i].att_Soc - 3;
                            if (TempCharacter[i].att_Psi > 22) TempCharacter[i].att_Psi += -22;
                            else TempCharacter[i].att_Psi = 0;
                        }




                        break;

                    //Case Career - 
                    //case "Career":

                    //    break;

                    //case "Reset":

                    //    break;

                    //default:
                    //    Console.WriteLine("How the Hell did you get here???");
                    //    break;
                }
            }
        }
    }
}


Was This Post Helpful? 0
  • +
  • -

#7 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7485
  • View blog
  • Posts: 15,514
  • Joined: 16-October 07

Re: RNG in the wrong spot

Posted 30 August 2019 - 10:15 AM

Hmm... since you already, forgive me, don't seem to understand the point of objects, you might as well use some static.

A helper class the does your random stuff makes sense:
public static class StatGenerator {
    // all your stuff that might use rand
    private static readonly Random rand = new Random();

    public static int Roll(int sides) => rand.Next(1, sides + 1);

    public static IEnumerable<int> Rolls(int number, int sides) {
        for(int i = 0; i<number; i++) {
            yield return Roll(sides);
        }
    }
    // public static void RollAttributes(int number, int sides) { // ewwww, ref string attRolls, ref int att) {
}



Your Character can then leverage that:
public class Character {
    public Character(IEnumerable<int> rolls) { Rolls = rolls.ToArray(); }
    public Character(int number, int sides) : this(StatGenerator.Rolls(number, sides)) { }
    public int[] Rolls { get; }
    public string AttrStr => string.Join(", ", Rolls.Select(x => x.ToString()));
    public int AttrValue => Rolls.Sum();
    public override string ToString() => $"{AttrStr} : {AttrValue}";
}



Note, the only state Character has here is their stats: the rest may be derived from that state.

And, a quick test:
var howMany = 20;
Character[] characters = Enumerable.Range(0, howMany).Select(_ => new Character(3, 6)).ToArray();
foreach (var c in characters) {
    Debug.WriteLine(c);
}



Hope this helps.
Was This Post Helpful? 0
  • +
  • -

#8 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7485
  • View blog
  • Posts: 15,514
  • Joined: 16-October 07

Re: RNG in the wrong spot

Posted 30 August 2019 - 10:30 AM

Heh, you snuck in while I was posting. Given this, something like:
public class CharacterAttr {
    public CharacterAttr(IEnumerable<int> rolls) { Rolls = rolls.ToArray(); }
    public CharacterAttr(int number, int sides) : this(StatGenerator.Rolls(number, sides)) { }
    public int[] Rolls { get; }
    public string AttrStr => string.Join(", ", Rolls.Select(x => x.ToString()));
    public int AttrValue => Rolls.Sum();
    public override string ToString() => $"{AttrStr} : {AttrValue}";
}

public class Character {
    public Character() {      }

    public CharacterAttr Str { get; } = new CharacterAttr(3, 6);
    public CharacterAttr Dex { get; } = new CharacterAttr(3, 6);
    public CharacterAttr Wea { get; } = new CharacterAttr(1, 6);
    public CharacterAttr Psi { get; } = new CharacterAttr(4, 10);
}



However, maintaining your initial rolls makes things messy, given your latter mods.

Maybe:
public class CharacterAttr {
    public CharacterAttr(string initRolls, int value) {
        InitRolls = initRolls;
        Value = value;
    }
    public CharacterAttr(IEnumerable<int> rolls) : this(string.Join(", ", rolls.Select(x => x.ToString())), rolls.Sum()) { }
    public CharacterAttr(int number, int sides) : this(StatGenerator.Rolls(number, sides)) { }
    public string InitRolls { get; }
    public int Value { get; }
}

public class Character {
    private CharacterAttr wea { get; } = new CharacterAttr(1, 6);
    public Character() { }
    public CharacterAttr Soc { get; } = new CharacterAttr(3, 6);
    public CharacterAttr Wea => new CharacterAttr(wea.InitRolls, wea.Value + Soc.Value - 3);

}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1