Leveraging AI to Create Games and Learn Python's Pygame Protocol

in LeoFinance22 hours ago (edited)

pixel-sword.png

Realizing the power of AI.

So after yesterday's post I figured now might be as good a time as any to jump back into the coding game. It's been quite a long time since I've had the opportunity to worth with Python to I started there.

image.png

image.png

Is Pygame worth it?

We can see here that this development toolkit isn't exactly anything to write home about, but as far as novice games are concerned it suits the purpose just fine. In fact some of these games like Flappy Bird make $18M a year just from advertising revenue (which is $50k/day for the single dev that created it). Not exactly chump change. It's only a matter of time before someone leverages AI to create a real winner.

So what do I have so far?

Well looking back to some of the ideas I've had in the past I stumbled back to my vision of what Marbles on Stream would look like on the blockchain. To reiterate: something like this would be huge because we've already seen how much engagement the game gets on platforms like Twitch.tv. Adding actual money and gambling to the mix makes it exponentially more interesting and addictive. In addition, creating a decentralized solution where no centralized house-entity is profiting from the gamblers is something that has yet to go viral within the ecosystem (for a myriad of reasons/regulations).

strangeattractor-butterfly-effect-chaos.gif


Thus, my first attempt with Grok3 was to create a deterministic simulation; one that will always have the same output (winner) given identical inputs across any machine running the simulation. This might not sound like a difficult thing to achieve, but after hours of research I realized that almost none of the physics engines are deterministic because they all use "chaotic floating point" math. Floats are notoriously unreliable to recreate a complex simulation and get the exact same result every time.

Gumball machine with windmill inside

This was my final iteration before I realized this project was probably going to be too complex to work. The marbles kept bouncing out of bounds. I asked the AI several times to start a countdown and open a latch at the bottom of the circle so the marbles could escape. I tried to fix this problem like 3 or 4 times before deciding it just wasn't going to work out.

However there's still a lot of value here.

I was very impressed with what the AI could do, even though it couldn't do what I wanted. If I was willing to jump into the weeds on this project the AI give me a ton of code for me to work with and manipulate. This is much MUCH easier than starting from scratch.

What took the AI less than an hour to get up and running would have taken me days of trying to figure out how pygame worked even on the most rudimentary of levels. Now I have some code with basic physics and collision that I can look at whenever I want to truly understand what's going on here.

I would say the most important lesson learned from this failure is that AI allows us to prototype a project so quickly it doesn't trap us into a web of sunk-cost-fallacy. Had I of spent weeks trying to figure out Pygame and build the above marble simulation it would have been a lot more difficult to realize I was in over my head and needed to pivot to something else.

Plinko First attempt:

Realizing that the prototype proof-of-concept simulation doesn't have to be custom or player-built, I decided to reduce the scope of the project and go for a basic game of Plinko instead. Only having one track for the marbles to bounce on makes it easier to bug-check confirm the deterministic nature I'm looking for; The idea being that anyone can plug the Bitcoin block hash into the simulation and figure out who won the prize without having to trust a centralized agent.

Technically I got a working game, but it's incorrect.

The first video was what I ended up with after several iterations. This Plinko game was the first iteration after one prompt, and it was a bit of a disaster for largely the same reasons.

Telling the AI that I need a deterministic simulation based on an SHA-256 hash confuses the AI into thinking that the marbles are guaranteed to end up in a certain place based on the hash... while this is true to an extent, the AI instead opts to remove all collision from the game and force the marbles to go where it has already decided it's going. Obviously this is not what I'm going for... so let me pause here and see if I can get some physics going like I had to do in the previous attempt.


...
...
...


Holy shit it actually works... lol

I just spent a little less than an hour trying to debug this thing and getting it more how I wanted it to look, but I would say it was time well spent. The Plinko solution is definitely the way to go just to create a default marble track proof of concept.

There were many iterations I had to get through:

  • One of the big problems was that the marbles were not losing any momentum when they bounced. The energy of the marble was 100% conserved on every bounce and I had to tinker with the global variables to stop them from going apeshit.
  • Another problem was that the AI decided to randomly take the bottom of the game away so that the marbles had nothing to fall in to. Also my second iteration didn't have sidewalls and the sim threw a runtime error when marbles when off the screen.
  • Then I broke the entire program asking the AI to make each marble a different color. I decided to just roll that change back because it wasn't technically necessary due to the marbles already being labeled with numbers.
  • Then I added the reset button and ability to change the hash for testing.
    • Then I had to move these labels around and ask for buttons again because it was just giving me text.

The determinism seems to work.

The winner is based on the BTC block hash but all the frontend user sees is a game of Plinko playing out "randomly". This makes the games verifiable in a decentralized say; anyone can run the simulation to confirm the winner. No one can cheat because in order to cheat you'd have to hack the Bitcoin blockchain (which would cost hundreds of thousands of dollars in lost block rewards just to reroll this game a single time).

image.png

# Fetch the head Bitcoin block hash
def get_bitcoin_head_block_hash():
    try:
        response = requests.get("https://blockchain.info/latestblock")

My code pulls real-time data from Bitcoin.

One huge unexpected thing that happened during this adventure was that the AI took it upon themselves to pull the hash from the Bitcoin blockchain and feed it into the algorithm with very little prompting. Interestingly enough this did not happen on my second try and I had to specifically ask for it to be added.

Realistically this is not how the simulation would work, but it's still cool that my program reaches out to some random node and picks up the information it needs. How a real functional version of the simulation would operate is that users would place bets on a future block that has yet to be mined. Nobody knows what the hash will be so no one can figure out the winner until the block is mined. By that time the money is locked in and ready to be released to the winner.

How can I get this up and running myself?

Well if you want to mess around with Python and Pygame like I've done here you can head on over to the official website and download Python. I had to upgrade to the latest version because what I was using was quite old. As luck would have it a stable release debuted on Feb 4th three weeks ago.

https://www.python.org/downloads/release/python-3132/

From here you have to download some dependencies

There should be an option to add python to your "path" if you're on a Windows machine like me. This allows you to open a terminal (search "CMD" for "command prompt") and type "python" into the terminal anywhere without actually being in the python folder.

image.png

This "game" has 3 dependencies.

pip install pygame
pip install pymunk
pip install requests

pygame is the main dependency that allows a graphical interface.
pymunk is a physics engine apparently.
requests allows the program to contact the Bitcoin node to fetch the head block.

image.png

import pygame
import pymunk
import pymunk.pygame_util
import random
import sys
import requests

# Initialize Pygame
pygame.init()

# Screen dimensions (increased height for UI area)
WIDTH, HEIGHT = 800, 700  # Added 100 pixels for UI panel
GAME_HEIGHT = 600  # Original play area height
UI_HEIGHT = 100    # Height of UI panel
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Square Plinko with UI Below Prize Boxes")

# Colors
BLUE = (0, 0, 255)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)  # For UI panel

# Plinko board parameters
ROWS = 12
COLUMNS = 12
PEG_RADIUS = 5
MARBLE_RADIUS = 10
SLOTS = 12
SLOT_HEIGHT = 60
SLOT_WIDTH = WIDTH / SLOTS
H_SPACING = WIDTH / (COLUMNS + 1)
V_SPACING = (GAME_HEIGHT - SLOT_HEIGHT - 50) / (ROWS + 1)

# Prize values for slots
PRIZES = [0, 100, 500, 1000, 5000, 10000, 10000, 5000, 1000, 500, 100, 0]

# Font for text
FONT = pygame.font.Font(None, 24)

# Fetch the head Bitcoin block hash
def get_bitcoin_head_block_hash():
    try:
        response = requests.get("https://blockchain.info/latestblock")
        response.raise_for_status()
        latest_height = response.json()["height"]
        response = requests.get(f"https://blockchain.info/block-height/{latest_height}?format=json")
        response.raise_for_status()
        block_data = response.json()["blocks"][0]
        block_hash = block_data["hash"]
        print(f"Using Bitcoin head block hash: {block_hash}")
        return block_hash
    except Exception as e:
        print(f"Failed to fetch block hash: {e}")
        fallback_hash = "00000000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98b"
        print(f"Using fallback hash: {fallback_hash}")
        return fallback_hash

# Seed random with block hash
def seed_random(block_hash):
    try:
        seed = int(block_hash, 16)
        random.seed(seed)
    except ValueError:
        print("Invalid hash; using default seed")
        random.seed(0)

# Create staggered grid of static pegs
def create_pegs(space):
    pegs = []
    for row in range(ROWS):
        offset = (row % 2) * 0.5 * H_SPACING
        for col in range(COLUMNS):
            x = offset + col * H_SPACING
            y = (row + 1) * V_SPACING
            body = pymunk.Body(body_type=pymunk.Body.STATIC)
            body.position = (x, y)
            shape = pymunk.Circle(body, PEG_RADIUS)
            shape.elasticity = 0.8
            shape.friction = 0.0
            space.add(body, shape)
            pegs.append((x, y))
    return pegs

# Create boundaries (sides and bottom)
def create_boundaries(space):
    boundaries = []
    
    # Left wall
    left_wall = pymunk.Segment(space.static_body, (0, 0), (0, GAME_HEIGHT), 5)
    left_wall.elasticity = 0.8
    left_wall.friction = 0.0
    space.add(left_wall)
    boundaries.append(left_wall)
    
    # Right wall
    right_wall = pymunk.Segment(space.static_body, (WIDTH, 0), (WIDTH, GAME_HEIGHT), 5)
    right_wall.elasticity = 0.8
    right_wall.friction = 0.0
    space.add(right_wall)
    boundaries.append(right_wall)
    
    # Bottom boundary
    bottom_y = GAME_HEIGHT
    bottom = pymunk.Segment(space.static_body, (0, bottom_y), (WIDTH, bottom_y), 5)
    bottom.elasticity = 0.8
    bottom.friction = 0.2
    space.add(bottom)
    boundaries.append(bottom)
    
    # Ridge pegs
    ridge_pegs = []
    for row in range(0, ROWS, 2):
        x = PEG_RADIUS
        y = (row + 1) * V_SPACING
        body = pymunk.Body(body_type=pymunk.Body.STATIC)
        body.position = (x, y)
        shape = pymunk.Circle(body, PEG_RADIUS)
        shape.elasticity = 0.8
        shape.friction = 0.0
        space.add(body, shape)
        ridge_pegs.append((x, y))
    
    for row in range(1, ROWS, 2):
        x = WIDTH - PEG_RADIUS
        y = (row + 1) * V_SPACING
        body = pymunk.Body(body_type=pymunk.Body.STATIC)
        body.position = (x, y)
        shape = pymunk.Circle(body, PEG_RADIUS)
        shape.elasticity = 0.8
        shape.friction = 0.0
        space.add(body, shape)
        ridge_pegs.append((x, y))
    
    return boundaries, ridge_pegs

# Create physical slot containers
def create_slots(space):
    slots = []
    for i in range(SLOTS):
        slot_left = i * SLOT_WIDTH
        slot_right = (i + 1) * SLOT_WIDTH
        slot_top = GAME_HEIGHT - SLOT_HEIGHT
        slot_bottom = GAME_HEIGHT
        
        # Left wall of slot
        left = pymunk.Segment(space.static_body, (slot_left, slot_top), (slot_left, slot_bottom), 2)
        left.elasticity = 0.8
        left.friction = 0.2
        space.add(left)
        
        # Right wall of slot (skip last right wall)
        if i < SLOTS - 1:
            right = pymunk.Segment(space.static_body, (slot_right, slot_top), (slot_right, slot_bottom), 2)
            right.elasticity = 0.8
            right.friction = 0.2
            space.add(right)
        
        slots.append((slot_left, slot_right, slot_top, slot_bottom))
    return slots

# Create a dynamic numbered marble
def create_marble(space, x, y, number):
    mass = 1
    radius = MARBLE_RADIUS
    moment = pymunk.moment_for_circle(mass, 0, radius)
    body = pymunk.Body(mass, moment)
    body.position = (x, y)
    shape = pymunk.Circle(body, radius)
    shape.elasticity = 0.8
    shape.friction = 0.2
    shape.collision_type = 1
    shape.number = number
    space.add(body, shape)
    return body

# Check if marbles have settled and assign prizes
def check_marble_slots(marbles, slots, total_prize, settled):
    for marble in marbles:
        if marble in settled:
            continue
        if abs(marble.velocity.x) < 1 and abs(marble.velocity.y) < 1 and marble.position.y > GAME_HEIGHT - SLOT_HEIGHT - 10:
            for i, (left, right, top, bottom) in enumerate(slots):
                if left <= marble.position.x <= right:
                    if marble not in settled:
                        for shape in marble.shapes:
                            number = shape.number
                            break
                        total_prize[0] += PRIZES[i]
                        print(f"Marble {number} landed in slot {i}: ${PRIZES[i]}")
                        settled.add(marble)
                    break

# Reset the game
def reset_game(space, block_hash, marbles, total_prize, settled):
    for marble in marbles[:]:
        for shape in marble.shapes:
            space.remove(shape)
        space.remove(marble)
    marbles.clear()
    total_prize[0] = 0
    settled.clear()
    
    seed_random(block_hash)
    drop_x = [random.uniform(H_SPACING, WIDTH - H_SPACING) for _ in range(5)]
    new_marbles = [create_marble(space, drop_x[i], 0, i + 1) for i in range(5)]
    marbles.extend(new_marbles)

# Main game function
def main():
    block_hash = get_bitcoin_head_block_hash()
    current_hash = block_hash
    hash_editing = False  # Track if hash field is active
    hash_cursor_pos = len(current_hash)  # Cursor position in hash
    
    # Setup Pymunk space
    space = pymunk.Space()
    space.gravity = (0, 900)
    space.damping = 0.95
    
    # Create static elements
    pegs = create_pegs(space)
    boundaries, ridge_pegs = create_boundaries(space)
    slots = create_slots(space)
    
    # Initial marbles
    seed_random(current_hash)
    N = 5
    drop_x = [random.uniform(H_SPACING, WIDTH - H_SPACING) for _ in range(N)]
    marbles = [create_marble(space, drop_x[i], 0, i + 1) for i in range(N)]
    
    # Game state
    total_prize = [0]
    settled = set()
    
    # UI elements
    reset_button = pygame.Rect(WIDTH - 150, GAME_HEIGHT + 60, 100, 30)  # Reset button
    hash_field = pygame.Rect(150, GAME_HEIGHT + 35, 500, 20)  # Hash text field
    
    # Pygame setup
    clock = pygame.time.Clock()
    draw_options = pymunk.pygame_util.DrawOptions(screen)
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if reset_button.collidepoint(event.pos):
                    reset_game(space, current_hash, marbles, total_prize, settled)
                elif hash_field.collidepoint(event.pos):
                    hash_editing = True
                    hash_cursor_pos = len(current_hash)  # Start at end by default
            elif event.type == pygame.KEYDOWN and hash_editing:
                if event.key == pygame.K_RETURN:
                    hash_editing = False
                    reset_game(space, current_hash, marbles, total_prize, settled)
                elif event.key == pygame.K_BACKSPACE and hash_cursor_pos > 0:
                    current_hash = current_hash[:hash_cursor_pos-1] + current_hash[hash_cursor_pos:]
                    hash_cursor_pos -= 1
                elif event.key == pygame.K_LEFT and hash_cursor_pos > 0:
                    hash_cursor_pos -= 1
                elif event.key == pygame.K_RIGHT and hash_cursor_pos < len(current_hash):
                    hash_cursor_pos += 1
                elif event.unicode.isalnum() and len(current_hash) < 64:  # Limit to typical hash length
                    current_hash = current_hash[:hash_cursor_pos] + event.unicode + current_hash[hash_cursor_pos:]
                    hash_cursor_pos += 1
        
        # Step physics
        space.step(1 / 60)
        
        # Check for settled marbles
        check_marble_slots(marbles, slots, total_prize, settled)
        
        # Draw game area
        screen.fill(BLUE, (0, 0, WIDTH, GAME_HEIGHT))
        for i, (left, right, top, bottom) in enumerate(slots):
            pygame.draw.rect(screen, RED, (left, top, SLOT_WIDTH, SLOT_HEIGHT))
            text = FONT.render(f"${PRIZES[i]}", True, BLACK)
            screen.blit(text, (left + SLOT_WIDTH / 2 - text.get_width() / 2, 
                              top + SLOT_HEIGHT / 2 - text.get_height() / 2))
        
        # Draw physics objects
        space.debug_draw(draw_options)
        
        # Draw marble numbers
        for marble in marbles:
            for shape in marble.shapes:
                number = shape.number
                text = FONT.render(str(number), True, BLACK)
                screen.blit(text, (marble.position.x - text.get_width() / 2, 
                                  marble.position.y - text.get_height() / 2))
                break
        
        # Draw UI panel
        pygame.draw.rect(screen, GREEN, (0, GAME_HEIGHT, WIDTH, UI_HEIGHT))
        
        # Total prize
        text = FONT.render(f"Total Prize: ${total_prize[0]}", True, BLACK)
        screen.blit(text, (10, GAME_HEIGHT + 10))
        
        # Hash field
        pygame.draw.rect(screen, WHITE, hash_field)
        hash_text = FONT.render(current_hash, True, BLACK)
        screen.blit(hash_text, (hash_field.x + 5, hash_field.y + 2))
        if hash_editing:
            cursor_x = hash_field.x + 5 + FONT.size(current_hash[:hash_cursor_pos])[0]
            pygame.draw.line(screen, BLACK, (cursor_x, hash_field.y + 2), 
                            (cursor_x, hash_field.y + 18), 2)  # Cursor
        
        # Reset button
        pygame.draw.rect(screen, WHITE, reset_button)
        reset_text = FONT.render("Reset", True, BLACK)
        screen.blit(reset_text, (reset_button.x + (reset_button.width - reset_text.get_width()) / 2, 
                                reset_button.y + (reset_button.height - reset_text.get_height()) / 2))
        
        # Instructions
        text = FONT.render("Click hash to edit, Enter to apply, click Reset", True, BLACK)
        screen.blit(text, (10, GAME_HEIGHT + 65))
        
        pygame.display.flip()
        clock.tick(60)
    
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()

Copy/paste this 330 lines of code into a text file.

Make sure the extension of the file is .py for Python (example: plinko.py)
If all went well you'll be able to run plinko.py in the terminal just by typing...

    python plinko.py

Or better yet just use AI to make your own game.
I bet many of you can do a lot better than what I've done here.
We seem to be at the dawn of a new era.

Conclusion

I'm a bit shocked that this actually worked. The value of AI is proving itself a thousand times over. There is a lot to unpack here but with clever use of AI generated code we should be able to script plug and play modules that are actually scalable rather than devolving into an unworkable brick of code like many are using this tool for. The amount of time that can be saved with this type of utility is astronomical. Programmers are about to become one-man armies.

Sort:  

Great stuff - try Pygbag and you can embed your game in a web browser :) https://peakd.com/python/@makerhacks/bringing-pygame-to-the-browser-with-pygbag--hfd

Do you think this is worth it or should I just be looking at JavaScript solutions that are already web native? I do like working with Python I may just stick with it for the meat and potatoes and deal with the annoying connectors later.

I bet many of you can do a lot better than what I've done here.

I am yet to use AI but based on few of your blogs, I am curious to use it, are you using a paid version ? And what or how to prompt the AI to give you code in an organized manner as you have put up ?

If I'm not wrong, @edicted is using Grok 3, which is free to use.

To get some working code you can just ask for it to the AI, something like "write a script in python which prints "Hello World!" every 3 seconds" is enough to generate a usable result. Of course you con ask for harder problems to be solved, and in those cases you will probably have to debug and tweak the code... but the AI can also do that, to some extent :)

As @edicted said this is an incredibile time-saving way to quickly test an idea or get something to start working on with, without having to start from scracth.

Grok3 is free (for now) but there are a couple other AI assistants that people say are comparable.
Ironically enough this is a question you should be asking AI.

image.png

Notice how grok3 doesn't even list itself.

Ask it why.

Many people on X say that Claude 3.7 Sonnet is the way to go but Grok3 is also amazing.

Some of the advice I'm seeing is to use "deepsearch" for Grok3 to come up with a plan... A GAME DESIGN DOCUMENT to create a flushed out plan for the game. And then once you have the GDD you use the "think" functionality to give the AI more time to use the GDD to create the game. This way the AI might spend something like 3-5 minutes coming up with everything rather than trying to crank it out in like 20 seconds.

Thank you so much for the detailed answer.

This is crazy bro...

Firstly I now understand how most of the "random" winner games are won so thanks😂

Yet I'm a bit confused with what you mean when you wrote "my first attempt with Grok3 was to create a deterministic simulation; one that will always have the same output (winner) given identical inputs across any machine running the simulation."

I wondered.

Is this a good thing?

A deterministic simulation with always the same output seems easy to hack.

All one or two or more users have to do is find the required inputs and always key it in...
Well that's if it's going to be a game...

Players will not be able to key inputs into the game.
The inputs are Bitcoin hashes.
Players bet on a block that doesn't exist yet.
No one knows what that block hash will be.

I see...

Solely luck...

I'd suck at such😂😂

WEN New game??. I have the latest version of python. I'd download the dependencies and try!

May as well do something useful because the markets are poop right now.

Dood, i'll tell you on discord something I'm working on right now. Precisely because I started playing with AI like this.

Talk about a paradigm shift.

This is a good one, I actually don't know that phyton can be used to produce games also the aspect of AI, it helps us test ideas fast, so we don’t waste time on things that won’t work.

AI's evolution is pretty admirable. I recently used Grok 3 to draft out a book recommendation and detailed summary on the best books talking about economics and geopolitics. It was quite detailed with more depth as compared to gemini.

it is impressive to see that the code is working well

I have a bit knowledge on Python and I'll try to test it on my side

the program is very cool I have just run it

Oh Yea!

GAME ON!!

😎😉👊

giphy.gif