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.

Working on an Elevator Programme

Discussion in 'Programming (In-game)' started by frayboy15, May 21, 2017.

  1. frayboy15

    Joined:
    May 1, 2015
    Messages:
    2
    Trophy Points:
    86
    Hello.
    I have been working on a script to work with a 1x1 elevator I designed. As it stands, I am not knowledgeable enough to fix the errors I've been getting on my own.
    Could someone help me out with making it compile in-game? I've commented the code as best I could so you'll know what my intent is.
    What it should do:
    1. set pistons(marked by a tag) to a list and .count to an int
    2. calculate and pass(to pistons[divided across equally]) distance from current floor to destination
    3. calculate and pass(to timer) time the car will take to reach the destination.(timer will turn pistons off and open the car door)
    4. save current floor to storage.
    5. properly initialize variables on reload and pull current floor from storage

    The program and the sole remaining error is here on Steam(which I am better able to monitor): http://steamcommunity.com/app/244850/discussions/0/1291816569116093810/
     
  2. Martin R Wolfe

    Joined:
    Jan 21, 2017
    Messages:
    67
    Trophy Points:
    7
    The error is on this line.
    GridTerminalSystem.SearchBlocksOfName(pistTag, stack);

    SearchBlocksOfName requiers the second parameter to be of type <List>IMyTerminalBlock.
    One solution is to use the following
    GridTerminalSystem.GetBlocksOfType(stack, x => x.CustomName.IndexOf(pistTag) ==0);

    A second solution is to use an intermediate variable of the type <List>IMyTerminalBlock and use the List class copy function to copy the results to the <List>IMyPistonBase variable/
     
    • Agree Agree x 1
  3. Georgik

    Joined:
    Sep 30, 2015
    Messages:
    126
    Trophy Points:
    47
    In your program you only use .SetValue and .ApplyAction, which don't need to be called on IMyPistonBase interface, so you are good to go with
    Code:
    List<IMyTerminalBlock> stack = new List<IMyTerminalBlock>();
    void startProgram(){//I need to store and retrieve At from the storage string. HELP.
    	GridTerminalSystem.SearchBlocksOfName(pistTag, stack, x => (x is IMyPistonBase));
     
    • Like Like x 1
    • Agree Agree x 1
  4. Jon Turpin

    Joined:
    Jul 15, 2017
    Messages:
    13
    Trophy Points:
    1
    So I clearly have only scratched one pebble while digging up a mountain..

    Any chance you would explain the line And what each part means?

    Code:
    GridTerminalSystem.GetBlocksOfType(stack, x => x.CustomName.IndexOf(pistTag) ==0);
     
  5. Georgik

    Joined:
    Sep 30, 2015
    Messages:
    126
    Trophy Points:
    47
    Code:
    GridTerminalSystem.GetBlocksOfType<T>(List<T> output, Func<T, bool> lambda)
    
    looks up (as the method's name says) all blocks of given type (T).
    The "type" parameter (GetBlocksOfType<T>) is optional, by it you exclusively tell method what blocks are you looking for, otherwise it searches blocks according to output List's type (if output is List<IMyPistonBase>, it will find all pistons and add them to the list). In given code stack is output list.
    Second parameter (lambda expression) could seems a bit difficult, but it is easy at the end.
    It is filter, and it behaves like a function. It takes one argument (block), and returns true/false depending on your filter condition.
    "x =>" means "pass block (of type T) as a variable 'x' to a next part of code"
    In next code, that block is represented by the variable 'x'. So according to properties of that block, you return if you want to add it to the list or not.
    Code:
    x.CustomName.IndexOf(pistTag)==0
    means "if string pistTag is located at the beginning of x's name, return true".
     
    • Informative Informative x 1
  6. Jon Turpin

    Joined:
    Jul 15, 2017
    Messages:
    13
    Trophy Points:
    1
    So, to check my understanding -

    You would use the list/lambda expression if you wanted to "get" blocks of varying names (ie. Not ALL pistons, as you could just have used GetBlocksOfType("Piston", x => x.CustomName.IndexOf(pistTag) = = 0))??

    Also, would this then work with something like this:

    GridTerminalSystem.GetBlocksOfType("Door", x => x.CustomName.IndexOf(outerAL))

    In order to get all outer airlock doors for further coding?

    Using this example since it's one of few that I've messed with in game since I started learning C#.
    --- Automerge ---
    Or is it that you use the list if you want to get more than one block, else it only gets one block of the type given?
     
  7. Georgik

    Joined:
    Sep 30, 2015
    Messages:
    126
    Trophy Points:
    47
    No, first parameter of GetBlocksOfType() is List<T>, not string. In the method, the list gets filled with blocks you wanted.
    Code:
    var listOfPistons = new List<IMyPistonBase>();
    string pistTag = "[ELEVATOR]";
    GridTerminalSystem.GetBlocksOfType(listOfPistons, x=>x.CustomName.IndexOf(pistTag)==0);
    foreach (var piston in listOfPistons) {
        Echo(piston.CustomName);
    }
    
    Also note:
    int string.IndexOf(string a)
    returns zero-based index of "a" located in string. If not found, return -1.
    E.g.:
    "Hello world!".IndexOf("world") returns 6.
    "Hello world!".IndexOf("llo") returns 2.
     
    • Informative Informative x 1
  8. Jon Turpin

    Joined:
    Jul 15, 2017
    Messages:
    13
    Trophy Points:
    1
    I think I understand..

    So in your snippet:

    #1- You created a variable for a list <IMyPistonBase>
    #2- You created a string variable for "pistTag" that equals Elevator (what are the brackets for?)
    #3- You are getting blocks that start with "pistTag" and adding them to <listOfPistons>
    #4- You are assigning a variable for every entry in the list created in #3
    #5- You are telling it to output all the names of the entries in #4

    So the lambda expression requires a list to start, no matter what?

    Thank you for your patience, btw
     
  9. Georgik

    Joined:
    Sep 30, 2015
    Messages:
    126
    Trophy Points:
    47
    Yes, you understood my snippet well. Brackets are just used for better readability of name - divide what is the tag and what a name. ("[ELEVATOR] Piston 15"). It's a thing of manner.

    I don't really understand what do you mean by "lambda requires a list to start".
    It's that function (GetBlocksOfType), which has defined what each parameter has to be. Even if you don't pass lambda expression as the second parameter, the first one has to be list, and thus no filter will be applied and all blocks of given type will be pushed to the list.

    There is one function which doesn't work with list (and also no lambda). GetBlockWithName returns only one block which matches (or contains, I'm not sure now) given string.
    Code:
    IMyTerminalBlock GridTerminalSystem.GetBlockWithName(string name)
    Usage:
    Code:
    IMyTextPanel lcd = GridTerminalSystem.GetBlockWithName(" LCD Panel 2") as IMyTextPanel;
    if (lcd != null) lcd.WritePublicText("Hello");
     
  10. Jon Turpin

    Joined:
    Jul 15, 2017
    Messages:
    13
    Trophy Points:
    1
    Ahhh, Okay!

    I'm slowly getting there lol. Depending on which function you use, the arguments have to be a certain thing. And it makes sense that getting blocks of "type" would need a list because there will be more than one of a type. And block with name is singular, therefore no list, just one name (string "").

    One thing I have noticed in creation of variables, if you can tell me if they mean the same thing?

    Code:
    IMyTextPanel lcd = GridTerminalSystem.GetBlockWithName(" LCD Panel 2") as IMyTextPanel;
    
    // Is the above == to below
    
    var LCD = (IMyTextPanel)GridTerminalSystem.GetBlockWithName ("LCD Panel 2");
    
    What is the purpose of "as IMyTextPanel" at the end of the line?
     
  11. Martin R Wolfe

    Joined:
    Jan 21, 2017
    Messages:
    67
    Trophy Points:
    7
    There is a third version of that line :-
    Code:
    var LCD = GridTerminalSystem.GetBlockWithName ("LCD Panel 2") as IMyTextPanel;
    The "as IMyTextPanel" at the end of the line has the same effect as (IMyTextPanel) following the assignment operator. Both effectively cast the result of GetBlockWithName from type IMyTerminalBlock to type IMyTextPanel. The difference is the "as" keyword will return a null if the conversion fails while the (aType) will throw an exception if it fails. As your are searching for a block with a Name of a particular type the "as" keyword is the best one to use as when a block is not found GetBlockWithName returns null.

    Exception handling is an advanced topic. Also throwing exceptions takes up a lot of CPU cycles and they have to be dealt with an exception handling routine if you want the script to continue to run without recompiling.

    Current C# coding practice is to use "var VariableName = Something" when the type of "Something" is known in advance and only use "aType VariableName" when the variable is declared before assignment. With declaration and assignment in the same statement var is the way to go. This also means there are less changes that would need to be needed to your code if the type of something is changed.

    As an example at the moment rotor angles are given as type float. Type float has problems as the least significant digits tend not to be constant whereas type double does not have this problem. So as an example:-
    Code:
                var ElevationRotor = GridTerminalSystem.GetBlockWithName("Solar Panel Elevation") as IMyMotorStator;
    
                //The following could fail if the type of IMyMotorStator.Angle is changed
                float CurrentAngle = ElevationRotor.Angle;
                //The following will not fail if the type of IMyMotorStator.Angle is changed
                var NewAngle = ElevationRotor.Angle;
    
     
    Last edited: Jul 17, 2017
  12. Jon Turpin

    Joined:
    Jul 15, 2017
    Messages:
    13
    Trophy Points:
    1
    I think I understand..

    So use of var is best when doing something like checking door status:
    Code:
    var InDoor = (IMyDoor)GTS.GBWN("Door1");
    //shortened syntax for speed
    DoorStatus InDoorStatus = InDoor.Status;
    
    and use the "as IMyRotorStator" if pulling something that may not have the same value every time:

    Code:
    var Rotor = GridTerminalSystem.GetBlockWithName("Solar Panel Elevation") as IMyMotorStator;
    // since the angle of the rotor may/will change, best to use this
    
    As for using "var" vs "aType", I'm trying to understand what you wrote..

    So, for instance, one of the scripts I played with last night set up two functions to be called in a follow on function, lockDoor() and unlockDoor(), and I'm assuming that because the variables didn't specify exactly what to lock/unlock (variable assignment was just doorName, not "inDoor" etc), they used "IMyDoor" at the beginning instead of "var". Am I on the right track?
     
    Last edited: Jul 17, 2017
  13. Martin R Wolfe

    Joined:
    Jan 21, 2017
    Messages:
    67
    Trophy Points:
    7
    Except to be constant with the use of var it should be as follows:-
    Code:
    var InDoor = (IMyDoor)GTS.GBWN("Door1");
    //shortened syntax for speed
    var InDoorStatus = InDoor.Status;
    Mostly correct "aType varName" is mainly used in declaring attributes of a class and the parameters of methods/functions as the declaration and assignment are in different places :-
    Code:
    class MySolarArray
    {
          //Member variables (also known as attributes)
          private string Name;
    
          //Constructor
          public MySolarArray(MyGridProgram ThisProg)
          {
                  if(ThisProg.Storage == String.Empty)
                  {
                        //Read configuration from ProgrammableBlock's CustomData
                        Name = ThisProg.Me.CustomData;
                  }
                  else
                  {
                        //Read configuration from storage
                        Name = ThisProg.Storage;
                  }
          }
    }
    
     
    Last edited: Jul 17, 2017
    • Informative Informative x 1