惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
博客园 - 司徒正美
博客园 - 【当耐特】
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
IT之家
IT之家
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
宝玉的分享
宝玉的分享
V
V2EX
S
SegmentFault 最新的问题
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
Martin Fowler
Martin Fowler
Jina AI
Jina AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
L
LangChain Blog
D
Docker
腾讯CDC

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Pygame Snake, Pt. 4
David Newber · 2026-05-03 · via DEV Community

In Parts 1, 2, & 3 we have put together everything, it would seem, besides the actual game. But now we come to the point.

The first set of changes we need to make involve the snake. Instead of a single dot variable, we'll need a snake list. We'll also introduce a variable to allow the snake to grow.

First, modify this line:

dot = pygame.Vector2(W / 2, H / 2)

Enter fullscreen mode Exit fullscreen mode

to

snake = [pygame.Vector2(W / 2, H / 2)]

Enter fullscreen mode Exit fullscreen mode

Just above the while loop, introduce the new variable:

grow = 3

Enter fullscreen mode Exit fullscreen mode

Inside the while loop, we'll make several changes.

A quick conceptual note: Previously, there was a single ("scalar") variable, dot. The animation worked by updating the x and y attributes of dot (via the code dot += vel). Now, the way the animation works will change slightly.

Each frame, the location where the head of the snake should move to in the next frame is calculated and stored in a variable called new_head. This location can be checked for conditions such as the snake running into itself, or into food, e.g.

As long as the user hasn't hit a game-ending condition, this new head position will be appended to the snake list. Then, under normal conditions, the first element is removed from the list. This (append and pop sequence) drives the animation.

For a single dot, this would produce the same effect in a somewhat convoluted way. But it makes it easy to let the snake grow -- simple skip the pop step.

Replace dot += vel with the following:

    new_head = snake[-1] + vel

Enter fullscreen mode Exit fullscreen mode

In Python, negative indexes start from the end of the list, with -1 being the last item, -2 second to last, and so on. It's particularly quick and handy way to reference the last item in the list without having to resort to calling len().

To check whether the snake is going to run into itself, under the above line you can add the following if statement:

    if new_head in snake:
        print("hit self")
        break

Enter fullscreen mode Exit fullscreen mode

And under that, perform the append step:

    snake.append(new_head)

Enter fullscreen mode Exit fullscreen mode

Next comes the pop step, which depends on the value of grow. It's initially set to 3 in order to easily start the snake length at 4 (e.g.), without having to specify the location of each snake segment in advance.

Under the above code, add the following:

    if grow > 0:
        grow -= 1
    else:
        snake.pop(0)

Enter fullscreen mode Exit fullscreen mode

Finally, and crucially, the code needs to be updated to draw the whole snake rather than a single dot. However, it will still drawn piece-by-piece, which can be exploited in updating the code.

Find the two lines of code that draw the dot.

    square = pygame.Rect(dot * S, (S, S))
    screen.fill("black", square)

Enter fullscreen mode Exit fullscreen mode

Add this for loop above those two lines, and then indent them. The result should look like this:

    for dot in snake:
        square = pygame.Rect(dot * S, (S, S))
        screen.fill("black", square)

Enter fullscreen mode Exit fullscreen mode

Try running your code. Your should immediately grow to a length of 4, and move around properly.

Full code at this point:

import pygame

W = 30
H = 30
S = 20

# pygame setup
pygame.init()
screen = pygame.display.set_mode((W * S, H * S))
clock = pygame.time.Clock()
running = True

# game setup
snake = [pygame.Vector2(W / 2, H / 2)]
vel = pygame.Vector2(1, 0)

key_vel = {
    pygame.K_RIGHT: pygame.Vector2(1, 0),
    pygame.K_DOWN: pygame.Vector2(0, 1),
    pygame.K_LEFT: pygame.Vector2(-1, 0),
    pygame.K_UP: pygame.Vector2(0, -1)
}

grow = 3

def place_food():
    global food_pos
    food_pos = pygame.Vector2(
        random.randrange(W),
        random.randrange(H)
    )
    while food_pos in snake:
        food_pos = pygame.Vector2(
            random.randrange(W),
            random.randrange(H)
        )

place_food()

while running:
    # poll for events
    for event in pygame.event.get():
        # pygame.QUIT = user closed window
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key in key_vel:
            vel = key_vel[event.key]

    # fill buffer with white
    screen.fill("white")

    new_head = snake[-1] + vel

    if new_head in snake:
        print("hit self")
        break

    if new_head == food_pos:
        grow += 1
        place_food()

    snake.append(new_head)

    if grow > 0:
        grow -= 1
    else:
        snake.pop(0)

    for dot in snake:
        square = pygame.Rect(dot * S, (S, S))
        screen.fill("black", square)

    square = pygame.Rect(food_pos * S, (S, S))
    screen.fill("green", square)

    # copy buffer to screen
    pygame.display.flip()

    # limits FPS
    clock.tick(20)

pygame.quit()

Enter fullscreen mode Exit fullscreen mode