Conway’s game of life


Author Message
Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 03:36am 25 Apr 2020      

Conway’s game of life has always fascinated me so when I saw the recent link to a LED display running it I decided to have a go and write it for MMBasic DOS with a view to perhaps doing a CMM version.

That went well enough and it runs well and very fast on the PC.  So I decided to convert it to the Micromite with TeraTerm. I was surprised how slow it was on the Micromite. With the DOS version there is a 300mS PAUSE instruction in the program between updates but with the Micromite that blew out to over 3 seconds with the pause removed!

With a fair bit of tweaking and MMEdit’s crunch on load it’s working a lot better but limited a bit by the serial link to TeraTerm. The un-tweaked DOS version is more watchable.

I’ll include them here if anyone wants to play and maybe tell me how to speed the Micromite version up a bit.

original MMBasic DOS version
Life03.zip

Micromite with TeraTerm version
LifeT01.zip

Edit: I forgot to say: MMEdit's crunch on load helped too.

Bill
Edited 2020-04-25 15:42 by Turbo46
Keep safe. Live long and prosper.

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 07:16pm 25 Apr 2020      

Hey Bill,

Very pretty.

I don't have a Micromite, but I ported your code to the Colour Maximite (replace "Else If" with "ElseIf") and gave it a bit of a prod. Note that results with the CMM may differ from the uM as I don't know if Geoff optimised variable resolution (a significant bottleneck) in the BASIC interpreter between v4.5 and 5.

The best optimisation I could find was this:

Sub NextGen ' Breed the next generation into a new matrix
 Print Chr$(27) "[H"
 For y = 1 To MatY
   b = 0
   c = CurM(1, y - 1) + CurM(1, y) + CurM(1, y + 1)
   s$ = Space$(MatX)
   For x = 1 To MatX
     d = CurM(x + 1, y - 1) + CurM(x + 1, y) + CurM(x + 1, y + 1)
     adj = b + c + d
     If CurM(x, y) Then
       Poke Var s$, x, &h4f
       MewM(x, y) = (adj = 3) Or (adj = 4)
     Else
       MewM(x, y) = (adj = 3)
     EndIf
     b = c
     c = d
   Next x
   Print s$
 Next y
End Sub


And similarly for NextGen2.

This was ~30% faster than the original code on my CMM.

Basically this:
1. reduces the number of variable lookups.
2. prints the matrix a line at a time instead of a cell at a time.

Shorter variable names also help, but I expect that is one of the things that "crunch  on load" might be doing for you; I have no experience with MMEdit since I am using a Raspberry Pi as my serial terminal not Windoze.

I believe there is a further optimisation at the expense of clarity & flexibility by using a hardcoded matrix width and using a 1D instead of 2D array but I would guess it would only get you another 10% at most.

Best wishes,

Tom

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 10:18pm 25 Apr 2020      

Hi Bill,

I hope you don't mind but I've taken the liberty of attaching my CMM port including some graphics (which are pretty but slow):

LIFE_CMM.zip

And you can see a video here:

Conway's Game of Life on YouTube

Yellow = births
Green  = survivors
Red    = deaths

Please let me know if you want either removed.

Best wishes,

Tom

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 10:45pm 25 Apr 2020      

Hi Tom,

Thanks for that, very clever!  

I know you are doing something clever with the matrix with the c and d variables but I need a coffee or two to work it out.

Printing a line at a time is clever too, If I'd even thought of it I would have used string functions to to do it but your POKE VAR method would have to be quicker. It does fix the character to "O" but that's OK. I have wondered whether using just 79 rather than &H4F is faster, but never tested it. I guess that CHR$(a$) would also work? But slower.

Using:

MewM(x, y) = (adj = 3)

Is also something I never do, I would use IF THEN. Did I say I'm not a programmer?

MMEdit's crunch just removes REMs, blank lines and formatting spaces I believe. I tried to keep the variable name lengths short but still have a meaning. Removing spaces within the program also helps I believe: a=b not a = b.

Thanks again Tom, I'll have a play again this arvo if I get the time.

Bill
Keep safe. Live long and prosper.

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 11:27pm 25 Apr 2020      

Hi Tom,

Thanks yet again, that's brilliant!  

Now I'm going to have to dust off my CMM and have a play. It may be just because it's a new concept but I find the different colours a bit confusing. I thought something like that may be interesting but take too much processing. Maybe an option to just show live cells?

I'd like to see a graphics only version and/or a print only version. I expect they would both run faster than the combined version?

BTW I noticed a stray PRINT in the InitM sub, I was printing the initial array in that sub as well and missed removing it.

There is a crunch utility in the library, it may be worth having a look at.

Bill
Keep safe. Live long and prosper.

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 06:47am 26 Apr 2020      

Hi again Tom,

It took more than a couple of coffees but I think I have it worked out.

b=0 represents the 3 values of (x-1,y-1) , (x-1,y)  and (x-1,y+1)  which are off the edge and are zero according to the rules.
c is same but around x and
d is the same around x+1
Adj includes the value of the current cell which you take into account when updating the new matrix.
And then you maintain that moving window of 9 cells as you move along x.
b=c
c=d
And you only need to calculate d each time around the loop.
Is that correct?

For a printing version the screen update could be quicker if instead of a$ being used to print one line, an array a$(x,y) could hold the whole screen which could be printed in a FOR NEXT loop?

Bill
Keep safe. Live long and prosper.

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 10:54pm 26 Apr 2020      

  Turbo46 said  Hi again Tom,

It took more than a couple of coffees but I think I have it worked out ... <snip> ... Is that correct?


Hey Bill,

Sounds like you've got it! and it saves a significant number of variable lookups - I can't stress too much how at least on the CMM these are the biggest "unexpected" performance killer.

Is my code measurably faster on the Micromite?

  Quote  Printing a line at a time is clever too, If I'd even thought of it I would have used string functions to to do it but your POKE VAR method would have to be quicker. It does fix the character to "O" but that's OK. I have wondered whether using just 79 rather than &H4F is faster, but never tested it. I guess that CHR$(a$) would also work? But slower.


I doubt there is any significant difference between using 79 and &h4F, but if you test it then let me know. Yes, you could use CHR$(a$) but that is an extra variable lookup and function call; if you do it then at least declare a = CHR$("O") at the start instead of a$, it would save you 250 bytes of memory too.

  Quote  For a printing version the screen update could be quicker if instead of a$ being used to print one line, an array a$(x,y) could hold the whole screen which could be printed in a FOR NEXT loop?


Actually you would only need a 1D array of strings, an element per line. But I don't believe this will help you because of all the additional variable lookups at the printing stage. Now if there were a PRINT (C)function that could print the entire array as a single operation it would be different. Note that printing a line at a time did not make as significant a performance improvement as I expected, and a great deal less than the variable lookup optimisation.

  Quote  Using:

MewM(x, y) = (adj = 3)

Is also something I never do, I would use IF THEN. Did I say I'm not a programmer?


I am a programmer but in my day job using modern PC hardware and compiled languages I wouldn't be using such aggressive hand optimisation as I applied to this subroutine.

  Quote  Removing spaces within the program also helps I believe: a=b not a = b.


I'll leave that to automated tools, I like my whitespace the way it is when developing.

  Quote  Now I'm going to have to dust off my CMM and have a play. It may be just because it's a new concept but I find the different colours a bit confusing. I thought something like that may be interesting but take too much processing. Maybe an option to just show live cells?


An exercise for the reader You might also try different circles instead of colour. Smaller circles for births and unfilled circles for deaths.

  Quote  I'd like to see a graphics only version and/or a print only version. I expect they would both run faster than the combined version?


Absolutely as you wouldn't need to lookup the GRAPHICS variable all the time.

  Quote  BTW I noticed a stray PRINT in the InitM sub, I was printing the initial array in that sub as well and missed removing it.


So there was. I tried to avoid rewriting/reformatting all your code

  Quote  There is a crunch utility in the library, it may be worth having a look at.


Thanks what we really need is a macro/preprocessor so that constant variable values get inlined on execution.

Anyway I don't want to stage a "takeover" of your fun, but here are some things you could experiment with:

1. On the Micromite (not Maximite) you could see whether you get better performance using integer variables, see Micromite User Manual, p33. On most computers integer operations are faster than floating point operations ... though I don't guarantee it makes a difference in MMBasic.

2. The "duplication" of the NextGen function is undesireable but not trivially fixed because (at least in MMBasic 4.5) we can't pass an array as a parameter to a Sub/Function. However if you declare a single matrix with twice the number of rows you can then just switch between the two "halves" using offsets into that matrix, e.g.

Sub NextGen ' Breed the next generation into a new matrix
 Print Chr$(27) "[H"
 For y = 1 To MatY
   b = 0
   c = CurM(1, y - 1) + CurM(1, y) + CurM(1, y + 1)
   ...

becomes

Sub NextGen(offset) ' Breed the next generation into a new matrix
 Print Chr$(27) "[H"
 For y = 1 + offset To MatY + offset
   b = 0
   c = CurM(1, y - 1) + CurM(1, y) + CurM(1, y + 1)
   ...

Any resulting performance penalty should be marginal.

3. You could try making the matrix toroidal by having the right hand side consider cells on the left hand side to be adjacent, and vice-versa. Same for top and bottom. You'll lose some performance but it makes smaller matrices more interesting; I will be doing it for my 16x16 LED matrix.

Best wishes,

Tom

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 12:17am 27 Apr 2020      

Hi Tom,

Yes, your code is faster on the Micromite. With the Micromite using both b$ and b is a no no, so I changed b$ to e$.

  Quote  Actually you would only need a 1D array of strings, an element per line. But I don't believe this will help you because of all the additional variable lookups at the printing stage. Now if there were a PRINT (C)function that could print the entire array as a single operation it would be different. Note that printing a line at a time did not make as significant a performance improvement as I expected, and a great deal less than the variable lookup optimisation

Yes, a 1D array of course. I was thinking of the visual appearance (?) hoping to reduce the delay between each line. I'll try it and see.

Re the grapics, Maximite can use custom fonts, I haven't investigated them yet but maybe a font could be used in place of grapics.

  Quote  On most computers integer operations are faster than floating point operations

I believe that on the 'Mites that integers can even be slower because floating point calculations use the on-board floating point processor. I deliberately didn't use integers because I was aiming for a Maximite version.

  Quote  However if you declare a single matrix with twice the number of rows you can then just switch between the two "halves" using offsets into that matrix,

That's a good idea to reduce the code size, it did annoy me to have to have two almost identical subroutines. However it was quicker than the method used in the DOS version.

  Quote  You could try making the matrix toroidal by having the right hand side consider cells on the left hand side to be adjacent, and vice-versa. Same for top and bottom. You'll lose some performance but it makes smaller matrices more interesting; I will be doing it for my 16x16 LED matrix.

Whoosh! that went straight over my head. Coffee won't help with that one.

Thanks again

Bill
Edited 2020-04-27 10:19 by Turbo46
Keep safe. Live long and prosper.

TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6108
Posted: 02:28am 27 Apr 2020      

This 'should' work on all mites with a LCD.
Tested on a H7
I left all the heavy lifting alone and just updated where necessary for the uMs.
The main change was RGB(colour) needed for the display routines.

 ' John Conway's Game of Life for Micromite MMBasic with TeraTerm
 ' Bill McKinley
 
 'Mode 3
 dim Bx$
 dim GRAPHICS = 1 ' Set = 0 for Terminal mode, = 1 for Graphics
 
 ' NOTE: If you make the matrix too small the Glider demo will cause an error
 ' and the matrix will not overwrite some of the text on the screen
 ' Make it too big and it will not fit on the screen
 ' A ratio of 2 of x to 1 of y gives a better looking display
 dim MatX = 40   ' Matrix horizontal size
 dim MatY = 25   ' Matrix vertical size
 dim RandL = 0.3 ' For the randomizing of life less than =< RandL means there is life
 ' Make it high and you get a lot of cells but they die quickly
 dim PT = 000    ' Pause time in mS between display updates
 dim DIAM
 
 If GRAPHICS Then 'DIAM = 480 / MatX : MatY = 35
   DIAM = mm.hres/matX
   MatY = int( MatX *mm.vres/mm.hres)
 endif
 
 ' Zero based matrix has one more cell all round than is displayed
 Dim CurM(MatX+1, MatY+1) ' The current matrix of life
 Dim NewM(MatX+1, MatY+1) ' The next generation of life
 
 ChkScr      ' Print intro
 SetupTimer  ' Randomize by waiting for a keypress
 ClrScr      ' Clear the screen (matrix area)
 InitM       ' Initialize the random matrix (or demo)
 
 Do          ' Program loop
   NextGen     ' Calculate the next generation
   Pause PT
   NextGen2    ' Calculate the next generation
   'UpdateM     ' Update the matrix with the new generation
   Pause PT
 Loop Until Inkey$ <> "" ' loop forever or until a keypres
 
Sub ChkScr ' Print intro
 ClrScr
 Print "   CONWAY'S GAME OF LIFE"
 Print
 Print "   Press a key to start"
 Print "   and another to stop"
 Print "   G for a glider demo"
End Sub
 
Sub SetupTimer ' Wait for a key press to start timer
 Do
   Bx$ = Inkey$
 Loop Until Bx$ <> ""
 'Randomize Timer
End Sub
 
Sub ClrScr 'Clear the screen area
 If Not GRAPHICS Then Print Chr$(27) "[2J" Chr$(27) "[H" Else Cls
End Sub
 
Sub InitM ' Initialise the matrix of life
 local integer x,y
 If Bx$ = "g" Or bx$ = "G" Then  'Set thr glider and target
   CurM(4, 7) = 1
   CurM(5, 7) = 1
   CurM(6, 7) = 1
   CurM(6, 6) = 1
   CurM(5, 5) = 1
   CurM(15, 15) = 1
   CurM(15, 16) = 1
   CurM(16, 15) = 1
   CurM(16, 16) = 1
 Else
   For y = 1 To MatY
     For x = 1 To MatX
       If Rnd() <= RandL Then
         CurM(x, y) = 1
       Else
         CurM(x, y) = 0
       EndIf
     Next x
     Print
   Next y
 EndIf
End Sub
 
Sub NextGen ' Breed the next generation into a new matrix
 local integer x, y, b, c, d, adj, dying, birth
 local s$
 If Not GRAPHICS Then Print Chr$(27) "[H"
 For y = 1 To MatY
   b = 0
   c = CurM(1, y - 1) + CurM(1, y) + CurM(1, y + 1)
   s$ = Space$(MatX)
   For x = 1 To MatX
     d = CurM(x + 1, y - 1) + CurM(x + 1, y) + CurM(x + 1, y + 1)
     adj = b + c + d
     If CurM(x, y) Then
       Poke Var s$, x, &h4f
       birth = NewM(x, y) = 0
       NewM(x, y) = (adj = 3) Or (adj = 4)
       If GRAPHICS Then
         If birth Then draw_cell(x, y, rgb(Yellow)) Else draw_cell(x, y, rgb(Green))
       EndIf
     Else
       dying = adj <> 3 And NewM(x, y) = 1
       NewM(x, y) = (adj = 3)
       If GRAPHICS Then
         If dying Then draw_cell(x, y, rgb(red)) Else draw_cell(x, y, rgb(Black))
       EndIf
     EndIf
     b = c
     c = d
   Next x
   If Not GRAPHICS Then Print s$
 Next y
End Sub
 
Sub draw_cell(x, y, col)
 Circle(x - 0.5) * DIAM, (y - 0.5) * DIAM, DIAM/2 - 2, 1,,col, col
End Sub
 
Sub NextGen2 ' Breed the next generation into a new matrix
 local integer x, y, b, c, d, adj, dying, birth
 local s$
 If Not GRAPHICS Then Print Chr$(27) "[H"
 For y = 1 To MatY
   b = 0
   c = NewM(1, y - 1) + NewM(1, y) + NewM(1, y + 1)
   s$ = Space$(MatX)
   For x = 1 To MatX
     d = NewM(x + 1, y - 1) + NewM(x + 1, y) + NewM(x + 1, y + 1)
     adj = b + c + d
     If NewM(x, y) Then
       Poke Var s$, x, &h4f
       birth = CurM(x, y) = 0
       CurM(x, y) = (adj = 3) Or (adj = 4)
       If GRAPHICS Then
         If birth Then draw_cell(x, y, rgb(Yellow)) Else draw_cell(x, y, rgb(Green))
       EndIf
     Else
       dying = adj <> 3 And CurM(x, y) = 1
       CurM(x, y) = (adj = 3)
       If GRAPHICS Then
         If dying Then draw_cell(x, y, rgb(red)) Else draw_cell(x, y, rgb(Black))
       EndIf
     EndIf
     b = c
     c = d
   Next x
   If Not GRAPHICS Then Print s$
 Next y
End Sub


Back when I did one on the Microbee (I think), I used an extra dimension to the one array and toggled between the two sides rather than 2 arrays.

Jim
VK7JH
MMedit   MMBasic Help

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 03:12am 27 Apr 2020      

Thanks Jim,

That's another idea that didn't occur to me.

  Quote  I used an extra dimension to the one array and toggled between the two sides rather than 2 arrays.


Just so I have that right, if I use:

Dim CurM(MatX+1, MatY+1, 1)

That will create a 3 dimensional array consisting of two 2 dimensional arrays:

CurM(MatX+1, MatY+1, 0)
and
CurM(MatX+1, MatY+1, 1)

That would be a lot easier to handle than one large array with offsets.

Bill
Keep safe. Live long and prosper.

TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6108
Posted: 03:19am 27 Apr 2020      

Yes
and

G1 = 1 - G1
G2 = 1 = G1

will do the toggling for each generation

then use

CurM(MatX+1, MatY+1, G1)
CurM(MatX+1, MatY+1, G2)


Jim
VK7JH
MMedit   MMBasic Help

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 03:43am 27 Apr 2020      

G1 = 1 - G1
G2 = 1 = G1


I understand (eventually)

I was thinking of using:

NextGen 0,1
NextGen 1,0

To tell NextGen which array to use and which to update but your suggestion means that I only have to call NextGen once with no parameters.

Neat!

Thanks again Jim.

Bill

Edit: Should that be:
G1 = 1 - G1
G2 = 0 = G1


Edit2: Got the TeraTerm version working with Jim's suggestion of a 3 dimensional matrix and will post it later.
Edited 2020-04-27 15:05 by Turbo46
Keep safe. Live long and prosper.

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 08:19am 27 Apr 2020      

For anyone interested here is the revised version for a Miromite with TeraTerm.

LifeT03.zip

and one text only version for the Maximite.

LifeC01.zip

The changes include: Tom's algorithm for updating the new matrix, Jim's 3 dimension array and printing the whole matrix after the whole array has been updated.

Bill
Keep safe. Live long and prosper.

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 07:00pm 27 Apr 2020      

  TassyJim said  Back when I did one on the Microbee (I think), I used an extra dimension to the one array and toggled between the two sides rather than 2 arrays.


I should have come up with that, I guess I literally demonstrated 2D thinking ...

... actually I was thinking of using a 1D array of floats and treating it as an array of bytes on which I could impose whatever structure I wanted with Poke & Peek.

Best wishes,

Tom
Edited 2020-04-28 05:01 by thwill

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 07:21pm 27 Apr 2020      

  Turbo46 said  
  Quote  On most computers integer operations are faster than floating point operations

I believe that on the 'Mites that integers can even be slower because floating point calculations use the on-board floating point processor.


Integer calculations being slower seems highly unlikely, as I understand it the reason there isn't also an on-board integer processor is you don't need one, it's only floating points that require the extra support. Aside: if I remember correctly Intel processors (386/486) came with SX and DX variants, the DX variant included the floating point support, and the SX did not. However both were manufactured as the same IC and then the FP unit on the SX versions were mechanically "damaged".

  Turbo46 said  I deliberately didn't use integers because I was aiming for a Maximite version.


A good reason.

  Turbo46 said  
  Quote  You could try making the matrix toroidal by having the right hand side consider cells on the left hand side to be adjacent, and vice-versa. Same for top and bottom. You'll lose some performance but it makes smaller matrices more interesting; I will be doing it for my 16x16 LED matrix.

Whoosh! that went straight over my head. Coffee won't help with that one.


From Wikipedia: "A more sophisticated trick is to consider the left and right edges of the field to be stitched together, and the top and bottom edges also, yielding a toroidal array. The result is that active areas that move across a field edge reappear at the opposite edge."

Basically if you have this array:
A B C D E
F G H I J
K L M N O
P Q R S T


Instead of treating it as having a border of dead cells:
X X X X X X X
X A B C D E X
X F G H I J X
X K L M N O X
X P Q R S T X
X X X X X X X


You pretend you are looking at a window of a larger array that extends and repeats infinitely:
    . . . . . . . . . .
    . . . . . . . . . .
    . . . . . . . . . .
... A B C D E A B C D E ...
... F G H I J F G H I J ...
... K L M N O K L M N O ...
... P Q R S T P Q R S T ...
... A B C D E A B C D E ...
... F G H I J F G H I J ...
... K L M N O K L M N O ...
... P Q R S T P Q R S T ...
    . . . . . . . . . .
    . . . . . . . . . .
    . . . . . . . . . .

Now when considering adjacencies A is considered to be next to 8 potentially "live "cells T, P, Q, E, B, J, F and G instead of 5 of the cells it is next to always being "dead".

Best wishes,

Tom

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 07:24pm 27 Apr 2020      

  Turbo46 said  ... printing the whole matrix after the whole array has been updated.


I haven't had opportunity to measure it myself, but isn't that perceptibly slower than printing as you go along?

Aside: it also uses more memory but that isn't an issue for such a small program and in any case you are already using 4 bytes to store each cell where you only need 1 bit.

Best wishes,

Tom

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 01:19am 28 Apr 2020      

  Quote  Integer calculations being slower seems highly unlikely

That was my expectation but I seem to recall being corrected on that myself (by CaptainBoing?). I'll do some tests.

  Quote  Now when considering adjacencies A is considered to be next to 8 potentially "live "cells T, P, Q, E, B, J, F and G instead of 5 of the cells it is next to always being "dead"

I get it. A 'glider' going off the right of the display would then enter from the right?
  Quote  I haven't had opportunity to measure it myself, but isn't that perceptibly slower than printing as you go along?

Yes, There is a pause while spawning the next generation but then printing the next generation is quicker. I think it looks better.
  Quote  Aside: it also uses more memory but that isn't an issue for such a small program and in any case you are already using 4 bytes to store each cell where you only need 1 bit.

Yes, unfortunately. Any packing and unpacking the data would slow it down though.

Bill
Keep safe. Live long and prosper.

Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1619
Posted: 03:26am 28 Apr 2020      

It seems that integers vs floats is not that simple, see this post

Bill
Keep safe. Live long and prosper.

thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4048
Posted: 11:37am 28 Apr 2020      

Hey Bill,

  Turbo46 said  It seems that integers vs floats is not that simple, see this post


Thanks for the link to such an interesting thread, I'm surprised but I guess that is modern hardware for you, it is more unpredicatable.

  Quote  I get it. A 'glider' going off the right of the display would then enter from the right?


A glider going off the right appears on the left, off the top appears on the bottom, and vice versa.

  Quote  Yes, unfortunately. Any packing and unpacking the data would slow it down though.


Time for CFunctions ... but not on the Maximite :-(

Regards,

Tom

TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6108
Posted: 06:39am 29 Apr 2020      

I've done some more playing.
This version should run on most platforms but Maximites will need the RGB(xxx) bits tweaked.

I added a timer that times the first 40 generations and displays the average
On my H7, seed pattern 5 gives a time of 234mS per generation and pattern 6 gives 250mS.

There are 10 starting patterns 0 to 9
Some patterns work best without the red dying cells.

I haven't tried wrapping the display.

The only speedup tried is flagging the dying cells to save overwriting all blank cells each generation.
 ' John Conway's Game of Life for Micromite MMBasic with TeraTerm
 ' Bill McKinley
 ' adapted by TassyJim
 
 DIM Bx$
 DIM INTEGER MatX = 40   ' Matrix horizontal size
 DIM MatY = 25   ' Matrix vertical size - gets calculated later depending on screen size
 DIM FLOAT RandL = 0.3 ' For the randomizing of life less than =< RandL means there is life
 ' Make it high and you get a lot of cells but they die quickly
 DIM PT = 000    ' Pause time in mS between display updates
 DIM INTEGER initialPause = 3000 ' time to display starting pattern
 DIM INTEGER DIAM, a, b = 1, j
 DIM INTEGER dying, dyingOn = RGB(127,0,0)
 
 DIAM = MM.HRES/matX
 MatY = INT( MatX *MM.VRES/MM.HRES)
 
 ' Zero based matrix has one more cell all round than is displayed
 DIM CurM(MatX+1, MatY+1,2) ' The  matrix of life
 DO
   ChkScr      ' Print intro
   IF Bx$ = "Q" OR Bx$ = "q" THEN EXIT DO
   CLS
   InitM       ' Initialize the random matrix (or demo)
   initial_gen
   PAUSE initialPause
   TIMER = 0  ' reset for next timer
   j = 0
   DO          ' Program loop
     NextGen     ' Calculate the next generation
     PAUSE PT
     j = j + 1
     IF j = 40 THEN PRINT TIMER /40
   LOOP UNTIL INKEY$ <> "" 'or j = 40' loop forever or until a keypres
 LOOP
 
SUB ChkScr ' Print intro
 CLS
 PRINT "   CONWAY'S GAME OF LIFE"
 PRINT
 PRINT "   Press a key to start "
 PRINT "   and another to stop  "
 PRINT "      0-9 for a demo"
 PRINT "        Q to quit       "
 DO
   Bx$ = INKEY$
 LOOP UNTIL Bx$ <> ""
END SUB
 
SUB InitM ' Initialise the matrix of life
 LOCAL INTEGER x,y, n
 FOR y = 1 TO MatY
   FOR x = 1 TO MatX
     CurM(x, y, b) = 0
   NEXT x
 NEXT y
 x = 0
 SELECT CASE Bx$
   CASE "1" ' glider
     RESTORE seed1
   CASE "2" ' blinker
     RESTORE seed2
   CASE "3" ' toad
     RESTORE seed3
   CASE "4" ' beacon
     RESTORE seed4
   CASE "5" 'Penta-decathlon
     RESTORE seed5
   CASE "6" ' pulsar
     RESTORE seed6
   CASE "7" '
     RESTORE seed7
   CASE "8" '
     RESTORE seed8
   CASE "9" '
     RESTORE seed9
   CASE "0" ' diehard
     RESTORE seed0
   CASE "Q","q" ' do nothing
     x = -1
   CASE ELSE ' random set
     FOR y = 1 TO MatY
       FOR x = 1 TO MatX
         IF RND() <= RandL THEN
           CurM(x, y, b) = 1
         ELSE
           CurM(x, y, b) = 0
         ENDIF
       NEXT x
     NEXT y
 END SELECT
 IF x = 0 THEN ' we need to read a set configuration
   DO
     READ x
     READ y
     IF x = -1 THEN EXIT DO
     IF y = -1 THEN PRINT "Error in Data",x,y: EXIT DO
     CurM(x, y, b) = 1
   LOOP
   dying = y*dyingOn
 ENDIF
 'Bx$=""
END SUB
 
SUB initial_gen
 FOR y = 1 TO MatY
   FOR x = 1 TO MatX
     IF CurM(x, y, b) = 1 THEN
       draw_cell(x, y, RGB(YELLOW))
     ENDIF
   NEXT x
 NEXT y
END SUB
 
SUB NextGen ' Breed the next generation into a side of the matrix
 LOCAL INTEGER x, y, d
 a = 1 - a
 b = 1 - a
 
 FOR y = 1 TO MatY
   FOR x = 1 TO MatX
     d = CurM(x - 1, y - 1, a) + CurM(x - 1, y, a) + CurM(x - 1, y+1, a)
     d = d + CurM(x , y - 1, a) + CurM(x , y+1, a)
     d = d + CurM(x+1,y-1,a)+CurM(x+1,y,a)+CurM(x+1,y+1,a)
     d = d MOD 10
     IF CurM(x, y , a) = 1 THEN
       IF d = 2 OR d = 3 THEN
         draw_cell(x, y, RGB(GREEN))
         CurM(x, y, b) = 1
       ELSE
         draw_cell(x, y, dying)
         CurM(x , y, b) = 10 ' flag this cell to be cleared next generation
       ENDIF
     ELSE
       IF d = 3 THEN
         draw_cell(x, y, RGB(YELLOW))
         CurM(x, y, b) = 1
       ELSE
         IF CurM(x, y, a) = 10 THEN draw_cell(x, y, RGB(BLACK))
         CurM(x , y, b) = 0
       ENDIF
     ENDIF
     
   NEXT x
 NEXT y
END SUB
 
SUB draw_cell(x, y, col)
 CIRCLE (x - 0.5) * DIAM, (y - 0.5) * DIAM, DIAM/2 - 2, 1,,col, col
 'text x * DIAM, y * DIAM,"O","LT",1,1,col ', col
END SUB
 
 ' seeds are pairs of x,y cells ending in -1 then 0 or 1 for coloured dying cells
 
seed1: ' glider
 DATA 4,7,5,7,6,7,6,6,5,5,-1,0
seed2: ' blinker
 DATA 4,7,5,7,6,7,-1,0
seed3: ' toad
 DATA 5,7,6,7,7,7,4,8,5,8,6,8,-1,0
seed4: ' beacon
 DATA 4,7,5,7,4,8,7,9,6,10,7,10,-1,0
seed5: 'Penta-decathlon
 DATA 5, 7,6, 7,7, 7,5, 8,7, 8,5, 9,6, 9,7, 9
 DATA 5, 10,6, 10,7, 10,5, 11,6, 11,7, 11,5, 12
 DATA 6, 12,7, 12,5, 13,7, 13,5, 14,6, 14,7, 14,-1,1
seed6: ' pulsar
 DATA 5,3,6,3,7,3,11,3,12,3,13,3,3,5,8,5,10,5,15,5
 DATA 3,6,8,6,10,6,15,6,3,7,8,7,10,7,15,7
 DATA 5,8,6,8,7,8,11,8,12,8,13,8,5,10,6,10,7,10,11,10,12,10,13,10
 DATA 3,11,8,11,10,11,15,11,3,12,8,12,10,12,15,12
 DATA 3,13,8,13,10,13,15,13,5,15,6,15,7,15,11,15,12,15,13,15
 DATA -1, 1
seed7: ' LW spaceship
 data 3,2,6,2,7,3,3,4,7,4,4,5,5,5,6,5,7,5,-1,1
seed8: ' MW spaceship
 data 5,2,3,3,7,3,8,4,3,5,8,5,4,6,5,6,6,6,7,6,8,6,-1,1
seed9: ' HW spaceship
 data 7,4,8,4,3,5,4,5,5,5,6,5,8,5,9,5,3,6,4,6,5,6,6,6,7,6,8,6
 data 4,7,5,7,6,7,7,7,-1,1
seed0:  'diehard
 data 8,2,2,3,3,3,4,3,7,4,8,4,9,4,-1,1


Jim
VK7JH
MMedit   MMBasic Help