WIP: Conway's Story Mode (CSM)

For general discussion about Conway's Game of Life.
User avatar
squareroot12621
Posts: 682
Joined: March 23rd, 2022, 4:53 pm

Re: WIP: Conway's Story Mode (CSM)

Post by squareroot12621 » July 11th, 2024, 9:15 pm

More concept art, now that it's been, what, a year?

Code: Select all

x = 192, y = 108, rule = websafe
192rH$2rHwWrHwW2rHwWrHwW2rH2wWrH2wW3rHwW3rH2wWrH2wW163rH$rH5wWrHwWrHwW
rHwW5rHwWrHwWrHwWrHwW5rHwW162rH$2rHwWrHwW2rH3wWrHwW4rHwW2rHwWrHwWrH2wW
2rH2wW163rH$rH5wW3rHwWrHwW3rHwW3rHwWrHwWrHwWrHwW3rHwW162rH$2rHwWrHwW4rH
wW2rH2wWrH3wW2rHwW3rHwW2rH2wW163rH$192rH$192rH$45rHwWrHwW2rH3wW2rHwW2rH
2wW3rHwW2rH3wW2rHwW120rH$44rH5wWrHwW3rHwWrHwWrHwWrHwWrHwWrHwWrHwW3rHwW
rHwW119rH$45rHwWrHwW2rH3wW2rH2wWrHwWrHwW2rHwW2rH3wWrHwWrHwW119rH$44rH
5wWrHwW5rHwWrHwWrHwWrHwWrHwWrHwW3rHwWrHwW119rH$45rHwWrHwW2rH3wWrH2wW2rH
2wW3rHwW2rHwW4rHwW120rH$192rH$45rH3uV32rHuV10rH3uV31rH2uV4rHuV5rHuV3rH
2uV49rH$44rHuV3rHuV31rHuV9rHuV3rHuV29rHuV6rHuV8rHuV51rH$44rHuV6rH3uV2rH
uVrH2uV2rHuV3rHuVrH3uV3rHuV3rHuV3rH4uV4rHuV5rH3uV3rH2uVrHuV3rH3uV6rH3uV
3rHuV6rHuV5rHuV2rHuV3rH3uV45rH$44rHuV5rHuV3rHuVrH2uV2rHuVrHuVrHuVrHuV
4rHuV2rHuV3rHuV2rHuV8rHuV2rH2uV4rHuV2rHuVrHuVrHuVrHuV3rHuV4rHuV3rHuVrH
3uV5rHuV5rHuVrH3uVrHuV3rHuV44rH$44rHuV5rHuV3rHuVrHuV3rHuVrHuVrHuVrHuV
2rH3uV2rHuV3rHuV3rH3uV5rHuV3rHuV2rH3uV2rHuVrHuVrHuVrH4uV5rHuV3rHuV2rH
uV6rHuV5rHuV2rHuV2rH4uV45rH$44rHuV3rHuVrHuV3rHuVrHuV3rHuVrHuVrHuVrHuV
rHuV2rHuV2rHuV3rHuV6rHuV4rHuV3rHuVrHuV2rHuV2rHuVrHuVrHuVrHuV8rHuV3rHuV
2rHuV6rHuV5rHuV2rHuV2rHuV48rH$45rH3uV3rH3uV2rHuV3rHuV2rHuVrHuV3rH2uVrH
uV2rH4uV2rH4uV6rH3uV3rH2uVrHuVrHuV3rHuV2rH4uV5rH3uV3rHuV6rH5uVrHuV2rH
uV3rH4uV44rH$78rHuV113rH$74rHuV3rHuV113rH$75rH3uV114rH$192rH$192rH$192rH
$192rH$86rHuV7rHuV9rHuV6rHuV16rH2wWrHwW2rHwW7rHwW2rHwW5rH2wW39rH$85rH
3uV5rH3uV15rHuV16rHwW4rH3wW6rHwW9rHwW39rH$67rH10uV3rH2uV4rHuV2rHuVrHuV
2rHuV3rHuV2rH2uVrHuV2rH2uV2rHuV3rH10uV3rHwW2rHwW2rHwW3rH2wW2rHwW2rHwW
2rH2wW2rHwW39rH$79rHuVrHuV4rHuV2rHuVrHuV2rHuV2rHuVrHuVrHuV2rHuVrHuVrH
uV2rHuV16rHwW2rHwW2rHwW2rHwWrHwW2rHwW2rHwWrHwW4rHwW39rH$80rHuVrHuV4rH
uV2rHuVrHuV2rHuV2rHuV2rHuV2rHuV2rHuVrHuV2rHuV15rH2wWrHwW3rHwW2rHwWrHwW
2rHwWrHwW2rH2wWrH2wW39rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH
$192rH$72rHwWrHwW2rH2wW3rH2wW2rHwW3rHwW2rH2wW2rH2wW6rHwWrHwW2rH2wW3rH
wW2rH2wW2rH2wW2rH2wW2rH3wW59rH$71rH5wW3rHwWrHwW3rH2wW2rHwWrHwW3rHwW3rH
wW4rH5wWrHwWrHwWrH2wW2rHwWrHwW3rHwWrHwWrHwWrHwW61rH$72rHwWrHwW3rHwW2rH
2wW3rHwW2rHwWrHwWrH2wW3rHwW6rHwWrHwW2rHwWrHwW2rHwW2rH2wW3rHwW2rHwWrHwW
rH3wW59rH$71rH5wWrHwW3rHwWrHwW2rHwW2rHwWrHwW3rHwWrHwW6rH5wWrHwWrHwW2rH
wW2rHwWrHwWrHwW3rHwWrHwWrHwW61rH$72rHwWrHwW2rH3wW2rHwW2rH3wW2rHwW2rH2wW
2rH3wWrHwW3rHwWrHwW2rH2wW2rH3wWrH2wW2rH3wWrH2wW2rH3wW59rH$101rHwW90rH
$192rH$71rH50uV71rH$71rHuV48pMuV71rH$71rHuV48pMuV71rH$71rHuV3pMuV44pM
uV71rH$71rHuV3pMuV44pMuV71rH$71rHuV3pMuV6pM3uV3pM4uV2pM4uV2pM3uV2pMuV
pM2uV3pM4uV4pMuV71rH$71rHuV3pMuV5pMuV3pMuVpMuV5pMuV5pMuV3pMuVpM2uV2pM
uVpMuV8pMuV71rH$71rHuV3pMuV5pM5uV2pM3uV3pM3uV2pMuV3pMuVpMuV3pMuV2pM3uV
5pMuV71rH$71rHuV3pMuV5pMuV9pMuV5pMuVpMuV3pMuVpMuV3pMuV5pMuV4pMuV71rH$
71rHuV3pM5uV2pM4uVpM4uV2pM4uV3pM3uV2pMuV3pMuVpM4uV5pMuV71rH$71rHuV48pM
uV71rH$71rHuV48pMuV71rH$71rH50uV71rH$192rH$192rH$192rH$192rH$71rH50uV
71rH$71rHuV48pMuV71rH$71rHuV48pMuV71rH$71rHuV4pM3uV18pMuVpMuV20pMuV71rH
$71rHuV3pMuV3pMuV17pMuVpMuV20pMuV71rH$71rHuV3pMuV5pM3uV3pMuVpM2uV3pM2uV
pMuVpMuVpM2uV3pM3uV2pMuV3pMuV4pMuV71rH$71rHuV4pM3uV5pMuV2pM2uV2pMuVpM
uV2pM2uVpM2uV2pMuVpMuV3pMuV2pMuVpMuV5pMuV71rH$71rHuV7pMuV2pM3uV2pMuV3pM
uVpMuV3pMuVpMuV3pMuVpMuV3pMuV3pMuV6pMuV71rH$71rHuV3pMuV3pMuVpMuV2pMuV
2pMuV3pMuVpMuV3pMuVpMuV3pMuVpMuV3pMuV2pMuVpMuV5pMuV71rH$71rHuV4pM3uV3pM
2uVpMuVpMuV3pMuV2pM4uVpM4uV3pM3uV2pMuV3pMuV4pMuV71rH$71rHuV48pMuV71rH
$71rHuV48pMuV71rH$71rH50uV71rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH
$192rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH$192rH$163rHwWrH
wW2rH2wW3rHwW2rH2wW2rH2wW2rH2wW2rH3wWrH$162rH5wWrHwWrHwWrH2wW2rHwWrHwW
3rHwWrHwWrHwWrHwW3rH$163rHwWrHwW2rHwWrHwW2rHwW2rH2wW3rHwW2rHwWrHwWrH3wW
rH$162rH5wWrHwWrHwW2rHwW2rHwWrHwWrHwW3rHwWrHwWrHwW3rH$163rHwWrHwW2rH2wW
2rH3wWrH2wW2rH3wWrH2wW2rH3wWrH$192rH$183rHuV5rHuV2rH$182rH2uV4rHuVrHuV
rH$178rHuVrHuV2rHuV4rHuVrHuVrH$178rHuVrHuV2rHuV4rHuVrHuVrH$179rHuV2rH
3uVrHuV2rHuV2rH$192rH! [[ ZOOM 3 QUALITY ]]

User avatar
squareroot12621
Posts: 682
Joined: March 23rd, 2022, 4:53 pm

Re: WIP: Conway's Story Mode (CSM)

Post by squareroot12621 » July 13th, 2024, 7:51 pm

(Sorry for double-posting. I've been working on this a lot lately.)
Here's some actual Python code (tested on 3.12.2 and IDLE) for Conway's Game of Life—a tutorial. The buttons don't work yet, though:

Code: Select all

# Conway's Game of Life--a tutorial
# Made by squareroot12621.
import random, time, turtle

turtle.setup(1280, 720)
window = turtle.Screen()
canvas = window.getcanvas()
window.title("Conway's Game of Life\u2014a tutorial")
turtle.tracer(0)

t = turtle.Turtle() # Draws objects that are refreshed every frame
f = turtle.Turtle() # Draws objects that are refreshed when mode changes

mode = 'loading'
lastMode = None # Must be different from 'loading'
                # so that draw_loading_first() triggers

lastLoadingFrame = -1

def read_and_parse_lesson_file():
    '''Yield loading messages until file 'CGOLATUTORIAL-lessons.txt' is
    read and parsed.
    '''
    global mode
    # I removed the part of the code that opens a file and parses it,
    # so that you don't have to make the file yourself.
    yield 'Loading message #1...'
    yield 'Loading message #2...'
    yield 'Loading message #3...'
        
    
def draw_loading_first():
    '''Draws the part of the loading screen that's only drawn once.'''
    global lessonParser
    lessonParser = read_and_parse_lesson_file() # Makes loading messages
    window.bgcolor('#261032')
    # Write the "Loading..." text
    f.goto(0, 0)
    f.color('#FFFFFF')
    f.write('Loading...', align='center', font=('Arial', 28, 'normal'))
    
def draw_loading():
    '''Draws the part of the loading screen that's drawn every frame.'''
    global lastLoadingFrame, lessonParser, mode

    # Draw the p30 glider stream
    t.color('#D1B2DE')
    loadingFrame = int(time.time() * 18 % 30) # 18 FPS, 30 frames
    if loadingFrame != lastLoadingFrame:
        lastLoadingFrame = loadingFrame
        gliderPhases = [[(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)],
                        [(0, 1), (2, 1), (1, 2), (2, 2), (1, 3)],
                        [(2, 1), (0, 2), (2, 2), (1, 3), (2, 3)],
                        [(1, 1), (2, 2), (3, 2), (1, 3), (2, 3)]]
        # Generate the cells
        cellsOn = []
        for glider in range(20):
            for cell in gliderPhases[(loadingFrame + 30 * glider) % 4]:
                cellsOn.append([dimension + (loadingFrame + 30 * glider) // 4
                                for dimension in cell])
        # Draw the cells and delete the old ones
        canvas.delete('loading')
        for cell in cellsOn:
            leftX = 6*cell[0] - 220
            topY = 6*cell[1] - 396
            canvas.create_rectangle(leftX, topY, leftX + 5, topY - 5,
                                    fill='#FFFFFF', width=0, tag='loading')

    # Write loading messages
    t.color('#D1B2DE')
    t.goto(0, -30)
    try:
        loadingMessage = next(lessonParser)
    except StopIteration:
        loadingMessage = 'Done!'
        mode = 'main'
    t.write(loadingMessage, align='center', font=('Arial', 18, 'normal'))

def draw_main_first():
    '''Draws the part of the main menu that's only drawn once.'''
    window.bgcolor('#4C2063')
    canvas.delete('loading')

    # Conway's Game of Life
    f.color('#E9D8F0')
    f.goto(0, 250)
    f.write("Conway's Game of Life", align='center',
            font=('Arial', 40, 'normal'))
    # --- the tutorial ---
    f.color('#B78FCC')
    f.goto(0, 210)
    f.write('\u2014\u2014\u2014 the tutorial \u2014\u2014\u2014',
            align='center', font=('Arial', 24, 'italic'))
    # Buttons
    f.pensize(3)
    for x, text, top, bottom in [(-420, 'Play',
                                  "Learn Conway's Game of",
                                  'Life with 5-minute lessons'),
                                 (-140, 'Sandbox',
                                  "Play with Conway's Game",
                                  'of Life on your own'),
                                 (140, 'Settings',
                                  'Change your language,',
                                  'reset your progress, etc.'),
                                 (420, 'Credits',
                                  'Thanks to everyone who',
                                  'has contributed to this!')]:
        # Button outline and fill
        f.goto(x - 120, 100)
        f.pencolor('#D1B2DE')
        f.fillcolor('#261032')
        f.pendown()
        f.begin_fill()
        f.goto(x + 120, 100)
        f.goto(x + 120, -200)
        f.goto(x - 120, -200)
        f.goto(x - 120, 100)
        f.end_fill()
        f.penup()
        # Artwork for each button
        if text == 'Play':
            # "Lessons"
            for rectX, rectY, num in [(-490, -90, '1'), (-450, 0, '2'),
                                      (-350, -10, '3'), (-380, -100, '4')]:
                # Inner border and fill
                f.fillcolor('#9559B3')
                f.pencolor('#261032')
                f.goto(rectX - 27, rectY + 27)
                f.pendown()
                f.begin_fill()
                f.goto(rectX + 27, rectY + 27)
                f.goto(rectX + 27, rectY - 27)
                f.goto(rectX - 27, rectY - 27)
                f.goto(rectX - 27, rectY + 27)
                f.end_fill()
                f.penup()
                # Outer border
                f.pencolor('#D1B2DE')
                f.goto(rectX - 30, rectY + 30)
                f.pendown()
                f.goto(rectX + 30, rectY + 30)
                f.goto(rectX + 30, rectY - 30)
                f.goto(rectX - 30, rectY - 30)
                f.goto(rectX - 30, rectY + 30)
                f.penup()
                # Text
                f.pencolor('#261032')
                for deltaX in range(2):
                    for deltaY in range(-22, -20):
                        f.goto(rectX + deltaX, rectY + deltaY)
                        f.write(num, align='center', font=('Arial', 28, 'normal'))
            # Lines between "lessons"
            f.pencolor('#D1B2DE')
            for x1, y1, x2, y2 in [(-540, -85, -520, -90),
                                   (-480, -60, -460, -30),
                                   (-420, -2, -380, -8),
                                   (-355, -40, -375, -70),
                                   (-350, -100, -300, -90)]:
                f.goto(x1, y1)
                f.pendown()
                f.goto(x2, y2)
                f.penup()
        elif text == 'Sandbox':
            # Sandbox
            f.goto(-220, 10)
            f.pendown()
            f.goto(-60, 10)
            f.goto(-40, -100)
            f.goto(-240, -100)
            f.goto(-220, 10)
            f.penup()
            f.goto(-40, -100)
            f.pendown()
            f.goto(-40, -120)
            f.goto(-240, -120)
            f.goto(-240, -100)
            f.goto(-40, -100)
            f.penup()
            # Boat
            f.fillcolor('#9559B3')
            f.goto(-100, 0)
            f.begin_fill()
            f.goto(-97, -24)
            f.goto(-107, -24)
            f.goto(-108, -16)
            f.goto(-88, -16)
            f.goto(-89, -8)
            f.goto(-109, -8)
            f.goto(-108, -16)
            f.goto(-118, -16)
            f.goto(-120, 0)
            f.goto(-100, 0)
            f.end_fill()
            # Beacon
            f.goto(-150, -50)
            f.begin_fill()
            f.goto(-150, -58)
            f.goto(-160, -58)
            f.goto(-160, -66)
            f.goto(-170, -66)
            f.goto(-170, -50)
            f.goto(-150, -50)
            f.end_fill()
            f.goto(-130, -82)
            f.begin_fill()
            f.goto(-130, -66)
            f.goto(-140, -66)
            f.goto(-140, -74)
            f.goto(-150, -74)
            f.goto(-150, -82)
            f.goto(-130, -82)
            f.end_fill()
            # Block
            f.goto(-195, -20)
            f.begin_fill()
            f.goto(-193, -4)
            f.goto(-173, -4)
            f.goto(-175, -20)
            f.goto(-195, -20)
            f.end_fill()
        elif text == 'Settings':
            f.goto(140, -80)
            f.pensize(20)
            f.pendown()
            f.circle(30)
            f.penup()
            f.pensize(10)
            f.goto(f.xcor() - 1, f.ycor() - 2)
            for tooth in range(8):
                f.backward(4)
                f.right(90)
                f.pendown()
                f.forward(18)
                f.left(90)
                f.forward(10)
                f.left(90)
                f.forward(18)
                f.right(180)
                f.penup()
                f.left(90)
                f.backward(4)
                f.circle(30, 45)
            f.pensize(3)
        elif text == 'Credits':
            # Glider trophy
            f.pencolor('#D1B2DE')
            f.fillcolor('#9559B3')
            f.goto(465, 39)
            f.pendown()
            f.begin_fill()
            f.goto(375, 39)
            f.goto(375, 7)
            f.goto(435, 7)
            f.goto(435, -57)
            f.goto(405, -57)
            f.goto(405, -25)
            f.goto(465, -25)
            f.goto(465, 39)
            f.end_fill()
            f.penup()
            # Left stickman
            f.goto(375, -140) # Body
            f.pendown()
            f.goto(375, -110)
            f.circle(17) # Head
            f.penup()
            f.goto(369, -140) # Left arm
            f.pendown()
            f.goto(370, -135)
            f.goto(375, -126)
            f.goto(390, -115) # Right arm
            f.goto(394, -108)
            f.goto(397, -102)
            f.goto(401, -90)
            f.goto(404, -61) # Right hand
            f.goto(409, -61)
            f.goto(409, -60)
            f.goto(404, -60)
            f.penup()
            f.goto(383, -84) # Eye
            f.pendown()
            f.goto(384, -87)
            f.penup()
            f.goto(384, -96) # Mouth
            f.pendown()
            f.goto(388, -98)
            f.goto(390, -98)
            f.penup()
            # Middle stickman
            f.goto(428, -140) # Body
            f.pendown()
            f.goto(428, -105)
            f.circle(15) # Head
            f.penup()
            f.goto(413, -60) # Left hand
            f.pendown()
            f.goto(418, -60)
            f.goto(418, -61)
            f.goto(413, -61)
            f.goto(413, -68) # Left arm
            f.goto(414, -74)
            f.goto(415, -82)
            f.penup()
            f.goto(420, -104)
            f.pendown()
            f.goto(423, -111)
            f.goto(428, -116) # Right arm
            f.goto(432, -111)
            f.goto(434, -104)
            f.penup()
            f.goto(436, -76)
            f.pendown()
            f.goto(436, -74)
            f.goto(435, -61) # Right hand
            f.goto(430, -61)
            f.goto(430, -60)
            f.goto(435, -60)
            f.penup()
            f.pensize(2)
            f.goto(421, -86) # Left eye
            f.pendown()
            f.goto(422, -83)
            f.goto(423, -82)
            f.goto(424, -83)
            f.goto(425, -86)
            f.penup()
            f.goto(431, -86) # Right eye
            f.pendown()
            f.goto(432, -83)
            f.goto(433, -82)
            f.goto(434, -83)
            f.goto(435, -86)
            f.penup()
            f.pensize(3)
            f.fillcolor('#D1B2DE')
            f.goto(422, -94) # Mouth
            f.pendown()
            f.begin_fill()
            f.goto(434, -94)
            f.goto(431, -97)
            f.goto(428, -98)
            f.goto(425, -97)
            f.goto(422, -94)
            f.end_fill()
            f.penup()
            # Right stickman
            f.goto(479, -140) # Body
            f.pendown()
            f.goto(479, -105)
            f.circle(16) # Head
            f.penup()
            f.goto(451, -28) # Left hand
            f.pendown()
            f.goto(447, -28)
            f.goto(447, -29)
            f.goto(451, -29)
            f.goto(465, -80) # Left arm
            f.penup()
            f.goto(470, -101)
            f.pendown()
            f.goto(473, -111)
            f.goto(476, -114)
            f.goto(479, -117)
            f.penup() # (waving right arm is drawn by the t turtle)
            f.goto(474, -83) # Left eye
            f.pendown()
            f.goto(474, -85)
            f.penup()
            f.goto(484, -83) # Right eye
            f.pendown()
            f.goto(484, -85)
            f.penup()
            f.pensize(2) # Mouth
            f.goto(472, -92)
            f.pendown()
            f.goto(486, -92)
            f.goto(483, -96)
            f.goto(479, -98)
            f.goto(475, -96)
            f.goto(472, -92)
            f.penup()
            f.pensize(3)
            
        # Button name and subtitle
        f.goto(x, 50)
        f.write(text, align='center', font=('Arial', 28, 'normal'))
        f.goto(x, -170)
        f.write(top, align='center', font=('Arial', 14, 'normal'))
        f.goto(x, -192)
        f.write(bottom, align='center', font=('Arial', 14, 'normal'))
    
def draw_main():
    '''Draws the part of the main menu that's redrawn every frame.'''
    # Draw the animated waving hand on the right stick figure
    t.pencolor('#D1B2DE')
    t.pensize(3)
    t.goto(479, -117)
    t.pendown()
    if time.time() * 5 % 1 > 0.7:
        t.goto(491, -115)
        t.goto(495, -112)
        t.penup()
        t.pensize(2)
        t.goto(498, -105)
        t.pendown()
        t.goto(500, -109)
        t.penup()
        t.goto(502, -100)
        t.pendown()
        t.goto(505, -107)
        t.penup()
        t.pensize(3)
    elif int(time.time() * 5 % 2) == 0:
        t.goto(490, -115)
        t.goto(494, -111)
        t.goto(501, -98)
    else:
        t.goto(492, -116)
        t.goto(505, -110)
    t.penup()
    
def draw_screen():
    '''Draws the screen depending on the mode.'''
    global lastMode

    newMode = mode != lastMode # Triggers draw_<mode>_first() functions
    lastMode = mode
    if newMode:
        f.reset()
        f.hideturtle()
        f.penup()
    t.reset()
    t.hideturtle()
    t.penup()
        
    if mode == 'loading':
        if newMode:
            draw_loading_first()
        draw_loading()
    elif mode == 'main':
        if newMode:
            draw_main_first()
        draw_main()
    
    window.update()
    window.ontimer(draw_screen, 20)

draw_screen()

window.listen()
window.mainloop()
(Don't ask why I'm using turtle.)

User avatar
wirehead
Posts: 275
Joined: June 18th, 2022, 2:37 pm
Location: /dev/full
Contact:

Re: WIP: Conway's Story Mode (CSM)

Post by wirehead » July 13th, 2024, 10:12 pm

First time posting in this thread, I think this project is a really good idea. I mostly do stuff in OCA wiring rules and the general number theory of CA (not really terribly active now, but once was), and I never really understood how to "do stuff" in CGoL. I think a program like this would be an invaluable tool to bring in new people, both people completely new to CA in general, and people like myself that mostly lurk in the OCA forum.
squareroot12621 wrote:
July 13th, 2024, 7:51 pm
(Sorry for double-posting. I've been working on this a lot lately.)
Here's some actual Python code (tested on 3.12.2 and IDLE) for Conway's Game of Life—a tutorial.
I tested it on 3.12.4, via the command line, also works. Haven't been able to test with other Pythons but it looks like it will work all the way back to 3.7 or before because it isn't using any terribly advanced features.

At any rate, I think that having the entire color scheme (and especially the background) be purple is a bit overpowering. At the very least, the background should be black or white -- I suggest black, to match the appearance of most other CA tools (even the dumb ones like playgameoflife.com).
squareroot12621 wrote:
July 13th, 2024, 7:51 pm
(Don't ask why I'm using turtle.)
If we do eventually go with Python to take advantage of lifelib support, I think using Pygame for the GUI would be better in terms of performance (but may be harder to program for, I've never used Pygame).
Langton's ant: Can't play the drums, can be taught.

User avatar
squareroot12621
Posts: 682
Joined: March 23rd, 2022, 4:53 pm

Re: WIP: Conway's Story Mode (CSM)

Post by squareroot12621 » July 14th, 2024, 9:20 am

wirehead wrote:
July 13th, 2024, 10:12 pm
...
At any rate, I think that having the entire color scheme (and especially the background) be purple is a bit overpowering. At the very least, the background should be black or white -- I suggest black, to match the appearance of most other CA tools (even the dumb ones like playgameoflife.com).
I was also thinking that there could be different color schemes (red, orange, green, blue, purple, gray). However, to make the entire window not look like one ginormous blob of purple, the following color changes should be employed:
  • The background becomes black/dark gray.
  • The title becomes white.
  • The subtitle becomes light gray.
  • The Play button becomes green. (And maybe gets an extra border to try and highlight it?)
  • The Sandbox button becomes yellow.
  • The Settings button becomes magenta/crimson.
  • The Credits button becomes light blue.
wirehead wrote:
July 13th, 2024, 10:12 pm
squareroot12621 wrote:
July 13th, 2024, 7:51 pm
(Don't ask why I'm using turtle.)
If we do eventually go with Python to take advantage of lifelib support, I think using Pygame for the GUI would be better in terms of performance (but may be harder to program for, I've never used Pygame).
I don't know pygame either, so...
Either way, when I'm drawing the cells in the CGoL grid, I can actually hack into the tkinter canvas and draw a rectangle there (which speeds things up by about 4 times). I used this in the loading screen, but you may need to add

Code: Select all

for i in range(100):
    yield 'See the loading screen'
to the read_and_parse_lesson_file() function to see it.

Edit: Also, is the waving stick figure on the right too noticeable?
Last edited by squareroot12621 on July 14th, 2024, 9:46 am, edited 1 time in total.

unname4798
Posts: 1122
Joined: July 15th, 2023, 10:27 am
Location: On the highest skyscraper

Re: WIP: Conway's Story Mode (CSM)

Post by unname4798 » July 14th, 2024, 9:22 am

squareroot12621 wrote:
July 14th, 2024, 9:20 am
wirehead wrote:
July 13th, 2024, 10:12 pm
...
At any rate, I think that having the entire color scheme (and especially the background) be purple is a bit overpowering. At the very least, the background should be black or white -- I suggest black, to match the appearance of most other CA tools (even the dumb ones like playgameoflife.com).
I was also thinking that there could be different color schemes (red, orange, green, blue, purple, gray). However, to make the entire window not look like one ginormous blob of purple, the following color changes should be employed:
  • The background becomes black/dark gray.
  • The title becomes white.
  • The subtitle becomes light gray.
  • The Play button becomes green. (And maybe gets an extra border to try and highlight it?)
  • The Sandbox button becomes yellow.
  • The Settings button becomes magenta/crimson.
  • The Credits button becomes light blue.
wirehead wrote:
July 13th, 2024, 10:12 pm
squareroot12621 wrote:
July 13th, 2024, 7:51 pm
(Don't ask why I'm using turtle.)
If we do eventually go with Python to take advantage of lifelib support, I think using Pygame for the GUI would be better in terms of performance (but may be harder to program for, I've never used Pygame).
I don't know pygame either, so...
Either way, when I'm drawing the cells in the CGoL grid, I can actually hack into the tkinter canvas and draw a rectangle there (which speeds things up by about 4 times). I used this in the loading screen, but you may need to add

Code: Select all

for i in range(100):
    yield 'See the loading screen'
to the read_and_parse_lesson_file() function to see it.
You can customize colors for everything.
This profile is sponsored by Unname©®™©©®®™™ (2022-2024)
Status: inactive. (until November 2024)

User avatar
squareroot12621
Posts: 682
Joined: March 23rd, 2022, 4:53 pm

Re: WIP: Conway's Story Mode (CSM)

Post by squareroot12621 » July 14th, 2024, 10:45 am

unname4798 wrote:
July 14th, 2024, 9:22 am
squareroot12621 wrote:
July 14th, 2024, 9:20 am
<long quote snipped>
You can customize colors for everything.
...Which part of the quote are you referring to?

Anyway, I applied the UI changes and made the Credits button work.

Code: Select all

# Conway's Game of Life--a tutorial
# Made by squareroot12621.
import random, time, turtle

turtle.setup(1280, 720)
window = turtle.Screen()
canvas = window.getcanvas()
window.title("Conway's Game of Life\u2014a tutorial")
turtle.tracer(0)

t = turtle.Turtle() # Draws objects that are refreshed every frame
f = turtle.Turtle() # Draws objects that are refreshed when mode changes

mode = 'loading'
lastMode = None # Must be different from 'loading'
                # so that draw_loading_first() triggers

lastLoadingFrame = -1

def in_rectangle(x, y, leftX, topY, rightX, bottomY, border=0):
    '''Return whether (x, y) is within border pixels of the rectangle
    (leftX, topY) to (rightX, bottomY).
    '''
    return (leftX - border / 2 <= x <= rightX + border / 2
            and bottomY - border / 2 <= y <= topY + border / 2)

def read_and_parse_lesson_file():
    '''Yield loading messages until file 'CGOLATUTORIAL-lessons.txt' is
    read and parsed.
    '''
    global mode
    # I removed the part of the code that opens a file and parses it,
    # so that you don't have to make the file yourself.
    yield 'Loading message #1...'
    yield 'Loading message #2...'
    yield 'Loading message #3...'
        
    
def draw_loading_first():
    '''Draws the part of the loading screen that's only drawn once.'''
    global lessonParser
    lessonParser = read_and_parse_lesson_file() # Makes loading messages
    window.bgcolor('#000000')
    # Write the "Loading..." text
    f.goto(0, 0)
    f.color('#FFFFFF')
    f.write('Loading...', align='center', font=('Arial', 28, 'normal'))
    
def draw_loading():
    '''Draws the part of the loading screen that's drawn every frame.'''
    global lastLoadingFrame, lessonParser, mode

    # Draw the p30 glider stream
    loadingFrame = int(time.time() * 18 % 30) # 18 FPS, 30 frames
    if loadingFrame != lastLoadingFrame:
        lastLoadingFrame = loadingFrame
        gliderPhases = [[(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)],
                        [(0, 1), (2, 1), (1, 2), (2, 2), (1, 3)],
                        [(2, 1), (0, 2), (2, 2), (1, 3), (2, 3)],
                        [(1, 1), (2, 2), (3, 2), (1, 3), (2, 3)]]
        # Generate the cells
        cellsOn = []
        for glider in range(20):
            for cell in gliderPhases[(loadingFrame + 30 * glider) % 4]:
                cellsOn.append([dimension + (loadingFrame + 30 * glider) // 4
                                for dimension in cell])
        # Draw the cells and delete the old ones
        canvas.delete('loading')
        for cell in cellsOn:
            leftX = 6*cell[0] - 220
            topY = 6*cell[1] - 396
            canvas.create_rectangle(leftX, topY, leftX + 5, topY - 5,
                                    fill='#FFFFFF', width=0, tag='loading')

    # Write loading messages
    t.color('#CCCCCC')
    t.goto(0, -30)
    try:
        loadingMessage = next(lessonParser)
    except StopIteration:
        loadingMessage = 'Done!'
        mode = 'main'
    t.write(loadingMessage, align='center', font=('Arial', 18, 'normal'))

def draw_main_first():
    '''Draws the part of the main menu that's only drawn once.'''
    window.bgcolor('#222222')
    canvas.delete('loading')

    # Conway's Game of Life
    f.color('#FFFFFF')
    f.goto(0, 250)
    f.write("Conway's Game of Life", align='center',
            font=('Arial', 40, 'normal'))
    # --- the tutorial ---
    f.color('#999999')
    f.goto(0, 210)
    f.write('\u2014\u2014\u2014 the tutorial \u2014\u2014\u2014',
            align='center', font=('Arial', 24, 'italic'))
    # Buttons
    f.pensize(3)
    for x, text, top, bottom in [(-420, 'Play',
                                  "Learn Conway's Game of",
                                  'Life with 5-minute lessons'),
                                 (-140, 'Sandbox',
                                  "Play with Conway's Game",
                                  'of Life on your own'),
                                 (140, 'Settings',
                                  'Change your language,',
                                  'reset your progress, etc.'),
                                 (420, 'Credits',
                                  'Thanks to everyone who',
                                  'has contributed to this!')]:
        # Button outline and fill
        f.goto(x - 120, 100)
        if text == 'Play':
            f.pencolor('#BFDEB2')
            f.fillcolor('#1C3210')
        elif text == 'Sandbox':
            f.pencolor('#DED1B2')
            f.fillcolor('#322610')
        elif text == 'Settings':
            f.pencolor('#D1B2DE')
            f.fillcolor('#261032')
        elif text == 'Credits':
            f.pencolor('#B2BFDE')
            f.fillcolor('#101C32')
        f.pendown()
        f.begin_fill()
        f.goto(x + 120, 100)
        f.goto(x + 120, -200)
        f.goto(x - 120, -200)
        f.goto(x - 120, 100)
        f.end_fill()
        f.penup()
        
        # Artwork for each button
        if text == 'Play':
            # "Lessons"
            for rectX, rectY, num in [(-490, -90, '1'), (-450, 0, '2'),
                                      (-350, -10, '3'), (-380, -100, '4')]:
                # Inner border and fill
                f.fillcolor('#77B359')
                f.pencolor('#1C3210')
                f.goto(rectX - 27, rectY + 27)
                f.pendown()
                f.begin_fill()
                f.goto(rectX + 27, rectY + 27)
                f.goto(rectX + 27, rectY - 27)
                f.goto(rectX - 27, rectY - 27)
                f.goto(rectX - 27, rectY + 27)
                f.end_fill()
                f.penup()
                # Outer border
                f.pencolor('#BFDEB2')
                f.goto(rectX - 30, rectY + 30)
                f.pendown()
                f.goto(rectX + 30, rectY + 30)
                f.goto(rectX + 30, rectY - 30)
                f.goto(rectX - 30, rectY - 30)
                f.goto(rectX - 30, rectY + 30)
                f.penup()
                # Text
                f.pencolor('#1C3210')
                for deltaX in range(2):
                    for deltaY in range(-22, -20):
                        f.goto(rectX + deltaX, rectY + deltaY)
                        f.write(num, align='center', font=('Arial', 28, 'normal'))
            # Lines between "lessons"
            f.pencolor('#BFDEB2')
            for x1, y1, x2, y2 in [(-540, -85, -520, -90),
                                   (-480, -60, -460, -30),
                                   (-420, -2, -380, -8),
                                   (-355, -40, -375, -70),
                                   (-350, -100, -300, -90)]:
                f.goto(x1, y1)
                f.pendown()
                f.goto(x2, y2)
                f.penup()
        elif text == 'Sandbox':
            # Sandbox
            f.goto(-220, 10)
            f.pendown()
            f.goto(-60, 10)
            f.goto(-40, -100)
            f.goto(-240, -100)
            f.goto(-220, 10)
            f.penup()
            f.goto(-40, -100)
            f.pendown()
            f.goto(-40, -120)
            f.goto(-240, -120)
            f.goto(-240, -100)
            f.goto(-40, -100)
            f.penup()
            # Boat
            f.fillcolor('#B39559')
            f.goto(-100, 0)
            f.begin_fill()
            f.goto(-97, -24)
            f.goto(-107, -24)
            f.goto(-108, -16)
            f.goto(-88, -16)
            f.goto(-89, -8)
            f.goto(-109, -8)
            f.goto(-108, -16)
            f.goto(-118, -16)
            f.goto(-120, 0)
            f.goto(-100, 0)
            f.end_fill()
            # Beacon
            f.goto(-150, -50)
            f.begin_fill()
            f.goto(-150, -58)
            f.goto(-160, -58)
            f.goto(-160, -66)
            f.goto(-170, -66)
            f.goto(-170, -50)
            f.goto(-150, -50)
            f.end_fill()
            f.goto(-130, -82)
            f.begin_fill()
            f.goto(-130, -66)
            f.goto(-140, -66)
            f.goto(-140, -74)
            f.goto(-150, -74)
            f.goto(-150, -82)
            f.goto(-130, -82)
            f.end_fill()
            # Block
            f.goto(-195, -20)
            f.begin_fill()
            f.goto(-193, -4)
            f.goto(-173, -4)
            f.goto(-175, -20)
            f.goto(-195, -20)
            f.end_fill()
        elif text == 'Settings':
            f.goto(140, -80)
            f.pensize(20)
            f.pendown()
            f.circle(30)
            f.penup()
            f.pensize(10)
            f.goto(f.xcor() - 1, f.ycor() - 2)
            for tooth in range(8):
                f.backward(4)
                f.right(90)
                f.pendown()
                f.forward(18)
                f.left(90)
                f.forward(10)
                f.left(90)
                f.forward(18)
                f.right(180)
                f.penup()
                f.left(90)
                f.backward(4)
                f.circle(30, 45)
            f.pensize(3)
        elif text == 'Credits':
            # Glider trophy
            f.pencolor('#B2BFDE')
            f.fillcolor('#5977B3')
            f.goto(465, 39)
            f.pendown()
            f.begin_fill()
            f.goto(375, 39)
            f.goto(375, 7)
            f.goto(435, 7)
            f.goto(435, -57)
            f.goto(405, -57)
            f.goto(405, -25)
            f.goto(465, -25)
            f.goto(465, 39)
            f.end_fill()
            f.penup()
            # Left stickman
            f.goto(375, -140) # Body
            f.pendown()
            f.goto(375, -110)
            f.circle(17) # Head
            f.penup()
            f.goto(369, -140) # Left arm
            f.pendown()
            f.goto(370, -135)
            f.goto(375, -126)
            f.goto(390, -115) # Right arm
            f.goto(394, -108)
            f.goto(397, -102)
            f.goto(401, -90)
            f.goto(404, -61) # Right hand
            f.goto(409, -61)
            f.goto(409, -60)
            f.goto(404, -60)
            f.penup()
            f.goto(383, -84) # Eye
            f.pendown()
            f.goto(384, -87)
            f.penup()
            f.goto(384, -96) # Mouth
            f.pendown()
            f.goto(388, -98)
            f.goto(390, -98)
            f.penup()
            # Middle stickman
            f.goto(428, -140) # Body
            f.pendown()
            f.goto(428, -105)
            f.circle(15) # Head
            f.penup()
            f.goto(413, -60) # Left hand
            f.pendown()
            f.goto(418, -60)
            f.goto(418, -61)
            f.goto(413, -61)
            f.goto(413, -68) # Left arm
            f.goto(414, -74)
            f.goto(415, -82)
            f.penup()
            f.goto(420, -104)
            f.pendown()
            f.goto(423, -111)
            f.goto(428, -116) # Right arm
            f.goto(432, -111)
            f.goto(434, -104)
            f.penup()
            f.goto(436, -76)
            f.pendown()
            f.goto(436, -74)
            f.goto(435, -61) # Right hand
            f.goto(430, -61)
            f.goto(430, -60)
            f.goto(435, -60)
            f.penup()
            f.pensize(2)
            f.goto(421, -86) # Left eye
            f.pendown()
            f.goto(422, -83)
            f.goto(423, -82)
            f.goto(424, -83)
            f.goto(425, -86)
            f.penup()
            f.goto(431, -86) # Right eye
            f.pendown()
            f.goto(432, -83)
            f.goto(433, -82)
            f.goto(434, -83)
            f.goto(435, -86)
            f.penup()
            f.pensize(3)
            f.fillcolor('#B2BFDE')
            f.goto(422, -94) # Mouth
            f.pendown()
            f.begin_fill()
            f.goto(434, -94)
            f.goto(431, -97)
            f.goto(428, -98)
            f.goto(425, -97)
            f.goto(422, -94)
            f.end_fill()
            f.penup()
            # Right stickman
            f.goto(479, -140) # Body
            f.pendown()
            f.goto(479, -105)
            f.circle(16) # Head
            f.penup()
            f.goto(451, -28) # Left hand
            f.pendown()
            f.goto(447, -28)
            f.goto(447, -29)
            f.goto(451, -29)
            f.goto(465, -80) # Left arm
            f.penup()
            f.goto(470, -101)
            f.pendown()
            f.goto(473, -111)
            f.goto(476, -114)
            f.goto(479, -117)
            f.penup() # (waving right arm is drawn by the t turtle)
            f.goto(474, -83) # Left eye
            f.pendown()
            f.goto(474, -85)
            f.penup()
            f.goto(484, -83) # Right eye
            f.pendown()
            f.goto(484, -85)
            f.penup()
            f.pensize(2) # Mouth
            f.goto(472, -92)
            f.pendown()
            f.goto(486, -92)
            f.goto(483, -96)
            f.goto(479, -98)
            f.goto(475, -96)
            f.goto(472, -92)
            f.penup()
            f.pensize(3)
            
        # Button name and subtitle
        f.goto(x, 50)
        f.write(text, align='center', font=('Arial', 28, 'normal'))
        f.goto(x, -170)
        f.write(top, align='center', font=('Arial', 14, 'normal'))
        f.goto(x, -192)
        f.write(bottom, align='center', font=('Arial', 14, 'normal'))
    
def draw_main():
    '''Draws the part of the main menu that's redrawn every frame.'''
    # Draw the animated waving hand on the right stick figure
    t.pencolor('#B2BFDE')
    t.pensize(3)
    t.goto(479, -117)
    t.pendown()
    if time.time() * 5 % 1 > 0.7:
        t.goto(491, -115)
        t.goto(495, -112)
        t.penup()
        t.pensize(2)
        t.goto(498, -105)
        t.pendown()
        t.goto(500, -109)
        t.penup()
        t.goto(502, -100)
        t.pendown()
        t.goto(505, -107)
        t.penup()
        t.pensize(3)
    elif int(time.time() * 5 % 2) == 0:
        t.goto(490, -115)
        t.goto(494, -111)
        t.goto(501, -98)
    else:
        t.goto(492, -116)
        t.goto(505, -110)
    t.penup()

def draw_credits_first():
    '''Draws the part of the credits that's only drawn once.'''
    window.bgcolor('#203763')
    # Back button
    f.pensize(3)
    f.pencolor('#B2BFDE')
    f.fillcolor('#101C32')
    f.goto(-700, -320)
    f.pendown()
    f.begin_fill()
    f.goto(-700, -272)
    f.goto(-480, -272)
    f.goto(-480, -320)
    f.goto(-700, -320)
    f.end_fill()
    f.penup()
    f.goto(-490, -315)
    f.write('\u2039 Back', align='right', font=('Arial', 24, 'normal'))
    f.goto(-300, 316)
    for role, *people in [('PROJECT STARTER', 'Aura'),
                          ('EXERCISE CREATORS', 'confocaloid',
                                                'hotcrystal0',
                                                'hotdogPi', ''),
                          ('CONCEPT ARTIST', 'squareroot12621'),
                          ('UI DESIGNERS', 'squareroot12621',
                                           'wirehead', ''),
                          ('SOFTWARE DEVELOPER', 'squareroot12621'),
                          ]:
        if role == '\n': # Column break
            f.goto(300, 316)
            continue
        f.write(role + ' \u2013 ', align='right', font=('Arial', 14, 'bold'))
        f.goto(f.xcor(), f.ycor() - 1)
        for person in people:
            f.write(person, align='left', font=('Arial', 16, 'normal'))
            f.goto(f.xcor(), f.ycor() - 20)
        f.goto(f.xcor(), f.ycor() - 4)

def draw_credits():
    '''Draws the part of the credits that's redrawn every frame.'''
    pass

def draw_screen():
    '''Draws the screen depending on the mode.'''
    global lastMode

    newMode = mode != lastMode # Triggers draw_<mode>_first() functions
    lastMode = mode
    if newMode:
        f.reset()
        f.hideturtle()
        f.penup()
    t.reset()
    t.hideturtle()
    t.penup()
        
    if mode == 'loading':
        if newMode:
            draw_loading_first()
        draw_loading()
    elif mode == 'main':
        if newMode:
            draw_main_first()
        draw_main()
    elif mode == 'credits':
        if newMode:
            draw_credits_first()
        draw_credits()
    
    window.update()
    window.ontimer(draw_screen, 20)

def click_handler(x, y):
    '''Handles clicks from the user.'''
    global mode
    if mode == 'main':
        if in_rectangle(x, y, -540, 100, -300, -200, 3): # Play
            mode = 'main' # Doesn't do anything yet
        elif in_rectangle(x, y, -260, 100, -20, -200, 3): # Sandbox
            mode = 'main' # Doesn't do anything yet
        elif in_rectangle(x, y, 20, 100, 260, -200, 3): # Settings
            mode = 'main' # Doesn't do anything yet
        elif in_rectangle(x, y, 300, 100, 540, -200, 3): # Credits
            mode = 'credits'
    elif mode == 'credits':
        if in_rectangle(x, y, -700, -272, -480, -320, 3): # Back from Credits
            mode = 'main'

window.onclick(click_handler, btn=1) # Activate on left-click
draw_screen()

window.listen()
window.mainloop()

User avatar
hotcrystal0
Posts: 2649
Joined: July 3rd, 2020, 5:32 pm
Location: United States

Re: WIP: Conway's Story Mode (CSM)

Post by hotcrystal0 » July 27th, 2024, 10:38 pm

How about a “catalyses” section? You should move the “place a block to eat a beehive” exercise to there. Also, how about this exercise to teach eater 1 catalyses?

Code: Select all

x = 31, y = 23, rule = LifeHistory
3$D3.D.D7.F$D5.2D2.2D2.F$D3.D.D.D.D.D.F$3D.D.2D2.D3.F$14.F$.2D.2D2.D.
D3.F$D.D.D.D.D.D3.F5.A$.2D.D4.2D3.F4.A2.A$10.D3.F5.A.A$8.2D4.F6.2A$14.
F$3.2C5.2D2.F$2.C.C.D.D2.D2.F$2.C4.D3.D2.F$.2C3.D.D.3D.F$14.F$14.F$14.
F$14.F!

Code: Select all

x = 192, y = 53, rule = B3/S23
33$42b4o$41b6o$40b2ob4o$41b2o3$41b2o$39bo6bo$38bo8bo$38bo8bo$38b9o3$42b
4o$41b6o$40b2ob4o$41b2o!

Post Reply