Results 1 to 25 of 25

Thread: FSM style Mainloop

  1. #1
    Join Date
    Feb 2007
    Location
    Alberta,Canada
    Posts
    2,358
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default FSM style Mainloop

    This will be a fairly quick tutorial and I will try to keep it as simple as possible.

    This tutorial will require a basic knowledge in the following area(s):
    • Pointers
    • conditional statements (if..then statements etc)
    • system/program flow


    I. Intro

    Well first of all I'm sure you're wondering "what is FSM"? FSM stands for Finite-state machine. Basically this type of mainloop mirrors that of RSBot's in the fact that the mainloop responds to "states" set by a previous function.

    A FSM loop should look something like this:

    SCAR Code:
    const
      {enum}
      BANK    = 1;
      WALK    = 2;
      COOK    = 3;
      ANTIBAN = 4;

      {enum: an enumeration of a set is an exact listing of all of its elements (perhaps with repetition).}

    type
      tStatus = record
        ID: Integer;
        Name: string;
      end;

      tPointerSet = record
        isProc: Boolean;
        proc: procedure;
        func: function: Boolean;
        name: string;
      end;

    var
      state: tStatus;
      Pointers: tPointerSet;
      TileArray: array of tTile;
      IDArray: array of Integer;

    function getState(): Integer;
    var
      tempID: Integer;
      i: Integer;
    begin
      if( (getAnimation() > -1) or (CharacterMoving()) )then
        result := ANTIBAN;
      if( result <> 0 )then
        exit;

      if( distanceFrom(TileArray[_BANK]) < 4 ) then
      begin
        tempID := getItemIDAt(1);
        if state.ID = tempID then
          result := WALK
        else
          result := BANK;
      end;
      if( result <> 0 )then
        exit;

      if( distanceFrom(TileArray[_COOK]) < 5 ) then
      begin
        tempID := getItemIDAt(1);
        if( state.ID = tempID )then
          result := COOK
        else
          result := WALK;
      end;

      if( result = 0 )then
      begin
        writeLn('getState is having issues. Checking for randoms.');
        for i := 0 to 15 do
        begin
          if( FindNormalRandoms() )then
            Exit;
          wait(50);
        end;
        writeLn('not in a Random and state enum = 0. NextPlayer');
        NextPlayer(False);
        Exit;
      end;
    end;

    procedure Loop();
    begin
      with Pointers do
      begin
        case getState() of
          ANTIBAN:
          begin
            name := 'antiban';
            isProc := true;
            Proc := @Anti_Ban;
            Func := nil;
          end;
          WALK:
          begin
            name := 'walk';
            isProc := false;
            Proc := nil;
            Func := @createWalkLocation;
          end;
          BANK:
          begin
            name := 'bank';
            isProc := false;
            Proc := nil;
            Func := @Bank_Loop;
          end;
          COOK:
          begin
            name := 'cook';
            isProc := true;
            Proc := @Cook_Loop;
            Func := nil;
          end;
        end;
      end;
    end;

    var
      mtile: ttile;
      p: tpoint;
    begin
      SMART_Signed := true;
      SMART_Server := 81;

      setUpSRL();
      setUpReflectionEx(true);
      DeclarePlayers();

      TileArray := [tile(3269, randomRange(3169, 3166)), tile(3273, 3181)];
      setArrayLength(IDArray, 1);
      IDArray   := [25730];

      repeat
        while not LoggedIn() do
          LogInPlayer();
        wait(randomRange(250, 500));
        setAngle(true);

        while Players[CurrentPlayer].Active do
        begin
          Loop();
          with Pointers do
          begin
            writeLn('entering '+name+' cycle.');
            if isProc then
              Proc()
            else
              Func();
          end;
        end;
      until AllPlayersInactive();
    end.

    Don't worry I will break it all down later. For now that is just a simple example.

    II. Advantages of FSM

    I have found in my 2 years of scripting recalibrating a script when it is lost is a difficult task. When a random mis-click or mis-hap occurs during the runtime of the script, it can alter the script and screw it up. This is because scripts normally flow with (for lack of a better phrase) a conditional flow. By this I mean,

    SCAR Code:
    if not Conditonal_Function then
    begin
      NextPlayer(False);
      Exit;
    end;

    if not Conditonal_Function2 then
    begin
      NextPlayer(False);
      Exit;
    end;

      // and so on and so fourth.

    A FSM style loop allows for a recalibration of errors and ensures a longer runtime. When the script dynamically outputs functions/procedures in a response to it's environment the script should leave few area's to falter and should pick up on these issue's.

    III. Warnings/Disadvantages

    First of all there should be a simple warning cautioned that developing a well functioning FSM can become difficult/complicated. This is where you will require knowledge in Program Flow. I will now address the getState() function.

    SCAR Code:
    function getState(): Integer;
    begin
      if This_is_true then
        result := CORRESPONDING_ENUM; // enums must begin at 1 not 0. or else the result could/would always = 0
                                     //after this first state.
      if result <> 0 then
        Exit;

      if This_is_true then
        result := NEXT_ENUM;

      // so on and so fourth.
    end;

    now addressing the flow problem here goes. So the issue lies here, what if two results are present on the screen, yet you can only have one result correct? So You must order the flow of the function to work according to the script. (That's a terrible explanation I know). Ummm, here's an example.

    SCAR Code:
    function getState(): Integer;
    begin
      if distanceFrom(tile(xxx, yyy) < 3 {<--- atTrees()} then
        result := CORRESPONDING_ENUM; // enums must begin at 1 not 0. or else the result could/would always = 0
                                     //after this first state.
      if result <> 0 then
        Exit;

      if not invfull() atTrees() then
        result := NEXT_ENUM;
    end;

    so let's say the character is on the specified tile that the function results with CORRESPONDING_ENUM. However, the script should have been chopping as that statement is also true because the player is by the tree's. So what happens is the player walks back to the bank instead of chopping like he should be.

    This is where you are faced with one of two things. Either

    A) You have to make each condition specific, meaning the first statement would become
    SCAR Code:
    if invfull() and distanceFrom(tile(xxx, yyy) < 3 then

    or

    B) You simply reconstruct the flow and put the Chop_Tree condition infront of the walking condition so the function returns that result first.

    Either way works, I like to use a conjunction of both, as the more complicated you make each if..then statement the harder to understand the code becomes and tweaking turns into a bitch.

    Sorry if that is confusing, I will try to readdress that later.

    IV. Setting Pointers

    Part A:

    Pointers are simple tools that are often overlooked. I'll do a quick, simple breakdown of pointers, link some good tutorials on them and then continue with the tutorial.

    A Pointer is simply a variable that can hold a function/procedure.

    example

    SCAR Code:
    program new;

    var
      point: procedure;

    procedure blah();
    begin
      writeLn('meow');
    end;

    begin
      point := @blah;
      point();
    end.

    same thing works with functions, however functions are a bit tricky. Say you have two functions, and you want one universal pointer. You have to keep in mind that these two functions must have the same parameters to used in conjunction with a single pointer.

    example:

    bad
    SCAR Code:
    program new;

    var
      point: function(): integer;

    function blah(): Integer;
    begin
      result := 1;
    end;

    function blahblah(int: Integer): Integer;
    begin
      result := int;
    end;

    begin
      if random(2) = 1 then
        point := @blah
      else
        point := @blahblah;
      point();
    end.
    good
    SCAR Code:
    program new;

    var
      point: function(): integer;
      Globally_set_Var: integer;

    function blah(): Integer;
    begin
      result := 1;
    end;

    function blahblah(): Integer;
    begin
      result := Globally_Set_Var;
    end;

    begin
      Globally_Set_Var := 9;
      if random(2) = 1 then
        point := @blah
      else
        point := @blahblah;
      writeLn(inttostr(point()));
    end.
    I hope that makes sense. Further explanation can be given upon request.

    Part B:

    Now Back to the mainloops. So we now have a setState() function created. Our conditionals set and results enumerated. Next step is to link these enums to specific functions/procedures. This is where pointers come into play.

    first let's make a type to hold our pointer variables.

    SCAR Code:
    type
      tPointerSet = record
        isProc: Boolean; // This will be used later in the mainloop execution
        proc: procedure;
        func: function: Boolean;
        name: string; // helps us keep track of the function being called
      end;

    next let's make an empty case statement that corresponds to the getState() function created.

    SCAR Code:
    procedure Loop();
    begin
      case getState() of
        ANTIBAN: // empty
        WALK: // empty
        BANK: // empty
        COOK: // empty
      end;
    end;

    now we simply fill the Loop() procedure in with our pointers. It will look like so.

    SCAR Code:
    procedure Loop();
    begin
      with Pointers do
      begin
        case getState() of
          ANTIBAN:
          begin
            name := 'antiban';
            isProc := true;
            Proc := @Anti_Ban;
            Func := nil;
          end;
          WALK:
          begin
            name := 'walk';
            isProc := false;
            Proc := nil;
            Func := @createWalkLocation;
          end;
          BANK:
          begin
            name := 'bank';
            isProc := false;
            Proc := nil;
            Func := @Bank_Loop;
          end;
          COOK:
          begin
            name := 'cook';
            isProc := true;
            Proc := @Cook_Loop;
            Func := nil;
          end;
        end;
      end;
    end;

    so if you haven't already figured out when you wish to keep a pointer empty simply setting it to "nil" will do. The isProc variable will come into play later, bear with me for that. It is infact useful

    V. The MainLoop

    This is the fun part. That code in an FSM Mainloop is infact quite simple (as you've seen from the example at the start of the tutorial).

    here is the example, then I will go in detail about it (although it's probably not even needed).

    SCAR Code:
    repeat
        while not LoggedIn() do
          LogInPlayer();
        wait(randomRange(250, 500));
        setAngle(true);

        while Players[CurrentPlayer].Active do
        begin
          Loop();
          with Pointers do
          begin
            writeLn('entering '+name+' cycle.');
            if isProc then
              Proc()
            else
              Func();
          end;
        end;
      until AllPlayersInactive();

    the main gyst we're going to be looking at really is:

    SCAR Code:
    while Players[CurrentPlayer].Active do
        begin
          Loop();
          with Pointers do
          begin
            writeLn('entering '+name+' cycle.');
            if isProc then
              Proc()
            else
              Func();
          end;
        end;

    Now the isProc boolean was a failsafe to ensure that we did not call a procedure/function that has been set to nil. It ensures that the right variable is called upon.

    Aside from that the loop is quite simple really. Loop() is called at the start of each succession and the proc/func is set in correspondence.

    VI. Final Notes

    A few final notes on my tutorial.

    Regarding breaking the loop, I did not show any failsafes within the MainLoop itself. Inside my functions/procedures I simply call NextPlayer(false) or Players[CurrentPlayer].Active := false to break out of the 'while active do' loop.

    If there are any questions/concerns feel free to PM me or ask here, I will be happy to help. Same goes if there are any corrections that need to be made, I wrote this quite fast and under a bit of pressure

    I Hope you (the reader) found this, if nothing else interesting. Comments and suggestions are appreciated.

    Some tutorials that you may find interesting:

    Records and Psuedo-OOP
    Function and Procedure 'variables'.
    Will Edit with more later
    “Ignorance, the root and the stem of every evil.”

  2. #2
    Join Date
    Jan 2009
    Location
    Belgium
    Posts
    175
    Mentioned
    0 Post(s)
    Quoted
    14 Post(s)

    Default

    Lawl, nice tutorial, even i don't know anything about what u wrote there xD
    im not that far in SRL :P

    anyways, +rep

  3. #3
    Join Date
    Mar 2006
    Location
    Behind you
    Posts
    3,193
    Mentioned
    61 Post(s)
    Quoted
    63 Post(s)

    Default

    I really want to understand this but I'm almost completely lost by the sections 3&4. Which Script do you use this in cause I might be able to beter understand seeing it work in a real scenario.

    "Sometimes User's don't need the Answer spelled out with Code. Sometimes all they need is guidance and explanation of the logic to get where they are going."

  4. #4
    Join Date
    Mar 2008
    Location
    In a cave
    Posts
    345
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Got quite a few good ideas how to build up my yet to come scripts. This not only makes it easier to keep track of your script, but it also makes it easier to get lots of scripts combined and well organized. Although, this is something you can't actually implement to every script. But to all 'mine/chop/etc & bank' scripts, it would be a good implementation.
    A Chinese wiseman once said: "Shu ciu!", it was considered very smart, but now people know it means: "Something stinks here!"
    FalBuggySmelter v.1.31
    [Updated on the 1st of March 2010]
    RimmBugger BETA V1.8

  5. #5
    Join Date
    Feb 2009
    Location
    Irvine, CA
    Posts
    2,873
    Mentioned
    8 Post(s)
    Quoted
    138 Post(s)

    Default

    wow very clear cut tutorial, I'll definitely have to incorporate this in to my 2 current projects!

  6. #6
    Join Date
    Jun 2007
    Location
    La Mirada, CA
    Posts
    2,484
    Mentioned
    1 Post(s)
    Quoted
    3 Post(s)

    Default

    Those aren't actually considered pointers are they? I thought we didn't have access to pointers...

    "Failure is the opportunity to begin again more intelligently" (Henry Ford)


  7. #7
    Join Date
    Mar 2006
    Location
    Behind you
    Posts
    3,193
    Mentioned
    61 Post(s)
    Quoted
    63 Post(s)

    Default

    Been reading up on Finite-state Machine(FSM) and Virtual Finite state Machine(VFSM) and I have to say it's definitely a concept that's worth learning for programming. Your link to wiki is broken it's Here. Which is funny because the only difference is the s in state and the m in machine are lower case. Wiki might be case sensitive.

    So do you have to set a different global var for each function/procedure with different parameters. Please explain it a little more.

    E: What I mean is:

    Simba Code:
    Function A(a:integer):integer;
    Function Getcolor(x , y :integer);
    Findcolorstoloerance(T :TPA; Color, xs, ys, xe, ye, tol : integer): boolean

    I'd have to globally set variables for each of these functions. I know they aren't a great example but they get my point across. But these below could be set on the same variable.

    Simba Code:
    Writeln(x: string);
    Debug(x: string);

    Also how would I write those up in a pointer. Would it be like.

    Simba Code:
    Writeln('sdf')();

    E2: Scratch what I said. I think I was lost in my last edit, but further explaining would be nice. Could you please include a small working code of how it should be set up so we can experiment in simba/scar with it?
    Last edited by BraK; 09-22-2010 at 12:41 PM.

    "Sometimes User's don't need the Answer spelled out with Code. Sometimes all they need is guidance and explanation of the logic to get where they are going."

  8. #8
    Join Date
    Jan 2008
    Location
    Ontario, Canada
    Posts
    7,805
    Mentioned
    5 Post(s)
    Quoted
    3 Post(s)

    Default

    When calling a procedural or functional pointer, the parenthesis are always needed. If you have parameters, just stick them where they normally go. Also, you call it using the var name, and when you are getting reference to the function as a pointer you prefix it with @.
    Writing an SRL Member Application | [Updated] Pascal Scripting Statements
    My GitHub

    Progress Report:
    13:46 <@BenLand100> <SourceCode> @BenLand100: what you have just said shows you 
                        have serious physchological problems
    13:46 <@BenLand100> HE GETS IT!
    13:46 <@BenLand100> HE FINALLY GETS IT!!!!1

  9. #9
    Join Date
    Mar 2006
    Location
    Behind you
    Posts
    3,193
    Mentioned
    61 Post(s)
    Quoted
    63 Post(s)

    Default

    Hmm so really you'd only want to use it on the main transitioning functions.

    Simba Code:
    const
      DepBank = 1;

    procedure GetState();
    begin
    if Bankscreen and inv_of_bows then
      Result := Depbank;
    end;

    and so on correct. So basically At the end of a transition, Getstate() determines where it's at and what it needs to do next if it is lost or malfunctions. Correct?


    E: I'm confusing myself and spitting out crap I just realized.
    Last edited by BraK; 09-22-2010 at 02:01 PM.

    "Sometimes User's don't need the Answer spelled out with Code. Sometimes all they need is guidance and explanation of the logic to get where they are going."

  10. #10
    Join Date
    Jan 2008
    Location
    Ontario, Canada
    Posts
    7,805
    Mentioned
    5 Post(s)
    Quoted
    3 Post(s)

    Default

    Quote Originally Posted by BraK View Post
    Hmm so really you'd only want to use it on the main transitioning functions.

    Simba Code:
    const
      DepBank = 1;

    procedure GetState();
    begin
    if Bankscreen and inv_of_bows then
      Result := Depbank;
    end;

    and so on correct. So basically At the end of a transition, Getstate() determines where it's at and what it needs to do next if it is lost or malfunctions. Correct?


    E: I'm confusing myself and spitting out crap I just realized.
    No, no. You are correct actually! At least how I do it.

    I have callibration functions which are used everytime a player moves on. I had it skip the callibration if it successfully completed the current section, or callibrated to the next section. I don't remember which I did now.
    Writing an SRL Member Application | [Updated] Pascal Scripting Statements
    My GitHub

    Progress Report:
    13:46 <@BenLand100> <SourceCode> @BenLand100: what you have just said shows you 
                        have serious physchological problems
    13:46 <@BenLand100> HE GETS IT!
    13:46 <@BenLand100> HE FINALLY GETS IT!!!!1

  11. #11
    Join Date
    Jan 2007
    Posts
    8,876
    Mentioned
    123 Post(s)
    Quoted
    327 Post(s)

    Default

    Hmm... I have used this style of mainlooping without knowing that a name existed for it :O
    Nice tutorial! Time to do some reading

  12. #12
    Join Date
    Feb 2007
    Location
    Alberta,Canada
    Posts
    2,358
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by BraK View Post
    Hmm so really you'd only want to use it on the main transitioning functions.

    Simba Code:
    const
      DepBank = 1;

    procedure GetState();
    begin
    if Bankscreen and inv_of_bows then
      Result := Depbank;
    end;

    and so on correct. So basically At the end of a transition, Getstate() determines where it's at and what it needs to do next if it is lost or malfunctions. Correct?


    E: I'm confusing myself and spitting out crap I just realized.
    You can use GetState to calibrate if you want, but I actually use it to do every task. Example would be my Essence Miner, where it responds to the enviroment it's in, which should in theory make it fool-proof.

  13. #13
    Join Date
    Mar 2006
    Location
    Behind you
    Posts
    3,193
    Mentioned
    61 Post(s)
    Quoted
    63 Post(s)

    Default

    Thanks for the reply and I'll have to look into your Essence Miner I haven't seen it yet. Love the code for your AK cooker and Fletch N String. Between you and Narcle I get Good Ideas of how to mod my scripts all the time.

    "Sometimes User's don't need the Answer spelled out with Code. Sometimes all they need is guidance and explanation of the logic to get where they are going."

  14. #14
    Join Date
    Feb 2013
    Posts
    465
    Mentioned
    6 Post(s)
    Quoted
    221 Post(s)

    Default

    Free bump, This is a good way to structure a script that can recover from any mishaps. I know its 3yr old bump, but in my opinion more scripts should be written like this, and not like:
    Simba Code:
    pickInvFlax;
       wait(1000);
       walkBank;
       wait(1000);
       bankItems;
       wait(1000)
       walktoFlax;
       wait(1000)
    until not LoggedIn;

    scripts like this ^^ will get users banned.

  15. #15
    Join Date
    Jun 2012
    Location
    Howell, Michigan
    Posts
    1,585
    Mentioned
    34 Post(s)
    Quoted
    553 Post(s)

    Default

    Quote Originally Posted by wthomas View Post
    Free bump, This is a good way to structure a script that can recover from any mishaps. I know its 3yr old bump, but in my opinion more scripts should be written like this, and not like:
    Simba Code:
    pickInvFlax;
       wait(1000);
       walkBank;
       wait(1000);
       bankItems;
       wait(1000)
       walktoFlax;
       wait(1000)
    until not LoggedIn;

    scripts like this ^^ will get users banned.
    Thankyou for the awesome grave dig, Im writing my next script like this(:

  16. #16
    Join Date
    Feb 2012
    Location
    Canada
    Posts
    1,164
    Mentioned
    26 Post(s)
    Quoted
    433 Post(s)

  17. #17
    Join Date
    Sep 2012
    Location
    Here.
    Posts
    2,007
    Mentioned
    88 Post(s)
    Quoted
    1014 Post(s)

    Default

    Quote Originally Posted by wthomas View Post
    Free bump, This is a good way to structure a script that can recover from any mishaps. I know its 3yr old bump, but in my opinion more scripts should be written like this, and not like:
    Simba Code:
    pickInvFlax;
       wait(1000);
       walkBank;
       wait(1000);
       bankItems;
       wait(1000)
       walktoFlax;
       wait(1000)
    until not LoggedIn;

    scripts like this ^^ will get users banned.
    I'm in no way saying FSM style programming is always bad, but I am offering an alternative for mishap recovery. Ignoring basic standards like randomization, let's look at your example script and add a 'stuck' failsafe. Obviously, it would be a little more fleshed out, but I tend to have my scripts follow logic more akin to this:
    Simba Code:
    var
      stuck: integer;

    begin
      whateverSetup;
      repeat
        stuck:= 0;
        if(not pickInvFlax)then
          Inc(stuck);
        wait(1000);
        if(not walkBank)then
          Inc(stuck);
        wait(1000);
        if(not bankItems)then
          Inc(stuck);
        wait(1000)
        if(not walktoFlax)then
          Inc(stuck);
        wait(1000)
        if(stuck >= totalStepCount)then//In this case, the main loop has 4 steps
          FailScript;//No single step could succeed, but if one could succeed, then a mishap was handled and the script follows it's normal pattern where it left off.
      until not LoggedIn;
    end;

  18. #18
    Join Date
    Feb 2013
    Posts
    465
    Mentioned
    6 Post(s)
    Quoted
    221 Post(s)

    Default

    Quote Originally Posted by nivek1989 View Post
    I'm in no way saying FSM style programming is bad, but I am offering an alternative for mishap recovery. Ignoring basic standards like randomization, let's look at your example script and add a 'stuck' failsafe. Obviously, it would be a little more fleshed out, but I tend to have my scripts follow logic more akin to this:
    Simba Code:
    var
      stuck: integer;

    begin
      whateverSetup;
      repeat
        stuck:= 0;
        if(not pickInvFlax)then
          Inc(stuck);
        wait(1000);
        if(not walkBank)then
          Inc(stuck);
        wait(1000);
        if(not bankItems)then
          Inc(stuck);
        wait(1000)
        if(not walktoFlax)then
          Inc(stuck);
        wait(1000)
        if(stuck >= totalStepCount)then//In this case, the main loop has 4 steps
          FailScript;//No single step could succeed, but if one could succeed, then a mishap was handled and the script follows it's normal pattern where it left off.
      until not LoggedIn;
    end;

    Your method will work with the way that you script your procedures, as soon as anything is hard coded into the script it'll still break. ie if the walktoBank procedure just looked like
    Simba Code:
    MakeCompass('n');
    mouse(MSCX,MSCY-50,5,5,mouse_left);
    wait(2500);
    mouse(MSCX,MSCY-50,5,5,mouse_left);
    wait(2500);
    The script would still break. The method you outline tries all of the possible steps in turn and executes them whereas the FSM decides which it should do and tries to execute that. Coded well they will both function exactly the same, but if the functions are coded without fail safes at the star then your method will still run about headless'ly before it realizes its lost. The main difference is the FSM would recognize its lost faster.

  19. #19
    Join Date
    Sep 2012
    Location
    Here.
    Posts
    2,007
    Mentioned
    88 Post(s)
    Quoted
    1014 Post(s)

    Default

    Quote Originally Posted by wthomas View Post
    Your method will work with the way that you script your procedures, as soon as anything is hard coded into the script it'll still break. ie if the walktoBank procedure just looked like
    Simba Code:
    MakeCompass('n');
    mouse(MSCX,MSCY-50,5,5,mouse_left);
    wait(2500);
    mouse(MSCX,MSCY-50,5,5,mouse_left);
    wait(2500);
    The script would still break. The method you outline tries all of the possible steps in turn and executes them whereas the FSM decides which it should do and tries to execute that. Coded well they will both function exactly the same, but if the functions are coded without fail safes at the star then your method will still run about headless'ly before it realizes its lost. The main difference is the FSM would recognize its lost faster.
    In general static movement/actions should be avoided with any script, and that is an important assumption. I easily agree with that, as well as the lack of failsafes causing my method to run about without an idea as to what's happening. FSM may recognize it is lost faster in a failure scenario/starting at any point in the run; I can agree with that as well. However, in a success scenario (which should occur the majority of the time in a well coded script), a script that does not need to make a state check after every action should run more efficiently.

  20. #20
    Join Date
    Aug 2013
    Posts
    41
    Mentioned
    0 Post(s)
    Quoted
    16 Post(s)

    Default

    Having taken several courses where most programs were structured as FSM's (we called them FSA's usually) and also being an ex-RSBot scriptwriter, can't stress enough how great structuring your main loop like this is. You can add all the necessary failsafes without this structure, but just thinking about and organizing your code in this way will make this far easier in the long run. Great tutorial, very thorough.

    My only question is, is it necessary to have the loop outside of the main begin end block (not sure what this is called, kinda new to Pascalscript)? Couldn't the loop have just been inside the main begin end block, and then you could call the functions/procedures directly from the main loop. It just seems to be an unncessary extra layer of complexity (including a very odd dual-function-procedure-pointer-type type thingy). Were you trying to limit the scope of the code in the loop for some reason?
    Last edited by stata; 08-30-2013 at 10:49 PM.

  21. #21
    Join Date
    Aug 2007
    Location
    Colorado
    Posts
    7,421
    Mentioned
    268 Post(s)
    Quoted
    1442 Post(s)

    Default

    Excuse my grave-dig but I'd like to bring this tutorial to light. This is a great way to structure your scripts and how they run, I'd strongly recommend this.

    Current projects:
    [ AeroGuardians (GotR minigame), Motherlode Miner, Blast furnace ]

    "I won't fall in your gravity. Open your eyes,
    you're the Earth and I'm the sky..."


  22. #22
    Join Date
    Jan 2007
    Posts
    8,876
    Mentioned
    123 Post(s)
    Quoted
    327 Post(s)

    Default

    Quote Originally Posted by Flight View Post
    Excuse my grave-dig but I'd like to bring this tutorial to light. This is a great way to structure your scripts and how they run, I'd strongly recommend this.
    Gravedig excused. This is indeed a very good way to structure your scripts

  23. #23
    Join Date
    Feb 2013
    Location
    Narnia
    Posts
    615
    Mentioned
    8 Post(s)
    Quoted
    252 Post(s)

    Default

    nice tut, i wrote a couple scripts this way in the past without knowing there was a name for this style lol. i found it quite useful to use when i had a script that did a fair amount of walking, i found this style easy to assess my location and what i needed to do next

    View my OSR Script Repository!


    Botted to max
    Guides: How to Report Bugs to the Scripter
    ~~~~ Moved to Java. Currently Lurking ~~~~

  24. #24
    Join Date
    Sep 2012
    Location
    Here.
    Posts
    2,007
    Mentioned
    88 Post(s)
    Quoted
    1014 Post(s)

    Default

    Quote Originally Posted by Flight View Post
    Excuse my grave-dig but I'd like to bring this tutorial to light. This is a great way to structure your scripts and how they run, I'd strongly recommend this.
    Quote Originally Posted by Zyt3x View Post
    Gravedig excused. This is indeed a very good way to structure your scripts
    Absolutely acknowledging the ease of failsafing a script using Finite State Machines, I disagree (and I think @Brandon has also stated a dislike for FSM, although maybe I'm wrong and am alone on this). The amount of time spent determining 'What step am I in?' for every step of a loop can be astronomical, especially as scripts become more complex. You could still allow for the ease of failsafing noted in FSM, while following normal code progressions (thus reducing the complexity of step checking) in a simple manner like this:
    Simba Code:
    var
      stuck: integer;

    begin
      whateverSetup;
      repeat
        stuck:= 0;
        if(not pickInvFlax)then
          Inc(stuck);
        wait(1000);
        if(not walkBank)then
          Inc(stuck);
        wait(1000);
        if(not bankItems)then
          Inc(stuck);
        wait(1000)
        if(not walktoFlax)then
          Inc(stuck);
        wait(1000)
        if(stuck >= totalStepCount)then//In this case, the main loop has 4 steps
          FailScript;//No single step could succeed, but if one could succeed, then a mishap was handled and the script follows it's normal pattern where it left off.
      until not LoggedIn;
    end;

  25. #25
    Join Date
    Dec 2007
    Posts
    289
    Mentioned
    4 Post(s)
    Quoted
    86 Post(s)

    Default

    Thanks for this Blumblebee.

    Not sure if you're still around, but anyone is welcome to answer...

    I noticed you provided the ability to call both procedures and functions in the loop.
    I'm assuming you included this as some parts of the loop (ie. banking and walking) will return a true/false dependant on it's success/failure.
    Would it be acceptable to only call procedures in the loop but have checks in getState() to determine whether that procedure should be called?
    (to clarify, if banking was a procedure and not a function, we would continuously call the banking procedure until the conditions to enter a different state were met (failsafes would obviously be integrated))

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •