Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
John Conway's Life with growth, aging
#1
Here is the latest code with bugs fixed:

Code: (Select All)
' LIFF v1.02
' BY SOFTINTHEHEADWARE

' A VERSION OF JOHN CONWAY'S GAME OF LIFE
' for more info see https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

' -----------------------------------------------------------------------------
' New in this version
' -----------------------------------------------------------------------------
' * Fixed logic issues, program now works as expected!
' * Fixed population count

' -----------------------------------------------------------------------------
' Instructions
' -----------------------------------------------------------------------------
' 1. Choose whether organisms will be allowed to age.
'    Select "n" for classic Conway's Game of Life where organisms don't age,
'    or "y" for aging, where organisms are born and grow with each generation,
'    and then die of old age after 5 generations.
' 2. Choose whether organisms are placed randomly on the virtual planet.
' 3. If you selected random, enter # of organisms to generate (or 0 to pick a random number).
'    The specified number of organisms are added to the virtual planet.
' 4. If you selected NOT random, an editor opens where you place each organism manually.
'    a. Use mouse to position the cursor.
'    b. Left click to add an organism at the cursor position.
'       If aging is enabled, click again to make the organism older.
'    c. Right click to remove organism at the cursor position.
'    d. Press "r" to randomly add a new organism.
'    e. When finished adding organisms, press Escape to begin the simulation.
' 5. The simulation proceeds according to the rules of Conway's Game of Life
'    * If a cell / space is empty, and surrounded by 2 or 3 organisms,
'      a new organism is born on that space.
'    * If a cell is occupied by an organism
'      - If the organism has 2 or 3 neighbors, it survives to the next generation.
'      - If the organism only has 1 or no neighbors, it dies of loneliness.
'      - If the organism has 4 or more neighbors, it dies of overcrowding.
'    * Statistics are displayed at the top:
'      - current generation #
'      - grand total of all organisms ever born / died
'      - running total of organisms born & died in the current generation
'      - current population
'    * The simulation proceeds at 2 generations per second
'      - To speed up  the simulation, press up or right or + or page up
'      - To slow down the simulation, press down or left or - or page down
'    * Press the spacebar to pause the simulation, press it again to continue.
'    * Press Escape to exit the current simulation.
'    * The simulation exits when no organisms are alive or the user presses Escape.
' 6. The final statistics for the simulation are displayed:
'    - The final generation number reached
'    - Grand total organisms born/died for all generations.
'    - Number of births/deaths in the last generation.
'    - Final population.
' 7. Select whether to play again. If you choose "y", continue back at 1.

' -----------------------------------------------------------------------------
' Patterns
' -----------------------------------------------------------------------------
' Certain patterns are known to remain stable, repeat / oscillate,
' eventually die, or "move" with each generation ("spaceships").
' Many of these have popular names. Here are the main known patterns:

' ....     ....     ....
' .OO. --> .OO. --> .OO. --> "Block" = stable
' .OO.     .OO.     .OO.
' ....     ....     ....

' .OO.
' O..O  <-- "Beehive" = stable
' .OO.

' .OO.
' O..O  <-- "Loaf" = stable
' .O O
' ..O.

' OO.
' O.O <-- "Boat" = stable
' .O.

' .O.
' O.O <-- "Tub" = stable
' .O.

' ...OO
' ....O
' ...O. <-- "Canoe" = stable
' O.O..
' OO...

' Some patterns die quickly:

' ....     ....
' ..O.     ....
' .O.. --> .OO. --> Dies
' .O..     ....
' ....     ....

' .....     .....
' ...O.     .....
' ..O.. --> ..O.. --> Dies
' .O...     .....
' .....     .....

' Some patterns change for several generations and eventually resolve into a stable pattern:

' ......     ......     ......     ......
' .OOO..     ...O..     ..OO..     ..OO..
' .O.... --> ..OO.. --> ..OO.. --> .O..O. --> Stable
' ......     ..O...     ..OO..     ..OO..
' ......     ......     ......     ......
' ......     ......     ......     ......

' .....     .....     .....
' ..O..     .....     ..O..
' ..O.. --> .OOO. --> .O.O. --> Stable
' ..O..     .OOO.     .O.O.
' ..O..     .....     ..O..
' .....     .....     .....

' ......     ......
' ...O..     ..OO..
' .OO... --> .O..O. --> Stable
' ..O...     .O..O.
' ......     ..OO..
' ......     ......

' Some patterns oscillate back and forth:

' .....     .....
' ..O..     .....
' ..O.. --> .OOO. --> "Blinker" oscillates
' ..O..     .....
' .....     .....

' ..O.
' O..O --> "Toad" (phase 2) oscillates
' O..O
' .O..

' OO..
' OO.. --> "Beacon" (phase 2) oscillates
' ..OO
' ..OO

' ..OOO...OOO..
' .............
' O....O.O....O
' O....O.O....O
' O....O.O....O
' ..OOO...OOO.. --> "Pulsar" (phase 3) oscillates
' .............
' ..OOO...OOO..
' O....O.O....O
' O....O.O....O
' O....O.O....O
' .............
' ..OOO...OOO..

' ...OOO...
' ..O...O..
' .O.....O.
' .........
' O.......O --> "Penta-decathalon" (phase 15) oscillates
' O.......O
' .........
' .O.....O.
' ..O...O..
' ...OOO...

' .........
' ...O.O...
' .OOO.OOO.
' O...O...O
' O.O...O.O --> "Cloverleaf" oscillates
' .OO.O.OO.
' O.O...O.O
' O...O...O
' .OOO.OOO.
' ...O.O...

' ...........               ...........               ...........
' ...........               .....O.....               ....OOO....
' ...........               .....O.....               ...........
' ...........               .....O.....               ..O.....O..
' .....O..... --> (TBD) --> ...........               ..O.....O..  --> Repeats / oscillates
' ....OOO....               .OOO...OOO.               ..O.....O..
' ...........               ...........               ...........
' ...........               .....O.....               ....OOO....
' ...........               .....O.....               ...........
' ...........               .....O.....               ...........
' ...........               ...........               ...........

' .........     .........     ....O....
' .........     ...OOO...     ...OOO...
' ..OOOOO..     ..O...O..     ..OOOOO..
' ..OOOOO..     .O.....O.     .OO...OO.
' ..OOOOO.. --> .O.....O. --> OOO...OOO --> oscillates
' ..OOOOO..     .O.....O.     .OO...OO.
' ..OOOOO..     ..O...O..     ..OOOOO..
' .........     ...OOO...     ...OOO...
' .........     .........     ....O....

' ...............     ...............     ...............
' ...............     ...............     ...............
' ...............     .......O.......     ......OOO......
' ......OOO......     ......OOO......     .....O...O.....
' .....O...O.....     .....OOOOO.....     .....O...O.....
' ....O.....O....     ....O.....O....     ...OO.....OO...
' ...O...O...O...     ...OO..O..OO...     ..O....O.....O.
' ...O..O.O..O... --> ..OOO.O.O.OOO.. --> ..O...O.O....O. --> oscillates
' ...O...O...O...     ...OO..O..OO...     ..O....O.....O.
' ....O.....O....     ....O.....O....     ...OO.....OO...
' .....O...O.....     .....OOOOO.....     .....O...O.....
' ......OOO......     ......OOO......     .....O...O.....
' ...............     .......O.......     ......OOO......
' ...............     ...............     ...............
' ...............     ...............     ...............

' ...............     ...............     .......O.......
' .......O.......     ......OOO......     .......O.......
' ......OOO......     ...............     ...............
' .....O.O.O.....     .....O.O.O.....     .....O.O.O.....
' .....O...O.....     .....OO.OO.....     .....OOOOO.....
' ...OO.....OO...     ...OO.....OO...     ...OO.OOO.OO...
' ..O....O....O..     .O..O..O..O..O.     ....OO.O.OO....
' .OOO..O.O..OOO. --> .O.O..O.O..O.O. --> OO.OOOO.OOOO.OO --> oscillates
' ..O....O....O..     .O..O..O..O..O.     ....OO.O.OO....
' ...OO....OO....     ...OO.....OO...     ...OO.OOO.OO...
' .....O...O.....     .....OO.OO.....     .....OOOOO.....
' .....O.O.O.....     .....O.O.O.....     .....O.O.O.....
' ......OOO......     ...............     ...............
' .......O.......     ......OOO......     .......O.......
' ...............     ...............     .......O.......

' ...............     ...............     ...............
' ...............     ...............     .......O.......
' ......O.O......     ......OOO......     ......O.O......
' .....O.O.O.....     ......OOO......     ......O.O......
' ...............     ...............     .......O.......
' ...O.......O...     ...............     ...............
' ..O.........O..     ..OO.......OO..     ..OO.......OO.. --> oscillates
' ...O.......O... --> ..OO.......OO.. --> .O..O.....O..O.
' ..O.........O..     ..OO.......OO..     ..OO.......OO..
' ...O.......O...     ...............     ...............
' ...............     ...............     .......O.......
' .....O.O.O.....     ......OOO......     ......O.O......
' ......O.O......     ......OOO......     ......O.O......
' ...............     ...............     .......O.......
' ...............     ...............     ...............

' Some patterns oscillate in such a way that they "move" around the world
' with each generation. These are called "spaceships":

' ..O
' O.O <-- "Glider" spaceship
' .OO

' .OOOO
' O...O <-- "Lightweight spaceship (LWSS)"
' ....O
' O..O.

' ..O...
' O...O.
' .....O <- "Middleweight spaceship (MWSS)"
' O....O
' .OOOOO

' ..OO...
' O....O.
' ......O <-- "Heavyweight spaceship (HWSS)"
' O.....O
' .OOOOOO

' OOOOO.............
' O....O.......OO...
' O...........OO.OOO
' .O.........OO.OOOO
' ...OO...OO.OO..OO.
' .....O....O..O....
' ......O.O.O.O.....
' .......O.......... <-- "Hammerhead" spaceship
' .......O..........
' ......O.O.O.O.....
' .....O....O..O....
' ...OO...OO.OO..OO.
' .O.........OO.OOOO
' O...........OO.OOO
' O....O.......OO...
' OOOOO.............

' Some patterns continue for several generations but ultimately die:

' ......     ......     ......     ......     ......
' ......     ..OO..     .OO...     ..O...     ......
' .O.... --> ..OO.. --> ...O.. --> ...O.. --> ..OO.. --> Death
' ..OOO.     ...O..     .OO...     ..O...     ......
' ......     ......     ......     ......     ......
' ......     ......     ......     ......     ......


' Other patterns:

' .....
' ..O..
' .O.O. "Tub"
' ..O..
' .....

' .....
' ..O..
' .O.O. "Boat"
' ..OO.
' .....

' ......
' .O.OO. "Snake"
' .OO.O.
' ......

' ......
' ......
' ..OO..
' ..O.O. "Ship"
' ...OO.
' ......

' ......
' .OO...
' .O..O. "Aircraft carrier"
' ...OO.
' ......

' ......
' ..OO..
' .O..O. "Beehive"
' ..OO..
' ......

' ......
' ..O...
' .O.O.. "Barge"
' ..O.O.
' ...O..
' ......

' .......
' .......
' ....OO. "Python"
' .O.O.O.
' .OO....
' .......

' .......
' ..O....
' .O.O... "Long boat"
' ..O..O.
' ....OO.
' .......

' ......
' .OO...
' .O.O.. "Eater", "Fish hook"
' ...O..
' ...OO.
' ......

'   .......
'   ..OOO..
'   .......
'
'   ...........
'   ....OOO....
'   ...........
'   ..O.....O..
'   ..O.....O..
'   ..O.....O..
'   ...........
'   ....OOO....
'
'   .......
'   ..OOO..
'   ..O....
'   ...O...
'   .......

' -----------------------------------------------------------------------------
' TO DO
' -----------------------------------------------------------------------------
' * add macro keys to editor to automatically draw given patterns at the cursor position
' * add option to select how many generations organisms live before dying of old age
' * option detect when the world is stuck in an endlessly repeating pattern (upto n frames) and exit
' * bigger 32-bit hires screen, more cells
' * show statistics on how many survived to what age, cause of death, etc.
' * save/playback history of a world
' * editor ability to save / load a given generation or start pattern
' * pause at any generation and edit or step back/forward
' * add keyboard option back to editor

' -----------------------------------------------------------------------------
' BEYOND SIMPLE CONWAY
' -----------------------------------------------------------------------------
' Future challenge = expand to other types of organisms, environment,
' ecosystem, evolution?
'
' Including parameters for
' * food
' * different species or creature types that make up a food chain
'   - animals vs plants vs microscopic life
'   - predators vs prey
'   - parasites
'   - algae, plankton
'   - plants drop seeds to reproduce, need water, seek light
'   - single cell creatures divide to reproduce
'   - complex animals mate to reproduce
'   - larger animals eat smaller / simpler
' * attributes
'   - fuel / energy
'   - health / "hit points"
'   - strength
'   - speed
'   - eyesight or other senses
'   - (attributes where a high score balances out by making another attribute low)
' * evolving behavior
' * reproduction passes on traits / mutations (simple genetics)
' * weather
' * terrain
' * excretion of waste / disease
' * ecosystem
' * mating behavior / selection / competition
' * track an individual organism's DNA or "heritage"

' DECLARE CONSTANTS
Const cMinCount = 1
Const cMaxCount = 529
Const cKeyDelay = 5

' Codes for reading keypresses with _BUTTON
Const KeyCode_Escape = 2
Const KeyCode_Spacebar = 58
Const KeyCode_0 = 12
Const KeyCode_1 = 3
Const KeyCode_2 = 4
Const KeyCode_3 = 5
Const KeyCode_4 = 6
Const KeyCode_5 = 7
Const KeyCode_6 = 8
Const KeyCode_7 = 9
Const KeyCode_8 = 10
Const KeyCode_9 = 11
Const KeyCode_A = 31
Const KeyCode_B = 49
Const KeyCode_C = 47
Const KeyCode_D = 33
Const KeyCode_E = 19
Const KeyCode_F = 34
Const KeyCode_G = 35
Const KeyCode_H = 36
Const KeyCode_I = 24
Const KeyCode_J = 37
Const KeyCode_K = 38
Const KeyCode_L = 39
Const KeyCode_M = 51
Const KeyCode_N = 50
Const KeyCode_O = 25
Const KeyCode_P = 26
Const KeyCode_Q = 17
Const KeyCode_R = 20
Const KeyCode_S = 32
Const KeyCode_T = 21
Const KeyCode_U = 23
Const KeyCode_V = 48
Const KeyCode_W = 18
Const KeyCode_X = 46
Const KeyCode_Y = 22
Const KeyCode_Z = 45
Const KeyCode_Up = 329
Const KeyCode_Down = 337
Const KeyCode_Left = 332
Const KeyCode_Right = 334
Const KeyCode_Minus = 13
Const KeyCode_Equal = 14
Const KeyCode_BkSp = 15
Const KeyCode_Ins = 339
Const KeyCode_Del = 340
Const KeyCode_Home = 328
Const KeyCode_End = 336
Const KeyCode_PgUp = 330
Const KeyCode_PgDn = 338
Const KeyCode_BracketLeft = 27
Const KeyCode_BracketRight = 28
Const KeyCode_CtrlRight = 286
Const KeyCode_Enter = 29

' COLOR CODES FOR PRINTING TEXT
Const cBlackT = 0
Const cBlueT = 1
Const cGreenT = 2
Const cLtBlueT = 3
Const cRedT = 4
Const cPurpleT = 5
Const cOrangeT = 6
Const cWhiteT = 7
Const cGrayT = 8
Const cPeriwinkleT = 9
Const cLtGreenT = 10
Const cCyanT = 11
Const cLtRedT = 12
Const cPinkT = 13
Const cYellowT = 14
Const cLtGrayT = 15

' START THE MAIN ROUTINE
Liff

' FINISHED, EXIT PROGRAM
System

' /////////////////////////////////////////////////////////////////////////////

Sub Liff
    ' DECLARE VARIABLES
    Dim RoutineName As String: RoutineName = "Liff"
    ReDim org%(0, 0, 0) ' array(column, row, generation)
    Dim popu As Long
    Dim a$
    Dim r$
    Dim iCount As Long
    Dim ax, ay As Integer
    Dim axOld, ayOld As Integer
    Dim MinCol, MaxCol, MinRow, MaxRow As Integer
    Dim MaxOrgs As Long
    Dim k$
    Dim gen As Long
    Dim sLine As String
    Dim CountPos As Integer
    Dim born As Long
    Dim died As Long
    Dim lived As Long
    Dim gone As Long
    Dim family As Long
    Dim North As Integer
    Dim South As Integer
    Dim East As Integer
    Dim West As Integer
    Dim age As Integer
    Dim speed As Long
    Dim bLeftClick As Integer
    Dim bRightClick As Integer
    Dim bOldLeftClick As Integer
    Dim bOldRightClick As Integer
    Dim arrKeyState(0 To 512) As Integer
    Dim bAdded As Integer
    Dim MaxAge As Integer
    Dim reason$
   
    ' MAIN LOOP
    Do
        ' INITIALIZE
        Randomize Timer ' always put this at the start of a program that uses random numbers
        MinRow = 1
        MaxRow = _Height - 2 ' # of rows available (minus 2 rows which we use to show instructions)
        MinCol = 1
        MaxCol = _Width ' # of columns on the screen
        ReDim org%(MinCol To MaxCol, MinRow To MaxRow, 1 To 2) ' set size of array
        MaxOrgs = MaxCol * MaxRow ' what's the most # of organisms that can fit on the screen?
        bLeftClick = _FALSE: bOldLeftClick = _FALSE
        bRightClick = _FALSE: bOldRightClick = _FALSE
        For iCount = LBound(arrKeyState) To UBound(arrKeyState)
            arrKeyState(iCount) = _FALSE
        Next iCount
        reason$ = "TBD"
       
        ' SHOW TITLE
        Cls , cBlackT ' clear screen and make it black

        Color cLtGreenT, cBlackT ' set print color to light green on black
        Print
        Print "           #   ### ### ###"
        Print "           #    #  #   #"
        Print "           #    #  ##  ###"
        Print "           #    #  #   #"
        Print "           ### ### #   ###"
        Print
        Color cWhiteT, cBlackT ' set print color to white on black

        ' ASK USER TO ENTER PREFERENCES...

        ' PLACE RANDOMLY?
        Do
            Input "Allow organisms to age (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "y" Then MaxAge = 5 Else MaxAge = 1

        ' PLACE RANDOMLY?
        Do
            Input "Place the organisms randomly (y/n)"; r$
            r$ = _Trim$(r$) ' remove extra spaces
            r$ = LCase$(r$) ' force to lowercase
            If r$ <> "y" And r$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until r$ = "y" Or r$ = "n"
       
        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' IF RANDOMLY, THEN HOW MANY?
        If r$ = "y" Then
            Do
                Print "How many organisms (1-" + _ToStr$(MaxOrgs) + ", or 0 for random)"
                Input popu
                If popu >= 1 Or popu <= MaxOrgs Then
                    ' User selected a valid number
                    Exit Do ' stop asking (exit the do loop)
                ElseIf popu = 0 Then
                    ' Select a random number of organisms to make
                    popu = RandomNumber%(1, MaxOrgs)
                Else
                    ' Value is not valid, let the user know and keep asking
                    Print "*** Value out of range. Type a number 1-" + _ToStr$(MaxOrgs) + ". ***"
                End If
            Loop
            Print
            Print "Simulation will proceed with " + _ToStr$(popu) + " organisms."
            Print
        End If

        ' INITIALIZE ORGANISM ARAY
        For ay = MinRow To MaxRow
            For ax = MinCol To MaxCol
                org%(ax, ay, 2) = 0 ' 2 is the next generation
            Next ax
        Next ay
       
        ' PLACE ORGANISMS
        Cls
        If r$ = "y" Then
            ' PLACE EACH ORGANISM RANDOMLY...
            For iCount = 1 To popu
                bAdded = PlaceRandomOrganism%(org%(), 2, MaxAge)
            Next iCount
           
        ElseIf r$ = "n" Then
            ' MANUALLY PLACE ORGANISMS...
           
            ' SHOW INSTRUCTIONS
            Color cYellowT, cBlackT ' set print color to yellow on black
            sLine = "Mouse selects location. Left click & right click to add/remove organisms."
            Locate 1, 1: Print sLine;
            sLine = "Press R to add randomly. Escape starts simulation. Population: "
            Locate 2, 1: Print sLine;
            CountPos = Len(sLine) + 1
           
            ' SHOW SCREEN AS A GRID TO HELP PLACEMENT
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    DrawOrganism org%(), ax, ay, 2, GetBgColor%(ax, ay), GetBgColor%(ax, ay), MaxAge
                Next ax
            Next ay
           
            ' RESET COUNT
            popu = 0 ' no organisms placed yet

            ' PLACE CURSOR AT TOP LEFT
            ax = MinCol: axOld = ax ' MaxCol
            ay = MinRow: ayOld = ay ' MaxRow
           
            ' HIDE MOUSE POINTER
            _MouseHide
           
            ' LOOP UNTIL USER HAS FINISH PLACING ORGANISMS
            ' array and mouse coordinates (ax, ay, axOld, ayOld) are 1-based
            ' drawing on screen starting at row 3, so ay+2
            Do
                ' ERASE OLD
                If axOld <> ax Or ayOld <> ay Then
                    DrawOrganism org%(), axOld, ayOld, 2, cGreenT, GetBgColor%(axOld, ayOld), MaxAge
                End If
               
                ' Draw cursor at current location
                DrawOrganism org%(), ax, ay, 2, GetBgColor%(ax, ay), cGreenT, MaxAge
               
                ' RESET OLD
                axOld = ax
                ayOld = ay
               
                ' READ MOUSE
                Do While _MouseInput: Loop
                ax = _MouseX
                ay = _MouseY
                bLeftClick = _MouseButton(1)
                bRightClick = _MouseButton(2)

                ' VERSION FOR HI-RES SCREEN:
                'ax = (_MouseX \ _FontWidth) * _FontWidth
                'ay = (_MouseY \ _FontHeight) * _FontHeight

                ' CHECK BOUNDARIES
                If ax < MinCol Then ax = MinCol Else If ax > MaxCol Then ax = MaxCol
                If ay < MinRow Then ay = MinRow Else If ay > MaxRow Then ay = MaxRow
                'If ax < 1 Then ax = 1 Else If ax > _Width Then ax = _Width
                'If ay < 1 Then ay = 1 Else If ay > _Height Then ay = _Height

                ' DID THEY CLICK?
                If bLeftClick Then
                    If bOldLeftClick = _FALSE Then
                        If org%(ax, ay, 2) = 0 Then popu = popu + 1
                        org%(ax, ay, 2) = org%(ax, ay, 2) + 1
                        If org%(ax, ay, 2) > MaxAge Then org%(ax, ay, 2) = 1
                        bOldLeftClick = _TRUE
                    End If
                Else
                    bOldLeftClick = _FALSE
                End If
                If bRightClick Then
                    If bOldRightClick = _FALSE Then
                        If org%(ax, ay, 2) > 0 Then popu = popu - 1
                        org%(ax, ay, 2) = 0
                        bOldRightClick = _TRUE
                    End If
                Else
                    bOldRightClick = _FALSE
                End If
               
                ' Read keyboard
                While _DeviceInput(1): Wend ' clear and update the keyboard buffer

                ' DON'T ACCEPT ANY MORE INPUT UNTIL THE LAST PRESSED KEY IS RELEASED
                If _Button(KeyCode_R) = _TRUE Then
                    If arrKeyState(KeyCode_R) = _FALSE Then
                        arrKeyState(KeyCode_R) = _TRUE
                       
                        ' TRY TO RANDOMLY PLACE AN ORGANISM
                        If PlaceRandomOrganism(org%(), 2, MaxAge) = _TRUE Then
                            popu = popu + 1
                        Else
                            ' PlaceRandomOrganism returned _FALSE
                            ' so we are full, exit edit mode
                            Exit Do
                        End If
                    End If
                Else
                    arrKeyState(KeyCode_R) = _FALSE
                End If
               
                ' Check which keys were pressed
                If _Button(KeyCode_Escape) = _TRUE Then
                    ' Exit edit mode
                    Exit Do
                End If
               
                ' SHOW POPULATION
                Color cLtGreenT, cBlackT
                Locate 2, CountPos: Print _ToStr$(popu) + "     ";
               
                ' Govern speed of loop to 60 frames per second:
                _Limit 60

            Loop Until _KeyDown(27) ' escape key exit
           
            ' RESTORE MOUSE
            _MouseShow "default": _Delay 0.5
           
            ' CLEAR KEYBOARD BUFFER
            _KeyClear

        End If

        ' THE CYCLE OF LIFE
        speed = 2
        gen = 0
        lived = popu ' total that ever lived
        gone = 0 ' total that have ever died
        Do
            Cls
            gen = gen + 1 ' INCREASE GENERATION
           
            ' SHOW GENERATION
            Color cYellowT, cBlackT
            Locate 1, 1: Print "Generation: " + _ToStr$(gen);

            ' SHOW TOTAL THAT EVER LIVED
            Color cLtGreenT, cBlackT
            Locate 1, 25: Print "Total ever born: " + _ToStr$(lived);

            ' SHOW TOTAL THAT EVER DIED
            Color cLtGrayT, cBlackT
            Locate 1, 50: Print "Total ever died: " + _ToStr$(gone);

            ' SHOW BIRTHS
            Color cCyanT, cBlackT
            Locate 2, 1: Print "Births: " + _ToStr$(born) + "     ";

            ' SHOW DEATHS
            Color cPurpleT, cBlackT
            Locate 2, 25: Print "Deaths: " + _ToStr$(died) + "     ";

            ' SHOW POPULATION
            Color cLtGreenT, cBlackT
            Locate 2, 50: Print "Population: " + _ToStr$(popu) + "     ";

            ' RESET THIS GENERATION'S STATISTICS
            born = 0
            died = 0
           
            ' SETUP FOR NEXT ROUND
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    ' COPY NEXT GENERATION TO CURRENT
                    org%(ax, ay, 1) = org%(ax, ay, 2)
                   
                    ' AND DRAW THE NEXT ORGANISM
                    DrawOrganism org%(), ax, ay, 1, cGreenT, cBlackT, MaxAge
                Next ax
            Next ay
           
            ' BORN, LIVE, DIE
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol

                    ' COUNT NEIGHBORS
                    family = 0

                    ' GET NEIGHBORS' POSITIONS
                    ' WRAP AROUND THE EDGES (THE WORLD IS ROUND!)
                    If ax = MinCol Then
                        West = MaxCol
                        East = ax + 1
                    ElseIf ax = MaxCol Then
                        West = ax - 1
                        East = MinCol
                    Else
                        West = ax - 1
                        East = ax + 1
                    End If
                    If ay = MinRow Then
                        North = MaxRow
                        South = ay + 1
                    ElseIf ay = MaxRow Then
                        North = ay - 1
                        South = MinRow
                    Else
                        North = ay - 1
                        South = ay + 1
                    End If

                    ' Check NW neighbor
                    If org%(West, North, 1) > 0 Then family = family + 1

                    ' Check N neighbor
                    If org%(ax, North, 1) > 0 Then family = family + 1

                    ' Check NE neighbor
                    If org%(East, North, 1) > 0 Then family = family + 1

                    ' Check W neighbor
                    If org%(West, ay, 1) > 0 Then family = family + 1

                    ' Check E neighbor
                    If org%(East, ay, 1) > 0 Then family = family + 1

                    ' Check SW neighbor
                    If org%(West, South, 1) > 0 Then family = family + 1

                    ' Check S neighbor
                    If org%(ax, South, 1) > 0 Then family = family + 1

                    ' Check SE neighbor
                    If org%(East, South, 1) > 0 Then family = family + 1

                    ' SEE WHO LIVES / DIES / IS BORN
                    ' look at current generation (x, y, 1)
                    ' to update next  generation (x, y, 2)
                    If org%(ax, ay, 1) = 0 Then
                        ' REPRODUCTION?
                        If family = 3 Then
                            ' BIRTH
                            org%(ax, ay, 2) = 1
                            popu = popu + 1 ' ** POPULATION INCREASES BY 1 **
                            born = born + 1 ' INCREASE COUNT BORN THIS GENERATION
                            lived = lived + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER BORN
                        End If
                    ElseIf org%(ax, ay, 1) > 0 Then
                        ' LIVE OR DIE?
                        If family < 2 Then
                            ' DIED OF LONELINESS
                            org%(ax, ay, 2) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family > 3 Then
                            ' DIED OF OVERCROWDING
                            org%(ax, ay, 2) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family = 2 Or family = 3 Then
                            ' LIFE GOES ON
                            If MaxAge > 1 Then
                                ' AGING! 1 = newborn, 5 = maximum age
                                org%(ax, ay, 2) = org%(ax, ay, 2) + 1

                                ' DIED OF OLD AGE
                                If org%(ax, ay, 2) > 5 Then
                                    ' DIED OF OLD AGE
                                    org%(ax, ay, 2) = 0
                                    popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                                    died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                                    gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                                End If
                            End If
                        End If
                    End If
                   
                    ' REFRESH SCREEN
                    _Display

                Next ax
            Next ay
           
            ' RE-COUNT POPULATION (BRUTE FORCE METHOD, SINCE NORMAL METHODS DON'T SEEM TO WORK WHEN AGING IS ENABLED)
            popu = 0
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    If org%(ax, ay, 2) > 0 Then
                        popu = popu + 1
                    End If
                Next ax
            Next ay
           
            ' If population falls to zero then exit
            If popu < 1 Then reason$ = "no organisms survived": Exit Do
           
            ' PROCESS KEYBOARD INPUT
            While _DeviceInput(1): Wend ' clear and update the keyboard buffer
            If _Button(KeyCode_Escape) Then
                ' ESCAPE = EXIT
                reason$ = "cancelled": Exit Do
               
            ElseIf _Button(KeyCode_Spacebar) Then
                ' SPACEBAR = PAUSE
                Color cBlackT, cWhiteT
                Locate 1, 1: Print "PAUSED - PRESS ANY KEY TO CONTINUE";
                _Display
                _KeyClear: _Delay 1
                Sleep
                _KeyClear: _Delay 1
                Color cWhiteT, cBlackT
                Locate 1, 1: Print "                                  ";
            ElseIf _Button(KeyCode_Equal) Or _Button(KeyCode_Right) Or _Button(KeyCode_Up) Or _Button(KeyCode_PgUp) Then
                ' + OR RIGHT ARROW OR UP ARROW OR PAGE UP = INCREASE SPEED
                speed = speed + 1
                If speed > 1000 Then speed = 1000
            ElseIf _Button(KeyCode_Minus) Or _Button(KeyCode_Left) Or _Button(KeyCode_Down) Or _Button(KeyCode_PgDn) Then
                ' - OR LEFT ARROW OR DOWN ARROW OR PAGE DOWN = SLOW DOWN
                speed = speed - 1
                If speed < 1 Then speed = 1
            End If

            ' CLEAR KEYBOARD BUFFER
            _KeyClear

            ' increment generation counter
            gen = gen + 1

            ' REFRESH SCREEN
            _Display

            ' LIMIT TO 2 GENERATIONS / SECOND
            _Limit speed
        Loop

        ' RESTORE NORMAL SCREEN REFRESH
        _AutoDisplay

        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' PLAY AGAIN?
        Cls
        Color cWhiteT, cBlackT
        Print "SIMULATION OVER (" + reason$ + ")."
        Print

        ' SHOW FINAL STATS
        Print "Final statistics:"
        Color cYellowT, cBlackT: Print "Generation            : " + _ToStr$(gen)
        Color cLtGreenT, cBlackT: Print "Total ever born       : " + _ToStr$(lived)
        Color cLtGrayT, cBlackT: Print "Total ever died       : " + _ToStr$(gone)
        Color cCyanT, cBlackT: Print "Last generation births: " + _ToStr$(born)
        Color cPurpleT, cBlackT: Print "Last generation Deaths: " + _ToStr$(died)
        Color cLtGreenT, cBlackT: Print "Final population      : " + _ToStr$(popu)
        Print

        Color cWhiteT, cBlackT
        Do
            Input "Start over and try again (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "n" Then Exit Do
       
    Loop

End Sub ' Life

' /////////////////////////////////////////////////////////////////////////////

Function GetBgColor% (ax%, ay%)
    If IsOdd%(ay%) Then
        If IsOdd%(ax%) Then
            GetBgColor% = cBlackT
        Else
            GetBgColor% = cGrayT
        End If
    Else
        If IsOdd%(ax%) Then
            GetBgColor% = cGrayT
        Else
            GetBgColor% = cBlackT
        End If
    End If
End Function ' GetBgColor%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if an organism was placed
' only should return _FALSE if no more empty spaces

Function PlaceRandomOrganism% (org%(), gen%, MaxAge%)
    Dim bResult As Integer
    Dim MinCol, MaxCol As Integer
    Dim MinRow, MaxRow As Integer
    Dim age As Integer
    Dim ax, ay As Integer
    Dim dx, dy As Integer
    Dim iCount As Integer
    Dim iMax As Integer

    ' Initialize
    MinCol = LBound(org%, 1)
    MaxCol = UBound(org%, 1)
    MinRow = LBound(org%, 2)
    MaxRow = UBound(org%, 2)

    ' Calculate maximum possible count
    dx = 1 - MinCol
    dy = 1 - MinRow
    iMax = (MaxCol + dx) * (MaxRow + dy)

    ' Count # currently present
    iCount = 0
    For ax = MinCol To MaxCol
        For ay = MinRow To MaxRow
            If org%(ax, ay, gen%) > 0 Then iCount = iCount + 1
        Next ay
    Next ax

    ' If room for more, place one randomly
    bResult = (iCount < iMax)
    If bResult = _TRUE Then
        ' KEEP LOOKING FOR A RANDOM UNOCCUPIED LOCATION
        Do
            ax = RandomNumber%(MinCol, MaxCol) ' SELECT A RANDOM COLUMN
            ay = RandomNumber%(MinRow, MaxRow) ' SELECT A RANDOM ROW

            ' MAKE SURE IT'S NOT ALREADY OCCUPIED
            If org%(ax, ay, gen%) = 0 Then
                age = RandomNumber%(1, MaxAge%)
                org%(ax, ay, gen%) = age
                DrawOrganism org%(), ax, ay, gen%, cGreenT, cBlackT, MaxAge%
                Exit Do ' break out of loop and move on to the next organism
            End If
        Loop
    End If

    ' Return result
    PlaceRandomOrganism = bResult
End Function ' PlaceRandomOrganism

' /////////////////////////////////////////////////////////////////////////////

Sub DrawOrganism (org%(), ax%, ay%, gen%, fgColor%, bgColor%, MaxAge%)
    Color fgColor%, bgColor%
    Locate ay% + 2, ax%
    Print GetOrganism$(org%(ax%, ay%, gen%), MaxAge%);
End Sub ' DrawOrganism

' /////////////////////////////////////////////////////////////////////////////
' Return different picture based on age:
' 1 = [.] = newborn
' 2 = [o] = kid
' 3 = [O] = fully grown
' 4 = [0] = old!
' 5 = [@] = really old!

Function GetOrganism$ (age%, MaxAge%)
    Dim org$
    If MaxAge% > 1 Then
        Select Case age%
            Case 1:
                org$ = "."
            Case 2:
                org$ = "o"
            Case 3:
                org$ = "O"
            Case 4:
                org$ = "0"
            Case 5:
                org$ = "@"
            Case Else:
                org$ = " "
        End Select
    Else
        If age% > 0 Then
            'org$ = "O"
            org$ = Chr$(177)
        Else
            org$ = " "
        End If
    End If
    GetOrganism$ = org$
End Function ' GetOrganism

' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.

' Make sure you put Randomize Timer at the start of your program

Function RandomNumber% (Min%, Max%)
    Dim NumSpread%
    NumSpread% = (Max% - Min%) + 1
    RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%

' /////////////////////////////////////////////////////////////////////////////
' Use to pretty print _TRUE and _FALSE values.

Function TrueFalse$ (myValue%)
    TrueFalse$ = _IIf(myValue%, "_TRUE", "_FALSE")
End Function ' TrueFalse$

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is even
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsEven% (n)
    If n Mod 2 = 0 Then
        IsEven% = _TRUE
    Else
        IsEven% = _FALSE
    End If
End Function ' IsEven%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is odd
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsOdd% (n)
    If n Mod 2 = 1 Then
        IsOdd% = _TRUE
    Else
        IsOdd% = _FALSE
    End If
End Function ' IsOdd%




Original post:

Back in high school I had a computer class where one of the assignments was to recreate the famous computer simulation Life. 
I added a feature in my version where organisms grow with each generation, where they die of old age after 5 generations.

Here is the basic program updated for QB64PE. 

NOTES: 
  • There is one bug: we track the current population in a variable popu%, which increases by 1 when an organism is born and decreases by 1 when one dies, but for some reason the value goes negative or really high. 
  • We tried limiting reproduction to only organisms of mating age (2-3, 2-4, etc.) but the population always died out in just a few generations. So this version uses the original rule where a new organism is born in any space surrounded by exactly 3 organisms.

It would be interesting to expand this to simulate other aspects of life such as 
  • food / natural resources
  • different species (animals vs plants, predators vs prey vs parasites)
  • evolving behavior
  • rules around mating (age, competition for mates, etc.)
  • reproduction passes on traits / mutations
  • environment (terrain, weather)
  • excretion of waste / disease
  • ecosystem

Enjoy

Code: (Select All)
' LIFF v1.00
' BY SOFTINTHEHEADWARE

' A VERSION OF LIFE BY JOHN CONWAY
' This version has aging: organisms die after they are 5 generations old.

' Bugs to fix
' * for some reason the population counter doesn't work
'   it drops below zero and gets really high, not sure why

' Other to do:
' * bigger screen, more cells
' * show statistics on how many survived to what age, cause of death, etc.
' * save/playback history of a world
' * What other parameters can we introduce?
'   - mating: we tried only letting them mate if surrounded by others age 2-4,
'             but they always seemed to die faster than they were born?
'   - food
'   - different species (animals vs plants, predators vs prey vs parasites)
'   - evolving behavior
'   - reproduction passes on traits / mutations
'   - weather
'   - terrain
'   - excretion of waste / disease
'   - ecosystem

' DECLARE CONSTANTS
Const cMinCount = 1
Const cMaxCount = 529
Const cKeyDelay = 5
Const cMinMateAge = 3
Const cMaxMateAge = 4

' Codes for reading keypresses with _BUTTON
Const KeyCode_Escape = 2
Const KeyCode_Spacebar = 58
Const KeyCode_0 = 12
Const KeyCode_1 = 3
Const KeyCode_2 = 4
Const KeyCode_3 = 5
Const KeyCode_4 = 6
Const KeyCode_5 = 7
Const KeyCode_6 = 8
Const KeyCode_7 = 9
Const KeyCode_8 = 10
Const KeyCode_9 = 11
Const KeyCode_A = 31
Const KeyCode_B = 49
Const KeyCode_C = 47
Const KeyCode_D = 33
Const KeyCode_E = 19
Const KeyCode_F = 34
Const KeyCode_G = 35
Const KeyCode_H = 36
Const KeyCode_I = 24
Const KeyCode_J = 37
Const KeyCode_K = 38
Const KeyCode_L = 39
Const KeyCode_M = 51
Const KeyCode_N = 50
Const KeyCode_O = 25
Const KeyCode_P = 26
Const KeyCode_Q = 17
Const KeyCode_R = 20
Const KeyCode_S = 32
Const KeyCode_T = 21
Const KeyCode_U = 23
Const KeyCode_V = 48
Const KeyCode_W = 18
Const KeyCode_X = 46
Const KeyCode_Y = 22
Const KeyCode_Z = 45
Const KeyCode_Up = 329
Const KeyCode_Down = 337
Const KeyCode_Left = 332
Const KeyCode_Right = 334
Const KeyCode_Minus = 13
Const KeyCode_Equal = 14
Const KeyCode_BkSp = 15
Const KeyCode_Ins = 339
Const KeyCode_Del = 340
Const KeyCode_Home = 328
Const KeyCode_End = 336
Const KeyCode_PgUp = 330
Const KeyCode_PgDn = 338
Const KeyCode_BracketLeft = 27
Const KeyCode_BracketRight = 28
Const KeyCode_CtrlRight = 286
Const KeyCode_Enter = 29

' COLOR CODES FOR PRINTING TEXT
Const cBlackT = 0
Const cBlueT = 1
Const cGreenT = 2
Const cLtBlueT = 3
Const cRedT = 4
Const cPurpleT = 5
Const cOrangeT = 6
Const cWhiteT = 7
Const cGrayT = 8
Const cPeriwinkleT = 9
Const cLtGreenT = 10
Const cCyanT = 11
Const cLtRedT = 12
Const cPinkT = 13
Const cYellowT = 14
Const cLtGrayT = 15

' START THE MAIN ROUTINE
Life

' FINISHED, EXIT PROGRAM
System

' /////////////////////////////////////////////////////////////////////////////

Sub Life
    ' DECLARE VARIABLES
    ReDim org%(0, 0)
    Dim popu%
    Dim r$
    Dim iCount%
    Dim x%
    Dim y%
    Dim oldX%
    Dim oldY%
    Dim MinCol%, MaxCol%, MinRow%, MaxRow%
    Dim MaxOrgs%
    Dim LastKey%
    Dim k$
    Dim KeyDelayCount%
    Dim DX%, DY%
    Dim gen&
    Dim sLine$
    Dim CountPos%
    Dim born%
    Dim died%
    Dim lived&
    Dim gone&
    Dim family%
    Dim mates%
    Dim px%
    Dim N%
    Dim S%
    Dim E%
    Dim W%
    Dim age%
    Dim speed%

    ' INITIALIZE
    Randomize Timer ' always put this at the start of a program that uses random numbers
    MinRow% = 1
    MaxRow% = _Height - 2 ' # of rows available (minus 2 rows which we use to show instructions)
    MinCol% = 1
    MaxCol% = _Width ' # of columns on the screen
    ReDim org%(MinCol% To MaxCol%, MinRow% To MaxRow%) ' set size of array
    MaxOrgs% = MaxCol% * MaxRow% ' what's the most # of organisms that can fit on the screen?

    ' SHOW TITLE
    Cls , cBlackT ' clear screen and make it black

    Color cLtGreenT, cBlackT ' set print color to light green on black
    Print
    Print "           #   ### ### ###"
    Print "           #    #  #   #"
    Print "           #    #  ##  ###"
    Print "           #    #  #   #"
    Print "           ### ### #   ###"
    Print
    Color cWhiteT, cBlackT ' set print color to white on black

    ' ASK USER TO ENTER PREFERENCES...

    ' PLACE RANDOMLY?
    Do
        Input "Place the organisms randomly (y/n)"; r$
        r$ = _Trim$(r$) ' remove extra spaces
        r$ = LCase$(r$) ' force to lowercase
        If r$ <> "y" And r$ <> "n" Then
            Print "*** Please type y or n ***"
        End If
    Loop Until r$ = "y" Or r$ = "n"

    ' CLEAR KEYBOARD BUFFER
    _KeyClear: _Delay 1

    ' IF RANDOMLY, THEN HOW MANY?
    If r$ = "y" Then
        Do
            Print "How many organisms (1-" + _ToStr$(MaxOrgs%) + ", or 0 for random)"
            Input popu%
            If popu% >= 1 And popu% <= MaxOrgs% Then
                ' User selected a valid number
                Exit Do ' stop asking (exit the do loop)
            ElseIf popu% = 0 Then
                ' Select a random number of organisms to make
                popu% = RandomNumber%(1, MaxOrgs%)
            Else
                ' Value is not valid, let the user know and keep asking
                Print "*** Value out of range. Type a number 1-" + _ToStr$(MaxOrgs%) + ". ***"
            End If
        Loop
        Print
        Print "Simulation will proceed with " + _ToStr$(popu%) + " organisms."
        Print
    End If

    ' INITIALIZE ORGANISM ARAY
    For y% = MinRow% To MaxRow%
        For x% = MinCol% To MaxCol%
            org%(x%, y%) = 0
        Next x%
    Next y%

    ' PLACE ORGANISMS
    Cls
    If r$ = "y" Then
        ' PLACE EACH ORGANISM RANDOMLY
        For iCount% = 1 To popu%
            ' TRY TO FIND A RANDOM UNOCCUPIED LOCATION
            Do
                ' SELECT A RANDOM COLUMN
                x% = RandomNumber%(MinCol%, MaxCol%)

                ' SELECT A RANDOM ROW
                y% = RandomNumber%(MinRow%, MaxRow%)

                ' MAKE SURE IT'S NOT ALREADY OCCUPIED
                If org%(x%, y%) = 0 Then
                    age% = RandomNumber%(1, 5)
                    org%(x%, y%) = age%
                    'org%(x%, y%) = 1
                    Exit Do ' break out of loop and move on to the next organism
                End If
            Loop
        Next iCount%

        ' MANUALLY PLACE ORGANISMS?
    ElseIf r$ = "n" Then

        ' SHOW INSTRUCTIONS
        Color cYellowT, cBlackT ' set print color to yellow on black
        sLine$ = "Use arrow keys to move cursor. Press Enter to add/remove organism."
        Locate 1, 1: Print sLine$;
        sLine$ = "Press Escape to quit adding and start simulation. Population: "
        Locate 2, 1: Print sLine$;
        CountPos% = Len(sLine$) + 1

        ' RESET COUNT
        popu% = 0 ' no organisms placed yet
        KeyDelayCount% = 0

        ' PLACE CURSOR AT TOP LEFT
        x% = 1: oldX% = x%: DX% = 0
        y% = 1: oldY% = y%: DY% = 0

        ' NO KEYS PRESSED YET
        LastKey% = 0

        ' LOOP UNTIL USER HAS FINISH PLACING ORGANISMS
        Do
            ' Draw cursor at current location
            DrawCursor org%(), x%, y%

            ' Read keyboard
            While _DeviceInput(1): Wend ' clear and update the keyboard buffer

            ' DON'T ACCEPT ANY MORE INPUT UNTIL THE LAST PRESSED KEY IS RELEASED
            If LastKey% <> 0 Then
                If _Button(LastKey%) = FALSE Then
                    LastKey% = 0
                    KeyDelayCount% = 0
                Else
                    KeyDelayCount% = KeyDelayCount% + 1
                    If KeyDelayCount% > cKeyDelay Then
                        LastKey% = 0
                        KeyDelayCount% = 0
                    End If
                End If
            End If

            ' Check which keys were pressed
            If _Button(KeyCode_Escape) And LastKey% <> KeyCode_Escape Then
                ' Escape
                LastKey% = KeyCode_Escape

                ' Stop placing organisms
                Exit Do

            ElseIf _Button(KeyCode_Up) And LastKey% <> KeyCode_Up Then
                ' Up arrow
                LastKey% = KeyCode_Up

                ' Move if not turning
                If DX% = 0 Then
                    ' Move up one row
                    y% = y% - 1


                    ' If they moved off the edge, wrap them around to the other side
                    If y% < MinRow% Then
                        y% = MaxRow%
                        x% = x% - 1: If x% < MinCol% Then x% = MaxCol%
                    End If
                End If
                DY% = -1: DX% = 0

            ElseIf _Button(KeyCode_Down) And LastKey% <> KeyCode_Down Then
                ' Down arrow
                LastKey% = KeyCode_Down

                ' Move if not turning
                If DX% = 0 Then
                    ' Move down one row
                    y% = y% + 1

                    ' If they moved off the edge, wrap them around to the other side
                    If y% > MaxRow% Then
                        y% = MinRow%
                        x% = x% + 1: If x% > MaxCol% Then x% = MinCol%
                    End If
                End If
                DY% = 1: DX% = 0

            ElseIf _Button(KeyCode_Left) And LastKey% <> KeyCode_Left Then
                ' Left arrow
                LastKey% = KeyCode_Left

                ' Move if not turning
                If DY% = 0 Then
                    ' Move left one column
                    x% = x% - 1

                    ' If they moved off the edge, wrap them around to the other side
                    If x% < MinCol% Then
                        x% = MaxCol%
                        y% = y% - 1: If y% < MinRow% Then y% = MaxRow%
                    End If
                End If
                DX% = -1: DY% = 0

            ElseIf _Button(KeyCode_Right) And LastKey% <> KeyCode_Right Then
                ' Right arrow
                LastKey% = KeyCode_Right

                ' Move if not turning
                If DY% = 0 Then
                    ' Move right one column
                    x% = x% + 1

                    ' If they moved off the edge, wrap them around to the other side
                    If x% > MaxCol% Then
                        x% = MinCol%
                        y% = y% + 1: If y% > MaxRow% Then y% = MinRow%
                    End If
                End If
                DX% = 1: DY% = 0

            ElseIf _Button(KeyCode_Home) And LastKey% <> KeyCode_Home Then
                ' Home key
                LastKey% = KeyCode_Home

                ' Jump to the beginning of the current row
                x% = MinCol%

            ElseIf _Button(KeyCode_End) And LastKey% <> KeyCode_End Then
                ' End key
                LastKey% = KeyCode_End

                ' Jump to the end of the current row
                x% = MaxCol%

            ElseIf _Button(KeyCode_PgUp) And LastKey% <> KeyCode_PgUp Then
                ' Page Up key
                LastKey% = KeyCode_PgUp

                ' Move to the top of the current column
                y% = MinRow%

            ElseIf _Button(KeyCode_PgDn) And LastKey% <> KeyCode_PgDn Then
                ' Page Down key
                LastKey% = KeyCode_PgDn

                ' Jump to the bottom of the current column
                y% = MaxRow%
                DrawOrganism org%(), oldX%, oldY%

            ElseIf _Button(KeyCode_Enter) And LastKey% <> KeyCode_Enter Then
                ' Enter
                LastKey% = KeyCode_Enter

                ' Place an organism here (or remove if there is one there already)
                If org%(x%, y%) = 0 Then
                    org%(x%, y%) = 1 ' set current location to have an organism
                    popu% = popu% + 1 ' Increase the count

                    ' If we have placed the maximum # of organisms, then exit
                    If popu% >= MaxOrgs% Then
                        Exit Do
                    End If
                Else
                    org%(x%, y%) = 0 ' set current locaton have NO organisms
                    popu% = popu% - 1 ' Decrease the count

                    ' # of organisms should never fall below zero, but make sure just in case
                    If popu% < 0 Then
                        popu% = 0
                    End If
                End If

                ' Show change in population
                Locate 2, CountPos%
                Color cYellowT, cBlackT ' set print color to yellow on black
                Print _ToStr$(popu%);

                ' *****************************************************************************
                ' SOME FANCY CODE TO
                ' MOVE ONE SPACE IN THE LAST DIRECTION THEY WERE GOING
                If DY% = -1 Then
                    ' Move up one row
                    y% = y% - 1

                    ' If they moved off the edge, wrap them around to the other side
                    If y% < MinRow% Then
                        y% = MaxRow%
                        x% = x% - 1: If x% < MinCol% Then x% = MaxCol%
                    End If
                ElseIf DY% = 1 Then
                    ' Move down one row
                    y% = y% + 1

                    ' If they moved off the edge, wrap them around to the other side
                    If y% > MaxRow% Then
                        y% = MinRow%
                        x% = x% + 1: If x% > MaxCol% Then x% = MinCol%
                    End If
                ElseIf DX% = -1 Then
                    ' Move left one column
                    x% = x% - 1

                    ' If they moved off the edge, wrap them around to the other side
                    If x% < MinCol% Then
                        x% = MaxCol%
                        y% = y% - 1: If y% < MinRow% Then y% = MaxRow%
                    End If
                Else
                    DX% = 1

                    ' Move right one column
                    x% = x% + 1

                    ' If they moved off the edge, wrap them around to the other side
                    If x% > MaxCol% Then
                        x% = MinCol%
                        y% = y% + 1: If y% > MaxRow% Then y% = MinRow%
                    End If
                End If
                ' END FANCY CODE
                ' *****************************************************************************

            End If

            ' CLEAR KEYBOARD BUFFER
            _KeyClear

            ' If user has moved then erase cursor
            If x% <> oldX% Or y% <> oldY% Then
                DrawOrganism org%(), oldX%, oldY%
            End If

            ' Reset previous location
            oldX% = x%
            oldY% = y%

            ' Govern speed of loop to 60 frames per second:
            _Limit 60
        Loop

    End If

    ' THE CYCLE OF LIFE
    speed% = 2
    gen& = 0
    lived& = popu% ' total that ever lived
    gone& = 0 ' total that have ever died
    Do
        Cls
        gen& = gen& + 1 ' INCREASE GENERATION

        ' SHOW GENERATION
        Color cYellowT, cBlackT
        Locate 1, 1: Print "Generation: " + _ToStr$(gen&);

        ' SHOW TOTAL THAT EVER LIVED
        Color cLtGreenT, cBlackT
        Locate 1, 25: Print "Total ever born: " + _ToStr$(lived&);

        ' SHOW TOTAL THAT EVER DIED
        Color cLtGrayT, cBlackT
        Locate 1, 50: Print "Total ever died: " + _ToStr$(gone&);

        ' SHOW BIRTHS
        Color cCyanT, cBlackT
        Locate 2, 1: Print "Births: " + _ToStr$(born%) + "     ";

        ' SHOW DEATHS
        Color cPurpleT, cBlackT
        Locate 2, 25: Print "Deaths: " + _ToStr$(died%) + "     ";

        ' SHOW POPULATION
        Color cLtGreenT, cBlackT
        Locate 2, 50: Print "Population: " + _ToStr$(popu%) + "     ";

        ' RESET THIS GENERATION'S STATISTICS
        born% = 0
        died% = 0

        ' DRAW THE RACE
        For y% = MinRow% To MaxRow%
            For x% = MinCol% To MaxCol%
                DrawOrganism org%(), x%, y%
            Next x%
        Next y%

        ' BORN, LIVE, DIE
        For y% = MinRow% To MaxRow%
            For x% = MinCol% To MaxCol%

                ' COUNT NEIGHBORS, MATES
                family% = 0
                mates% = 0 ' *** THIS DOESN'T SEEM TO WORK! THEY DIE FASTER THAN THEY REPRODUCE! ***

                ' GET NEIGHBORS' POSITIONS
                ' WRAP AROUND THE EDGES (THE WORLD IS ROUND!)
                If x% = MinCol% Then
                    W% = MaxCol%
                    E% = x% + 1
                ElseIf x% = MaxCol% Then
                    W% = x% - 1
                    E% = MinCol%
                Else
                    W% = x% - 1
                    E% = x% + 1
                End If
                If y% = MinRow% Then
                    N% = MaxRow%
                    S% = y% + 1
                ElseIf y% = MaxRow% Then
                    N% = y% - 1
                    S% = MinRow%
                Else
                    N% = y% - 1
                    S% = y% + 1
                End If

                ' NW
                age% = org%(W%, N%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' N
                age% = org%(x%, N%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' NE
                age% = org%(E%, N%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' W
                age% = org%(W%, y%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' E
                age% = org%(E%, y%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' SW
                age% = org%(W%, S%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' S
                age% = org%(x%, S%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' SE
                age% = org%(E%, S%)
                If age% > 0 Then family% = family% + 1
                If age% >= cMinMateAge And age% <= cMaxMateAge Then mates% = mates% + 1

                ' SEE WHO LIVES / DIES / IS BORN
                If org%(x%, y%) = 0 Then
                    ' REPRODUCTION
                    'If mates% = 3 Then ' *** ONLY REPRODUCING WITH ELIGIBLE MATES HAS NOT WORKED...
                    If family% = 3 Then
                        ' BIRTH
                        org%(x%, y%) = 1
                        born% = born% + 1
                        'TRY ADJUSTING AT END OF GENERATION: popu% = popu% + 1
                        lived& = lived& + 1
                    End If
                ElseIf org%(x%, y%) > 0 Then
                    ' LIVE OR DIE?
                    If family% < 2 Or family% > 3 Then
                        ' DIED OF LONELINESS
                        org%(x%, y%) = 0
                        died% = died% + 1
                        'TRY ADJUSTING AT END OF GENERATION: popu% = popu% - 1
                        gone& = gone& + 1
                    ElseIf family% = 2 Or family% = 3 Then
                        ' AGING!
                        ' 1 = newborn, 5 = maximum age
                        org%(x%, y%) = org%(x%, y%) + 1

                        If org%(x%, y%) > 5 Then
                            ' DIED OF OLD AGE
                            died% = died% + 1
                            'TRY ADJUSTING AT END OF GENERATION: popu% = popu% - 1
                            gone& = gone& + 1
                        End If
                    End If
                End If

                ' REFRESH SCREEN
                _Display

            Next x%
        Next y%

        ' ADJUST POPULATION <- this number gets out of control, not sure why
        popu% = popu% + born%
        popu% = popu% - died%

        ' PROCESS KEYBOARD INPUT
        While _DeviceInput(1): Wend ' clear and update the keyboard buffer
        If _Button(KeyCode_Escape) Then
            ' EXIT
            Exit Do
        ElseIf _Button(KeyCode_Spacebar) Then
            ' PAUSE
            Color cBlackT, cWhiteT
            Locate 1, 1: Print "PAUSED - PRESS ANY KEY TO CONTINUE";
            _Display
            _KeyClear: _Delay 1
            Sleep
            _KeyClear: _Delay 1
            Color cWhiteT, cBlackT
            Locate 1, 1: Print "                                  ";
        ElseIf _Button(KeyCode_Equal) Or _Button(KeyCode_Right) Or _Button(KeyCode_Up) Or _Button(KeyCode_PgUp) Then
            ' INCREASE SPEED
            speed% = speed% + 1
            If speed% > 1000 Then speed% = 1000
        ElseIf _Button(KeyCode_Minus) Or _Button(KeyCode_Left) Or _Button(KeyCode_Down) Or _Button(KeyCode_PgDn) Then
            ' SLOW DOWN
            speed% = speed% - 1
            If speed% < 1 Then speed% = 1
        End If

        ' CLEAR KEYBOARD BUFFER
        _KeyClear

        ' increment generation counter
        gen& = gen& + 1

        ' REFRESH SCREEN
        _Display

        ' LIMIT TO 2 GENERATIONS / SECOND
        _Limit speed%
    Loop

    ' RESTORE NORMAL SCREEN REFRESH
    _AutoDisplay

End Sub ' Life

' /////////////////////////////////////////////////////////////////////////////

Sub DrawCursor (org%(), x%, y%)
    ' If there is an organism there, show it
    If org%(x%, y%) > 0 Then
        Color cGreenT, cWhiteT ' set print color to green on white
        Locate y% + 2, x%
        'Print "O";
        Print GetOrganism$(org%(x%, y%));
    Else
        Color cWhiteT, cWhiteT ' set print color to white on white
        Locate y% + 2, x%
        Print " ";
    End If
End Sub ' DrawCursor

' /////////////////////////////////////////////////////////////////////////////

Sub DrawOrganism (org%(), x%, y%)
    If x% >= LBound(org%, 1) And x% <= UBound(org%, 1) Then
        If y% >= LBound(org%, 2) And y% <= UBound(org%, 2) Then
            Locate y% + 2, x%
            If org%(x%, y%) > 0 Then
                Color cLtGreenT, cBlackT
                'Print "O";
                Print GetOrganism$(org%(x%, y%));
            Else
                Color cLtGreenT, cBlackT
                Print " ";
            End If
        End If
    End If
End Sub ' DrawOrganism

' /////////////////////////////////////////////////////////////////////////////
' Return different picture based on age:
' 1 = [.] = newborn
' 2 = [o] = kid
' 3 = [O] = fully grown
' 4 = [0] = old!
' 5 = [@] = really old!

Function GetOrganism$ (age%)
    Dim org$
    Select Case age%
        Case 1:
            org$ = "."
        Case 2:
            org$ = "o"
        Case 3:
            org$ = "O"
        Case 4:
            org$ = "0"
        Case 5:
            org$ = "@"
        Case Else:
            org$ = ""
    End Select ' SELECT CASE iDirection%
    GetOrganism$ = org$
End Function ' GetOrganism

' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.

' Make sure you put Randomize Timer at the start of your program

Function RandomNumber% (Min%, Max%)
    Dim NumSpread%
    NumSpread% = (Max% - Min%) + 1
    RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%
Reply
#2
popu% needs to be popu&

or maybe even popu&&

remember that in qb64 integer values can wrap around.  not like basica which could report overflow.

in case of zero or negative, if the value must be positive, you should be checking for its value and restrict it.  sadly this means popu variable should be at least a LONG instead of INTEGER.  because making it _UNSIGNED INTEGER would cause it to wrap back up to 65535.  when one expects it to become negative below zero.
Reply
#3
When I start to test this placing organisms, immediately I run into problem of multi-key hits when I mean only to place just 1, often 2 or 3 get placed. This slows down design time for the seed., kinda shoddy.
b = b + ...
Reply
#4
Thanks for your replies! 

On the popu%, being there are 80 rows and 40 columns, and an organism is only born in a blank space, the most there could ever be would be 80x40 =3,200 organisms, which is well under the 32,767 limit of the integer data type. Is that not correct? 

Multiple keypresses would definitely seem shoddy - I didn't have that happen, and even added code to _prevent_ the same key from being held down or pressed twice (that's what the variable LastKey% is for). And it uses _KeyClear to make sure the buffer doesn't fill up with phantom keypresses. In addition, there is a constant cKeyDelay that controls how many frames the program waits before it allows a held down key to repeat. I tried to pay a lot of attention to the keyboard behavior, so to hear it called shoddy by @bplus of all people is somewhat alarming! What am I doing wrong??

Really, an easier interface for placing organisms would be to just use a mouse, but I would like to know what's not right in the keyboard routine, if only because I use that same method in my other programs. 

Thanks again for your feedback guys.
Reply
#5
Quote:Multiple keypresses would definitely seem shoddy - I didn't have that happen, and even added code to _prevent_ the same key from being held down or pressed twice (that's what the variable LastKey% is for). And it uses _KeyClear to make sure the buffer doesn't fill up with phantom keypresses. In addition, there is a constant cKeyDelay that controls how many frames the program waits before it allows a held down key to repeat. I tried to pay a lot of attention to the keyboard behavior, so to hear it called shoddy by @bplus of all people is somewhat alarming! What am I doing wrong??

Oh looks like same attempts at prevention of multi-executions from one key press I would use!

Regrets, I am dealing with other major issues so don't have time to dig in here. I also regret not mentioning this mod of Conway's Life is first rate idea! I cut my Basic teeth on Conway's and mods of it. I am glad @madscijr you seem to take my critcism in good spirit that is most important to me.

If I did have more time first thing I would check are you using _KeyHit or Inkey$ but I did see all those key constants at top of code so probably _KeyHit. Now did you know that with _KeyHit you can detect when key is still down, returns neg that might be helpful?
b = b + ...
Reply
#6
Sure - thanks for the kind words. Actually I don't use Inkey$ or _KeyHit, I use_Button. I think the problem you're seeing is probably in the repeat key logic. I'll give it a look when I get back to the PC for the next round!
Reply
#7
@madscijr

There's always the straightforward Steve approach to getting ONE _Keydown() press or mouse click:

Code: (Select All)
key32 = _KeyDown(32)

If key32 _AndAlso Not oldKey32 Then '

    '[your code here]

End If

oldKey32 = key32
Reply
#8
Any keyboard issues are gone, because now you just use the mouse to place organisms. 
I also added the option to disable aging and just do "classic" Conway's game of Life. 
It's not 100% doing what I expect - for example certain famous patterns like "blinkers" don't repeat like they're supposed to.
I think it's close, just needs some debugging...

Code: (Select All)
' LIFF v1.01
' BY SOFTINTHEHEADWARE

' A VERSION OF LIFE BY JOHN CONWAY
' This version has aging: organisms die after they are 5 generations old.

' New in this version
' * Added support for mouse to place organisms
' * Added option to disable aging (for classic Conway's Game of Life rules)
' * Fixed population count (using "brute force" method for now) but still
'   sometimes the number is off when aging is enabled.
' * Simulation ends when no organisms left.
' * Added displaying statistics at the end of the simulation.
' * Changed variable types for popu, etc. to Long.

' Bugs to fix
' * For some reason when aging is enabled, weird things happen
'   - the population counter doesn't work the way it's supposed to -
'     it drops below zero and gets really high, not sure why.
'   - game is supposed to exit when there are zero organisms left,
'     however there can be none on the screen but the game doesn't exit,
'     and the final statistics show a population > 0 (e.g., 16 phantom organisms)
'
' * Classic patterns like "blinkers" and "gliders"
'   don't repeat or oscillate or behave like they're
'   supposed to, even with aging disabled, for example,
'   these patterns should repeat:
'
'   .......
'   ..OOO..
'   .......
'
'   ...........
'   ....OOO....
'   ...........
'   ..O.....O..
'   ..O.....O..
'   ..O.....O..
'   ...........
'   ....OOO....
'
'   .......
'   ..OOO..
'   ..O....
'   ...O...
'   .......

' Other to do:
' * bigger 32-bit hires screen, more cells
' * show statistics on how many survived to what age, cause of death, etc.
' * save/playback history of a world
' * pause at any generation and edit or step back/forward
' * What other parameters can we introduce?
'   - mating: we tried only letting them mate if surrounded by others age 2-4,
'             but they always seemed to die faster than they were born?
'   - food
'   - different species (animals vs plants, predators vs prey vs parasites)
'   - evolving behavior
'   - reproduction passes on traits / mutations
'   - weather
'   - terrain
'   - excretion of waste / disease
'   - ecosystem

' DECLARE CONSTANTS
Const cMinCount = 1
Const cMaxCount = 529
Const cKeyDelay = 5

' Codes for reading keypresses with _BUTTON
Const KeyCode_Escape = 2
Const KeyCode_Spacebar = 58
Const KeyCode_0 = 12
Const KeyCode_1 = 3
Const KeyCode_2 = 4
Const KeyCode_3 = 5
Const KeyCode_4 = 6
Const KeyCode_5 = 7
Const KeyCode_6 = 8
Const KeyCode_7 = 9
Const KeyCode_8 = 10
Const KeyCode_9 = 11
Const KeyCode_A = 31
Const KeyCode_B = 49
Const KeyCode_C = 47
Const KeyCode_D = 33
Const KeyCode_E = 19
Const KeyCode_F = 34
Const KeyCode_G = 35
Const KeyCode_H = 36
Const KeyCode_I = 24
Const KeyCode_J = 37
Const KeyCode_K = 38
Const KeyCode_L = 39
Const KeyCode_M = 51
Const KeyCode_N = 50
Const KeyCode_O = 25
Const KeyCode_P = 26
Const KeyCode_Q = 17
Const KeyCode_R = 20
Const KeyCode_S = 32
Const KeyCode_T = 21
Const KeyCode_U = 23
Const KeyCode_V = 48
Const KeyCode_W = 18
Const KeyCode_X = 46
Const KeyCode_Y = 22
Const KeyCode_Z = 45
Const KeyCode_Up = 329
Const KeyCode_Down = 337
Const KeyCode_Left = 332
Const KeyCode_Right = 334
Const KeyCode_Minus = 13
Const KeyCode_Equal = 14
Const KeyCode_BkSp = 15
Const KeyCode_Ins = 339
Const KeyCode_Del = 340
Const KeyCode_Home = 328
Const KeyCode_End = 336
Const KeyCode_PgUp = 330
Const KeyCode_PgDn = 338
Const KeyCode_BracketLeft = 27
Const KeyCode_BracketRight = 28
Const KeyCode_CtrlRight = 286
Const KeyCode_Enter = 29

' COLOR CODES FOR PRINTING TEXT
Const cBlackT = 0
Const cBlueT = 1
Const cGreenT = 2
Const cLtBlueT = 3
Const cRedT = 4
Const cPurpleT = 5
Const cOrangeT = 6
Const cWhiteT = 7
Const cGrayT = 8
Const cPeriwinkleT = 9
Const cLtGreenT = 10
Const cCyanT = 11
Const cLtRedT = 12
Const cPinkT = 13
Const cYellowT = 14
Const cLtGrayT = 15

' START THE MAIN ROUTINE
Liff

' FINISHED, EXIT PROGRAM
System

' /////////////////////////////////////////////////////////////////////////////

Sub Liff
    ' DECLARE VARIABLES
    Dim RoutineName As String: RoutineName = "Liff"
    ReDim org%(0, 0)
    Dim popu As Long
    Dim a$
    Dim r$
    Dim iCount As Long
    Dim ax, ay As Integer
    Dim axOld, ayOld As Integer
    Dim MinCol, MaxCol, MinRow, MaxRow As Integer
    Dim MaxOrgs As Long
    Dim k$
    Dim gen As Long
    Dim sLine As String
    Dim CountPos As Integer
    Dim born As Long
    Dim died As Long
    Dim lived As Long
    Dim gone As Long
    Dim family As Long
    Dim North As Integer
    Dim South As Integer
    Dim East As Integer
    Dim West As Integer
    Dim age As Integer
    Dim speed As Long
    Dim bLeftClick As Integer
    Dim bRightClick As Integer
    Dim bOldLeftClick As Integer
    Dim bOldRightClick As Integer
    Dim arrKeyState(0 To 512) As Integer
    Dim bAdded As Integer
    Dim MaxAge As Integer
    Dim reason$
   
    ' MAIN LOOP
    Do
        ' INITIALIZE
        Randomize Timer ' always put this at the start of a program that uses random numbers
        MinRow = 1
        MaxRow = _Height - 2 ' # of rows available (minus 2 rows which we use to show instructions)
        MinCol = 1
        MaxCol = _Width ' # of columns on the screen
        ReDim org%(MinCol To MaxCol, MinRow To MaxRow) ' set size of array
        MaxOrgs = MaxCol * MaxRow ' what's the most # of organisms that can fit on the screen?
        bLeftClick = _FALSE: bOldLeftClick = _FALSE
        bRightClick = _FALSE: bOldRightClick = _FALSE
        For iCount = LBound(arrKeyState) To UBound(arrKeyState)
            arrKeyState(iCount) = _FALSE
        Next iCount
        reason$ = "TBD"
       
        ' SHOW TITLE
        Cls , cBlackT ' clear screen and make it black

        Color cLtGreenT, cBlackT ' set print color to light green on black
        Print
        Print "           #   ### ### ###"
        Print "           #    #  #   #"
        Print "           #    #  ##  ###"
        Print "           #    #  #   #"
        Print "           ### ### #   ###"
        Print
        Color cWhiteT, cBlackT ' set print color to white on black

        ' ASK USER TO ENTER PREFERENCES...

        ' PLACE RANDOMLY?
        Do
            Input "Allow organisms to age (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "y" Then MaxAge = 5 Else MaxAge = 1

        ' PLACE RANDOMLY?
        Do
            Input "Place the organisms randomly (y/n)"; r$
            r$ = _Trim$(r$) ' remove extra spaces
            r$ = LCase$(r$) ' force to lowercase
            If r$ <> "y" And r$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until r$ = "y" Or r$ = "n"
       
        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' IF RANDOMLY, THEN HOW MANY?
        If r$ = "y" Then
            Do
                Print "How many organisms (1-" + _ToStr$(MaxOrgs) + ", or 0 for random)"
                Input popu
                If popu >= 1 Or popu <= MaxOrgs Then
                    ' User selected a valid number
                    Exit Do ' stop asking (exit the do loop)
                ElseIf popu = 0 Then
                    ' Select a random number of organisms to make
                    popu = RandomNumber%(1, MaxOrgs)
                Else
                    ' Value is not valid, let the user know and keep asking
                    Print "*** Value out of range. Type a number 1-" + _ToStr$(MaxOrgs) + ". ***"
                End If
            Loop
            Print
            Print "Simulation will proceed with " + _ToStr$(popu) + " organisms."
            Print
        End If

        ' INITIALIZE ORGANISM ARAY
        For ay = MinRow To MaxRow
            For ax = MinCol To MaxCol
                org%(ax, ay) = 0
            Next ax
        Next ay

        ' PLACE ORGANISMS
        Cls
        If r$ = "y" Then
            ' PLACE EACH ORGANISM RANDOMLY...
            For iCount = 1 To popu
                bAdded = PlaceRandomOrganism%(org%(), MaxAge)
            Next iCount
           
        ElseIf r$ = "n" Then
            ' MANUALLY PLACE ORGANISMS...
           
            ' SHOW INSTRUCTIONS
            Color cYellowT, cBlackT ' set print color to yellow on black
            sLine = "Mouse selects location. Left click & right click to add/remove organisms."
            Locate 1, 1: Print sLine;
            sLine = "Press R to add randomly. Escape starts simulation. Population: "
            Locate 2, 1: Print sLine;
            CountPos = Len(sLine) + 1
           
            ' SHOW SCREEN AS A GRID TO HELP PLACEMENT
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    DrawOrganism org%(), ax, ay, GetBgColor%(ax, ay), GetBgColor%(ax, ay), MaxAge
                Next ax
            Next ay
           
            ' RESET COUNT
            popu = 0 ' no organisms placed yet

            ' PLACE CURSOR AT TOP LEFT
            ax = MinCol: axOld = ax ' MaxCol
            ay = MinRow: ayOld = ay ' MaxRow
           
            ' HIDE MOUSE POINTER
            _MouseHide
           
            ' LOOP UNTIL USER HAS FINISH PLACING ORGANISMS
            ' array and mouse coordinates (ax, ay, axOld, ayOld) are 1-based
            ' drawing on screen starting at row 3, so ay+2
            Do
                ' ERASE OLD
                If axOld <> ax Or ayOld <> ay Then
                    'DrawOrganism org%(), axOld, ayOld, cGreenT, cBlackT, MaxAge
                    DrawOrganism org%(), axOld, ayOld, cGreenT, GetBgColor%(axOld, ayOld), MaxAge
                End If
               
                ' Draw cursor at current location
                'DrawOrganism org%(), ax, ay, cBlackT, cGreenT, MaxAge
                DrawOrganism org%(), ax, ay, GetBgColor%(ax, ay), cGreenT, MaxAge
               
                ' RESET OLD
                axOld = ax
                ayOld = ay
               
                ' READ MOUSE
                Do While _MouseInput: Loop
                ax = _MouseX
                ay = _MouseY
                bLeftClick = _MouseButton(1)
                bRightClick = _MouseButton(2)

                ' VERSION FOR HI-RES SCREEN:
                'ax = (_MouseX \ _FontWidth) * _FontWidth
                'ay = (_MouseY \ _FontHeight) * _FontHeight

                ' CHECK BOUNDARIES
                If ax < MinCol Then ax = MinCol Else If ax > MaxCol Then ax = MaxCol
                If ay < MinRow Then ay = MinRow Else If ay > MaxRow Then ay = MaxRow
                'If ax < 1 Then ax = 1 Else If ax > _Width Then ax = _Width
                'If ay < 1 Then ay = 1 Else If ay > _Height Then ay = _Height

                ' DID THEY CLICK?
                If bLeftClick Then
                    If bOldLeftClick = _FALSE Then
                        If org%(ax, ay) = 0 Then popu = popu + 1
                        org%(ax, ay) = org%(ax, ay) + 1
                        If org%(ax, ay) > MaxAge Then org%(ax, ay) = 1
                        bOldLeftClick = _TRUE
                    End If
                Else
                    bOldLeftClick = _FALSE
                End If
                If bRightClick Then
                    If bOldRightClick = _FALSE Then
                        If org%(ax, ay) > 0 Then popu = popu - 1
                        org%(ax, ay) = 0
                        bOldRightClick = _TRUE
                    End If
                Else
                    bOldRightClick = _FALSE
                End If
               
                ' Read keyboard
                While _DeviceInput(1): Wend ' clear and update the keyboard buffer

                ' DON'T ACCEPT ANY MORE INPUT UNTIL THE LAST PRESSED KEY IS RELEASED
                If _Button(KeyCode_R) = _TRUE Then
                    If arrKeyState(KeyCode_R) = _FALSE Then
                        arrKeyState(KeyCode_R) = _TRUE
                       
                        ' TRY TO RANDOMLY PLACE AN ORGANISM
                        If PlaceRandomOrganism(org%(), MaxAge) = _TRUE Then
                            popu = popu + 1
                        Else
                            ' PlaceRandomOrganism returned _FALSE
                            ' so we are full, exit edit mode
                            Exit Do
                        End If
                    End If
                Else
                    arrKeyState(KeyCode_R) = _FALSE
                End If
               
                ' Check which keys were pressed
                If _Button(KeyCode_Escape) = _TRUE Then
                    ' Exit edit mode
                    Exit Do
                End If
               
                ' SHOW POPULATION
                Color cLtGreenT, cBlackT
                Locate 2, CountPos: Print _ToStr$(popu) + "     ";
               
                ' Govern speed of loop to 60 frames per second:
                _Limit 60

            Loop Until _KeyDown(27) ' escape key exit
           
            ' RESTORE MOUSE
            _MouseShow "default": _Delay 0.5
           
            ' CLEAR KEYBOARD BUFFER
            _KeyClear

        End If

        ' THE CYCLE OF LIFE
        speed = 2
        gen = 0
        lived = popu ' total that ever lived
        gone = 0 ' total that have ever died
        Do
            Cls
            gen = gen + 1 ' INCREASE GENERATION

            ' SHOW GENERATION
            Color cYellowT, cBlackT
            Locate 1, 1: Print "Generation: " + _ToStr$(gen);

            ' SHOW TOTAL THAT EVER LIVED
            Color cLtGreenT, cBlackT
            Locate 1, 25: Print "Total ever born: " + _ToStr$(lived);

            ' SHOW TOTAL THAT EVER DIED
            Color cLtGrayT, cBlackT
            Locate 1, 50: Print "Total ever died: " + _ToStr$(gone);

            ' SHOW BIRTHS
            Color cCyanT, cBlackT
            Locate 2, 1: Print "Births: " + _ToStr$(born) + "     ";

            ' SHOW DEATHS
            Color cPurpleT, cBlackT
            Locate 2, 25: Print "Deaths: " + _ToStr$(died) + "     ";

            ' SHOW POPULATION
            Color cLtGreenT, cBlackT
            Locate 2, 50: Print "Population: " + _ToStr$(popu) + "     ";

            ' RESET THIS GENERATION'S STATISTICS
            born = 0
            died = 0

            ' DRAW THE RACE
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    DrawOrganism org%(), ax, ay, cGreenT, cBlackT, MaxAge
                Next ax
            Next ay

            ' BORN, LIVE, DIE
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol

                    ' COUNT NEIGHBORS
                    family = 0

                    ' GET NEIGHBORS' POSITIONS
                    ' WRAP AROUND THE EDGES (THE WORLD IS ROUND!)
                    If ax = MinCol Then
                        West = MaxCol
                        East = ax + 1
                    ElseIf ax = MaxCol Then
                        West = ax - 1
                        East = MinCol
                    Else
                        West = ax - 1
                        East = ax + 1
                    End If
                    If ay = MinRow Then
                        North = MaxRow
                        South = ay + 1
                    ElseIf ay = MaxRow Then
                        North = ay - 1
                        South = MinRow
                    Else
                        North = ay - 1
                        South = ay + 1
                    End If

                    ' Check NW neighbor
                    If org%(West, North) > 0 Then family = family + 1

                    ' Check N neighbor
                    If org%(ax, North) > 0 Then family = family + 1

                    ' Check NE neighbor
                    If org%(East, North) > 0 Then family = family + 1

                    ' Check W neighbor
                    If org%(West, ay) > 0 Then family = family + 1

                    ' Check E neighbor
                    If org%(East, ay) > 0 Then family = family + 1

                    ' Check SW neighbor
                    If org%(West, South) > 0 Then family = family + 1

                    ' Check S neighbor
                    If org%(ax, South) > 0 Then family = family + 1

                    ' Check SE neighbor
                    If org%(East, South) > 0 Then family = family + 1

                    ' SEE WHO LIVES / DIES / IS BORN
                    If org%(ax, ay) = 0 Then
                        ' REPRODUCTION?
                        If family = 3 Then
                            ' BIRTH
                            org%(ax, ay) = 1
                            popu = popu + 1 ' ** POPULATION INCREASES BY 1 **
                            born = born + 1 ' INCREASE COUNT BORN THIS GENERATION
                            lived = lived + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER BORN
                        End If
                    ElseIf org%(ax, ay) > 0 Then
                        ' LIVE OR DIE?
                        If family < 2 Then
                            ' DIED OF LONELINESS
                            org%(ax, ay) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family > 3 Then
                            ' DIED OF OVERCROWDING
                            org%(ax, ay) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family = 2 Or family = 3 Then
                            ' LIFE GOES ON
                            If MaxAge > 1 Then
                                ' AGING! 1 = newborn, 5 = maximum age
                                org%(ax, ay) = org%(ax, ay) + 1

                                ' DIED OF OLD AGE
                                If org%(ax, ay) > 5 Then
                                    ' DIED OF OLD AGE
                                    popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                                    died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                                    gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                                End If
                            End If
                        End If
                    End If
                   
                    ' REFRESH SCREEN
                    _Display

                Next ax
            Next ay
           
            ' RE-COUNT POPULATION (BRUTE FORCE METHOD, SINCE NORMAL METHODS DON'T SEEM TO WORK WHEN AGING IS ENABLED)
            popu = 0
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    If org%(ax, ay) > 0 Then
                        popu = popu + 1
                    End If
                Next ax
            Next ay
           
            ' If population falls to zero then exit
            If popu < 1 Then reason$ = "no organisms survived": Exit Do
           
            ' PROCESS KEYBOARD INPUT
            While _DeviceInput(1): Wend ' clear and update the keyboard buffer
            If _Button(KeyCode_Escape) Then
                ' ESCAPE = EXIT
                reason$ = "cancelled": Exit Do
               
            ElseIf _Button(KeyCode_Spacebar) Then
                ' SPACEBAR = PAUSE
                Color cBlackT, cWhiteT
                Locate 1, 1: Print "PAUSED - PRESS ANY KEY TO CONTINUE";
                _Display
                _KeyClear: _Delay 1
                Sleep
                _KeyClear: _Delay 1
                Color cWhiteT, cBlackT
                Locate 1, 1: Print "                                  ";
            ElseIf _Button(KeyCode_Equal) Or _Button(KeyCode_Right) Or _Button(KeyCode_Up) Or _Button(KeyCode_PgUp) Then
                ' + OR RIGHT ARROW OR UP ARROW OR PAGE UP = INCREASE SPEED
                speed = speed + 1
                If speed > 1000 Then speed = 1000
            ElseIf _Button(KeyCode_Minus) Or _Button(KeyCode_Left) Or _Button(KeyCode_Down) Or _Button(KeyCode_PgDn) Then
                ' - OR LEFT ARROW OR DOWN ARROW OR PAGE DOWN = SLOW DOWN
                speed = speed - 1
                If speed < 1 Then speed = 1
            End If

            ' CLEAR KEYBOARD BUFFER
            _KeyClear

            ' increment generation counter
            gen = gen + 1

            ' REFRESH SCREEN
            _Display

            ' LIMIT TO 2 GENERATIONS / SECOND
            _Limit speed
        Loop

        ' RESTORE NORMAL SCREEN REFRESH
        _AutoDisplay

        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' PLAY AGAIN?
        Cls
        Color cWhiteT, cBlackT
        Print "SIMULATION OVER (" + reason$ + ")."
        Print

        ' SHOW FINAL STATS
        Print "Final statistics:"
        Color cYellowT, cBlackT: Print "Generation            : " + _ToStr$(gen)
        Color cLtGreenT, cBlackT: Print "Total ever born       : " + _ToStr$(lived)
        Color cLtGrayT, cBlackT: Print "Total ever died       : " + _ToStr$(gone)
        Color cCyanT, cBlackT: Print "Last generation births: " + _ToStr$(born)
        Color cPurpleT, cBlackT: Print "Last generation Deaths: " + _ToStr$(died)
        Color cLtGreenT, cBlackT: Print "Final population      : " + _ToStr$(popu)
        Print

        Color cWhiteT, cBlackT
        Do
            Input "Start over and try again (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "n" Then Exit Do
       
    Loop

End Sub ' Life

' /////////////////////////////////////////////////////////////////////////////

Function GetBgColor% (ax%, ay%)
    If IsOdd%(ay%) Then
        If IsOdd%(ax%) Then
            GetBgColor% = cBlackT
        Else
            GetBgColor% = cGrayT
        End If
    Else
        If IsOdd%(ax%) Then
            GetBgColor% = cGrayT
        Else
            GetBgColor% = cBlackT
        End If
    End If
End Function ' GetBgColor%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if an organism was placed
' only should return _FALSE if no more empty spaces

Function PlaceRandomOrganism% (org%(), MaxAge%)
    Dim bResult As Integer
    Dim MinCol, MaxCol As Integer
    Dim MinRow, MaxRow As Integer
    Dim age As Integer
    Dim ax, ay As Integer
    Dim dx, dy As Integer
    Dim iCount As Integer
    Dim iMax As Integer
   
    ' Initialize
    MinCol = LBound(org%, 1)
    MaxCol = UBound(org%, 1)
    MinRow = LBound(org%, 2)
    MaxRow = UBound(org%, 2)
   
    ' Calculate maximum possible count
    dx = 1 - MinCol
    dy = 1 - MinRow
    iMax = (MaxCol + dx) * (MaxRow + dy)
   
    ' Count # currently present
    iCount = 0
    For ax = MinCol To MaxCol
        For ay = MinRow To MaxRow
            If org%(ax, ay) > 0 Then iCount = iCount + 1
        Next ay
    Next ax
   
    ' If room for more, place one randomly
    bResult = (iCount < iMax)
    If bResult = _TRUE Then
        ' KEEP LOOKING FOR A RANDOM UNOCCUPIED LOCATION
        Do
            ax = RandomNumber%(MinCol, MaxCol) ' SELECT A RANDOM COLUMN
            ay = RandomNumber%(MinRow, MaxRow) ' SELECT A RANDOM ROW
           
            ' MAKE SURE IT'S NOT ALREADY OCCUPIED
            If org%(ax, ay) = 0 Then
                age = RandomNumber%(1, MaxAge%)
                org%(ax, ay) = age
                DrawOrganism org%(), ax, ay, cGreenT, cBlackT, MaxAge%
                Exit Do ' break out of loop and move on to the next organism
            End If
        Loop
    End If
   
    ' Return result
    PlaceRandomOrganism = bResult
End Function ' PlaceRandomOrganism

' /////////////////////////////////////////////////////////////////////////////

Sub DrawOrganism (org%(), ax%, ay%, fgColor%, bgColor%, MaxAge%)
    Color fgColor%, bgColor%
    Locate ay% + 2, ax%
    Print GetOrganism$(org%(ax%, ay%), MaxAge%);
End Sub ' DrawOrganism

' /////////////////////////////////////////////////////////////////////////////
' Return different picture based on age:
' 1 = [.] = newborn
' 2 = [o] = kid
' 3 = [O] = fully grown
' 4 = [0] = old!
' 5 = [@] = really old!

Function GetOrganism$ (age%, MaxAge%)
    Dim org$
    If MaxAge% > 1 Then
        Select Case age%
            Case 1:
                org$ = "."
            Case 2:
                org$ = "o"
            Case 3:
                org$ = "O"
            Case 4:
                org$ = "0"
            Case 5:
                org$ = "@"
            Case Else:
                org$ = " "
        End Select
    Else
        If age% > 0 Then
            'org$ = "O"
            org$ = Chr$(177)
        Else
            org$ = " "
        End If
    End If
    GetOrganism$ = org$
End Function ' GetOrganism

' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.

' Make sure you put Randomize Timer at the start of your program

Function RandomNumber% (Min%, Max%)
    Dim NumSpread%
    NumSpread% = (Max% - Min%) + 1
    RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%

' /////////////////////////////////////////////////////////////////////////////
' Use to pretty print _TRUE and _FALSE values.

Function TrueFalse$ (myValue%)
    TrueFalse$ = _IIf(myValue%, "_TRUE", "_FALSE")
End Function ' TrueFalse$

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is even
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsEven% (n)
    If n Mod 2 = 0 Then
        IsEven% = _TRUE
    Else
        IsEven% = _FALSE
    End If
End Function ' IsEven%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is odd
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsOdd% (n)
    If n Mod 2 = 1 Then
        IsOdd% = _TRUE
    Else
        IsOdd% = _FALSE
    End If
End Function ' IsOdd%
Reply
#9
I was thinking about how the program updates and may have figured out why the strange behavior - the updates are saved to each cell as the program scans row by row, column by column so the current generation is being overwritten as it is scanned. To do it properly, the next generation needs to be written to a different place, so the updates are based on the generation as it appears at the start of the round. We'll give that a try during the next QB64PE session and see how it goes...
Reply
#10
Yep, it worked. 

Here is the latest version which finally works the way it should! 

There are still some nifty features I would like to add - see "TO DO" comments toward top of code.

Also, there are plenty of interesting things we could try - see "BEYOND SIMPLE CONWAY" comments.

Enjoy

Code: (Select All)
' LIFF v1.02
' BY SOFTINTHEHEADWARE

' A VERSION OF JOHN CONWAY'S GAME OF LIFE
' for more info see https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

' -----------------------------------------------------------------------------
' New in this version
' -----------------------------------------------------------------------------
' * Fixed logic issues, program now works as expected!
' * Fixed population count

' -----------------------------------------------------------------------------
' Instructions
' -----------------------------------------------------------------------------
' 1. Choose whether organisms will be allowed to age.
'    Select "n" for classic Conway's Game of Life where organisms don't age,
'    or "y" for aging, where organisms are born and grow with each generation,
'    and then die of old age after 5 generations.
' 2. Choose whether organisms are placed randomly on the virtual planet.
' 3. If you selected random, enter # of organisms to generate (or 0 to pick a random number).
'    The specified number of organisms are added to the virtual planet.
' 4. If you selected NOT random, an editor opens where you place each organism manually.
'    a. Use mouse to position the cursor.
'    b. Left click to add an organism at the cursor position.
'       If aging is enabled, click again to make the organism older.
'    c. Right click to remove organism at the cursor position.
'    d. Press "r" to randomly add a new organism.
'    e. When finished adding organisms, press Escape to begin the simulation.
' 5. The simulation proceeds according to the rules of Conway's Game of Life
'    * If a cell / space is empty, and surrounded by 2 or 3 organisms,
'      a new organism is born on that space.
'    * If a cell is occupied by an organism
'      - If the organism has 2 or 3 neighbors, it survives to the next generation.
'      - If the organism only has 1 or no neighbors, it dies of loneliness.
'      - If the organism has 4 or more neighbors, it dies of overcrowding.
'    * Statistics are displayed at the top:
'      - current generation #
'      - grand total of all organisms ever born / died
'      - running total of organisms born & died in the current generation
'      - current population
'    * The simulation proceeds at 2 generations per second
'      - To speed up  the simulation, press up or right or + or page up
'      - To slow down the simulation, press down or left or - or page down
'    * Press the spacebar to pause the simulation, press it again to continue.
'    * Press Escape to exit the current simulation.
'    * The simulation exits when no organisms are alive or the user presses Escape.
' 6. The final statistics for the simulation are displayed:
'    - The final generation number reached
'    - Grand total organisms born/died for all generations.
'    - Number of births/deaths in the last generation.
'    - Final population.
' 7. Select whether to play again. If you choose "y", continue back at 1.

' -----------------------------------------------------------------------------
' Patterns
' -----------------------------------------------------------------------------
' Certain patterns are known to remain stable, repeat / oscillate,
' eventually die, or "move" with each generation ("spaceships").
' Many of these have popular names. Here are the main known patterns:

' ....     ....     ....
' .OO. --> .OO. --> .OO. --> "Block" = stable
' .OO.     .OO.     .OO.
' ....     ....     ....

' .OO.
' O..O  <-- "Beehive" = stable
' .OO.

' .OO.
' O..O  <-- "Loaf" = stable
' .O O
' ..O.

' OO.
' O.O <-- "Boat" = stable
' .O.

' .O.
' O.O <-- "Tub" = stable
' .O.

' ...OO
' ....O
' ...O. <-- "Canoe" = stable
' O.O..
' OO...

' Some patterns die quickly:

' ....     ....
' ..O.     ....
' .O.. --> .OO. --> Dies
' .O..     ....
' ....     ....

' .....     .....
' ...O.     .....
' ..O.. --> ..O.. --> Dies
' .O...     .....
' .....     .....

' Some patterns change for several generations and eventually resolve into a stable pattern:

' ......     ......     ......     ......
' .OOO..     ...O..     ..OO..     ..OO..
' .O.... --> ..OO.. --> ..OO.. --> .O..O. --> Stable
' ......     ..O...     ..OO..     ..OO..
' ......     ......     ......     ......
' ......     ......     ......     ......

' .....     .....     .....
' ..O..     .....     ..O..
' ..O.. --> .OOO. --> .O.O. --> Stable
' ..O..     .OOO.     .O.O.
' ..O..     .....     ..O..
' .....     .....     .....

' ......     ......
' ...O..     ..OO..
' .OO... --> .O..O. --> Stable
' ..O...     .O..O.
' ......     ..OO..
' ......     ......

' Some patterns oscillate back and forth:

' .....     .....
' ..O..     .....
' ..O.. --> .OOO. --> "Blinker" oscillates
' ..O..     .....
' .....     .....

' ..O.
' O..O --> "Toad" (phase 2) oscillates
' O..O
' .O..

' OO..
' OO.. --> "Beacon" (phase 2) oscillates
' ..OO
' ..OO

' ..OOO...OOO..
' .............
' O....O.O....O
' O....O.O....O
' O....O.O....O
' ..OOO...OOO.. --> "Pulsar" (phase 3) oscillates
' .............
' ..OOO...OOO..
' O....O.O....O
' O....O.O....O
' O....O.O....O
' .............
' ..OOO...OOO..

' ...OOO...
' ..O...O..
' .O.....O.
' .........
' O.......O --> "Penta-decathalon" (phase 15) oscillates
' O.......O
' .........
' .O.....O.
' ..O...O..
' ...OOO...

' .........
' ...O.O...
' .OOO.OOO.
' O...O...O
' O.O...O.O --> "Cloverleaf" oscillates
' .OO.O.OO.
' O.O...O.O
' O...O...O
' .OOO.OOO.
' ...O.O...

' ...........               ...........               ...........
' ...........               .....O.....               ....OOO....
' ...........               .....O.....               ...........
' ...........               .....O.....               ..O.....O..
' .....O..... --> (TBD) --> ...........               ..O.....O..  --> Repeats / oscillates
' ....OOO....               .OOO...OOO.               ..O.....O..
' ...........               ...........               ...........
' ...........               .....O.....               ....OOO....
' ...........               .....O.....               ...........
' ...........               .....O.....               ...........
' ...........               ...........               ...........

' .........     .........     ....O....
' .........     ...OOO...     ...OOO...
' ..OOOOO..     ..O...O..     ..OOOOO..
' ..OOOOO..     .O.....O.     .OO...OO.
' ..OOOOO.. --> .O.....O. --> OOO...OOO --> oscillates
' ..OOOOO..     .O.....O.     .OO...OO.
' ..OOOOO..     ..O...O..     ..OOOOO..
' .........     ...OOO...     ...OOO...
' .........     .........     ....O....

' ...............     ...............     ...............
' ...............     ...............     ...............
' ...............     .......O.......     ......OOO......
' ......OOO......     ......OOO......     .....O...O.....
' .....O...O.....     .....OOOOO.....     .....O...O.....
' ....O.....O....     ....O.....O....     ...OO.....OO...
' ...O...O...O...     ...OO..O..OO...     ..O....O.....O.
' ...O..O.O..O... --> ..OOO.O.O.OOO.. --> ..O...O.O....O. --> oscillates
' ...O...O...O...     ...OO..O..OO...     ..O....O.....O.
' ....O.....O....     ....O.....O....     ...OO.....OO...
' .....O...O.....     .....OOOOO.....     .....O...O.....
' ......OOO......     ......OOO......     .....O...O.....
' ...............     .......O.......     ......OOO......
' ...............     ...............     ...............
' ...............     ...............     ...............

' ...............     ...............     .......O.......
' .......O.......     ......OOO......     .......O.......
' ......OOO......     ...............     ...............
' .....O.O.O.....     .....O.O.O.....     .....O.O.O.....
' .....O...O.....     .....OO.OO.....     .....OOOOO.....
' ...OO.....OO...     ...OO.....OO...     ...OO.OOO.OO...
' ..O....O....O..     .O..O..O..O..O.     ....OO.O.OO....
' .OOO..O.O..OOO. --> .O.O..O.O..O.O. --> OO.OOOO.OOOO.OO --> oscillates
' ..O....O....O..     .O..O..O..O..O.     ....OO.O.OO....
' ...OO....OO....     ...OO.....OO...     ...OO.OOO.OO...
' .....O...O.....     .....OO.OO.....     .....OOOOO.....
' .....O.O.O.....     .....O.O.O.....     .....O.O.O.....
' ......OOO......     ...............     ...............
' .......O.......     ......OOO......     .......O.......
' ...............     ...............     .......O.......

' ...............     ...............     ...............
' ...............     ...............     .......O.......
' ......O.O......     ......OOO......     ......O.O......
' .....O.O.O.....     ......OOO......     ......O.O......
' ...............     ...............     .......O.......
' ...O.......O...     ...............     ...............
' ..O.........O..     ..OO.......OO..     ..OO.......OO.. --> oscillates
' ...O.......O... --> ..OO.......OO.. --> .O..O.....O..O.
' ..O.........O..     ..OO.......OO..     ..OO.......OO..
' ...O.......O...     ...............     ...............
' ...............     ...............     .......O.......
' .....O.O.O.....     ......OOO......     ......O.O......
' ......O.O......     ......OOO......     ......O.O......
' ...............     ...............     .......O.......
' ...............     ...............     ...............

' Some patterns oscillate in such a way that they "move" around the world
' with each generation. These are called "spaceships":

' ..O
' O.O <-- "Glider" spaceship
' .OO

' .OOOO
' O...O <-- "Lightweight spaceship (LWSS)"
' ....O
' O..O.

' ..O...
' O...O.
' .....O <- "Middleweight spaceship (MWSS)"
' O....O
' .OOOOO

' ..OO...
' O....O.
' ......O <-- "Heavyweight spaceship (HWSS)"
' O.....O
' .OOOOOO

' OOOOO.............
' O....O.......OO...
' O...........OO.OOO
' .O.........OO.OOOO
' ...OO...OO.OO..OO.
' .....O....O..O....
' ......O.O.O.O.....
' .......O.......... <-- "Hammerhead" spaceship
' .......O..........
' ......O.O.O.O.....
' .....O....O..O....
' ...OO...OO.OO..OO.
' .O.........OO.OOOO
' O...........OO.OOO
' O....O.......OO...
' OOOOO.............

' Some patterns continue for several generations but ultimately die:

' ......     ......     ......     ......     ......
' ......     ..OO..     .OO...     ..O...     ......
' .O.... --> ..OO.. --> ...O.. --> ...O.. --> ..OO.. --> Death
' ..OOO.     ...O..     .OO...     ..O...     ......
' ......     ......     ......     ......     ......
' ......     ......     ......     ......     ......


' Other patterns:

' .....
' ..O..
' .O.O. "Tub"
' ..O..
' .....

' .....
' ..O..
' .O.O. "Boat"
' ..OO.
' .....

' ......
' .O.OO. "Snake"
' .OO.O.
' ......

' ......
' ......
' ..OO..
' ..O.O. "Ship"
' ...OO.
' ......

' ......
' .OO...
' .O..O. "Aircraft carrier"
' ...OO.
' ......

' ......
' ..OO..
' .O..O. "Beehive"
' ..OO..
' ......

' ......
' ..O...
' .O.O.. "Barge"
' ..O.O.
' ...O..
' ......

' .......
' .......
' ....OO. "Python"
' .O.O.O.
' .OO....
' .......

' .......
' ..O....
' .O.O... "Long boat"
' ..O..O.
' ....OO.
' .......

' ......
' .OO...
' .O.O.. "Eater", "Fish hook"
' ...O..
' ...OO.
' ......

'   .......
'   ..OOO..
'   .......
'
'   ...........
'   ....OOO....
'   ...........
'   ..O.....O..
'   ..O.....O..
'   ..O.....O..
'   ...........
'   ....OOO....
'
'   .......
'   ..OOO..
'   ..O....
'   ...O...
'   .......

' -----------------------------------------------------------------------------
' TO DO
' -----------------------------------------------------------------------------
' * add macro keys to editor to automatically draw given patterns at the cursor position
' * add option to select how many generations organisms live before dying of old age
' * option detect when the world is stuck in an endlessly repeating pattern (upto n frames) and exit
' * bigger 32-bit hires screen, more cells
' * show statistics on how many survived to what age, cause of death, etc.
' * save/playback history of a world
' * editor ability to save / load a given generation or start pattern
' * pause at any generation and edit or step back/forward
' * add keyboard option back to editor

' -----------------------------------------------------------------------------
' BEYOND SIMPLE CONWAY
' -----------------------------------------------------------------------------
' Future challenge = expand to other types of organisms, environment,
' ecosystem, evolution?
'
' Including parameters for
' * food
' * different species or creature types that make up a food chain
'   - animals vs plants vs microscopic life
'   - predators vs prey
'   - parasites
'   - algae, plankton
'   - plants drop seeds to reproduce, need water, seek light
'   - single cell creatures divide to reproduce
'   - complex animals mate to reproduce
'   - larger animals eat smaller / simpler
' * attributes
'   - fuel / energy
'   - health / "hit points"
'   - strength
'   - speed
'   - eyesight or other senses
'   - (attributes where a high score balances out by making another attribute low)
' * evolving behavior
' * reproduction passes on traits / mutations (simple genetics)
' * weather
' * terrain
' * excretion of waste / disease
' * ecosystem
' * mating behavior / selection / competition
' * track an individual organism's DNA or "heritage"

' DECLARE CONSTANTS
Const cMinCount = 1
Const cMaxCount = 529
Const cKeyDelay = 5

' Codes for reading keypresses with _BUTTON
Const KeyCode_Escape = 2
Const KeyCode_Spacebar = 58
Const KeyCode_0 = 12
Const KeyCode_1 = 3
Const KeyCode_2 = 4
Const KeyCode_3 = 5
Const KeyCode_4 = 6
Const KeyCode_5 = 7
Const KeyCode_6 = 8
Const KeyCode_7 = 9
Const KeyCode_8 = 10
Const KeyCode_9 = 11
Const KeyCode_A = 31
Const KeyCode_B = 49
Const KeyCode_C = 47
Const KeyCode_D = 33
Const KeyCode_E = 19
Const KeyCode_F = 34
Const KeyCode_G = 35
Const KeyCode_H = 36
Const KeyCode_I = 24
Const KeyCode_J = 37
Const KeyCode_K = 38
Const KeyCode_L = 39
Const KeyCode_M = 51
Const KeyCode_N = 50
Const KeyCode_O = 25
Const KeyCode_P = 26
Const KeyCode_Q = 17
Const KeyCode_R = 20
Const KeyCode_S = 32
Const KeyCode_T = 21
Const KeyCode_U = 23
Const KeyCode_V = 48
Const KeyCode_W = 18
Const KeyCode_X = 46
Const KeyCode_Y = 22
Const KeyCode_Z = 45
Const KeyCode_Up = 329
Const KeyCode_Down = 337
Const KeyCode_Left = 332
Const KeyCode_Right = 334
Const KeyCode_Minus = 13
Const KeyCode_Equal = 14
Const KeyCode_BkSp = 15
Const KeyCode_Ins = 339
Const KeyCode_Del = 340
Const KeyCode_Home = 328
Const KeyCode_End = 336
Const KeyCode_PgUp = 330
Const KeyCode_PgDn = 338
Const KeyCode_BracketLeft = 27
Const KeyCode_BracketRight = 28
Const KeyCode_CtrlRight = 286
Const KeyCode_Enter = 29

' COLOR CODES FOR PRINTING TEXT
Const cBlackT = 0
Const cBlueT = 1
Const cGreenT = 2
Const cLtBlueT = 3
Const cRedT = 4
Const cPurpleT = 5
Const cOrangeT = 6
Const cWhiteT = 7
Const cGrayT = 8
Const cPeriwinkleT = 9
Const cLtGreenT = 10
Const cCyanT = 11
Const cLtRedT = 12
Const cPinkT = 13
Const cYellowT = 14
Const cLtGrayT = 15

' START THE MAIN ROUTINE
Liff

' FINISHED, EXIT PROGRAM
System

' /////////////////////////////////////////////////////////////////////////////

Sub Liff
    ' DECLARE VARIABLES
    Dim RoutineName As String: RoutineName = "Liff"
    ReDim org%(0, 0, 0) ' array(column, row, generation)
    Dim popu As Long
    Dim a$
    Dim r$
    Dim iCount As Long
    Dim ax, ay As Integer
    Dim axOld, ayOld As Integer
    Dim MinCol, MaxCol, MinRow, MaxRow As Integer
    Dim MaxOrgs As Long
    Dim k$
    Dim gen As Long
    Dim sLine As String
    Dim CountPos As Integer
    Dim born As Long
    Dim died As Long
    Dim lived As Long
    Dim gone As Long
    Dim family As Long
    Dim North As Integer
    Dim South As Integer
    Dim East As Integer
    Dim West As Integer
    Dim age As Integer
    Dim speed As Long
    Dim bLeftClick As Integer
    Dim bRightClick As Integer
    Dim bOldLeftClick As Integer
    Dim bOldRightClick As Integer
    Dim arrKeyState(0 To 512) As Integer
    Dim bAdded As Integer
    Dim MaxAge As Integer
    Dim reason$
   
    ' MAIN LOOP
    Do
        ' INITIALIZE
        Randomize Timer ' always put this at the start of a program that uses random numbers
        MinRow = 1
        MaxRow = _Height - 2 ' # of rows available (minus 2 rows which we use to show instructions)
        MinCol = 1
        MaxCol = _Width ' # of columns on the screen
        ReDim org%(MinCol To MaxCol, MinRow To MaxRow, 1 To 2) ' set size of array
        MaxOrgs = MaxCol * MaxRow ' what's the most # of organisms that can fit on the screen?
        bLeftClick = _FALSE: bOldLeftClick = _FALSE
        bRightClick = _FALSE: bOldRightClick = _FALSE
        For iCount = LBound(arrKeyState) To UBound(arrKeyState)
            arrKeyState(iCount) = _FALSE
        Next iCount
        reason$ = "TBD"
       
        ' SHOW TITLE
        Cls , cBlackT ' clear screen and make it black

        Color cLtGreenT, cBlackT ' set print color to light green on black
        Print
        Print "           #   ### ### ###"
        Print "           #    #  #   #"
        Print "           #    #  ##  ###"
        Print "           #    #  #   #"
        Print "           ### ### #   ###"
        Print
        Color cWhiteT, cBlackT ' set print color to white on black

        ' ASK USER TO ENTER PREFERENCES...

        ' PLACE RANDOMLY?
        Do
            Input "Allow organisms to age (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "y" Then MaxAge = 5 Else MaxAge = 1

        ' PLACE RANDOMLY?
        Do
            Input "Place the organisms randomly (y/n)"; r$
            r$ = _Trim$(r$) ' remove extra spaces
            r$ = LCase$(r$) ' force to lowercase
            If r$ <> "y" And r$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until r$ = "y" Or r$ = "n"
       
        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' IF RANDOMLY, THEN HOW MANY?
        If r$ = "y" Then
            Do
                Print "How many organisms (1-" + _ToStr$(MaxOrgs) + ", or 0 for random)"
                Input popu
                If popu >= 1 Or popu <= MaxOrgs Then
                    ' User selected a valid number
                    Exit Do ' stop asking (exit the do loop)
                ElseIf popu = 0 Then
                    ' Select a random number of organisms to make
                    popu = RandomNumber%(1, MaxOrgs)
                Else
                    ' Value is not valid, let the user know and keep asking
                    Print "*** Value out of range. Type a number 1-" + _ToStr$(MaxOrgs) + ". ***"
                End If
            Loop
            Print
            Print "Simulation will proceed with " + _ToStr$(popu) + " organisms."
            Print
        End If

        ' INITIALIZE ORGANISM ARAY
        For ay = MinRow To MaxRow
            For ax = MinCol To MaxCol
                org%(ax, ay, 2) = 0 ' 2 is the next generation
            Next ax
        Next ay
       
        ' PLACE ORGANISMS
        Cls
        If r$ = "y" Then
            ' PLACE EACH ORGANISM RANDOMLY...
            For iCount = 1 To popu
                bAdded = PlaceRandomOrganism%(org%(), 2, MaxAge)
            Next iCount
           
        ElseIf r$ = "n" Then
            ' MANUALLY PLACE ORGANISMS...
           
            ' SHOW INSTRUCTIONS
            Color cYellowT, cBlackT ' set print color to yellow on black
            sLine = "Mouse selects location. Left click & right click to add/remove organisms."
            Locate 1, 1: Print sLine;
            sLine = "Press R to add randomly. Escape starts simulation. Population: "
            Locate 2, 1: Print sLine;
            CountPos = Len(sLine) + 1
           
            ' SHOW SCREEN AS A GRID TO HELP PLACEMENT
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    DrawOrganism org%(), ax, ay, 2, GetBgColor%(ax, ay), GetBgColor%(ax, ay), MaxAge
                Next ax
            Next ay
           
            ' RESET COUNT
            popu = 0 ' no organisms placed yet

            ' PLACE CURSOR AT TOP LEFT
            ax = MinCol: axOld = ax ' MaxCol
            ay = MinRow: ayOld = ay ' MaxRow
           
            ' HIDE MOUSE POINTER
            _MouseHide
           
            ' LOOP UNTIL USER HAS FINISH PLACING ORGANISMS
            ' array and mouse coordinates (ax, ay, axOld, ayOld) are 1-based
            ' drawing on screen starting at row 3, so ay+2
            Do
                ' ERASE OLD
                If axOld <> ax Or ayOld <> ay Then
                    DrawOrganism org%(), axOld, ayOld, 2, cGreenT, GetBgColor%(axOld, ayOld), MaxAge
                End If
               
                ' Draw cursor at current location
                DrawOrganism org%(), ax, ay, 2, GetBgColor%(ax, ay), cGreenT, MaxAge
               
                ' RESET OLD
                axOld = ax
                ayOld = ay
               
                ' READ MOUSE
                Do While _MouseInput: Loop
                ax = _MouseX
                ay = _MouseY
                bLeftClick = _MouseButton(1)
                bRightClick = _MouseButton(2)

                ' VERSION FOR HI-RES SCREEN:
                'ax = (_MouseX \ _FontWidth) * _FontWidth
                'ay = (_MouseY \ _FontHeight) * _FontHeight

                ' CHECK BOUNDARIES
                If ax < MinCol Then ax = MinCol Else If ax > MaxCol Then ax = MaxCol
                If ay < MinRow Then ay = MinRow Else If ay > MaxRow Then ay = MaxRow
                'If ax < 1 Then ax = 1 Else If ax > _Width Then ax = _Width
                'If ay < 1 Then ay = 1 Else If ay > _Height Then ay = _Height

                ' DID THEY CLICK?
                If bLeftClick Then
                    If bOldLeftClick = _FALSE Then
                        If org%(ax, ay, 2) = 0 Then popu = popu + 1
                        org%(ax, ay, 2) = org%(ax, ay, 2) + 1
                        If org%(ax, ay, 2) > MaxAge Then org%(ax, ay, 2) = 1
                        bOldLeftClick = _TRUE
                    End If
                Else
                    bOldLeftClick = _FALSE
                End If
                If bRightClick Then
                    If bOldRightClick = _FALSE Then
                        If org%(ax, ay, 2) > 0 Then popu = popu - 1
                        org%(ax, ay, 2) = 0
                        bOldRightClick = _TRUE
                    End If
                Else
                    bOldRightClick = _FALSE
                End If
               
                ' Read keyboard
                While _DeviceInput(1): Wend ' clear and update the keyboard buffer

                ' DON'T ACCEPT ANY MORE INPUT UNTIL THE LAST PRESSED KEY IS RELEASED
                If _Button(KeyCode_R) = _TRUE Then
                    If arrKeyState(KeyCode_R) = _FALSE Then
                        arrKeyState(KeyCode_R) = _TRUE
                       
                        ' TRY TO RANDOMLY PLACE AN ORGANISM
                        If PlaceRandomOrganism(org%(), 2, MaxAge) = _TRUE Then
                            popu = popu + 1
                        Else
                            ' PlaceRandomOrganism returned _FALSE
                            ' so we are full, exit edit mode
                            Exit Do
                        End If
                    End If
                Else
                    arrKeyState(KeyCode_R) = _FALSE
                End If
               
                ' Check which keys were pressed
                If _Button(KeyCode_Escape) = _TRUE Then
                    ' Exit edit mode
                    Exit Do
                End If
               
                ' SHOW POPULATION
                Color cLtGreenT, cBlackT
                Locate 2, CountPos: Print _ToStr$(popu) + "     ";
               
                ' Govern speed of loop to 60 frames per second:
                _Limit 60

            Loop Until _KeyDown(27) ' escape key exit
           
            ' RESTORE MOUSE
            _MouseShow "default": _Delay 0.5
           
            ' CLEAR KEYBOARD BUFFER
            _KeyClear

        End If

        ' THE CYCLE OF LIFE
        speed = 2
        gen = 0
        lived = popu ' total that ever lived
        gone = 0 ' total that have ever died
        Do
            Cls
            gen = gen + 1 ' INCREASE GENERATION
           
            ' SHOW GENERATION
            Color cYellowT, cBlackT
            Locate 1, 1: Print "Generation: " + _ToStr$(gen);

            ' SHOW TOTAL THAT EVER LIVED
            Color cLtGreenT, cBlackT
            Locate 1, 25: Print "Total ever born: " + _ToStr$(lived);

            ' SHOW TOTAL THAT EVER DIED
            Color cLtGrayT, cBlackT
            Locate 1, 50: Print "Total ever died: " + _ToStr$(gone);

            ' SHOW BIRTHS
            Color cCyanT, cBlackT
            Locate 2, 1: Print "Births: " + _ToStr$(born) + "     ";

            ' SHOW DEATHS
            Color cPurpleT, cBlackT
            Locate 2, 25: Print "Deaths: " + _ToStr$(died) + "     ";

            ' SHOW POPULATION
            Color cLtGreenT, cBlackT
            Locate 2, 50: Print "Population: " + _ToStr$(popu) + "     ";

            ' RESET THIS GENERATION'S STATISTICS
            born = 0
            died = 0
           
            ' SETUP FOR NEXT ROUND
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    ' COPY NEXT GENERATION TO CURRENT
                    org%(ax, ay, 1) = org%(ax, ay, 2)
                   
                    ' AND DRAW THE NEXT ORGANISM
                    DrawOrganism org%(), ax, ay, 1, cGreenT, cBlackT, MaxAge
                Next ax
            Next ay
           
            ' BORN, LIVE, DIE
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol

                    ' COUNT NEIGHBORS
                    family = 0

                    ' GET NEIGHBORS' POSITIONS
                    ' WRAP AROUND THE EDGES (THE WORLD IS ROUND!)
                    If ax = MinCol Then
                        West = MaxCol
                        East = ax + 1
                    ElseIf ax = MaxCol Then
                        West = ax - 1
                        East = MinCol
                    Else
                        West = ax - 1
                        East = ax + 1
                    End If
                    If ay = MinRow Then
                        North = MaxRow
                        South = ay + 1
                    ElseIf ay = MaxRow Then
                        North = ay - 1
                        South = MinRow
                    Else
                        North = ay - 1
                        South = ay + 1
                    End If

                    ' Check NW neighbor
                    If org%(West, North, 1) > 0 Then family = family + 1

                    ' Check N neighbor
                    If org%(ax, North, 1) > 0 Then family = family + 1

                    ' Check NE neighbor
                    If org%(East, North, 1) > 0 Then family = family + 1

                    ' Check W neighbor
                    If org%(West, ay, 1) > 0 Then family = family + 1

                    ' Check E neighbor
                    If org%(East, ay, 1) > 0 Then family = family + 1

                    ' Check SW neighbor
                    If org%(West, South, 1) > 0 Then family = family + 1

                    ' Check S neighbor
                    If org%(ax, South, 1) > 0 Then family = family + 1

                    ' Check SE neighbor
                    If org%(East, South, 1) > 0 Then family = family + 1

                    ' SEE WHO LIVES / DIES / IS BORN
                    ' look at current generation (x, y, 1)
                    ' to update next  generation (x, y, 2)
                    If org%(ax, ay, 1) = 0 Then
                        ' REPRODUCTION?
                        If family = 3 Then
                            ' BIRTH
                            org%(ax, ay, 2) = 1
                            popu = popu + 1 ' ** POPULATION INCREASES BY 1 **
                            born = born + 1 ' INCREASE COUNT BORN THIS GENERATION
                            lived = lived + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER BORN
                        End If
                    ElseIf org%(ax, ay, 1) > 0 Then
                        ' LIVE OR DIE?
                        If family < 2 Then
                            ' DIED OF LONELINESS
                            org%(ax, ay, 2) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family > 3 Then
                            ' DIED OF OVERCROWDING
                            org%(ax, ay, 2) = 0
                            popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                            died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                            gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                        ElseIf family = 2 Or family = 3 Then
                            ' LIFE GOES ON
                            If MaxAge > 1 Then
                                ' AGING! 1 = newborn, 5 = maximum age
                                org%(ax, ay, 2) = org%(ax, ay, 2) + 1

                                ' DIED OF OLD AGE
                                If org%(ax, ay, 2) > 5 Then
                                    ' DIED OF OLD AGE
                                    org%(ax, ay, 2) = 0
                                    popu = popu - 1 ' ** POPULATION DECREASES BY 1 **
                                    died = died + 1 ' INCREASE COUNT DIED THIS GENERATION
                                    gone = gone + 1 ' INCREASE COUNT OF ALL ORGANISMS EVER DIED
                                End If
                            End If
                        End If
                    End If
                   
                    ' REFRESH SCREEN
                    _Display

                Next ax
            Next ay
           
            ' RE-COUNT POPULATION (BRUTE FORCE METHOD, SINCE NORMAL METHODS DON'T SEEM TO WORK WHEN AGING IS ENABLED)
            popu = 0
            For ay = MinRow To MaxRow
                For ax = MinCol To MaxCol
                    If org%(ax, ay, 2) > 0 Then
                        popu = popu + 1
                    End If
                Next ax
            Next ay
           
            ' If population falls to zero then exit
            If popu < 1 Then reason$ = "no organisms survived": Exit Do
           
            ' PROCESS KEYBOARD INPUT
            While _DeviceInput(1): Wend ' clear and update the keyboard buffer
            If _Button(KeyCode_Escape) Then
                ' ESCAPE = EXIT
                reason$ = "cancelled": Exit Do
               
            ElseIf _Button(KeyCode_Spacebar) Then
                ' SPACEBAR = PAUSE
                Color cBlackT, cWhiteT
                Locate 1, 1: Print "PAUSED - PRESS ANY KEY TO CONTINUE";
                _Display
                _KeyClear: _Delay 1
                Sleep
                _KeyClear: _Delay 1
                Color cWhiteT, cBlackT
                Locate 1, 1: Print "                                  ";
            ElseIf _Button(KeyCode_Equal) Or _Button(KeyCode_Right) Or _Button(KeyCode_Up) Or _Button(KeyCode_PgUp) Then
                ' + OR RIGHT ARROW OR UP ARROW OR PAGE UP = INCREASE SPEED
                speed = speed + 1
                If speed > 1000 Then speed = 1000
            ElseIf _Button(KeyCode_Minus) Or _Button(KeyCode_Left) Or _Button(KeyCode_Down) Or _Button(KeyCode_PgDn) Then
                ' - OR LEFT ARROW OR DOWN ARROW OR PAGE DOWN = SLOW DOWN
                speed = speed - 1
                If speed < 1 Then speed = 1
            End If

            ' CLEAR KEYBOARD BUFFER
            _KeyClear

            ' increment generation counter
            gen = gen + 1

            ' REFRESH SCREEN
            _Display

            ' LIMIT TO 2 GENERATIONS / SECOND
            _Limit speed
        Loop

        ' RESTORE NORMAL SCREEN REFRESH
        _AutoDisplay

        ' CLEAR KEYBOARD BUFFER
        _KeyClear: _Delay 1

        ' PLAY AGAIN?
        Cls
        Color cWhiteT, cBlackT
        Print "SIMULATION OVER (" + reason$ + ")."
        Print

        ' SHOW FINAL STATS
        Print "Final statistics:"
        Color cYellowT, cBlackT: Print "Generation            : " + _ToStr$(gen)
        Color cLtGreenT, cBlackT: Print "Total ever born       : " + _ToStr$(lived)
        Color cLtGrayT, cBlackT: Print "Total ever died       : " + _ToStr$(gone)
        Color cCyanT, cBlackT: Print "Last generation births: " + _ToStr$(born)
        Color cPurpleT, cBlackT: Print "Last generation Deaths: " + _ToStr$(died)
        Color cLtGreenT, cBlackT: Print "Final population      : " + _ToStr$(popu)
        Print

        Color cWhiteT, cBlackT
        Do
            Input "Start over and try again (y/n)"; a$
            a$ = _Trim$(a$) ' remove extra spaces
            a$ = LCase$(a$) ' force to lowercase
            If a$ <> "y" And a$ <> "n" Then
                Print "*** Please type y or n ***"
            End If
        Loop Until a$ = "y" Or a$ = "n"
        If a$ = "n" Then Exit Do
       
    Loop

End Sub ' Life

' /////////////////////////////////////////////////////////////////////////////

Function GetBgColor% (ax%, ay%)
    If IsOdd%(ay%) Then
        If IsOdd%(ax%) Then
            GetBgColor% = cBlackT
        Else
            GetBgColor% = cGrayT
        End If
    Else
        If IsOdd%(ax%) Then
            GetBgColor% = cGrayT
        Else
            GetBgColor% = cBlackT
        End If
    End If
End Function ' GetBgColor%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if an organism was placed
' only should return _FALSE if no more empty spaces

Function PlaceRandomOrganism% (org%(), gen%, MaxAge%)
    Dim bResult As Integer
    Dim MinCol, MaxCol As Integer
    Dim MinRow, MaxRow As Integer
    Dim age As Integer
    Dim ax, ay As Integer
    Dim dx, dy As Integer
    Dim iCount As Integer
    Dim iMax As Integer

    ' Initialize
    MinCol = LBound(org%, 1)
    MaxCol = UBound(org%, 1)
    MinRow = LBound(org%, 2)
    MaxRow = UBound(org%, 2)

    ' Calculate maximum possible count
    dx = 1 - MinCol
    dy = 1 - MinRow
    iMax = (MaxCol + dx) * (MaxRow + dy)

    ' Count # currently present
    iCount = 0
    For ax = MinCol To MaxCol
        For ay = MinRow To MaxRow
            If org%(ax, ay, gen%) > 0 Then iCount = iCount + 1
        Next ay
    Next ax

    ' If room for more, place one randomly
    bResult = (iCount < iMax)
    If bResult = _TRUE Then
        ' KEEP LOOKING FOR A RANDOM UNOCCUPIED LOCATION
        Do
            ax = RandomNumber%(MinCol, MaxCol) ' SELECT A RANDOM COLUMN
            ay = RandomNumber%(MinRow, MaxRow) ' SELECT A RANDOM ROW

            ' MAKE SURE IT'S NOT ALREADY OCCUPIED
            If org%(ax, ay, gen%) = 0 Then
                age = RandomNumber%(1, MaxAge%)
                org%(ax, ay, gen%) = age
                DrawOrganism org%(), ax, ay, gen%, cGreenT, cBlackT, MaxAge%
                Exit Do ' break out of loop and move on to the next organism
            End If
        Loop
    End If

    ' Return result
    PlaceRandomOrganism = bResult
End Function ' PlaceRandomOrganism

' /////////////////////////////////////////////////////////////////////////////

Sub DrawOrganism (org%(), ax%, ay%, gen%, fgColor%, bgColor%, MaxAge%)
    Color fgColor%, bgColor%
    Locate ay% + 2, ax%
    Print GetOrganism$(org%(ax%, ay%, gen%), MaxAge%);
End Sub ' DrawOrganism

' /////////////////////////////////////////////////////////////////////////////
' Return different picture based on age:
' 1 = [.] = newborn
' 2 = [o] = kid
' 3 = [O] = fully grown
' 4 = [0] = old!
' 5 = [@] = really old!

Function GetOrganism$ (age%, MaxAge%)
    Dim org$
    If MaxAge% > 1 Then
        Select Case age%
            Case 1:
                org$ = "."
            Case 2:
                org$ = "o"
            Case 3:
                org$ = "O"
            Case 4:
                org$ = "0"
            Case 5:
                org$ = "@"
            Case Else:
                org$ = " "
        End Select
    Else
        If age% > 0 Then
            'org$ = "O"
            org$ = Chr$(177)
        Else
            org$ = " "
        End If
    End If
    GetOrganism$ = org$
End Function ' GetOrganism

' /////////////////////////////////////////////////////////////////////////////
' Generate random value between Min and Max inclusive.

' Make sure you put Randomize Timer at the start of your program

Function RandomNumber% (Min%, Max%)
    Dim NumSpread%
    NumSpread% = (Max% - Min%) + 1
    RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%

' /////////////////////////////////////////////////////////////////////////////
' Use to pretty print _TRUE and _FALSE values.

Function TrueFalse$ (myValue%)
    TrueFalse$ = _IIf(myValue%, "_TRUE", "_FALSE")
End Function ' TrueFalse$

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is even
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsEven% (n)
    If n Mod 2 = 0 Then
        IsEven% = _TRUE
    Else
        IsEven% = _FALSE
    End If
End Function ' IsEven%

' /////////////////////////////////////////////////////////////////////////////
' Returns _TRUE if number n is odd
' https://slaystudy.com/qbasic-program-to-check-if-number-is-even-or-odd/

Function IsOdd% (n)
    If n Mod 2 = 1 Then
        IsOdd% = _TRUE
    Else
        IsOdd% = _FALSE
    End If
End Function ' IsOdd%
Reply




Users browsing this thread: 1 Guest(s)