Assistance with C# Logic

luke127

The Ghost
Messages
868
Location
Australia
Hey guys,

So there needs to be a bit of back story for you to understand this one, and I'll try to keep it as minimal as possible.

So I'm a moderate modded minecraft player these days, and I've taken quite the fancy to a mod named RotaryCraft. However, this mod is quite mathematically intensive compared to many other minecraft mods, and so therefore many players dislike it because of this. However its gameplay is awesome and I'm sure many more players would enjoy it if they didn't have to worry so much about the mathematical side of it. The maths itself is really quite simple, however many play minecraft to relax and *not* use their brain.

Now, RotaryCraft has a series of engines that output a specific amount of speed (measured in Rad/s) and torque (measured in Nm) which when multiplied together give the engine's total power output in Watts. But this is not the problem.

RotaryCraft's main set of mechanics rely on using gearboxes to modify the torque or speed outputs of each engine according to what the engine is required to power. For example, a Pulse Jet Furnace from RotaryCraft requires 131072 Rad/s of speed to run, but has no torque requirement.

This means that there are a series of gearboxes in the mod that allow you to change the torque and speed. These gearboxes come in the ratios of 2:1, 4:1, 8:1, 16:1 and 32:1. However, most of the time it's not just using gearboxes to achieve the desired result, it also requires up to 4 of the same engine in order to achieve certain results. This is done using a shaft junction, and only 4 engines may be chained at any given time. Different engines cannot be chained for obvious reasons (we're talking about shafts here, try merging the speeds of two different shafts and see what happens.). Using shaft junctions will multiply the outputted torque by the number of engines being used.

Here comes the fun part.

What I want this program to do, is calculate what gearbox ratios are required to reach a user inputted speed or torque, and output this, along with what engines were used to achieve that result.

I've already written some dictionaries containing all the necessary values, and I've written a Gearbox method that *should* deal with the ratios. But now I need the program to loop over all the engines, take each engine's speed and torque, and attempt to achieve the user inputted value. Of course if the value is unattainable from any engine, it will output this as well.

Another important thing to note is that more than 1 gearbox can be used, and they multiply. Meaning that I could chain a 2:1 and a 4:1 gearbox to achieve the same result as an 8:1 gearbox. This becomes more relevant once you start needing ratios of like 512:1. (A 32:1 and a 16:1 would achieve this).

Here's the code I've got so far, and it also has all the power information for each engine.

Code:
namespace Engine_Selection
{
    public partial class Form1 : Form
    {
        Dictionary<string, int> EngineSpeeds = new Dictionary<string, int>();
        Dictionary<string, int> EngineTorques = new Dictionary<string, int>();
        Dictionary<string, int> EnginePower = new Dictionary<string, int>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            InitialiseDictionary();
        }

        private void InitialiseDictionary()
        {
            // Engine speed entries. (Rad/s)
            EngineSpeeds.Add("DC Electric Engine", 256);
            EngineSpeeds.Add("Wind Turbine", 1024);
            EngineSpeeds.Add("Steam Engine", 512);
            EngineSpeeds.Add("Gasoline Engine", 512);
            EngineSpeeds.Add("AC Electric Engine", 256);
            EngineSpeeds.Add("Performance Engine", 1024);
            EngineSpeeds.Add("Hydrokinetic Engine", 32);
            EngineSpeeds.Add("Microturbine", 131072);
            EngineSpeeds.Add("Gas Turbine", 65536);

            // Engine torque entries. (Nm)
            EngineTorques.Add("DC Electric Engine", 4);
            EngineTorques.Add("Wind Turbine", 4);
            EngineTorques.Add("Steam Engine", 32);
            EngineTorques.Add("Gasoline Engine", 128);
            EngineTorques.Add("AC Electric Engine", 512);
            EngineTorques.Add("Performance Engine", 256);
            EngineTorques.Add("Hydrokinetic Engine", 16384);
            EngineTorques.Add("Microturbine", 16);
            EngineTorques.Add("Gas Turbine", 1024);

            // Engine power entries. (W)
            EnginePower.Add("DC Electric Engine", 1024);
            EnginePower.Add("Wind Turbine", 4096);
            EnginePower.Add("Steam Engine", 16384);
            EnginePower.Add("Gasoline Engine", 65536);
            EnginePower.Add("AC Electric Engine", 131072);
            EnginePower.Add("Performance Engine", 262144);
            EnginePower.Add("Hydrokinetic Engine", 524188);
            EnginePower.Add("Microturbine", 2097152);
            EnginePower.Add("Gas Turbine", 67108864);

            return;
        }

        private void BtnCalculate_Click(object sender, EventArgs e)
        {
            int Speed = 0;
            int Torque = 0;
            int Power = 0;

            if (TxtSpeed.Text != "")
                if (int.TryParse(TxtSpeed.Text, out Speed)) ;

            if (TxtTorque.Text != "")
                if (int.TryParse(TxtTorque.Text, out Torque)) ;

            if (TxtPower.Text != "")
                if (int.TryParse(TxtPower.Text, out Power)) ;

            

            // A generic method works best, but now I need to call this method from the
            // loop logic, and that's a bitch... Perhaps a while statement can help here?

            



        }

        private void Gearbox(int InputSpeed, int InputTorque, string Mode, int Magnitude, out int OutputSpeed, out int OutputTorque)
        {
            // Setting outputs to inputs in order to fulfil requirements for out parameters.
            OutputSpeed = InputSpeed;
            OutputTorque = InputTorque;

            // Gearbox set to speed.
            if (Mode == "Speed")
            {
                OutputSpeed = InputSpeed * Magnitude;
                OutputTorque = InputTorque / Magnitude;
            }

            // Gearbox set to torque.
            if (Mode == "Torque")
            {
                OutputSpeed = InputSpeed / Magnitude;
                OutputTorque = InputTorque * Magnitude;
            }

            return;
        }
    }
}
 
Instead of using separate dictionaries...why not just create an Engine class with all the necessary properties of Speed/Torque/Power, and have an Enum for the Type?

Then you can pass a method a List<Engine> object, and do your calculations that way, so they can easily interact and you'll cut down on doing some nasty string compares.

So, in some code/pseudocode:

Code:
public class Engine
{
	public EngineType Type { get; set; }
	//maybe even consider making these Double instead of int? Would help with casting down the road, esp. for division 
	public int Speed { get; set; }
	public int Torque { get; set; }
	public int Power { get; set; }
	public int GearRatio { get; set; } //or Magnitude?  I'm guessing that's what this is 
}

//can put this in a separate CS file - doens't need to be within a class 
public enum EngineType 
{
	DCElectricEngine,
	WindTurbine,
	SteamEngine,
	GasolineEngine,
	ACElectricEngine,
	PerformanceEngine,
	HydrokineticEngine,
	Microturbine,
	GasTurbine
}

//then something like 
var speed = 0.0;
var torque = 0.0;

foreach(var engine in engineList)
{
	speed += CalculateSpeed(engine)
	torque += CalculateTorque(engine)
}
 
Ok Carnage you might have to ELI5 with regards to this, because while I am fluent in the basic functions of the C# language, I've never used specific things like enum. (Though I've indirectly used them when using foreach statements iterating over an array, but that's different, they're easy to understand XD ).

How exactly does this work?
 
How does what work? How do the enums work? Enums are just 0 based integer object collections, but allow you to make it more readable to humans. They make it so you don't have to use "magic strings" and string comparisons, which are somewhat expensive operations to perform.

I suggest checking out DotNetPerls info on enums: https://www.dotnetperls.com/enum
 
Last edited:
So I got bored and decided to write up how I would do it, based on what I understand you're doing.

Created a Services class that would contain all of the business logic for calculation (inside of a Services folder in the solution), and you could call the Calculate method from this class in your main program:
Code:
namespace MCGearCalc.Services
{
    public class EngineService
    {
        private List<Engine> _engines; 

        public EngineService()
        {
            InitializeEngines();
        }

        public void InitializeEngines(List<Engine> engines = null)
        {
            if(engines != null && engines.Count > 0) { _engines = engines;  return; }

            _engines = new List<Engine>
            {
                new Engine { Type = EngineType.DCElectricEngine, Speed = 256, Torque = 4, Power = 1024 },
                new Engine { Type = EngineType.WindTurbine, Speed = 1024, Torque = 4, Power = 4096 },
                new Engine { Type = EngineType.SteamEngine, Speed = 512, Torque = 32, Power = 16384 },
                new Engine { Type = EngineType.GasolineEngine, Speed = 512, Torque = 128, Power = 65536 },
                new Engine { Type = EngineType.ACElectricEngine, Speed = 256, Torque = 512, Power = 131072 },
                new Engine { Type = EngineType.PerformanceEngine, Speed = 1024, Torque = 256, Power = 262144 },
                new Engine { Type = EngineType.HydrokineticEngine, Speed = 32, Torque = 16384, Power = 524188 },
                new Engine { Type = EngineType.Microturbine, Speed = 131072, Torque = 16, Power = 2097152 },
                new Engine { Type = EngineType.GasTurbine, Speed = 65536, Torque = 1024, Power = 67108864 }
            };
        }

        public LoopTotal Calculate(List<Engine> engines, CalcMode mode)
        {
            var lt = new LoopTotal()
            {
                Speed = 0.0,
                Torque = 0.0,
                Power = 0.0
            };

            foreach(var engine in engines)
            {
                var speed = CalculateGearboxSpeed(engine, mode);
                if(speed != null) { lt.Speed += speed; } else { lt.Speed = null; break; }

                var torque = CalculateGearboxTorque(engine, mode);
                if (torque != null) { lt.Torque += torque; } else { lt.Torque = null;  break; }
            }

            return lt;
        }

        public double? CalculateGearboxSpeed(Engine engine, CalcMode mode)
        {
            if (mode == CalcMode.Speed)
            {
                return engine.Speed * engine.GearRatio;
            }
            else if(mode == CalcMode.Torque)
            {
                return engine.Speed / engine.GearRatio;
            }
            else
            {
                return null;
            }
        }

        public double? CalculateGearboxTorque(Engine engine, CalcMode mode)
        {
            if (mode == CalcMode.Speed)
            {
                return engine.Torque / engine.GearRatio;
            }
            else if (mode == CalcMode.Torque)
            {
                return engine.Torque * engine.GearRatio;
            }
            else
            {
                return null;
            }
        }
    }
}

Here's the EngineType enum (inside a Models folder in the solution):
Code:
namespace MCGearCalc.Models
{
    public enum EngineType
    {
        DCElectricEngine,
        WindTurbine,
        SteamEngine,
        GasolineEngine,
        ACElectricEngine,
        PerformanceEngine,
        HydrokineticEngine,
        Microturbine,
        GasTurbine
    }
}

Here's the Engine class file (inside the Models folder):
Code:
namespace MCGearCalc.Models
{
    public class Engine
    {
        public EngineType Type { get; set; }
        public double Speed { get; set; }
        public double Torque { get; set; }
        public double Power { get; set; }
        public double GearRatio { get; set; } //or Magnitude?  I'm guessing that's what this is 
    }
}

And here's a "loop total" (loop as in all the engines, not the programming term) class so you can pass a single object back to your main program (again, inside the Models folder), so the main Calculate method from the Service class isn't messy with reference or out parameters:
Code:
namespace MCGearCalc.Models
{
    public class LoopTotal
    {
        public double? Speed { get; set; }
        public double? Torque { get; set; }
        public double? Power { get; set; }
    }
}

And here's the CalcMode enum, based on whether you're calculating Speed or Torque (saw you were just passing in a string for this and doing a compare that way...much nicer to use enums)...again, class is in the Models folder:
Code:
namespace MCGearCalc.Models
{
    public enum CalcMode
    {
        Speed,
        Torque
    }
}

Let me know if there's something you don't understand, and I'll try to explain my reasoning.
 
Last edited:
The reason why I chose integers instead of doubles for Torque and Speed is because in game, these values cannot have decimal places, and cannot be negative. They have to be a whole number that is => 0.

Edit: From what I can see of your code (and what I understand), I cannot see an area where you're taking some form of user input, and then attempting to find engines that can create an output to match it.

I'm beginning to think this project might just be too much for what it's worth tbh. It's quite simple to figure this out on paper, but perhaps programming is simply over complicating it.
 
Last edited:
The reason why I chose integers instead of doubles for Torque and Speed is because in game, these values cannot have decimal places, and cannot be negative. They have to be a whole number that is => 0.
You can cast/round to an int in your UI layer - I made them double in the service layer in case you wanted/needed them as doubles down the road. If they're >= 0, might as well go all the way and use UInt instead then :p.

Edit: From what I can see of your code (and what I understand), I cannot see an area where you're taking some form of user input, and then attempting to find engines that can create an output to match it.
I'm only taking in a List<Engine> in the Calculate method as well as a CalcMode enum - that'd be what you call from your UI layer.

I only did the bare-basic business logic, nothing to do with user input/UI or finding/searching. In your UI layer, some ideas you could do:
1) have a checklist (or dropdown checklist even) that displayed the string value of the EngineType enum, and then add a method in the EngineService class to lookup from the initialized object (you could even make this hard coded right away as a private Property instead of having the constructor initialize it - either way should be fine). That lookup method could take in a list of EngineType enums, match to each item in the _engineList property, add them to a new list, and then return that new list. So something like:

Code:
public List<Engine> EngineLookup(List<EngineType> types)
{
	var engines = new List<Engine>();

	foreach (var type in types)
	{
		var e = _engines.Find(t => t.Type == type);
		if (e != null) { engines.Add(e); }
	}

	return engines;
}

Or, you could even:
2) Instead of that, just have a select list of the actual objects, and then you can have the user select those objects, and those can be added to a List<Engine> and sent directly into the Calculate method.

I'm beginning to think this project might just be too much for what it's worth tbh. It's quite simple to figure this out on paper, but perhaps programming is simply over complicating it.

Eh, I don't necessarily think it's overcomplicated. Maybe I'm just adding too many "new" things into it for you? Besides, you could at least look at it as a learning experience ;).
 
*snip*

Eh, I don't necessarily think it's overcomplicated. Maybe I'm just adding too many "new" things into it for you? Besides, you could at least look at it as a learning experience ;).

Definitely adding loads of new **** xD I've seen those get; set; statements before, but I've never used them, nor have I ever created my own class and instantiated that class, like I said, I have a basic understanding of the bare fundamentals, but anything beyond that starts getting more complicated.

I really should be putting effort into Java since that's what my University course will teach, but I haven't been able to find a tutorial series that is even close to the quality of Bob Tabor's C# Fundamentals videos, and C# just looks visually more appealing... Java is just... ew. I mean I hate the way if statements are written like this:

Code:
if (bla == "blabla") {
    foo = bar
}
 
Last edited:
Definitely adding loads of new **** xD I've seen those get; set; statements before, but I've never used them, nor have I ever created my own class and instantiated that class, like I said, I have a basic understanding of the bare fundamentals, but anything beyond that starts getting more complicated.
Classes are something you should focus heavily on when learning C# - it is after all an Object Oriented Language. Usually most tutorials will introduce objects after basic syntax / usage.

get; sets are really basic too - they're called Properties. What you've probably ususally been using are called "fields" (which is also what the _engineList variable I made is). Properties have getters/setters - in which you can put logic into, make them only getters, make them only setters, etc.

I really should be putting effort into Java since that's what my University course will teach, but I haven't been able to find a tutorial series that is even close to the quality of Bob Tabor's C# Fundamentals videos, and C# just looks visually more appealing... Java is just... ew.
If you learn one, you can learn the other pretty quickly - pick the one you like more (for me it was C#) and learn it...then when it comes time to learn Java, you'll be able to pick it up fairly quickly if you know a lot of C#. Just some minor differences. You know that C# used to be a modification of Java until Sun sued Microsoft, so MS said "lol ok we'll make our own language" - and they did. And it's better.

But I agree, I'm not really a fan of Java either.

I mean I hate the way if statements are written like this:

Code:
if (bla == "blabla") {
    foo = bar
}

That's just a style guide. I know people that write them like that in C# too. You can put the beginning brace on a new line if you like. I don't like to do it the "java-style" in C# either....but I'm fine with doing it in the JavaScript work I do. Java is like C# in the sense that whitespace doesn't matter.
 
Looks like I need to do some serious reading, and some examples while I'm at it so that I can break all of this down into bite size pieces, and then use each part in the next. Going from something like this: https://github.com/LC14199/Series-Calculator/blob/master/Series Solver/Form1.cs

To the kinda stuff you're describing is a big leap. (Also please note, that Series Solver was designed for a specific exercise in my Grade 12 Mathematics textbook, and as such does not contain the usual checks that a program designed for widespread use would contain.)
 
Last edited:
Back
Top Bottom