Welcome to Keen Software House Forums! Log in or Sign up to interact with the KSH community.
  1. You are currently browsing our forum as a guest. Create your own forum account to access all forum functionality.

Is there a possibility to use a group of blocks in a method?

Discussion in 'Programming Questions and Suggestions' started by Ekuah, Jun 18, 2018.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. Ekuah Trainee Engineer

    Messages:
    99
    I have a problem.
    I have a running script that is very long, because there are a repetitive calculations done on several different kinds of blocks.

    basically:
    Code:
    void Main(string argument)
    {
      ...
      List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyRadioAntenna>(blocks);
      ...
      for(int i = 0; i < blocks.Count; i++) {
    	if(blocks[i].IsWorking) {
    	  ...
    	  do something
    	  ...
    	}
      }
      ..
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyBeacon>(blocks);
      ...
      for(int i = 0; i < blocks.Count; i++) {
    	if(blocks[i].IsWorking) {
    	  ...
    	  do something
    	  ...
    	}
      }
      ...
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyCameraBlock>(blocks);
      ...
    ect.
    }
    You see, this will quickly blow up the amount of code.

    Now I would like to transfer the repetitive parts into a method, like this:
    Code:
    void Main(string argument)
    {
      ...
      List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyRadioAntenna>(blocks);
      number= my_method(blocks);
      ...
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyBeacon>(blocks);
      number= my_method(blocks);
      ...
    //-------------------------------------------------------------------------------------------
      GridTerminalSystem.GetBlocksOfType<IMyCameraBlock>(blocks);
      ...
    ect.
    }


    But I'm unable to get method to use chosen the block-list.
    Even this test code won't work:
    Code:
    public Program()void Main(string argument)
    {
      List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
      GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(blocks);
      int amount=my_method(blocks);
      Echo(Convert.ToString(amount));
    }
    int my_method(IMyTerminalBlock cubes) {
      int number=cubes.Count;
      return number;
    }
    Does anyone have some advice? (Except quitting SE again)
     
  2. Malware Master Engineer

    Messages:
    9,032
    You need to pass the list, not just "IMyTerminalBlock" in my_method. You've declared the method to receive one block while trying to send it a list.

    I strongly advise you to use an intellisense-enabled IDE. My MDK plugin will help you if you want to use Visual Studio - which again will help you find problems like these.

    Also don't forget that there's a dedicated ingame programming channel on Keen's discord.

    https://github.com/malware-dev/MDK-SE/wiki/Getting-Started

    Also you should avoid allocating new lists and fetching blocks every time the script is run. This is a slow process.
     
    • Like Like x 1
  3. Ekuah Trainee Engineer

    Messages:
    99
    Okay, how do I do that?
    I'm allocating a new lists only once:
    Code:
    List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
    The rest is done with just this list.

    P.S.
    Thank you for your quick reply.
    I must confess that I nearly have no experience in C-Coding.
    I originally learned basic (decades ago) and I usually code in Assembler (AVR, Z80 and DMG90).
    My first contact with C programming was tainted by a useless compiler.
    Cryptic error messages by the dozen and not even the mandatory "Hello World" would run.
    This has spoiled C for me for at least a decade.
     
  4. Malware Master Engineer

    Messages:
    9,032
    Well, this isn't C, it's C#. Major difference :) No, I'm not joking. There really is just a basic similarity between the languages. There's no more correlation between C and C# than there is between C and Java. There is one, but it's only in their basics.

    No, you're not allocating a new list only once but every time the script is run. You are also fetching the blocks every time the script is run.

    You need to specify the entire type definition, the full List<IMyTerminalBlock>.


    C# is a very popular language. You will find a whole bunch of tutorials online to teach you the language itself.
     
  5. Ekuah Trainee Engineer

    Messages:
    99
    Thanks, your advice works.
    Code:
    void Main(string argument)
    {
      List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
      GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(blocks);
      int amount=my_method(blocks);
      Echo(Convert.ToString(amount));
    }
    int my_method(List<IMyTerminalBlock> cubes) {
      int number=cubes.Count;
      return number;
    }
    Well I don't know anymore which kind of C it was. C, C++, C#, ??? don't know. I can just remember that I was so frustrated that I nearly quit coding altogether.

    Btw. I fetch the list for each run, because the 'grid' can change.
    It is basically a detailed power monitor I use for energy-debugging a large survival station.
    Too often, the Solar panels were sourcing at full power, the batteries were discharging and still the reactors were maxed out. I wanted to know were all that power was drained to.
    Since it is a station, there are ships which can dock and undock, changing the extent of the grid.

    You can find the script as "Ekuah's crude but detailed Power Monitor" under:
    https://steamcommunity.com/sharedfiles/filedetails/?id=1411920061
    in it's crude and unrefined form.
    I hope with your tips that I can streamline it to a more sensible form.
     
  6. Malware Master Engineer

    Messages:
    9,032
    Not likely to have been C# as it didn't exist back then. I started the same way you did, the first language I ever wrote a program with (alongside my father) was gwBasic. That's dangerously close to 30 years ago now... :eek: Was a couple of years after that before I actually wrote a real program though.

    You should still reuse the existing list instance rather than creating a new one. Remember, for all intents and purposes, you're writing game code here. This means that you need to be highly aware of performance and performance impact.

    Also be careful of how often your script runs.
     
  7. Ekuah Trainee Engineer

    Messages:
    99
    How do I reuse it?
    By putting it outside the Main method as a global list?
    But how do I update it then?

    The finished script will be run in part every 5-10 seconds.
    The output of the script is so extensive that it would never fit on one text panel.
    So I planned to change the screen every 5-10 seconds to show another power group.
    (Power Generation;Lighting;Broudcast;Defense;Production;etc.)
    So the computing load should be insignificant compared to the station.

    P.S.
    I started with Locomotive Basic back in 1984 and was soon coding myself.
    One or two years later I was tutoring my IT teacher (because she had learned on an old Apple II and had no clue about the machines we were using)
     
  8. Malware Master Engineer

    Messages:
    9,032
    @Ekuah Yes - but that doesn't make it global, because the script is just another class. It makes it a class field. This means that you can access it in all your methods, but you can't access it in any subclass.

    You don't need to change how you update it. You're just reusing the same list so it doesn't reallocate memory every time.
     
  9. Ekuah Trainee Engineer

    Messages:
    99
    I'm somehow still missing your point here.
    Don 't forget, I'm nearly C-illiterate.
    Your technical terms are all 'Bohemian villages' to me. ;-)
    (means: They're all Greek to me.)
     
  10. Malware Master Engineer

    Messages:
    9,032
    You really should get on Discord, you're likely to get much faster answers there.

    Code:
    List<IMyTerminalBlock> _blocks = new List<IMyTerminalBlock>();
    
    void Main(string argument)
    {
    	GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(_blocks);
    	int amount = MyMethod(_blocks);
    	Echo(amount.ToString();
    }
    
    int MyMethod(List<IMyTerminalBlock> cubes) {
    	int number=cubes.Count;
    	return number;
    }
    
    I made some adjustments to your coding standards too, to match something more natural for C#. The underscore before blocks is a convention that denotes class fields. It's by no means required, but it's recommended to use that or to specify all members using the `this` keyword:

    Code:
    List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
    
    void Main(string argument)
    {
    	GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(this.blocks);
    	int amount = this.MyMethod(this.blocks);
    	Echo(amount.ToString();
    }
    
    int MyMethod(List<IMyTerminalBlock> cubes) {
    	int number=cubes.Count;
    	return number;
    }
    
    I also replaced your Convert.ToString method with a simple `.ToString()` call.
     
  11. Ekuah Trainee Engineer

    Messages:
    99
    Thank you very much.

    I got my C# coding 'skills' from VSB, and it used Convert.ToString(). So I copied that.
    Will try to add the this. thingy. By the way. My new Power monitor script is running. (not published yet)
    You may cringe when you see the coding, but hey, it works.
     
    Last edited: Jun 22, 2018
    • Like Like x 1
  12. Regn Trainee Engineer

    Messages:
    60
    I will never recommend fetching all IMyTerminalBlocks on the grid and iterating through them because it is often unnecessary, can quickly turn into a lot of instructions, and become messy to read, but since I'm not entirely sure what you want, here are a few examples of how your code can be marginally reduced in terms of characters.

    Fetching all IMyTerminalBlocks, sorting through them with "is", and classifying them with "as"...

    Code:
    var terminals = new List<IMyTerminalBlock>();
    GridTerminalSystem.GetBlocksOfType(terminals);
    foreach (var terminal in terminals)
    {
    	if (terminal is IMySensorBlock)
    	{
    		var warhead = terminal as IMyWarhead;
    		if (!warhead.IsArmed) warhead.IsArmed = true;
    	}
    	if (terminal is IMyBeacon)
    	{
    		var beacon = terminal as IMyBeacon;
    		if (beacon.Radius > 1000f) beacon.Radius = 100f;
    	}
    }
    Using override in case of similar terminals that will behave the same but will need slight distinction for other reasons...

    Code:
    class HydrogenTanks
    {
    
    	public List<IMyGasTank> List { get; set; }
    	public int Count { get; set; }
    
    	public HydrogenTanks()
    	{
    		GetTanks();
    		Count = List.Count;
    	}
    
    	private virtual void GetTanks()
    	{
    		List = new List<IMyGasTank>();
    		Grid.GetBlocksOfType(List, block => block.CubeGrid == PB.CubeGrid && block.DefinitionDisplayNameText.Contains("Hydrogen"));
    	}
    
    }
    
    class OxygenTanks : HydrogenTanks
    {
    	public override void GetTanks()
    	{
    		List = new List<IMyGasTank>();
    		Grid.GetBlocksOfType(List, block => block.CubeGrid == PB.CubeGrid && block.DefinitionDisplayNameText.Contains("Oxygen"));
    	}
    }
    In the latter OxygenTanks inherits everything from HydrogenTanks thanks to semicolon. In HydrogenTanks, we instantiate the virtual method GetTanks(). In OxygenTanks we override said method and say we want Oxygen instead of Hydrogen. Note, "HydrogenTanks()" is an instantiatior and is considered equal to "OxygenTanks()" in OxygenTanks, which is something we don't want to override in case we may want to add different behavior to the tanks later, hence why we wrote it in GetTanks() instead.

    Ps. "Grid" is equivalent to "GridTerminalSystem" and "PB" is equivalent to "Me". I've made these static otherwise I will have to write a lot of unnecessary garbage code all the time.
     
Thread Status:
This last post in this thread was made more than 31 days old.