BMX Save State (Source Included)

BlitzMax Forums/BlitzMax Programming/BMX Save State (Source Included)

dw817(Posted 2016) [#1]
I was looking at Monkey-X's own SaveState command which allows programmers to save a single string of data to recall variables later.

http://www.monkey-x.com/Community/posts.php?topic=1305

This can be adapted to BlitzMAX and with easier access by my code thus.



Question: Is there a way to really save and load all variables in use without defining them during runtime and shutdown ?

I.E.:

local a,b,c,cards[52],rod$[4]

loadstate

These variables (defined at top as either local or global) will automatically be set:

a=17
b=23
c=4
cards[4]=17
rod$[2]="Silver"

Naturally using this method means ALL variables are loaded and saved with no choice of ignoring particular ones easily.


Brucey(Posted 2016) [#2]
It would probably be a bit more useful if it stored Objects - since this is how most people use BlitzMax.


dw817(Posted 2016) [#3]
Hi Brucey.

Could you give an example of this please ?


Brucey(Posted 2016) [#4]
An example of an Object or an example of how to store the state of an object?

An Object :
Type TDog
  Field name:String
  Field weight:Int
  Field age:int
End Type


And an example of storing and retrieving object state :
https://github.com/maxmods/bah.mod/blob/master/persistence.mod/persistence.bmx


dw817(Posted 2016) [#5]
Reading your source code ... Hmm ... Mmm hmm ... Yeah, that's what I thought ... Okay.

I think mine is a little easier to understand, Brucey. Noobs can definitely use my code as is and use it right out of the box without requiring special compilation or learning. I'm also using a lot more remarks to explain things.

Running your code I get, "Modules does not match commandline module." and I'm sure you can give me a detailed explanation of how to get your code running after I have to compile it manually a unique way - and so forth.

Reading more, I don't think we're on the same page here. In all of your code, I'm not even sure how I would save or load a single integer variable.

Now I'm not going to generalize and say this is how most people use BlitzMAX, because I really think each and every one of us is unique in our own ways and we have our own methods of coding and defining our programs.

But I will say those that want simplicity will likely find my code easier to understand, and, once again, it runs as is, no special libraries needed for execution.

We definitely code differently, Brucey. :)

Now if there is an interest in my program, I CAN add it so you can load/save state for arrays of any length and type, but that's about as complex as I wanna go here.

As Chris tells me, KISS. Keep It Simple Stupid.

. . .

On a different note, Brucey, you should write a routine for Monkey-X to replace their SaveState. As mentioned from what I can tell, it only saves and loads a single string requiring the user to provide the code between to save or load more than one variable at a time.


Brucey(Posted 2016) [#6]
In all of your code, I'm not even sure how I would save or load a single integer variable.

You wouldn't. It's designed for persisting Objects (like TLists, TMaps, any Types, Arrays, Strings)

There some nice "ini" file format modules available (that people made) for persisting config data like your save state is intended to do.


How would you go about storing information about say, a dog (it's name, weight and age) ? (perhaps for some database app or whatever)


Brucey(Posted 2016) [#7]
On a different note, Brucey, you should write a routine for Monkey-X to replace their SaveState

No thanks :-)
I played with Monkey when it first came out, but I found it too many steps backwards (compared to BlitzMax) for my use.
So I decided instead to enhance BlitzMax (adding native 64-bit support, amongst other things). That way, all my previous work (modules and apps) required little or no modifications to run on new architectures (x64, ARM, iOS, Android, RPi). A move to Monkey would have required scrapping everything and starting over.


dw817(Posted 2016) [#8]
The simplest way would be:

global dog_name$[99],dog_weight[99],dog_age[99]

I know you can do this with TYPES but looking them over I decided I just really don't like them at all. I'm comfortable with TLIST, however.

And Brucey, you SHOULD allow for saving a single integer. That situation will definitely come up in someone's code or videogame.

As for saving a TLIST, that is exactly what my program is doing.

Also, I am not using BlitzMAX for business software, just games. Simple games. My code will definitely work for your classic videogames to keep track of, for instance, the top ten high scores. I can easily add this routine if it is desired.

Global highscore(10) ' top ten high scores
loadstate
loadarray highscore,"highscore"
. . . game
. . .
Ready to leave ?
savearray highscore,"highscore"
savestate

BlitzMAX is smart enough to determine the length of arrays even if they are not known.


Brucey(Posted 2016) [#9]
Heh, okay :-)


dw817(Posted 2016) [#10]
Brucey, I have a 64-bit machine. Can I ask what you are doing that is better than current BlitzMAX 32-bit ? I don't know but I want to ask as you are far advanced in coding than I am. And to be honest, I really don't understand a lot of your coding.

I wish you would write out-of-the-box code and functions sometime so I could really explore it and learn from you.

And - have you developed a language yourself, and IDE, or somehow the ability to port BlitzMAX to Flash (SWF) instead of just debug or final state EXE ?


dw817(Posted 2016) [#11]
Bit of work but got it up. This version will save state regular data in addition to any arrays you want, both string and numeric. Example code included.




Bobysait(Posted 2016) [#12]
I won't tell you how you must implement your code, every body has its own obsession ...
I will just tell you 3 Things I don't like in your "coding-style" :

1 - you always put the demo part on top.
> In my opinion, you shouldn't, there is many codes that embedded global variables outside of encapsulation, your demo won't initialize them when your program is loaded first. If you really want to put a demo on top, put it in a type and call it on the last line of the file, then the library part will be loaded first and the main scope will init everything before running the demo function.

2 - you use space instead of tabulation
> it's really too tight for my eyes, it's stressing my small brain with lots of characters with no air between words. I feel opressed and I can't force myself reading that.

3 - in your loop you always draw stuff before updating the logic.
> you'd better update the states of every thing before starting to draw a single rect, to ensure initialization is done and every thing is in its right place. And, while the drawing part is the most important for the user (it's all what he sees), for the program, it's almost always the less relevant. The drawing part should be a simple "if condition then draw stuff" where the condition is already a known state.

Then, once again, as I said above, I won't tell you how to code, if you're confortable with this "workflow", why not, if you don't you have now 3 clues to make a friend ^_^ (me).


Derron(Posted 2016) [#13]
- When closing the app I get a segfault (access field or method of null object)

- what about storing "[" and "]" strings -> enquoting of such things is needed


Bruces Persistence-module is doing what it should do: it allows persistence of objects. For simple objects you could just serialize them ... array of strings (which allows floats, ints...), enquote strings correctly (replace "|" with "\|" and "\" with "\\" ...), glue them together using your stitch-string ("||" or so).
On Deserializing you then do the opposite way - result is an array of strings (which you cast accordingly when requesting special types).



bye
Ron


dw817(Posted 2016) [#14]
Hi Bobysait.

[1] I put my main code on top as that is the one that gives me the most trouble and I always want it to be first in appearance.

I'm actually doing better in my coding ! There was a time when I would call a function named, "Startup()" that initialized all the global variables leaving ONLY the main code on top and nothing else at all. :) I don't do that anymore.

[2] I don't like to use non-ASCII codes in my source (unless it's binary data - and that's not a good idea for BlitzMAX). And that includes TAB.

I've been using 2-spaces to indent all the way back to TRS 80 Turbo Pascal, the first language that did not require line numbers. That's going to be a hard habit to break, Bobysait.

[3] The greatest difficulty I have in programming will always be output, and I want output at the top to ensure it works if nothing else does (as visual to me is far more important than process when beginning a program).

Now I will vary this. If I call a full update routine, I may save it for last, but that's only because I have written the routine in such a way that the output is directly affected by the code, and it is a total rewrite of the screen each time.

For small and most programs, I always want the output first as I test that first. I will also always try to put the glflush() at the end before the either infinite loop or exit by ESCAPE. So essentially I am only updating the screen at the end of the loop.

. . .

As for friendships, truly I would not let someone's personal preferences in coding affect a friendship one way or the other.

No, it is honesty, compassion, understanding, and empathy. I see these elements as vital to one's determination of friendships.

A good friend is someone that knows you're a little strange in the head on some things too but does not let that affect the connection and bond you have.

And I can't claim credit for this, but it's a good one. "A good friend should never make you feel any different than who you really are."

One of my own quotes, "It is not the position of a person that makes them great, but their DISPOSITION."


Bobysait(Posted 2016) [#15]

As for friendships, truly I would not let someone's personal preferences in coding affect a friendship one way or the other.



Sorry, << you have now 3 clues to make a friend ^_^ (me). >>
I was just kidding a bit :)

For point 2 and 3, I understand.
For point 1, you just have to encapsulate the "demo" part

Superstrict
Framework ...
Import ...

Type Demo
   Function main()
      SetGraphicsDriver GLMax2DDriver(),0 ' zero forces front buffer
      Graphics 768,576
      [...]
      End
   End Function
End Type


[...]
' At End of File, run the demo, so the code is on top
' and the call is at the bottom, and ensure everything is perfectly loaded
Demo.main()


It doesn't change too many things but it will prevent non-encapsulated variables from not being initialized.

Now, it's just an habit (usefull or not), I'm just guessing because I'm a bit lazy, and when I test a user code, there is always lots of things to check before hittin F5. (I care about my computer, and don't want to crash it just because somebody want his code to be tested)
Those habits allow to prevent many errors and I wish I live in a world where every programmers code just like me ... but I can't be ignorant too :) so, if I can ask someone to make some changes, I do, if he is not disposed to change, there is no problem, I understand that, but then, I'm still lazzy, so I probably will skip running his code if it's too far from something readable "for me".

by the way, you don't need to work in FrontBuffer if you use a Flip in your loop (I might be wrong but, the Flip is used to swap the back and front buffers)


dw817(Posted 2016) [#16]
The problem is Types. I now know why I don't like them. They are a function I don't choose to write because I want ALL display to be done by a routine like nano() (for massive programs), or in the MAIN code itself.

Whenever I try someone's source, if I can't find it at the top, I press CTRL-PGDN to jump to the bottom because I know that is where their main program is.

Main to me will always be main, and to me it stays on top. functions will always be secondary. Playing 2nd fiddle or wingman to the main code itself.

You'll have to forgive but I have truly fallen in love with Front Buffering and wild horses couldn't drag me from it now. :) I oft write code in BlitzMAX and wind up with flicker problems. Using this method it never happens now - and gives me a chance to do debugging of code without flicker additionally.


Derron(Posted 2016) [#17]
Regarding Save Load:


	Method SerializeTLocalizedStringToString:string()
		local s:string = ""
		'concencate all into one string
		'de::TextGerman::en::TextEnglish::...
		For local language:string = EachIn values.Keys()
			if s <> "" then s :+ "::"
			s :+ language.replace("\","\\").replace(":", "\:")
			s :+ "::"
			s :+ string(values.ValueForKey(language)).replace("\","\\").replace(":", "\:")
		Next
		return s
	End Method


	Method DeSerializeTLocalizedStringFromString(text:String)
		local vars:string[] = text.split("::")
		local language:string, value:string
		local mode:int = 0
		For local s:string = EachIn vars
			s = s.replace("\:", ":").replace("\\", "\")
			if mode = 0
				language = s
				mode :+ 1
			else
				value = s
				mode = 0
				Set(value, language)
			endif
		Next
	End Method


This serializes a group of "key=>value" into "key::value::key::value::key::::" (last being "null"). Think it is not that hard to adjust it to your needs (eg create a tmap on deserialization and returning this instead).
Such an TMap is useful to store various information.


bye
Ron


dw817(Posted 2016) [#18]
Erm ... I have no idea what that code is doing above, Ron, especially with zero remarks.

Understand you guys started out likely on the IBM-pc w Internet, whereas I started out on the TRS 80 w audiocassette. I still think of code like this:

[REMARKS - TITLE, VERSION, CHANGES]
[EXTERNAL INCLUSIONS - INCBIN()]
[STARTING DEFINITIONS, VARIABLES, AND ARRAYS]
[FLAGS - HIGHLIGHTED VARIABLES MEANT FOR CHANGE BY USER]
[INITIALIZING - ANYTHING THAT HAPPENS ONLY ONCE PER EXECUTION]
[MAIN - USUALLY IN AN INFINITE LOOP, UPDATES THE SCREEN BEFORE LOOPING]
[FUNCTIONS, PROCEDURES, AND ROUTINES I WROTE]
[FUNCTIONS ,PROCEDURES, AND ROUTINES I DIDN'T WRITE - ALWAYS GIVE CREDIT]

The greatest answer to give someone means naught if they do not originally understand the question. :)


Derron(Posted 2016) [#19]
The important part is:

serialization:
- replace "glue" in the value's string representation with some enquoted value (":" becomes "\:")
- concat values with some "glue"
-> serialized result

deserialization:
- split into parts by using "glue" as scissor
- replace enquoted glue again ("\:" becomes ":)
...

bye
Ron


dw817(Posted 2016) [#20]
Clear as mud, Ron. Look at the code I wrote above and compare that to yours. If you want me to understand, you are going to have to speak to me at a level I understand.

Understand it's about 3-4 of you in these forums and you all code way over my head. I'm here often cause I'm retired and get to work on code. Occasionally I think I can help someone with simple problems or something involving a formula or method to get a desired result in code.

That's about the size of it.


Derron(Posted 2016) [#21]
This is why I gave an abstracted overview on the principles (for you to learn, if you are interested).

- you have various (named) values
- you want them in one single string
- you have to connect them via "glue" (eg. "::" or "||" or whatever you like)
- as a value could contain "::" already, you have to modify this value by replacing ":" with "\:". As "\:" is also a potential original string possibility, you have to encode "\" as "\\" before (short: first encode the "encode char" ("\"), then encode the splitter char (":"))

"value \:2" (original) becomes then "value \\\:2"

- now you could safely chain them together (string + "::" + string2 + "::" + string3 ...) (result eg "value1::value \\\:2::value3 with spaces")


When you then are deserializing, you reverse the process
- all values are still glued by "::" - so we could use 'myStrings:string[] = serializedString.Split("::")'
- now you have multiple array entries ("value1", "value \\\:2", "value3 with spaces")
- next step is to decode the "\" and ":" again (also in reversed order) - so first decode the splitter (Replace("\:", ":")) - now the text does only contain the "\"-encoded "\" ("\\"). Means: Replace("\\", "\").
- example for value2: "value \\\:2" => "value \\:2" => "value \:2"

Do not frighten the "written so utterly complex" portion, think about it, it is really simple (once you understood it).

The hardest part is the encoding of your special chars.


bye
Ron


dw817(Posted 2016) [#22]
Still clear as mud. It's up to you to explain at my level - or not. That's entirely up to you.

I'm still working on the 750k Carryall. I would've been done by now if it could handle only just a single file, but I'd like to see it handle folders and with recursion and encryption - so it's taking a bit longer.

bye
David


Bobysait(Posted 2016) [#23]

Understand you guys started out likely on the IBM-pc w Internet, whereas I started out on the TRS 80 w audiocassette. I still think of code like this:


mmm ... hey <<papy>>, you're maybe retired, but actually, I started on an old (that was not that old at this time) Thomson TO7, (the one with the optical stylus ^^)
Internet wasn't democratized, it was more about geeking technology. (I remember the long time spent copy-pasting char after char the undreds of line to compile a game from a book ... with 90 percent chance to had a mistake on the first lines.

Difference between us is probably that we update our code style as soon as new technologies allow it :)
Curiously, I don't feel the nostalgy of those old hardwares. It gave me so many headackes

But, sorry, it's totally off-topic, so, I skip this subject.


@serialization:
You add some <<escape characters>> (which means that the next character following your "escape char" will be identified differently from what it is, example : "\n" the n will be interpreted as a carriage line feed in the string instead of a "n")

It allows to store complex texts in a single string and can contain any special characters that a string is not supposed to handle, like "0" char or else.
In php, it's a commun use, because it allows to store all the code of a message (on a forum for example) in a single string in the database

when you deserialize, you actually rebuild the original text replacing the interpreted chars by the real values. (it's the "reverse" side)


dw817(Posted 2016) [#24]
Now I understand this, Bobysait. \n for CR.

In my primary TRX routine for S3 there are quite a few commands I've developed.

^n = ^ & digit, display special characters
^f = different font
^s = different character set (up to 255 of them)
^c00 = letter or two-digits, change to colorset f=fire, glow red, l (el)=lime, glow green, i=ice, glow blue
^^ = CR
^[ = left justify next text
^] = right justify next text
^= = center next text
^_ = add input type

And there are many other commands

I'm not sure if that is considered serializing.


Bobysait(Posted 2016) [#25]

I'm not sure if that is considered serializing.


It can be.
Serialization is a generic term that doesn't imply a specifif method. There is tons of way to serialize a message, most of them are not generic at all (actually, the more generic, the lower the serialization).
bbcodes in forum are serialization. Transfert packet from client to server and back using some encryption is serialization.
Anytime you translate something into something else and can reverse to the original (deserialize) it can be considered "serialization".


dw817(Posted 2016) [#26]
OK so let's say I understand serialization now, Bobysait. What does that have to do with a program that saves and recalls variables ?


Brucey(Posted 2016) [#27]
OK so let's say I understand serialization now, Bobysait. What does that have to do with a program that saves and recalls variables ?

Heh. Your question implies therefore that you don't understand serialisation ;-)

I'd link to the Wikipedia page about Serialization [sic], but it doesn't have any code examples in BlitzMax, only an explanation about what it is :-p


dw817(Posted 2016) [#28]
It's okay Brucey. I've gotten as far as I have by coding and learning one step at a time. Just today I discovered the CreateTimer() and WaitTimer() routine.

I'm surprised that the 'smooth' animation number needs 120 and not 60 as 60 staggers a bit. I thought games were 60fps ?


dw817(Posted 2016) [#29]
Hi Brucey. Surely someone has posted a simple and working example of serialization in BlitzMAX ?

If it's within my realm, I think I've always managed to post pretty simple examples of what could be complex mechanics and thinking given the tedious turn.


Derron(Posted 2016) [#30]
Hi Brucey. Surely someone has posted a simple and working example of serialization in BlitzMAX ?


If you are not able to adopt the code in #17 then this might help:


Output:
./bmk makeapp -t console -quick -r -x "/testcodes/serialization.bmx"
[ 77%] Processing:serialization.bmx
[ 88%] Compiling:serialization.bmx.console.release.linux.x86.s
flat assembler  version 1.68  (32768 kilobytes memory)
3 passes, 2596 bytes.
[100%] Linking:serialization
Executing:serialization
Serialized: 10::Hello, the "\:\:" is the escape character::Yeah!
valA: 10
valB: Hello, the "::" is the escape character
valC: Yeah!


And NO ... I wont write you a more generalized example as this gets longer code then - which you seem to dislike.

The most generalized code would be to use Brucey's TPersistence as it automagically takes care of objects. To store simple integer/strings/... you would have to wrap them in a custom type (TSaveGame with Fields "live:int", "name:string" ...). This is needed, because globals are not available via brl.Reflection.


bye
Ron


dw817(Posted 2016) [#31]
No that's fine,Ron. I think I'm understanding. Now, what if I wrote code to get the same results as you have. Would that be 'correct' ?
Global valA:Int = 10
Global valB:String = "Hello, the ~q::~q is the escape character"
Global valC:String = "Yeah!"

Global serializedString:String = Serialize([String(valA), valB, String(valC)])
Print "Serialized: "+serializedString

Global deserializedValues:String[] = DeSerialize(serializedString)
Print "valA: "+deserializedValues[0]
Print "valB: "+deserializedValues[1]
Print "valC: "+deserializedValues[2]
Because I see what you're doing here, and I can definitely write this.


Derron(Posted 2016) [#32]
You do it correct if you are able to put "whatever string content" (all chars) together - and then to split them apart without trouble.

so:

in->(box)->out
in = out


bye
Ron


dw817(Posted 2016) [#33]
Awright, let's see if I got this, Ron:




Derron(Posted 2016) [#34]
Your code will fail if the valB-string is "hi \:\: escape char used". you only escape the usage of "::".

You need to escape the character used to escape the "glue". We decided to use "\" (this is how blitzmax does it too). So we have to escape "\" chars too...with again "\".

Also I used stringVal.Split(glue) to avoid using Mid() for each character.
As you could loop over the return of Split() you are able to do

For local s:string = EachIn serialized.Split("::")
....
Next

As Blitzmax calculates the result once for the whole loop, this isnt that cpu hungry.

I used the
For i = 0 to array.length
approach as this allowed me to have the index variable adjusted automatically... which is good as I replace the content of the array instead of creating and filling a new array to return at the end.


Also: the "::" is just defined by me...you could use a single colon, a pipe ("|")... just like the visual appearance of the double-colon.


Bye
Ron


dw817(Posted 2016) [#35]
Your code will fail if the valB-string is "hi \:\: escape char used". you only escape the usage of "::".

[over my head]You need to escape the character used to escape the "glue". We decided to use "\" (this is how blitzmax does it too). So we have to escape "\" chars too...with again "\".

Also I used stringVal.Split(glue) to avoid using Mid() for each character.
As you could loop over the return of Split() you are able to do

For local s:string = EachIn serialized.Split("::")
....
Next[/over my head]

As Blitzmax calculates the result once for the whole loop, this isnt that cpu hungry.

I used the
For i = 0 to array.length
approach as this allowed me to have the index variable adjusted automatically... which is good as I replace the content of the array instead of creating and filling a new array to return at the end.

Also: the "::" is just defined by me...you could use a single colon, a pipe ("|")... just like the visual appearance of the double-colon.


Well, I thought I was just supposed to duplicate the results, which I think I did, but maybe that's not enough.

Tom has another coding challenge for me at the moment, some code I can find and work with.

And - looking at this code - it's not me. I wear a gray cap that has pins on it of all kinds, usually about Texas and places I've been.

This code is a dark oversized purple velvet cowboy hat that lights up around the sides with 2 replaceable AA batteries in the stem. It's just not me. :)

I can't see a use for this code in my work. The closest thing I can think of to this is my YANK() routine (or pull, if you want to call it that). It works. I'm using TLIST and TMAP variables now. I didn't do that earlier - so some of this advanced coding I'm finding is useful - just not all of it.

Verbally speaking (outside code) what would serialization (deserialization ?) be especially useful for that you think I couldn't write the code to without ?


Derron(Posted 2016) [#36]
(ok, if you use the wrong shortcut for a new tab, the current tab reloads - and your posting content is gone, so you now get the even shorter "new attempt" post -- normal forums should warn if you leave the forum with post content in the reply box...)


@Serialization
Imagine someone asks you for straight information: age, weight, size, name. They are not accepting "I am 19 years old and weight...", pure information is wanted. So you answer "19 years [breath] 190 kg [breath] 150 cm [breath] Blobby".
Your answer is then a bit "serialized" already.
Maybe you use CSV - this is "serialized" data too.


@Escaping
First of all: https://en.wikipedia.org/wiki/Escape_character

Imagine you decided for ":" to split your data sections apart (CSV would use ",", others use "tab").
As long as you only put in numeric values, everything looks nice and clean (1:2:1.22:3). But as soon as we add strings, these could contain your glue code (":" in our case). So you might end up with "a:b:glue code is -:-:c". When now trying to get the original variables back,you would check for your glue code ":" . You then would find: a, b, "glue code is -", "-", c.

So what to do? You have to mark the glue-code-signs used in the given string. So you escape/mark the characters ":" in every value we glue together.
Now the important part as we do not have "regex" for more advanced splits so we need to run through each char of the string, if it is the glue, handle it (check if escaped char, or glue). Important: As we add something to escape "glue", we have to handle that added char too. Often "\" is used as escape char, so a "\" in one of the variables has to get escaped too (Replace("\","\\")).

The serialized code would be then: "a:b:glue code is -\:-:c".

[run "split by glue code"-code]
Result is a, b, "glue code is -\:-", c. Last steps are then, to unescape each ":" which is left (escaped as "\:") and to unescape the escape-char-"\" we used to escape "\" in the string-variable.

Just as information (which has shown, that my "split()" is not catching this ...so it is incorrect):
So what happens, if someone wants to explain the escaping part in the serialized stream: a, b, ": is escaped as \:", c. The serialized string would look like this: "a:b:\: is escaped as \\::c". A simple split(":") would fail now - as it ignores the preceeding "\".


Conclusion (don't want to delete aboves content :-)) is, that your "for loop" is a step in the correct direction - but like said, it will fail in certain situations (might be based on my misleading assumptions I posted before).


bye
Ron


Derron(Posted 2016) [#37]
Hope this works now:




I tried to comment my deserializing code so the "logic behind" gets explained.


valA: "10"
valB: ": becomes \:"
valC: "20"
valD: "escape char \ is useable too"
valE: "escape char \ is useable too"
serialized: "10:\: becomes \\\::20:escape char \\ is useable too:"
deSerialized
valA: "10"
valB: ": becomes \:"
valC: "20"
valD: "escape char \ is useable too"
valE: ""



As you see, I had to escape the escape char "\" too - as you else wont be allowed to use the escape-char in a string you want to serialize (see valD).


bye
Ron


Derron(Posted 2016) [#38]
An alternative I used is:

Function EscapeString:string(in:string, escapeChar:string=":")
	return in.replace("\","\\").replace(escapeChar, "\"+escapeChar)
End Function


Function UnEscapeString:string(in:string, escapeChar:string=":")
	return in.replace("\"+escapeChar, escapeChar).replace("\\", "\")
End Function


But like said, this is only 50% of the rent, because glueing together multiple escaped strings then still need the "for loop" to unglue them ("hi:"+"ho" glued with ":" becomes "hi\::ho" - unglueing cannot rely on split(":") but needs that manual "escape-char-\"-check).

bye
Ron


dw817(Posted 2016) [#39]
Hi Ron. I appreciate you trying to teach me this. Now there is one thing I don't understand. In your code you have:
print "valA: ~q"+deSerialized[0]+"~q"
print "valB: ~q"+deSerialized[1]+"~q"
print "valC: ~q"+deSerialized[2]+"~q"
print "valD: ~q"+deSerialized[3]+"~q"
print "valE: ~q"+deSerialized[4]+"~q"
And in my code to recall a virtual variable, I would use:
print "x: "+vvar("x")
print "y: "+vvar("y")
print "c: "+vvar("c")
print "n$: "+vvar("n$")
In your code you are outputting virtual variables that have no identifying marks, so you must index them by number. What is this used for - and why is this better than being able to recall virtual variables by identifying and matching names ?

Also, can you please write this one line out minus using periods.
		result :+ v.Replace("\", "\\").Replace(glue, "\"+glue)
I think I may understand it better here what's going on.


Cocopino(Posted 2016) [#40]
Derron used this function
Function DeSerialize:string[](serialized:string, glue:string=":")

which returns an array of strings. Therefore, you can get this array of strings by using
local deSerialized:string[] = DeSerialize(serialized)


Why is this better than using identifying names? Because now you can do this:
for s:string = eachin deSerialized
'do something with s
next

no matter how many items are in the array. Much more flexible and less code than having to write and allocate each variable separately.

result :+ v.Replace("\", "\\").Replace(glue, "\"+glue)

v is a string. A string is just an object to Blitzmax, having its own methods (like Replace). Replace searches for the first parameter in the string, replaces that by the second parameter, and then returns a new string. This newly replaced string has the same methods, so you can "chain" methods like e.g.
Local mystring:String
mystring = "this string is old".Replace("old", "new")
print mystring
mystring = mystring.Replace("new", "newer").Replace("newer", "newest!")
print mystring


Do you have Blide IDE? It will help a lot because the intellisense will show you the available methods once you enter a dot.


Derron(Posted 2016) [#41]
I used
result :+ v...

because I append it the same as if doing
result = result + v...


@eachin
Because If I do not know the length of the result set in advance I have to use a "select ...end select".
Eg you get only one string, then it might be "z"-value, if you get two, it might be "x,y" ...

Also: if you do use "eachIn" you will have to increase a "currentArrayIndex" variable - as you else do not know at which position you are.

How would you do an eachin-loop deserializing 3 properties x,y,z ?



bye
Ron


dw817(Posted 2016) [#42]
Awright guyz, I understand this:
a$="123"
a$:+a$.Replace("1","a").Replace("2","b").Replace("3","c")
Print a$


That's a very TRICKY way of replacing the same string with new data based upon its original contents.

I'm not familiar with BLIDE ?


Derron(Posted 2016) [#43]
a$="123"
a$:+a$.Replace("1","a").Replace("2","b").Replace("3","c")


could be --- surprice --- get shortened to

a$="123".Replace("123","abc")


@blide
it is not needed / not a "must have". But it helps a lot if you do not know the classes you use (foreign modules, 3rd party imports).


bye
Ron


dw817(Posted 2016) [#44]
I think I'm more familiar with:
a$=replace$(a$,"123","abc")
I've known about this and have been using it. These "." commands are definitely shortcuts is the way I see it.


kfprimm(Posted 2016) [#45]
https://github.com/blitz-research/blitzmax/blob/master/mod/brl.mod/retro.mod/retro.bmx#L105-L107

Here's the source for Replace. There's alot you can learn from checking out the module source code. Most of it is BlitzMax.


dw817(Posted 2016) [#46]
Yep, I know. This is the compatibility module. Minju if I couldn't use these I really wouldn't be happy.

I'm good where I'm at, and I am using the "." reference in a few of my works now. No worries.

n=Instr(filedat$,a$[i..i+1])-1

This is a convenient way of getting my FNC$ without having to call a function.

And I'm not going to be using this in my code any more.
' >> RETRIEVE SINGLE CHARACTER FROM STRING AT POSITION
Function fnc$(a$,b)
Return Mid$(a$,b,1)
EndFunction



Cocopino(Posted 2016) [#47]

I've known about this and have been using it. These "." commands are definitely shortcuts is the way I see it.



No, they are not "shortcuts", they are methods belonging to objects, in other words: a function specific to that Type. In the case of String.Replace it doesn't add much, but there are many reasons why most programmers nowadays use Object Oriented Programming. One of the main reasons is, it's much more simple to separate code that has nothing to do with other parts of your code.

Let's take your rain code for example:
http://www.blitzbasic.com/Community/posts.php?topic=105666

How will you run that code as a screen FX, while the game is running and other parts (player, enemies etc) also need to be moving? That will be pretty tricky to do, but with using OOP you'll just have something like this (pseudocode):

While Game.Running() 'main loop
	Rain.Update()
	Player.Update()
	Enemies.Update()
	GUI.Update() 'menus, buttons etc
	Game.Draw() 'draw all the updated items
Wend


The rain code is now completely separated from the other parts of your code. Once you are satisfied with your rain code, you'll never have to touch it again, it will not intervene with any other stuff. And, if there IS something off with the rain code, you'll instantly know where to look (I keep each meaningful Type in a different file named TypeName.bmx to keep searching and scrolling to a minimum).

What's more, re-usability will become a breeze (maybe a wizard that can create fire- or toxic rain?). And my personal favorite: OOP allows a good IDE to use intellisense on your types. So once you've typed "Rain." the IDE will show you every available property and method and make it selectable in a dropdown list. No need to remember the names of your functions anymore.

There's a pretty good Blitzmax OOP tutorial around:
http://brucey.net/programming/blitz/misc/library/BlitzMax_OOP_Tutorial.pdf

I can promise you that once you "get it", you won't be looking back. I know, I've been there :)


dw817(Posted 2016) [#48]
Hi Cocopino.

In truth I've never had any problems with effects and I haven't written one lick of Object-Oriented code nor used TYPEs for years, and yet I managed to write S2, a rather powerful RPG Maker in its own right.

I'm not very happy with TYPES. I will use them in the future for 'fire and forget' items like sparkles or effects, but not anything deeper than this.

Actually my main concern ATM is a way to load and use any font that is NOT installed in Windows and I can make use of it for all my gadgets in MaxGui.Drivers. Apparently no-one to date has done this yet.


Cocopino(Posted 2016) [#49]
Of course, nearly anything could be accomplished without ever using a Type. I started out with VB6, never touching any Class ever. I programmed like that for years, and made many programs, a lot of them commercially. However, I will never build anything so unstructured like that again.

I've had many a doubt (and a lot of fear) about OOP. I also remember my first baby steps, something like:
Type THuman
	
	Field firstName:String
	Field lastName:String
	
	Method GetName:String()
		Return firstName + " " + lastName
	End Method
	
End Type

Local human:THuman
Print human.GetName()


Crash. Null Object? WTF? The object is right there !!!
I did not understand it at all.

Can't remember what the breakthrough was exactly, but once I got it, I almost instantly also knew how to program in C# and Java. The concepts are so similar that languages are now just tools to me, and I now choose the correct one for a job instead of trying to bend another one to my will. (e.g. I once tried to use VB6 as a webserver backend language, not a very good idea)

Long story short - yes, you can do almost anything without OOP but you are making life difficult for yourself, and others trying to help you or read your code. I don't think there are many people on these forums that do not use types and methods.


Cocopino(Posted 2016) [#50]

Actually my main concern ATM is a way to load and use any font that is NOT installed in Windows and I can make use of it for all my gadgets in MaxGui.Drivers



If you are distributing your program, why not just install the font during installation?


dw817(Posted 2016) [#51]
Hi Cocopino.

Yes, that is what Grable suggested. However, if I can find a way to display a gadget, like CreateTextArea to appear above another gadget like CreateCanvas, then I can use BlitzMAX's own LoadImageFont command to detail the frame, and that will be sufficient.


Brucey(Posted 2016) [#52]
What would the point be of having a TextArea gadget on top of a Canvas gadget if you are going to be drawing text in the canvas?
You are going to read the text from the text area and then render it in the canvas instead so that the user thinks they are typing with a different font?

Heh :-)


dw817(Posted 2016) [#53]
No. The font in the text area being system is fine. I will use the font loaded by LoadImageFont to detail the names of the items only, thus:



The aqua represents actual drawing area. The white represents the fields for gadgets, and this can vary in both size and number. Any option to draw static text will be done in the drawing area. The WHITE area will only be for fields, buttons, drag bars, and so on.


Cocopino(Posted 2016) [#54]
Many programs install their own font(s).
A couple of extra fonts will hardly slow down the OS so I wouldn't worry about it.


dw817(Posted 2016) [#55]
I am understanding that a font can be installed and removed seamlessly. This is then a possibility I will look into.