Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Qwerkey

Pages: 1 2 3 [4] 5 6 ... 8
46
QB64 Discussion / When is "Variable Type" _OFFSET Not an Unsigned Integer?
« on: December 16, 2019, 05:50:14 am »
Steve McNeill has been teaching us _MEM techniques, which I used to a great extent in a recent project.  The use of a _MEM object requires a DIM statement as:
Code: QB64: [Select]
  1. DIM CMem AS _MEM, COff AS _OFFSET
with subsequent working code of an image "Image&" as:
Code: QB64: [Select]
  1. CMem = _MEMIMAGE(Image&)
  2. COff = 0
  3. WHILE COff < CMem.SIZE
  4.     IF _MEMGET(CMem, CMem.OFFSET + COff + 3, _UNSIGNED _BYTE) < 50 THEN
  5.         _MEMPUT CMem, CMem.OFFSET + COff + 3, 0 AS _UNSIGNED _BYTE 'Alpha
  6.     ELSE
  7.         _MEMPUT CMem, CMem.OFFSET + COff + 3, 120 AS _UNSIGNED _BYTE 'Alpha
  8.     END IF
  9.     COff = COff + 4
The variable "COff" indexes along the _MEM block CMem, and is only ever an unsigned integer.  Why, therefore, does it need to be DIMed?  In fact, before I found out from Steve that the DIM COff AS _OFFSET statement was required, I just used the COff as an unsigned integer, and everything worked OK.

I've read all the _OFFSET wiki pages and I'm afraid that didn't help me: the wiki is written for people who actually understand coding!

This is a question from one of the DIM members!

47
Programs / Pi-in-the-Sky Graphics Demonstration Program (with 3D)
« on: December 10, 2019, 09:44:13 am »
This is a program which demonstrates the amazing graphics capabilities of QB64, using simple coding techniques and without resorting to the complexities of Open_GL.  Impressive 3D effects can be obtained using the QB64 _MAPTRIANGLE statement: the 3D _MAPTRIANGLE method gives a convincing 3D display along with perspective.

The program features a number of animations (some 2D and some 3D) enhanced with accompanying audio.  In order to run this program you will need to download the zip file from the URL.  Extract the folder "pi in the sky" and place that folder in your QB64 folder – be careful not to create and extra folder layer.  Load the "pi in the sky" bas file and run (QB64v2 or higher).  You will need to have the Run Option "Output EXE to Source Folder" checked.  The program uses quite a bit of processor power (up to nearly 2 CPU's-worth at times on my Core i5 machine) and a lot of memory, so you'll need a modern machine.  At program start there is a delay while the disk files are read and loaded into memory (and there are image manipulations being carried out).

The display shows a landscape within which a variety of animations will occur.  It takes approximately 10 mins to run through all of the animation sequences.  There are earthly animations and celestial animations.  During the celestial animations, there are ghostly appearances in the terrestrial graveyard and related sounds.

In the blue text I give details of the program coding methods, descriptions of the animations and background information.  You do not need to read the blue text to run and enjoy (well, maybe!) the program.

Download:
https://www.dropbox.com/s/goekmtjtmt0n7m1/pi%20in%20the%20sky.zip?dl=1

I started this project purely because the phrase "pi in the sky" popped into my head, and so started coding to produce a pi symbol randomly rotating about three axes in 3D.  The obvious QB64 method is _MAPTRIANGLE(3D).  All you have to do is plug in the mathematics of rotations of a rectangle about the three Cartesian co-ordinates and place the resulting corner positions into _MAPTRIANGLE(3D).  The surface of the image is everywhere flat, but its corners move freely in the 3D space of _MAPTRIANGLE(3D), and as (vanishing point) perspective is coded into the _MAPTRIANGLE(3D) method, a very good-looking 3D display is produced.  I must emphasise that this QB64 method is so well produced that it can be used by novice and low-skill coders.  In the Skill Range Venn Diagram, I sit in the low-skill sector, and I cannot do the real-3D (SUB _GL) stuff of our competent members.

To do this project I have had to go back to school-level calculus, geometry & algebra; oh, happy days!  Having got the pi animation working, I then set about adding other features to demonstrate what can done with QB64 graphics.  The final project uses both software and hardware imaging, and for the hardware images both _MAPTRIANGLE(2D) and _MAPTRIANGLE(3D).  The program is designed to show how such images will interact.  The program also uses a load of _MEM object processing in order to manipulate images, both at initiation and on-the-fly in real time.

Background Image
The graphical animations take place in front of a background image.  This background (a landscape with added features) is software (,32 type image) and has a single _PUTIMAGE directly after creating the 32-bit screen.  This image stays displayed despite the running loop executing a _DISPLAY at each cycle (30 fps).  All other images are hardware (,33 type) and are placed in front of the background (naturally!).

pi Animation
In order to change colour as the pi moves around, real-time editing of the image file is required.  This is achieved by _MEM object manipulation at each display cycle.  _MEM processing can only be achieved with software images, whereas the pi image is hardware.  So, firstly a software image is created at initiation and a _MEM object of the software image is taken.  Then at each cycle that object is manipulated and the software image is copied to hardware and that image is displayed.  The hardware image is freed each cycle at the earliest opportunity.  This method is repeated for all other real-time image manipulations.

Earthly and Celestial Animations
There are both earth-bound and celestial animations.  The earth-bound animations (along with clouds and some rain) are continuous, whereas the celestial animations cycle in a random manner.

Earthly Animations

Beehive
Bees emerge and swarm from a suspended beehive in the foreground.  The bees fly in the 3D space of _MAPTRIANGLE(3D) and both the hive and the bee images (QB64 bee logo) are hardware (3D: ,33 image type).  After emerging in an outward direction the bees turn and fly in the opposite direction away from the viewer "into" the screen.  Bees in front of the hive occlude it, and behind the hive they are occluded by it.  As the hive image is a flat object, a bee moving from directly in front of to directly behind the hive travels right through it.  The bees all appear to move to the right as they move away, but this is just the perspective.  All the bees return to the hive after their flight out.  In order to achieve such paths, the bees are flying in parabolic curves in the 3D space.  There is the sound of humming bees, the more bees that are out the louder they sound.

Fountain
A fountain squirts droplets of water into the pond.  There are 500 droplets of water each moving under gravity in the _MAPTRIANGLE(3D) space.  Although as many droplets are sent behind the fountain as in the front, it looks as if they only fall at the front.  This is just an artefact of the perspective.

Windmill
The moving windmill sails clearly show the 3D and perspective effects  of _MAPTRIANGLE(3D).  The sails appear to move in and out as they rotate and are smaller the further away they are.  As the bees, fountain droplets and windmill sails are _MAPTRIANGLE(3D) images, they occlude each other dependent upon which is further away.  The windmill itself is not a 3D object but part of the software background.  The industrious Miller Heitor looks out from the windmill.

Clouds and Rain
Clouds pass above.  They are slightly transparent so that objects behind can still be seen.  One of the clouds is rain-bearing and a shower of 500 raindrops passes along.  The clouds and raindrops are not 3D, and occlusions occur based upon order in which the _PUTIMAGE functions occur.

Flower Growth
As the rain passes, flowers can spring up in the earth.  Flower images are 2D.

Rainbow
When the rain is in just the right place, a rainbow appears.  The rainbow is a _MAPTRIANGLE(3D) image and occlusions with other such objects are dependent upon distance into the screen.

Graveyard
Ghostly images appear in the graveyard when certain celestial events take place.  The gravestones themselves are part of the background 2D image.

Celestial Animations
At times, celestial animations occur.  When a celestial animation takes place, there are accompanying ghostly images in the graveyard.  There is accompanying related audio.

Concorde
Fly-past of a supersonic passenger aircraft.  The Concorde image is 2D.  The aircraft creates a sonic boom.  A ghostly image of Douglas Bader appears in the graveyard.  Eventually the graveyard image gradually disappears into thin air.  This graphic effect is achieved, again, by _MEM object real-time processing (in this case changing alpha sequentially).  Images here are 2D.

ET
A silhouette from a well-known science fiction film travels across the sky.  The image is 2D.  A ghostly film director appears in the graveyard.

Star Trek
The spacecraft from a well-known science-fiction television series moves through the _MAPTRIANGLE(3D) space coming from the far distance.  Members of the crew appear in the graveyard.  Because the graveyard images are small and transparent, it is rather difficult to recognise who they are.  From left to right they are McCoy, Uhura, Kirk, Sulu and Spock.

ISS
The 2D International Space Station passes overhead.  The sound is that of Sputnik 1.  Cosmonaut Yuri Gagarin appears in the graveyard.

Meteors
A 3D meteor shower occurs while Galileo looks on from the grave.

Transit
A transit of Venus occurs.  This is a very rare event, the last occurring in 2012 and the next due in 2117, so we are very privileged.  The goddess appears in the graveyard.  She is The Bringer of Peace and has her appropriate accompanying music.

Thunderbirds
A bomb falls onto the windmill.  Call for action from a well-known science-fiction television series.  Help arrives and the bomb is safely removed.  All images are 2D.  The graveyard crew are Alan, Scott, Virgil, Gordon and John.

Zodiac
The signs of the Zodiac circle in the heavens.  The image is the surface of an open-ended cylinder.  This is the only non-flat 3D image in this program, and the real-3D effect requires multiple _MAPTRIANGLE(3D) processes.  The Zodiac sign at the front is brightened, and this effect is, again, produced by real-time _MEM processing.  Zodiac symbols of the prominent sign appear in the graveyard.  The music is appropriately mystical.
For you I see a romantic liaison with a coach party of ageing workers from the shoe-making industry.  Astrology – what a load of old cobblers!

UFO
An alien spacecraft arrives and settles for a moment, and after probing the environment it lifts off and flies away.  The image is _MAPTRIANGLE(3D) and occlusions with the windmill sails are correct.  In order to make the landing, the spacecraft follows a helical path (circular x-, z- path and linear y- path).  The arrival of this alien craft arouses the interest of two FBI special agents.  As the craft probes the ground, the little green man appears in the graveyard.

Spotlight
A spotlight illuminates the sky and reveals a pertinent message.  The spotlight image is only 2D, and would have been better as 3D but is done this way to show the limitations.  Again, _MEM processing is required to make the message image respond to the spotlight.  The graveyard shows how this program was produced.


Discussion
This has been a tinkering project.  As ideas came, I'd change to new areas of coding and then return to unfinished parts.  This is a bad method to do projects.  Returning to old parts, you don't quite remember exactly what you previously did and end up making mistakes and taking a great deal of time.  The project could still benefit from further improvements/changes and the tinkering could go on for ever (certainly some of the animations are rather naff, if not great fun for the coder).  But I leave it as is so  as to demonstrate where 2D imaging is not good enough to show completely correct perspective/occlusion.


 
screenshot.jpg




48
QB64 Discussion / Offer to help with moderation
« on: December 04, 2019, 08:03:03 am »
Membership Secretary:

Some time ago I noted that some enrolled members are "one-hit wonders" - they appear once (usually with one specific request for help) and then we never see them again, and I suggested that Odin might review (& remove) members who have not contributed for 18 months.

Then recently I noticed that a bot-sourced appeared entry in one of my posts (advertising some on-line casino site) which Odin quickly removed.

Our team has asked for contributions to the Wiki pages, a very good idea if you are able.  My understanding of the coding fundamentals is so poor that I could not contribute to this activity, regrettably.

However, if useful to the team I could look after membership and deal with such issues as described, and could (after a small amount of training) remove such boring tasks from Odin to leave them to work on the important stuff.

As an aside, we could also enrol Pete as Social Secretary.  With his all-round gregariousness he could organise the annual charabanc outing to the Hillary Clinton Appreciation Society AGM.

49
QB64 Discussion / _RGB32 Values for Image (Not Displayed)
« on: November 09, 2019, 12:49:59 pm »
Is there an equivalent of POINT(x,y) of the graphics screen for an undisplayed image?

I want to extract _RGB32 values for all x,y positions of an image which is loaded to memory (either by _NEWIMAGE or _LOADIMAGE) and which is the current _DEST but which is not yet displayed.

If this is possible, I expect that Steve has already told me (a number of times!!), but my one brain cell left hasn't retained that bit of data.  I've gone through all the Wiki statements and cannot find anything that returns such a value.

If this is not possible, I'd have to temporarily load the image, do POINT and make an array to keep the _RGB32 information and then clear the screen: that would be slightly messier.


50
Programs / The Vile-Tempered Clavier
« on: November 03, 2019, 11:23:47 am »
There are 12 notes in an octave, each a semitone apart.  And there are 8 notes in a scale, inclusive of the lower and upper Doh.

Harmony (in Occidental music) for notes in the scale was defined by ratios in the length of organ pipes (half, third and fifth, I believe).  And these were used to set the notes.

This meant, however, that good tonality only occurred within one Key.

At around the time of JS Bach (I may be incorrect in my musicology), it was realised that a near-universal scale could be produced if the ratio in frequencies between each semitone was a constant.  As the frequency doubles for each octave, and there are 12 semitones, the step in frequency is the twelfth root of 2.

This is the Well-Tempered Scale.  Aficionados will know that JS Bach went off and wrote 48 pieces, two in each of the 24 keys (12 major, 12 minor).

It has always struck me: "Why 12?", "What is special about the twelfth root of 2?".  There appears to be nothing fundamental in 12 - maybe it's that 12 is divisible by lots of numbers (but not 5)??

So, out of daftness, I thought that I'd see what a 13-note octave would sound like.

The program uses the SOUND command to play notes on the computer sound card.  I have used Richard Rogers's Do-Re-Mi melody, first with the standard 12-note octave, and then the same thing with a 13-note octave.  You will not be surprised that the second version sounds odd.  Some of the atonality may be to do with the difficulty of fitting the note intervals into the thirteen spaces: in this configuration D# is not the same as Eb.  If the 13-note version is played by itself several times, it becomes more tolerable, so maybe there is nothing particularly special about 12.  But note that no chords are played here.
Code: QB64: [Select]
  1. CONST NoNotes% = 66, A4% = 440, Tempo% = 100, Twelfth! = 2 ^ (1 / 12), Thirteenth! = 2 ^ (1 / 13), P! = 18 * 60 / Tempo%
  2. DIM Tonic!(7), Notonic!(7)
  3. Tonic!(1) = 1
  4. FOR N%% = 1 TO 6
  5.     READ M%
  6.     Tonic!(N%% + 1) = Tonic!(N%%)
  7.     FOR K%% = 1 TO M%
  8.         Tonic!(N%% + 1) = Tonic!(N%% + 1) * Twelfth!
  9.     NEXT K%%
  10. NEXT N%%
  11. Notonic!(1) = 1
  12. FOR N%% = 1 TO 6
  13.     READ M%
  14.     Notonic!(N%% + 1) = Notonic!(N%%)
  15.     FOR K%% = 1 TO M%
  16.         Notonic!(N%% + 1) = Notonic!(N%% + 1) * Thirteenth!
  17.     NEXT K%%
  18. NEXT N%%
  19.  
  20. FOR M% = 1 TO NoNotes%
  21.     READ Dum$, Period!
  22.     Note% = ASC(LEFT$(Dum$, 1)) - 64
  23.     SolFa! = A4% * Tonic!(Note%) * 2 ^ (VAL(RIGHT$(Dum$, 1)) - 4)
  24.     IF MID$(Dum$, 2, 1) = "#" THEN
  25.         SolFa! = SolFa! * Twelfth!
  26.     ELSEIF MID$(Dum$, 2, 1) = "b" THEN
  27.         SolFa! = SolFa! / Twelfth!
  28.     END IF
  29.     SOUND SolFa!, (Period! * P!) - 1
  30.     SOUND 0, 1
  31. NEXT M%
  32.  
  33. SOUND 0, 18
  34.  
  35. RESTORE Rodgers
  36. FOR M% = 1 TO NoNotes%
  37.     READ Dum$, Period!
  38.     Note% = ASC(LEFT$(Dum$, 1)) - 64
  39.     SolFa! = A4% * Notonic!(Note%) * 2 ^ (VAL(RIGHT$(Dum$, 1)) - 4)
  40.     IF MID$(Dum$, 2, 1) = "#" THEN
  41.         SolFa! = SolFa! * Thirteenth!
  42.     ELSEIF MID$(Dum$, 2, 1) = "b" THEN
  43.         SolFa! = SolFa! / Thirteenth!
  44.     END IF
  45.     SOUND SolFa!, (Period! * P!) - 1
  46.     SOUND 0, 1
  47. NEXT M%
  48.  
  49.  
  50. DATA 2,1,2,2,1,2
  51. DATA 2,1,2,3,1,2
  52.  
  53. Rodgers:
  54. DATA C3,1.5,D3,0.5,E3,1.5,C3,0.5,E3,1,C3,1,E3,2
  55. DATA D3,1.5,E3,0.5,F3,0.5,F3,0.5,E3,0.5,D3,0.5,F3,4
  56. DATA E3,1.5,F3,0.5,G3,1.5,E3,0.5,G3,1,E3,1,G3,2
  57. DATA F3,1.5,G3,0.5,A4,0.5,A4,0.5,G3,0.5,F3,0.5,A4,4
  58. DATA G3,1.5,C3,0.5,D3,0.5,E3,0.5,F3,0.5,G3,0.5,A4,4
  59. DATA A4,1.5,D3,0.5,E3,0.5,F#3,0.5,G3,0.5,A4,0.5,B4,4
  60. DATA B4,1.5,E3,0.5,F#3,0.5,G#3,0.5,A4,0.5,B4,0.5,C4,3,B4,0.5,Bb4,0.5
  61. DATA A4,1,F3,1,B4,1,G3,1,C4,4.5
  62. DATA C3,0.5,D3,0.5,E3,0.5,F3,0.5,G3,0.5,A4,0.5,B4,0.5,C4,1,G3,1,C4,1
  63.  

51
Programs / Re: Fisher-Yates Shuffle
« on: October 27, 2019, 04:53:04 am »
Note: This message is awaiting approval by a moderator.
[banned user] (or maybe it was bplus who is everywhere on this site), I believe that you directed me to this efficient shuffle method, used in my Clock Patience program (in the InForm Programs section).

52
Being interested in how much memory one of my programs uses, I loaded Task Manager (Windows), and was surprised to find that my program's memory usage increased monotonically with time.

With the program doing nothing but waiting for a keypress:

Code: QB64: [Select]
  1. k$ = ""
  2. WHILE k$ = ""
  3.         k$ = INKEY$
  4.         _LIMIT 30

there is a gradual increase in memory usage, when you might expect it to remain constant.  The initial increase rate is about 5kB/s which deceases to about 0.2kB/s after about 5 minutes.

Other programs have a varying amount of memory usage (depending upon what they're doing).

The rate of increase continues to get smaller with time.

In a usage of about 68MB this is hardly worrying, but any ideas why this happens?  (I did notice the same thing happens in another QB64-produced program).  Could it even be a reporting inaccuracy phenomenon?  More likely to be that I understand zero about how a computer uses memory!

53
QB64 Discussion / An Array of _MEM Objects?
« on: October 01, 2019, 03:53:20 pm »
Is it possible to have an array of _MEM objects (This is not an array sent to a _MEM block)?

I have an index which can vary from 4 to 10 and for each index I want to address a particular and distinct _MEM object.  Is it possible to do this without doing a SELECT CASE on the index and pointing to the appropriate _MEM object?

I've tried the following code and it works OK, so unless informed otherwise, I'll cary on along these lines.
Code: QB64: [Select]
  1. DIM M(10) AS _MEM
  2.  
  3. M(4) = _MEMNEW(4)
  4. M(5) = _MEMNEW(5)
  5. M(6) = _MEMNEW(6)
  6. 'Et cetera
  7.  
  8. FOR N%% = 4 TO 6
  9.     _MEMPUT M(N%%), M(N%%).OFFSET, N%% AS _UNSIGNED LONG
  10. NEXT N%%

54
QB64 Discussion / More Inform Programs
« on: September 17, 2019, 09:14:18 am »
Guys, why not incorporate InForm into your next project?  This excellent resource is so easy to use with your work, and will give added professionalism to any program.  Using InForm is easy to learn.

Tempo di Basic and I have virtually monopolised the InForm Programs section, with the most recent program added way back in July.

What the Press have said about these InForm programs:

  • The New York Times
  • Well-informed, indeed!
  • The Washington Post
  • This work keeps up a tremendous tempo.
  • The Sydney Morning Herald
  • Quirky, cobbers!
  • The Guardian
  • Mr Q's porgrims are outsnatding.
  • The Times of India
  • We are delighted with such excellence.

55
QB64 Discussion / DIM SHARED Array Into a FUNCTION
« on: September 06, 2019, 07:36:07 am »
I assume that a DIM SHARED array is shared into a FUNCTION in exactly the same way that it is shared into a SUB.  The Wiki states that the sharing is with "sub-procedures" which again I assume includes FUNCTION: I tried a sample routine without passing the array as an argument to demonstrate it to myself (it worked as if shared).  You might have supposed that an old-stager such as I really ought to know this, but I thought that I had better just check.

56
Dear Odin, I wonder if STxAxTIC's post (in Programs Section) "Samples/Toolbox re-awakening. Call for entries" could be made a Header document in that Section, rather than just a standard post.  Where it is, it is likely to get pushed out of existence by the large number of actual Program postings being added.  I know that the Samples Section already has its own Headers, but this one in the Programs Section will remind members to apply for Samples.

57
Programs / Crossword Generator
« on: August 23, 2019, 06:32:59 am »
This program creates a crossword grid and fills it with words.  The filling routine is quite simple and a number of tries are required before a completed grid is produced.

The program creates a standard 15x15 grid with the following rules:

  • Words can be only between 4 and 10 letters long
  • The grid is 2-fold symmetric (if you rotate it through 180 degrees, it'll be the same)
  • There must be perfect contiguousness

The routine for checking contiguousness is fairly simple, but I believe that it will trap faulty grids: if you discover a noncontiguous grid, please let me know.  The routine for placing the blanks is also fairly simple and some grids do not have the standard crossword look (although all are acceptable).

The method for filling the grid with words is a simple blundering process: a random word of the correct length is tried: if all its squares are blank then this word is added: if some of its squares are already filled and the word does not fit, another word is tried.  If (as is likely) the grid cannot be successfully filled, then another grid is generated and the process begins again.

This blundering process takes some time to achieve a completely filled grid, and some efforts have been made to improve the chance of success.  The dictionary used was created from two readily available on the web: quite a large number of words are thus contained in the dictionary.  You may find (as I do) that a completed grid uses some seemingly improbable words, but somebody has judged them acceptable.  The dictionary also includes some phrases.

When selecting words from the dictionary, the program makes some judgement of how likely the word is to cross successfully.  A word which would produce a cross word ending with unlikely letters like Q or Z is avoided.  And a word which would be less likely to cross with another in the middle (letters like J and X) are also avoided.

So you will notice that a completed grid has words which favour the common letters like E in the crossing positions.  The Qs, J's, K's et cetera will only appear at the start of words or in non-crossing positions.

The longest words are placed first, as fitting the shorter words to existing ones has a better chance.

The program is multilingual, and you can choose which language to use.  The alternatives are:
  • British English
  • US English
  • German
  • Spanish
  • French
  • Italian
  • Dutch


Running the program

Extract the folder "Crossword Generator" from the .zip file and place the folder in your QB64 folder (be careful not to create an extra folder layer).  Open the "Multilingual Crossword Generator.bas" in the IDE (with "Output EXE to Source Folder" checked).  [The source folder contains another .bas file which is an older version (English only) of the program].

You will be presented with a Menu List to choose your language.  Although I have constructed this Options Menu to look like an InForm program (Windows native look), there is no mouse function available.  Press the Spacebar to change the language and Return to select your language.


The program will generate a grid and slowly fill with words.  When a word can no longer be found to fit, asterisks are filled in that place - it is unlikely that the grid will be successfully filled - and the program waits for keyboard input.  If you press the Spacebar, another grid will be generated and again words will be added.  If you press "f" when the filling stops, the program will quickly generate new grids and try to fill and the process will continue until a filled grid is obtained. Then the program will again wait for keyboard input.  Spacebar will create another grid, "f" will revert to the step-by-step method.  Esc will quit.

It takes a number of attempts to produce a completely filled grid, and on my Core i5 PC this takes some seconds (dependent upon the language, and if you've a slower machine you may have to wait for longer).

The process to fill the grid clearly has no intelligence to find words that will allow crossing words to be found in a small number of attempts.  An actual human compiler will be careful to think in advance.  Also, there may be some cases where the program, as is, is rejecting acceptable words.  So if you're sufficiently interested, you may like to create a rather cleverer program.

You may well suggest that a naive bystanding youngster would say of this program "But it doesn't do anything!".  This innocent would be correct: the emperor is entirely unclothed.

Multilingual Program now included (Updated 13/09/19)

58
Programs / Methinks It Is Like A Weasel - An Evolutionary Program
« on: August 11, 2019, 10:24:10 am »
Hamlet thinks that Polonius is a fool, which he demonstrates in the following conversation:

Hamlet: “Do you see yonder cloud that’s almost in shape of a camel?"
Polonius: "By the mass, and ‘tis like a camel, indeed."
Hamlet: "Methinks it is like a weasel."
Polonius: "It is backed like a weasel."
Hamlet: "Or like a whale?"
Polonius: "Very like a whale.”


You will be very familiar with Richard Dawkins (ethologist and evolutionary biologist) and his expositions of evolution which are always completely proof against any counter-argument.  In one of his books "The Blind Watchmaker" he describes his attempts to demonstrate natural selection by mutation and survival of the fittest by creating a computer program.  See the Wikipedia URL for detailed information.
https://en.wikipedia.org/wiki/Weasel_program

His computer program simulates an analogous process by attempting to create the phrase "Methinks It Is Like A Weasel" from a starting string block of random letters.  At some point in each of our lives there is probably a desire to write our own "Methinks It Is Like A Weasel" program, so this is my attempt (program updated 15/8/19 for more realistic probabilities, error removal and tidied).
Code: QB64: [Select]
  1. 'METHINKS IT IS LIKE A WEASEL by QWERKEY 15/8/19
  2.  
  3. '128 Males + 128 Females
  4. '128*Birthrate Male Offspring + 128*Birthrate Female Offspring: Male Odd-recessive, Female Even-recessive
  5. 'A certain percentage of mutations in the offspring
  6. 'Only the most adapted 128 Male and 128 Female Offspring survive
  7. 'As Methinks2 (tidied up) but with immovable correct gene removed, mutation rate much reduced, and errors corrected
  8.  
  9. CONST True = -1, False = 0
  10. CONST NoMates% = 128, NoIssue% = 141, JumpStart% = 256, MutationRate! = 2 / 1000
  11. CONST Elite! = 3 / 7, Kappa! = 4, Prob0! = 0.23, GenLimit% = 30000
  12. DIM Mates$(NoMates% - 1, 1), MatesDat%(NoMates% - 1, 1), IssueDat%(NoIssue% - 1, 1)
  13. DIM Mating%%(NoMates% - 1)
  14.  
  15. _TITLE "Methinks It Is Like A Weasel"
  16. Weasel$ = "METHINKS[IT[IS[LIKE[A[WEASEL"
  17. Alphabet$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ["
  18.  
  19. SCREEN _NEWIMAGE(984, 720, 32)
  20.  
  21. 'Initialise 1st Generation
  22. Generation% = 1
  23. FOR R%% = 0 TO 1
  24.     FOR N%% = 0 TO NoMates% - 1
  25.         Mates$(N%%, R%%) = MID$(Alphabet$, INT(RND * 26) + 1, 1)
  26.         FOR M%% = 2 TO 28
  27.             Mates$(N%%, R%%) = Mates$(N%%, R%%) + MID$(Alphabet$, INT(RND * 27) + 1, 1)
  28.         NEXT M%%
  29.         MatesDat%(N%%, R%%) = 0
  30.         FOR M%% = 1 TO 28
  31.             MatesDat%(N%%, R%%) = MatesDat%(N%%, R%%) + ABS(ASC(MID$(Weasel$, M%%, 1)) - ASC(MID$(Mates$(N%%, R%%), M%%, 1)))
  32.         NEXT M%%
  33.     NEXT N%%
  34. NEXT R%%
  35. 'Sort & Display 1st Generation
  36. CALL Sort1(MatesDat%(), Mates$())
  37.  
  38. 'Cycle through generations:
  39. REDIM Papa%%(NoIssue% - 1, 1)
  40. Dawkins%% = True
  41. OneByOne%% = True
  42. WHILE Dawkins%% AND Generation% < GenLimit%
  43.     'Display Current Generation
  44.     CLS
  45.     COLOR _RGB32(255, 255, 255)
  46.     _PRINTSTRING (4, 4), "Generation:"
  47.     _PRINTSTRING (92, 4), STR$(Generation%)
  48.     _PRINTSTRING (186, 4), "(" + LTRIM$(STR$(MatesDat%(0, 0))) + ")"
  49.     FOR R%% = 0 TO 1
  50.         IF R%% = 0 THEN
  51.             COLOR _RGB32(255, 255, 0)
  52.         ELSE
  53.             COLOR _RGB32(0, 255, 255)
  54.         END IF
  55.         FOR N%% = 0 TO NoMates% - 1
  56.             W$ = Mates$(N%%, R%%) 'W$ gets modified by Spaced$()
  57.             IF N%% <= 63 THEN
  58.                 _PRINTSTRING (4 + R%% * 500, 15 + N%% * 11), Spaced$(W$)
  59.             ELSE
  60.                 _PRINTSTRING (4 + 250 + R%% * 500, 15 + (N%% - 64) * 11), Spaced$(W$)
  61.             END IF
  62.         NEXT N%%
  63.     NEXT R%%
  64.     IF NOT OneByOne%% THEN
  65.         _LIMIT 6
  66.         _DISPLAY
  67.     END IF
  68.     'Find partners (monogamous, brother & sister not allowed to mate)
  69.     PartnersAvailable%% = False
  70.     WHILE NOT PartnersAvailable%%
  71.         PartnersAvailable%% = True
  72.         REDIM Mated%%(NoMates% - 1)
  73.         FOR N%% = 0 TO NoMates% - 1
  74.             CanMate%% = False
  75.             NoTries% = 0
  76.             WHILE NOT CanMate%%
  77.                 IF N%% <= 63 THEN
  78.                     N1%% = INT(RND * NoMates% / 2)
  79.                     IF RND <= Elite! THEN N1%% = N1%% + 64
  80.                 ELSE
  81.                     N1%% = INT(RND * NoMates%)
  82.                 END IF
  83.                 NoTries% = NoTries% + 1
  84.                 IF NOT Mated%%(N1%%) AND (Papa%%(N%%, 0) <> Papa%%(N1%%, 1) OR Generation% = 1) THEN 'By this time, Papa%% has been re-ordered
  85.                     Mated%%(N1%%) = True
  86.                     Mating%%(N%%) = N1%%
  87.                     CanMate%% = True
  88.                 END IF
  89.                 IF NoTries% >= 1200 THEN
  90.                     CanMate%% = True
  91.                     PartnersAvailable%% = False
  92.                     EXIT FOR
  93.                 END IF
  94.             WEND
  95.         NEXT N%%
  96.     WEND
  97.     'Produce Children (always 1 male & 1 female offspring at the same time)
  98.     REDIM Issue$(NoIssue% - 1, 1), Papa%%(NoIssue% - 1, 1)
  99.     NoKidsLess1% = 0
  100.     N%% = 0
  101.     WHILE NoKidsLess1% <= NoIssue% - 1
  102.         IF RND <= (Prob0! + ((Prob0! * N%% * (1 - Kappa!)) / ((NoMates%% - 1) * Kappa!))) THEN
  103.             FOR R%% = 0 TO 1 'Male/Female Offspring
  104.                 FOR M%% = 1 TO 28
  105.                     IF RND < MutationRate! THEN
  106.                         IF M%% = 1 THEN
  107.                             Issue$(NoKidsLess1%, R%%) = MID$(Alphabet$, INT(RND * 26) + 1, 1)
  108.                         ELSE
  109.                             Issue$(NoKidsLess1%, R%%) = Issue$(NoKidsLess1%, R%%) + MID$(Alphabet$, INT(RND * 27) + 1, 1)
  110.                         END IF
  111.                     ELSEIF M%% MOD 2 = 0 THEN
  112.                         Issue$(NoKidsLess1%, R%%) = Issue$(NoKidsLess1%, R%%) + MID$(Mates$(N%%, 0), M%%, 1) 'From father
  113.                     ELSE
  114.                         Issue$(NoKidsLess1%, R%%) = Issue$(NoKidsLess1%, R%%) + MID$(Mates$(Mating%%(N%%), 1), M%%, 1) 'From mother
  115.                     END IF
  116.                 NEXT M%%
  117.                 IssueDat%(NoKidsLess1%, R%%) = 0
  118.                 FOR M%% = 1 TO 28
  119.                     IssueDat%(NoKidsLess1%, R%%) = IssueDat%(NoKidsLess1%, R%%) + ABS(ASC(MID$(Weasel$, M%%, 1)) - ASC(MID$(Issue$(NoKidsLess1%, R%%), M%%, 1)))
  120.                 NEXT M%%
  121.                 Papa%%(NoKidsLess1%, R%%) = N%%
  122.             NEXT R%%
  123.             NoKidsLess1% = NoKidsLess1% + 1
  124.         END IF
  125.         IF N%% = NoMates%% - 1 THEN
  126.             N%% = 0
  127.         ELSE
  128.             N%% = N%% + 1
  129.         END IF
  130.     WEND
  131.     IF OneByOne%% THEN
  132.         'Delay
  133.         _DELAY 1.5
  134.         'Display Childless Parents
  135.         FOR N%% = 0 TO NoMates% - 1 'N%% is the father
  136.             Progeny%% = False
  137.             T% = 0 'T% is the child
  138.             WHILE NOT Progeny%% AND T% <= NoIssue% - 1
  139.                 IF Papa%%(T%, 0) = N%% THEN Progeny%% = True
  140.                 T% = T% + 1
  141.             WEND
  142.             IF NOT Progeny%% THEN
  143.                 COLOR _RGB32(127, 127, 0)
  144.                 W$ = Mates$(N%%, 0) 'W$ gets modified by Spaced$() - don't actually need this substitution here, as updated below
  145.                 IF N%% <= 63 THEN
  146.                     _PRINTSTRING (4, 15 + N%% * 11), Spaced$(W$)
  147.                 ELSE
  148.                     _PRINTSTRING (4 + 250, 15 + (N%% - 64) * 11), Spaced$(W$)
  149.                 END IF
  150.                 COLOR _RGB32(0, 127, 127)
  151.                 N2%% = Mating%%(N%%)
  152.                 W$ = Mates$(N2%%, 1) 'W$ gets modified by Spaced$() - don't actually need this substitution here, as updated below
  153.                 IF N2%% <= 63 THEN
  154.                     _PRINTSTRING (4 + 500, 15 + N2%% * 11), Spaced$(W$)
  155.                 ELSE
  156.                     _PRINTSTRING (4 + 250 + 500, 15 + (N2%% - 64) * 11), Spaced$(W$)
  157.                 END IF
  158.             END IF
  159.         NEXT N%%
  160.         'Use up keypresses & wait for keypress
  161.         _KEYCLEAR
  162.         SetAwhile%% = True
  163.         WHILE SetAwhile%%
  164.             _LIMIT 30
  165.             K% = _KEYHIT
  166.             SELECT CASE K%
  167.                 CASE 32
  168.                     SetAwhile%% = False
  169.                 CASE 27
  170.                     SetAwhile%% = False
  171.                     Dawkins%% = False
  172.                 CASE 102
  173.                     OneByOne%% = False
  174.                     SetAwhile%% = False
  175.             END SELECT
  176.         WEND
  177.     ELSE
  178.         K% = _KEYHIT
  179.         SELECT CASE K%
  180.             CASE 102
  181.                 OneByOne%% = True
  182.                 _AUTODISPLAY
  183.             CASE 27
  184.                 Dawkins%% = False
  185.         END SELECT
  186.     END IF
  187.     IF Dawkins%% THEN
  188.         ' Order children & set new generation
  189.         CALL Sort2(IssueDat%(), Issue$(), Papa%%())
  190.         FOR R%% = 0 TO 1
  191.             FOR N%% = 0 TO NoMates%% - 1
  192.                 Mates$(N%%, R%%) = Issue$(N%%, R%%)
  193.                 MatesDat%(N%%, R%%) = 0
  194.                 FOR M%% = 1 TO 28
  195.                     MatesDat%(N%%, R%%) = MatesDat%(N%%, R%%) + ABS(ASC(MID$(Weasel$, M%%, 1)) - ASC(MID$(Mates$(N%%, R%%), M%%, 1)))
  196.                 NEXT M%%
  197.             NEXT N%%
  198.         NEXT R%%
  199.         RANDOMIZE (TIMER)
  200.         Generation% = Generation% + 1
  201.     END IF
  202.     'IF NOT OneByOne%% THEN _DISPLAY
  203.  
  204. IF Generation% = GenLimit% THEN
  205.     CLS
  206.     _FONT 16
  207.     PRINT "Generation Limit Reached"
  208.     _KEYCLEAR
  209.     WHILE INKEY$ = ""
  210.         _LIMIT 30
  211.     WEND
  212.  
  213.  
  214. FUNCTION Spaced$ (X$)
  215.     I%% = INSTR(X$, "[")
  216.     WHILE I%% > 0
  217.         MID$(X$, I%%, 1) = " "
  218.         I%% = INSTR(X$, "[")
  219.     WEND
  220.     Spaced$ = X$
  221.  
  222. SUB Sort1 (Numbers%(), Names$())
  223.     FOR R%% = 0 TO 1
  224.         Jump% = JumpStart%
  225.         WHILE Jump% > 1
  226.             Jump% = (Jump% - 1) \ 2
  227.             Finished% = False
  228.             WHILE NOT Finished%
  229.                 Finished% = True
  230.                 FOR Upper% = 1 TO NoMates% - Jump%
  231.                     Lower% = Upper% + Jump%
  232.                     IF Numbers%(Upper% - 1, R%%) > Numbers%(Lower% - 1, R%%) THEN
  233.                         SWAP Names$(Upper% - 1, R%%), Names$(Lower% - 1, R%%)
  234.                         SWAP Numbers%(Upper% - 1, R%%), Numbers%(Lower% - 1, R%%)
  235.                         Finished% = False
  236.                     END IF
  237.                 NEXT Upper%
  238.             WEND
  239.         WEND
  240.     NEXT R%%
  241.  
  242. SUB Sort2 (Numbers%(), Names$(), WhosTheDaddy%%())
  243.     FOR R%% = 0 TO 1
  244.         Jump% = JumpStart%
  245.         WHILE Jump% > 1
  246.             Jump% = (Jump% - 1) \ 2
  247.             Finished% = False
  248.             WHILE NOT Finished%
  249.                 Finished% = True
  250.                 FOR Upper% = 1 TO NoIssue% - Jump%
  251.                     Lower% = Upper% + Jump%
  252.                     IF Numbers%(Upper% - 1, R%%) > Numbers%(Lower% - 1, R%%) THEN
  253.                         SWAP Names$(Upper% - 1, R%%), Names$(Lower% - 1, R%%)
  254.                         SWAP Numbers%(Upper% - 1, R%%), Numbers%(Lower% - 1, R%%)
  255.                         SWAP WhosTheDaddy%%(Upper% - 1, R%%), WhosTheDaddy%%(Lower% - 1, R%%)
  256.                         Finished% = False
  257.                     END IF
  258.                 NEXT Upper%
  259.             WEND
  260.         WEND
  261.     NEXT R%%
  262.  
  263.  

The assumptions/conditions in the program are as follows:

  • Each generation has 128 males and 128 females who mate monogamously with a birthrate of 2.2 children (on average!).
  • Each male and female of the first generation starts with a "gene sequence" defined by a 28-character string of random letters from "A" to "Z" (capitals only) and the Space character.
  • The fitness-for-use criterion is defined by how close each character is to same-position character in the phrase "METHINKS IT IS LIKE A WEASEL".
  • Each child inherits half its "genes" from the father and half from the mother.  Male parents are odd-numbered "gene-recessive", whilst female parents are even-numbered recessive.  Thus if the "DNA" were three characters long and the father was XYX and the mother YZY, the child would be YYY.  At each breeding, one male and one female offspring are produced.  Male and female offspring have the same genes as each other, except for the mutations.
  • At each stage there is a chance (0.2% in this case) of gene mutation where a random character is produced in place of the inherited character.  This mutation rate is still much higher than in the biological case.
  • Brother and sister may not mate (we are not only simulating superior genetics, but are also completely morally sound).
  • The mate desirability is higher for the top half of the population.  (There is a 4:3 probability of top-half males selecting top-half females).
  • For each 128/128 male/female generation, the top father has a 4-times probability of reproduction compared to that of the bottom pair.
  • Of the 141 male & female offspring, only the top 128 survive to the next generation.


Run the program as follows:

The display shows two groups of 128 parents, "males" in yellow and "females" in cyan, each in two columns.  They are shown in fitness-for-use order (highest at the top).  The 64 highest are in the left-hand column, with the 64 lowest at the right.

After a short delay, the parents who fail to breed successfully are shown darkened.  The program will wait for a Spacebar press to proceed to the next generation.  You can cycle through the generations in this way.  You will see that the most poorly fit-for-use parents are most likely not to reproduce.

Evolution proceeds quite slowly and you will very soon want to speed things up.  Pressing "f" instead of the Spacebar will then quickly cycle through the generations.  Pressing "f" will revert to the one-by-one generation display.  Esc at any time quits the program.

You will see that about 3000 generations are required to produce a population in which all 256 individuals have the desired gene sequence (this takes about 10 minutes).  This is despite the fact that gene evolution is quite heavily biassed in favour of the desired result.  This is because of the presence in the population of such a large gene diversity.  You will also notice that the likelihood of gene changes falls as the population gets closer to the final result.  This is because any mutation tends to be quickly bred out by the extant majority.  In Dawkins's program, he obtains the final result in 47 generations.  I believe that this is so because his had a simpler procedure and less overall gene diversity.

I apologise for the slight bias in favour of the male sex, both in the coding and in this discussion.  No matter how we try, sexism is difficulty to battle.

Of course, Dawkins was quite aware that writing a program to head towards a known result does not describe how biology works, and the program here is a just-for-fun exercise.

This is my contribution to the demonstration of selection by fitness-for-use.  Please contribute your own program if you care to, describing what assumptions are made and how many generations it takes to produce the final population result.  Methinks it could be fun for you too.

59
QB64 Discussion / FUNCTION and SUB By Reference and By Value
« on: August 06, 2019, 07:19:51 am »
In Steve's Posting about the Wiki being incorrect for FUNCTION

https://www.qb64.org/forum/index.php?topic=1531.0

I pointed out that arguments passed to a FUNCTION are passed By Reference by default and not by By Value.  That is to say, the argument gets modified in the FUNCTION and that modified value gets passed back to the calling argument.  This was a surprise to me, not to say rather a shock.

Incidentally, I never can remember which is By Reference and which is By Value (always have to look them up) – I'd rather they were called CHANGED and UNCHANGED respectively.

So, I discovered that if you don't want a FUNCTION to change your calling argument, you must pass the argument By Value, and this is done (QB64 methodology) by placing the argument within brackets, viz. if a FUNCTION F%() is called using a calling argument a% then

b% = F%(a%) - By Reference: a% will be modified

b% = F%((a%)) - By Value: a% will remain unchanged


So, I thought that I'd got that understood until I tried passing a String to a FUNCTION.  In this case, the String is always passed By Reference (and gets modified), no matter whether it is inside brackets or not.  Then I found that exactly the same thing happens with a SUB – a String is always modified by a SUB whether or not it is inside brackets.  I hope that you're still with me on this!

Code: QB64: [Select]
  1. 'Function / Sub By Val or By Ref
  2.  
  3. 'Now, the authentic definition is:
  4. 'When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.
  5. 'When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.
  6.  
  7. C1% = 3
  8. PRINT C1%; "- Numeric Original"
  9. CALL Cg((C1%))
  10. PRINT C1%; "- Numeric After SUB By Value"
  11. PRINT Ch%((C1%)); "- FUNCTION Result After FUNCTION By Value"
  12. PRINT C1%; "- Numeric After FUNCTION By Value"
  13. PRINT Ch%(C1%); "- FUNCTION Result After FUNCTION By Reference"
  14. PRINT C1%; "- Numeric After FUNCTION By Reference"
  15. CALL Cg(C1%)
  16. PRINT C1%; "- Numeric After SUB By Reference"
  17.  
  18. W2$ = "Change[to[Spaces"
  19. PRINT W2$; " - String Original"
  20. CALL Spacef((W2$))
  21. PRINT W2$; " - String After SUB By Value"
  22. PRINT Spaced$((W2$)); " - FUNCTION Result After FUNCTION By Value"
  23. PRINT W2$; " - String After FUNCTION By Value"
  24. PRINT Spaced$(W2$); " - FUNCTION Result After FUNCTION By Reference"
  25. PRINT W2$; " - String Result After FUNCTION By Reference"
  26. CALL Spacef(W2$)
  27. PRINT W2$; " - String Result After SUB By Reference"
  28.  
  29.  
  30. FUNCTION Ch% (C%)
  31.     C% = C% + 1
  32.     Ch% = 2 * C%
  33.  
  34. FUNCTION Spaced$ (X$)
  35.     I%% = INSTR(X$, "[")
  36.     WHILE I%% > 0
  37.         MID$(X$, I%%, 1) = " "
  38.         I%% = INSTR(X$, "[")
  39.     WEND
  40.     Spaced$ = X$
  41.  
  42. SUB Cg (C%)
  43.     C% = 3 * C%
  44.  
  45. SUB Spacef (X$)
  46.     X$ = X$ + "[zzz"

In the given code, there is a FUNCTION Ch%() which takes an Integer argument and a FUNCTION Spaced$() which takes a String argument, and there is a SUB Cg() which takes an Integer argument and a SUB Spacef() which takes a String argument.  The main block calls the FUNCTIONs and SUBs By Value and By Reference.

The code demonstrates what I have declared here:  a Numeric may be passed to a SUB or to a FUNCTION either By Reference (default) or By Value, but a String is always passed to a SUB or to a FUNCTION  By Reference.

Is this supposed to happen, and if so should a String behave differently to a Numeric?


From a queasy Qwerkey.

60
InForm-based programs / Fairground Duck Shoot Simulation Game
« on: July 07, 2019, 10:37:42 am »
This is a simulation of a Duck Shoot booth.  A game of skill where you have to aim at the moving targets (pigeons in this game) and fire a projectile to knock them over.

The simulation of the firing apparatus ('rifle') in this game is effected by having two aiming images which have to be correctly aligned.

You will need to download from within the User Manual.


The use of InForm, once again, adds a certain professionalism to the running program.  Fellippe has delivered a coding methodology which is so easy to incorporate into QB64 Basic, and again I encourage its use.  In this program I have used the following InForm controls:

Frame (for grouping together controls)
Button (for native-look program control)
Radio Button (to allow program parameter selection)
Drop-down List (to select from a given list)
Label (does what you'd expect)


and this is just a small sample of the available InForms controls.

During preparation of this program I was finding that the displayed Pigeon images sometimes were missing their target image: this without changing anything.  I believe that this is not now happening, but let me know if you see this happening.

Program updates (date format UK style):
09/07/19


Pages: 1 2 3 [4] 5 6 ... 8