Randomize Methods?
JBWolf
Posts: 405
Hello,
I am working on a lighting project with long strips of rgb led's.
I've programmed lots of different display methods, but right now they are all named and so I have to manually write out the method order.
I would like to randomize this, and the only way I can think to randomize calling the different methods is to number them instead of using names so I can make calls using a variable created from a random integer.
I.E... coginit(8, randomvariable(A,B,C), @stack[0])
Does someone know a more organized way?
Thanks and Happy Holidays
I am working on a lighting project with long strips of rgb led's.
I've programmed lots of different display methods, but right now they are all named and so I have to manually write out the method order.
I would like to randomize this, and the only way I can think to randomize calling the different methods is to number them instead of using names so I can make calls using a variable created from a random integer.
I.E... coginit(8, randomvariable(A,B,C), @stack[0])
Does someone know a more organized way?
Thanks and Happy Holidays
Comments
Sounds like a job for a "case" statement. Basically: Have a repeat loop that: Gets a random number from somewhere. Uses it in a case statement to select your display method. The display method returns after a while and you go round and select another one.
One general outline is like so:
I will probably go with that unless another solution is presented.... would be a pain renaming and documenting the 70+ methods I am using.
The only reason I am wary to use it, is because I would need to make more than 70 case statements.
What about putting the names in a DAT section?
Would storing them as byte work properly for a method call?
i.e....
coginit(8, methods[randvar](A, B, C), @stack[0])
DAT
Methods BYTE "Method1", "Method2", "Method3", .....
I'm not really sure what you are suggesting. If you have an outline of some code, that compiles, to show us that would be good.
As far as I know there is no way to call methods via a variable or via an array of variables. (Well, I suspect some gurus here have hacks to do that but it's not "normal" Spin usage.
The only way I can see to do it is via "case" or a bunch of "if"s one after the other.
In that case you could probably arrange to start different bits of PASM in a COG just by looking up their address in an array and calling COGINIT with it.
There is possibly another approach.
Sounds to me like you have all your light sequences hard coded as Spin statements. It might be possible to define the sequences in some form of data structure and only have a single method that "plays" the sequence. Basically your display method becomes an interpreter of the display sequence format you have defined. Then you only have to point that "play" method at different sequence data.
They are individual rgb LED's with integrated WS2811 chip.
here is one method as an example, it displays 255 colors gradually fading across all led's in the string:
I have tried using DAT which didnt work.
Going to go with case for now
I am not sure but if IIRC that was a way to use spin objects in array published somewhere in the forums and/or obex. perhaps this can work also for methods. At the end an object can be a single method.
I would like to display all of the methods once without repeating any one twice, until all in the list have been used once.
I can think of one way to do this, but seems absurdly archaic to me.
each method including the main loop has its own counter variable just for this purpose.
The main loop on the first cycle of running all methods has a counter variable equaling 1... on the second, 2 and so on... this is only incremented when all methods have been used once (we'll call this variable "LoopCount").
When a specific display method is run (determined by random variable in main loop), +1 is added to that specific methods dedicated counter variable (we'll call these variables "MethodCount1", "MethodCount2" and so on).
The main loop then generates a new random number, but before running the correlated method, the main loop compares the "LoopCount" against that methods "MethodCountX", if they are equal, then it has already been run and a new random number should be generated.
I know this will work, but seems a terrible way to accomplish the task.
Basically let's say you have 10 different things to do. Number them. Put those numbers in an array. Apply the F-Y algorthm.
You can find a description of Fisher-Yates here : http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
There you will find a few different versions to consider and play with. They are all very simple.
One issue, if you are very fussy, is that having played through everything at random and once only, as you require, you may end up starting all over again and occasionally picking as the first sequence to play the one the was played last on the previous cycle.
Be careful picking your random numbers. It's easy to introduce I bias when generating a random number within some desired range. You may or may not care about that.
If you are shuffling 70 odd different things you ideally want a really good random number generator otherwise there will be a bias in the results.
Spin's built in random operator is terrible. It's a pseudo random number generator which:
a) Fails the most basic statistical tests of randomness.
b) Has a very short cycle before it repeats itself again.
What this means is that your shuffling will not be evenly distributed because of a) and your shuffling won't generate all the possible permutations because of b).
I would suggest you use Chip's "Real Random" object that generates randomness from electrical activity in the Propellers phased lock loops. I think Real Random is in the OBEX somewhere.
That only leaves the problems of generating unbiased random numbers in the ranges required and perhaps ensuring that one shuffle does not end with the same thing that the next shuffle begins with.
For fun you might want to check out JKISS32. A very long sequence and statistically sound random number generator that can be seeded with Real Random.
Indispensable for amateur programmers like myself, I sincerely appreciate the help!
I will definitely check those out
I created a random number generator to use with an effect I called 'sparkle' which quickly flashes random led's white, no idea just how many repeats I get, but the visual result turned out very nice.
After writing it, I watched closely on the led chain to make sure that there weren't certain sections hat stuck out as being always dark or excessively used. Looked like they were all being used including the first and last in the chain, and I didnt notice any patterns.
Here is the code:
"Encyclopedia", if only. You just happen to have hit on a seam of useful information in the sentiments of my mind that is not too deep to have been forgotten yet:)
Now, we have to fix that modulus operator you are using to generate random numbers within a range. Problem is it produces a bias in the resulting sequence, some numbers will occur more than others.
Consider: If your random number generator produces numbers between 0 and 15 with equal probability. And then you do a modulus 10 so as to get numbers 0 to 9. Then numbers 0 to 9 from the generator will become 0 to 9 in your output. BUT numbers 10 to 15 from the generator will become 0 to 5 in your output. You then have more possibilities of getting 0 to 5 than you have 6 to 15. Not good.
One can get obsessive about random number generation, as you see:)
I would suggest you keep things easy at first. With 32 possiblities you can use a single long to keep track of everything. Generate a random # between 0 and 31, check that bit in your playlist variable, and if it was clear, play that routine. When played, mark it. When all bits in the playlist are set, clear it and start again. If the bit was already set, then run the randomizer again until you land on a clear bit.
Of course you can use multiple playlist variables, but I think it's best to get the process working with something a little more manage.
On a client project I had to randomize the playback of 600 audio files and maintain them between power outages. In this case I used an array of 600 bytes and when I file was played I marked the byte in RAM and wrote it to the EEPROM as well -- that way the next reboot would auto-populate the array. I also kept a counter variable (save to EE the same way) so I would have to scan 600 bytes to see if all were non-zero.
BTW... for my random projects I use ReadRandom (by Chip) to seed Heater's random object. After the Heater's object is seeded I can stop RealRandom to recover a cog. It's only two additional lines of code in the start-up sequence and ensures the customer doesn't get the same sequence every start-up.
The method called play_the_sequence would hold the mechanism to jump to the appropriate sequence routine -- I think case is best.
Here's a recent project using code like this: In the foreground of this photo is Limp Bizkit guitarist Wes Borland in a costume made by Steve Wang's shop. It uses 300 WS2801 LEDs that are controlled by a Propeller QuickStart with an output board I designed. There is quite a lot of "random" code in this dude -- thought it's not as exciting as we would like because we have to manage power to ensure that it stays lit for the entirety of a 2+ hour show (using a single 3300mAH LiPo).
That was some cool gig.
Are you telling me that some derived version of my JKISS32 object + Real Random is playing live on stage there ?
I'm overwhelmed!
Do you have a link to the vid? I'm in need of some counter Christmas music!
The only video I'm aware of is from the Riot Games championship. I had dinner with Steve and his crew on Friday and Steve told me Wes has posted video from the tour, but I have yet to find it. Here's a link to the first public appearance of that costume (Wes appears at the 5-minute mark).
-- http://www.youtube.com/watch?feature=player_detailpage&v=pO5P7-LxzTE
In this case, we ran a specific (monochrome) sequence so there is no randomizing. There are multiple shows in the tour code and Wes can change to another show by pressing a button on the back the helmet (left side, as that arm is free of costume).
BTW... the prng object I use is your code; I'm just a nut about formatting, and as I knew I would be sharing project code that contained this, I re-formatted your code to my liking -- with full attribution as you can see in the attachment.
I have no idea what LoL is (as opposed to "LOL", LOL) and I have no idea what a Limb Bizkit might be. But that looks like an awesome event.
No attribution required, after all I only ported JKISS32 from David Jones to Spin, who adapted it from the work of the late great Professor George Marsaglia.
Limp Bizkit is an American band; very popular world-wide. They play a mix of heavy metal with rap. It's an interesting combo but not really what I would listen to all the time (prefer classics like Led Zeppelin, Stones, Hendrix, Aerosmith, et al).
Steve's shop is building another LoL game character display. I was over in his shop one night last week using a Propeller (EFX-TEK HC-8+) and some high-power amber and red LEDs to create the look of fire using steam and light. Your random object is used is in that, too!
Limb Bizkit seem to have a good idea, rap needs something to make it interesting...anything.
quick off topic Q...
I know about using the 'result' command for returning a value from a method.... but what about if I want to get a value back from a non TSR cog?
I have used long addresses many times to do this, but the new cog is just a simple one time run to get a random number when needed. Do I have to use a long address, or can I do something like:
ReturnValue := coginit(8, methodname(variable), @stack)
The book says if i were to use that code with 'cognew' in place of 'coginit', that 'returnvalue' would simply equal the cog number that was launched... but it doesnt say if the same happens with coginit.
Seems to me that since I used coginit, i already know what cog number was launched, so 'returnvalue' might work with a 'result' in the method.
would this code return like I want?:
What I do notice is that this looks like it can generate a very large number.
How can I modify this to have a maximum value? I'll probably never need a number value more than 100 for this particular project.
But at the moment i need a max value of 30.. then later i'll be increasing this to between 75 and 100.
Should I just do this?
RandNum := (rr.random //30)
myRandom := Random MOD (myRandomRange)
Where:
Random is the big number coming out of a random number generator, in the range zero to something huge.
myRandomRange is the range over which I want random numbers. So for example for an output range zero to nine, which is ten numbers, myRandomRange would be 10.
myRandom is the result we are going to use.
This works at providing numbers in range but does suffer from bias as explained above. Unless the range you want is a power of two.
With cognew you are in fact requesting a cog if one is free. If the call was successful, you'll get the cog number 0..7 back; if there were no available cogs you get -1 back. This is why you see +1 in a lot of programs at the end of the cognew line: it promotes the result such that it can be evaluated as true or false (0..7 becomes 1..8 [true], -1 becomes 0 [false]).
With coginit you are not asking for a cog, you're taking it -- no matter what else is happening in that cog. This is why you need to be cautious with coginit; if you've not kept track of cogs used you could overwrite a cog that is in use.
Here's some super ugly C++ code to demo (and yes, I know rand() is horrible [8^):
Jonathan
I have to think about that.
My naive idea is that if you want numbers in the range 0 to 9, say, then mask off all but the low four bits of the generator, then when you get a number greater than 9 throw it away and take the next one, check it for greater than 9 again, and so on.
That reduces the rate at which you can get random numbers but I think it removes the bias totally. (Emphasis on the word "think" there).
Jonathan