Author Topic: Multi-Function Commands. Your opinions?  (Read 6767 times)

0 Members and 1 Guest are viewing this topic.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Multi-Function Commands. Your opinions?
« on: October 03, 2018, 05:02:43 pm »
Ok, the sprite library is getting big with LOTS of commands. This is creating command overload:

SL_GET_DESTINATION_LAYER
SL_GET_SOURCE_LAYER
SL_SET_DESTINATION_LAYER
SL_SET_SOURCE_LAYER ... etc... etc..

Steve mentioned in another post that these long commands are troublesome, and I agree. Since they all deal with the same topic, layers in this case, I though about combining everything into one generalized command.

As a test I took all the layer related commands and built a multi-function command that does it all based on how it's configured. Being a FUNCTION it will always require a return value, even if it's doing something like setting a value. To this end I decided to incorporate error detection into the return value. Also, if the command is not configured properly, the return value will indicate an error as well as an audible beep.

See the code sample below to get an idea of what I'm going for here. Let me know your opinion on this.

Code: QB64: [Select]
  1. FUNCTION SL_LAYER (layer AS INTEGER, action AS INTEGER) ' (MULTI-FUNCTION COMMAND)
  2.  
  3.     ' An audible tone (BEEP) will be heard if command is configured improperly and a value of 0 returned to indicate the command did not complete successfully.
  4.     '
  5.     ' usage:
  6.     '           value = SL_LAYER(x, SL_DEST)            set layer x as destination                              returns -1 if command completed successfully, 0 otherwise
  7.     '           value = SL_LAYER(SL_GET, SL_DEST)       return the value of current destination layer           returns the value of the destination layer, 0 if no destination found
  8.     '           value = SL_LAYER(x, SL_SOURCE)          set layer x as the source                               returns -1 if command completed successfully, 0 otherwise
  9.     '           value = SL_LAYER(SL_GET, SL_SOURCE)     return the value of the current source layer            returns the value of the source layer, 0 if no source found
  10.     '           value = SL_LAYER(x, SL_CLS)             clear layer x                                           returns -1 if command completed successfully, 0 otherwise
  11.     '           value = SL_LAYER(SL_ALL, SL_CLS)        clear all layers                                        returns -1 if command completed successfully, 0 otherwise
  12.     '           value = SL_LAYER(x, SL_VALID)           return either -1 or 0 depending on layer x being valid  returns -1 if layer is valid, 0 if layer is invalid
  13.     '           value = SL_LAYER(x, SL_VISBILE)         set layer x as visible '                                returns -1 if command completed successfully, 0 otherwise
  14.     '           value = SL_LAYER(SL_ALL, SL_VISIBLE)    set all layers as visible                               returns -1 if command completed successfully, 0 otherwise
  15.     '           value = SL_LAYER(x, SL_INVISIBLE)       set layer x as invisible                                returns -1 if command completed successfully, 0 otherwise
  16.     '           value = SL_LAYER(SL_ALL, SL_INVISIBLE)  set all layers as invisible                             returns -1 if command completed successfully, 0 otherwise
  17.  
  18.     ' declare global variables
  19.  
  20.     SHARED SL_layers() AS SL_LAYERS '   master layer array
  21.     SHARED SL_global AS SL_GLOBAL '     common globals array
  22.  
  23.     ' declare local variables
  24.  
  25.     DIM count AS INTEGER '              layer counter
  26.     DIM temp AS LONG '                  temp source/destination iamge
  27.     DIM visibility AS INTEGER '         layer visibility status
  28.  
  29.     IF (layer <> SL_ALL) AND (layer <> SL_GET) THEN '                                                   was a layer value supplied?
  30.         IF layer < 1 OR layer > UBOUND(SL_layers) THEN SL_LAYER = 0: EXIT FUNCTION '                    yes, if not valid then report error and leave
  31.     END IF
  32.     SELECT CASE action '                                                                                which action was requested?
  33.         CASE SL_DEST '                                                                             (0)  get or set destination layer
  34.             IF layer = SL_ALL THEN SL_LAYER = 0: BEEP: EXIT FUNCTION '                                  SL_ALL not valid with SL_DEST
  35.             IF layer = SL_GET THEN '                                                                    retrieve current destination layer?
  36.                 SL_LAYER = SL_global.destination '                                                      yes, report current destination layer
  37.             ELSE '                                                                                      no, a layer value was supplied
  38.                 _DEST SL_layers(layer).image '                                                          set destination as layer software image
  39.                 SL_layers(layer).dest = -1 '                                                            mark this layer as the destination
  40.                 SL_global.destination = layer '                                                         save destination layer in global settings
  41.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  42.             END IF
  43.         CASE SL_SOURCE '                                                                           (1)  get or set source layer
  44.             IF layer = SL_ALL THEN SL_LAYER = 0: BEEP: EXIT FUNCTION '                                  SL_ALL not valid with SL_SOURCE
  45.             IF layer = SL_GET THEN '                                                                    retrieve current source layer?
  46.                 SL_LAYER = SL_global.source '                                                           yes, report current source layer
  47.             ELSE '                                                                                      no, a layer value was supplied
  48.                 _SOURCE SL_layers(layer).image '                                                        set source as layer software image
  49.                 SL_layers(layer).source = -1 '                                                          mark this layer as the source
  50.                 SL_global.source = layer '                                                              save source layer in global settings
  51.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  52.             END IF
  53.         CASE SL_CLS '                                                                              (2)  clear layer(s)
  54.             IF layer = SL_GET THEN SL_LAYER = 0: BEEP: EXIT FUNCTION '                                  SL_GET not valid with SL_CLS
  55.             temp = _NEWIMAGE(1, 1, 32) '                                                                great a temp image for source / destination
  56.             IF layer = SL_ALL THEN '                                                                    clear all layers?
  57.                 _SOURCE temp '                                                                          yes, make temp image the current source
  58.                 _DEST temp '                                                                            make temp image the current destination
  59.                 DO '                                                                                    cycle through all layers
  60.                     count = count + 1 '                                                                 increment layer counter
  61.                     _FREEIMAGE SL_layers(count).image '                                                 remove current layer from memory
  62.                     SL_layers(count).image = _NEWIMAGE(SL_global.swidth, SL_global.sheight, 32) '       create new layer image
  63.                     IF SL_global.source = count THEN _SOURCE SL_layers(count).image '                   set it back to source if necessary
  64.                     IF SL_global.destination = count THEN _DEST SL_layers(count).image '                set it back to destination if necessary
  65.                 LOOP UNTIL UBOUND(SL_layers) '                                                          leave when all layers cleared
  66.                 _FREEIMAGE temp '                                                                       remove temp image from memory
  67.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  68.             ELSE '                                                                                      no, just clear one layer
  69.                 IF SL_global.source = layer THEN _SOURCE temp '                                         make temp image the source if necessary
  70.                 IF SL_global.destination = layer THEN _DEST temp '                                      make temp image the destination if necessary
  71.                 _FREEIMAGE SL_layers(layer).image '                                                     remove currentl layer from memory
  72.                 SL_layers(layer).image = _NEWIMAGE(SL_global.swidth, SL_global.sheight, 32) '           create new layer image
  73.                 IF SL_global.source = layer THEN _SOURCE SL_layers(layer).image '                       set it back to source if necessary
  74.                 IF SL_global.destination = layer THEN _DEST SL_layers(layer).image '                    set it back to destination if necessary
  75.                 _FREEIMAGE temp '                                                                       remove temp image from memory
  76.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  77.             END IF
  78.         CASE SL_VALID '                                                                            (3)  check for valid layer
  79.             IF layer = SL_ALL OR layer = SL_GET THEN SL_LAYER = 0: BEEP: EXIT FUNCTION '                SL_ALL and SL_GET not valid with SL_VALID
  80.             SL_LAYER = -1 '                                                                             yes, report that this is a valid layer
  81.         CASE SL_VISIBLE, SL_INVISIBLE '                                                         (4, 5)  make layer(s) (in)visible
  82.             IF action = SL_VISIBLE THEN visibility = -1 '                                               determine visibility setting
  83.             IF layer = SL_GET THEN SL_LAYER = 0: BEEP: EXIT FUNCTION '                                  SL_GET not valid with SL_(IN)VISIBLE
  84.             IF layer = SL_ALL THEN '                                                                    change visibility of all layers?
  85.                 DO '                                                                                    cycle through all layers
  86.                     count = count + 1 '                                                                 increment layer counter
  87.                     SL_layers(count).visible = visibility '                                             change visibility of layer
  88.                 LOOP UNTIL count = UBOUND(SL_layers) '                                                  leave when all layers changed
  89.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  90.             ELSE '                                                                                      no, change visibility of one layer
  91.                 SL_layers(layer).visible = visibility '                                                 change visibility of layer
  92.                 SL_LAYER = -1 '                                                                         report that command executed correctly
  93.             END IF
  94.         CASE ELSE '                                                                                     unknown error?
  95.             BEEP: SL_LAYER = 0 '                                                                        report that command did not execute correctly
  96.     END SELECT
  97.  
« Last Edit: October 03, 2018, 05:12:52 pm by TerryRitchie »
In order to understand recursion, one must first understand recursion.

FellippeHeitor

  • Guest
Re: Multi-Function Commands. Your opinions?
« Reply #1 on: October 03, 2018, 05:39:46 pm »
It makes sense and does look elegant this new way.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #2 on: October 03, 2018, 06:27:00 pm »
Thanks :)  It will require the programmer to use the constants included with the library to make sense of the command:

value = SL_LAYER(-1, 2)   vs   value = SL_LAYER(SL_ALL, SL_CLS)

But when I used VB years ago you pretty much had to use the constants built into it as well to make the code readable:

ok = MsgBox(3)    vs  ok = MsgBox(vbOkCancel)    (or something like that)

I like the fact that the command count will go way down (and they'll get shorter) in the long run as well.

« Last Edit: October 03, 2018, 06:29:15 pm by TerryRitchie »
In order to understand recursion, one must first understand recursion.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Multi-Function Commands. Your opinions?
« Reply #3 on: October 03, 2018, 11:58:18 pm »
I like this method.  ;)

I also like the idea of passing options via custom type variables, like so:

TYPE example
    X as integer
    Y as integer
    move as integer
    visible as integer
    whatever as double
END TYPE

DIM PassVariable as example

SUB LibraryRoutine (passed as example)

...........

Then you can set the PassVariable.visible to TRUE, and even if you make 40 different calls to the LibraryRoutine, you don't need to retype that value again.   
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #4 on: October 04, 2018, 01:52:06 am »
I like this method.  ;)

I also like the idea of passing options via custom type variables, like so:

TYPE example
    X as integer
    Y as integer
    move as integer
    visible as integer
    whatever as double
END TYPE

DIM PassVariable as example

SUB LibraryRoutine (passed as example)

...........

Then you can set the PassVariable.visible to TRUE, and even if you make 40 different calls to the LibraryRoutine, you don't need to retype that value again.

I like this way too, except from save typing of "canonical" values over and over for each value, it also saves you from changing your SUB/FUNC argument list ever again in the future, as you can add new required parameters simply into the UDT. Hence you can add new features in the future, without the need for any user of your library when upgrading to a new lib version, to run through all his source and changing SUB/FUNC calls just because they got another parameter and the IDE throws errors on it now. I do use a similar approch in the object classes of my GuiTools Framework.
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #5 on: October 04, 2018, 10:15:26 am »
Hy guys
my little opinion:
I agree totally about advantages to use an UDT for getting less mantenance of code and backward compatibility.
The exception comes up when we use the same UDT to save data in the files. In this case we must build a trans-lator from the last UDT to the new UDT.

I love this community!
Programming isn't difficult, only it's  consuming time and coffee

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Multi-Function Commands. Your opinions?
« Reply #6 on: October 04, 2018, 11:10:21 am »
Hy guys
my little opinion:
I agree totally about advantages to use an UDT for getting less mantenance of code and backward compatibility.
The exception comes up when we use the same UDT to save data in the files. In this case we must build a trans-lator from the last UDT to the new UDT.

I love this community!

Translator from old structure to new structure, or else we need to plan a dynamic database to store our information that we can expand upon.

For example, let's say we want to start with the following DataType:

TYPE WeaponResources
    WeaponName AS STRING * 20
    WeaponDamageMin AS INTEGER
    WeaponDamageMax AS INTEGER
END TYPE

DIM GameWeapons(1 TO 100) AS WeaponResources

*****************

Now, if we take the easy way out and just PUT #ResourceFile, ,GameWeapons(), then we'd have to "translate" the data file as you mentioned in your post.

But, let's save it differently...
OPEN "ResourceFile.BIN" FOR BINARY AS #2
FOR I = 1 TO 100
  PUT #1, ,GameWeapons(I).WeaponName
NEXT
FOR I = 1 TO 100
   PUT #1, ,GameWeapons(I).WeaponDamageMin
NEXT
'Same for max damage....

Now, if we want to add a new field (such as weapon element for lightning, fire, ice, ect), we can just tack it on to the end of the database without having to rewrite/translate our data file.

It's actually not that much slower than a direct PUT of all the contents at once (especially if you track data position for direct access), and it allows for easy future expansion of your database.
 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

FellippeHeitor

  • Guest
Re: Multi-Function Commands. Your opinions?
« Reply #7 on: October 04, 2018, 11:45:23 am »
I'm probably misunderstanding what both of you mean by "translate" the data file. If you PUT the total number of records, then the full array(), you just need to read back the total number of records, REDIM the array and GET it full again. What am I missing?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Multi-Function Commands. Your opinions?
« Reply #8 on: October 04, 2018, 12:02:10 pm »
I'm probably misunderstanding what both of you mean by "translate" the data file. If you PUT the total number of records, then the full array(), you just need to read back the total number of records, REDIM the array and GET it full again. What am I missing?

TYPE example
   X AS INTEGER
   Y AS INTEGER
END TYPE

DIM whatever1(100) AS example

........

Now, save that with a PUT #1, , whatever()

..........

Now, alter the UDT:


TYPE example
   X AS INTEGER
   Y AS INTEGER
   Z AS INTEGER
END TYPE

..................

At this point, you've broke the data file.  You can't just GET #1, , whatever() and expect it to work.  You have to read that existing database and "translate" it into a new one, where the fields will match up properly, with those extra bytes for Z...

..................

OR, You need to structure your database in a method suitable for expansion, from the very beginning, when you create it, as I mentioned above.   ;)

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

FellippeHeitor

  • Guest
Re: Multi-Function Commands. Your opinions?
« Reply #9 on: October 04, 2018, 12:03:49 pm »
Ah, now I get it. Thanks!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Multi-Function Commands. Your opinions?
« Reply #10 on: October 04, 2018, 12:10:35 pm »
Not to be picky, but since this isn't an actual "program", shouldn't this topic be moved to "discussion" instead?  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #11 on: October 05, 2018, 02:57:46 pm »
Instead of multi-function, why not just make smart commands? You know, like any image display automatically gets coded as SCREEN 0.

Pete :D
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #12 on: October 05, 2018, 05:31:08 pm »
Another approach if adding to a database is to create a file with the row values and join them, writing to disk to create a file of monolithic rows for the newly changed UDT. Yes, there's no other convenient way to add columns to a database.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Multi-Function Commands. Your opinions?
« Reply #13 on: October 05, 2018, 05:44:49 pm »
You need to get to the grammar store before they run out of prepositional phrases.

I always made my databases so I could add any elements I wanted in the future, within reason of length. I used ASCII characters 1-30 do identify them and instr() to parse them into the program. This way, id didn't have to put up with fixed length types, but still with RA files, you do have to fix the length of the complete record. Actually, in some of my oldest work, I just set that length to the two more characters than the largest string. Weird, but RA files can actually be used that way, without fixed length elements.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Multi-Function Commands. Your opinions?
« Reply #14 on: October 05, 2018, 06:14:44 pm »
You need to get to the grammar store before they run out of prepositional phrases.

I always made my databases so I could add any elements I wanted in the future, within reason of length. I used ASCII characters 1-30 do identify them and instr() to parse them into the program. This way, id didn't have to put up with fixed length types, but still with RA files, you do have to fix the length of the complete record. Actually, in some of my oldest work, I just set that length to the two more characters than the largest string. Weird, but RA files can actually be used that way, without fixed length elements.

Pete

Easiest way I found to deal with random length types was to use a second file to index the first one.  For example, let's say my data consists of the following:
"Hello World"
"My name is Steve."
"I like boobs."

Read those and put them in one data file, and then index them in a second file to track their position in the file.
11
28
41

Then you can use the second file to tell you where the proper data sits in the first file...

********************

Easiest way I've found to track and use variable length data in a database.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!