WeBlog · Games

A frog, a fly and some black tea: a Python game written in Portuguese

A hungry frog, flies falling from the sky, five levels that get faster and faster, and arrows you can tap on a phone. It's a complete arcade game from the gallery, written in Python and in Portuguese. Here's how it works, what its words mean, and a few ways to make it your own.

Meet Chá Preto

O chá preto come as moscas means "Black Tea eats the flies". Black Tea (chá preto) is the frog's name. The game was made by Nilsan L. and shared to the PyWebLib gallery. Around here we call it Chapreto for short.

Nilsan L.'s game running in the Playground, in real time, from level 1 to level 3. Play it in the gallery

Here's how you play:

Play it Opens Chá Preto in the gallery. Works on a phone too.

Why it's a great game

Lots of first games are a sprite that moves. This one has everything a real arcade game needs, in 206 lines:

And it's written in Portuguese. Python's own words stay in English (if, while, import), but everything Nilsan named, the frog's speed, the scoreboard, the buttons, the comments, is in Portuguese. Python doesn't mind at all. You can name things in whatever language you think in, and your code is easier to read and fix when you do.

Portuguese to English: the words in the code

If you don't speak Portuguese, here's what the names in the code mean. Reading the code with this table is a great way to learn a few words, too.

PortugueseEnglish
sapofrog
moscafly
chá pretoblack tea (the frog's name)
jogadorplayer
pontospoints
vidas, vidalives, life
nivel, nívellevel
velocidade_moscafly speed
velocidade_sapofrog speed
placarscoreboard
nivel_textolevel text
obstaculoobstacle
esquerda, direitaleft, right
cima, baixoup, down
cenárioscenery
botões para celularbuttons for the phone
tecladokeyboard
paredeswalls
corcolour
mosca caindofalling fly
sapo comeu a moscathe frog ate the fly
nova posiçãonew position
chegou ao chãoreached the ground
fim do jogoend of the game (game over)

How the frog eats the fly

The heart of the game is a few lines inside the game loop. Every frame (30 times a second), the fly falls a little, and the game checks whether the frog has caught it:

from Nilsan's game
# 🪰 Mosca caindo
mosca.y += velocidade_mosca

# 🐸 Sapo comeu a mosca
if player.touches(mosca):

    pontos += 1
    placar.content = "Pontos: " + str(pontos)

    # (the level code goes here, see below)

    # 🪰 Nova posição da mosca
    mosca.x = random.randint(30, 450)
    mosca.y = 50

# 🪰 Mosca chegou ao chão
elif mosca.y > 350:

    vidas -= 1
    vida.content = "Vidas: " + str(vidas)

    mosca.x = random.randint(30, 450)
    mosca.y = 50

Line by line:

The scoreboard is three labels, made once at the top of the program with game.label(). Changing a label's .content changes the words on screen, which is what placar.content = "Pontos: " + str(pontos) does:

from Nilsan's game
# Placar
placar = game.label("Pontos: 0", 70, 30, size=20)
vida = game.label("Vidas: 3", 400, 30, size=20)
nivel_texto = game.label("Nível: 1", 240, 30, size=20)

Arrow buttons for phones, made from sprites

This is the clever part. A phone has no arrow keys, so Nilsan made four arrow emoji into sprites and put them in the bottom-left corner:

from Nilsan's game
# 🎮 Botões para celular
esquerda = game.sprite("⬅️", 50, 315, size=40)
baixo = game.sprite("⬇️", 100, 315, size=40)
cima = game.sprite("⬆️", 100, 265, size=40)
direita = game.sprite("➡️", 150, 315, size=40)

Look at the numbers: the up arrow sits at (100, 265), right above the down arrow at (100, 315), with left and right on either side. That's the same shape as the arrow keys on a keyboard. Then, in the loop, the keyboard and the touch buttons each get their turn (here are left and right; up and down work the same way):

from Nilsan's game
# ⌨️ Teclado
if game.pressed("left"):
    player.x -= velocidade_sapo

if game.pressed("right"):
    player.x += velocidade_sapo

# 📱 Celular
if esquerda.at_mouse() and game.clicked():
    player.x -= velocidade_sapo

if direita.at_mouse() and game.clicked():
    player.x += velocidade_sapo

Together, "the finger is on the left arrow and it just tapped" means "move left". The order is a nice touch, too. Python checks at_mouse() first, and only asks game.clicked() about the arrow that's actually under your finger. That matters, because clicked() answers True only once per tap: ask it about the wrong button first and the tap gets used up. The game.clicked() page warns about exactly that, and this game gets it right.

Try it yourself: any sprite can be a button. A 🔄 to restart, a ⏸️ to pause, a 🛒 to open a shop: put the sprite where you want it, then check sprite.at_mouse() and game.clicked() in your loop.

Levels that speed up

Each time the frog eats a fly, the game works out which level you're on:

from Nilsan's game
# 🎯 Nível + velocidade da mosca + velocidade do sapo
if pontos >= 40:
    nivel = 5
    velocidade_mosca = 10
    velocidade_sapo = 36

elif pontos >= 30:
    nivel = 4
    velocidade_mosca = 8
    velocidade_sapo = 35

elif pontos >= 20:
    nivel = 3
    velocidade_mosca = 6
    velocidade_sapo = 33

elif pontos >= 10:
    nivel = 2
    velocidade_mosca = 4
    velocidade_sapo = 32

else:
    nivel = 1
    velocidade_mosca = 2
    velocidade_sapo = 20

nivel_texto.content = "Nível: " + str(nivel)

Two things make this work well:

Just after this, a second if chain picks the pond's colour for each level, and a third part of the loop starts the 💗 falling once nivel >= 2. Same idea each time: one number, nivel, decides how the whole game looks and feels.

Lives, walls and the end

The frog has to stay in the pond. If it hops past an edge, it's put back on the edge, and it costs a life:

from Nilsan's game
# 🧱 Paredes
if player.x < 25:
    player.x = 25
    vidas -= 1
    vida.content = "Vidas: " + str(vidas)

The same four lines are there for the right edge, the top and the bottom. Whenever lives run out, game.game_over() shows a banner across the game and ends the loop:

from Nilsan's game
# ❤️ Fim do jogo
if vidas <= 0:
    game.game_over(
        "Fim do jogo! Pontos: " + str(pontos)
    )

Make it yours

Here are a few small things you could add, to Chá Preto or to your own version. Each is a line or two, and each has a page in the docs.

A leaderboard, in one line

Every published game can have its own leaderboard, and you don't have to build it. Add game.submit_score(pontos) right before game.game_over, and the game's page in the gallery gets a top 10. Add retry=True and one tap starts a new game, which is very handy on a phone:

upgrade.py
# ❤️ Fim do jogo
if vidas <= 0:
    game.submit_score(pontos)      # onto the leaderboard
    game.game_over(
        "Fim do jogo! Pontos: " + str(pontos),
        retry=True                 # one tap starts a new game
    )

Chá Preto has a game.game_over in two places, so give each one the new line. Each player keeps only their best score on the board, so it doesn't matter if a score is sent twice.

Sounds

game.sound() has ten built-in arcade sounds, no files needed. A "coin" for every fly and a "hit" for every lost life make the game feel alive:

upgrade.py
# 🐸 Sapo comeu a mosca
if player.touches(mosca):
    game.sound("coin")       # a happy "ding" for every fly

    pontos += 1
upgrade.py (next to each vidas -= 1)
vidas -= 1
game.sound("hit")        # ouch

Change the colour with game.background()

Chá Preto changes the pond's colour by calling game.window() again with a new background. PyWebLib also has a function made just for this, game.background(). It changes only the colour, and leaves the window, the keys being held and the score counter as they are. The game.window() page suggests keeping one game.window() at the top and using game.background() for every colour change after that:

upgrade.py
# 🎨 Cor do cenário
if nivel == 5:
    game.background("#800080")

elif nivel == 4:
    game.background("#FF0000")

Remember the best score

game.save() and game.load() remember a value between games, in that browser. Load the recorde (record, or best score) at the top and show it, then save it when it's beaten:

upgrade.py (at the top)
recorde = game.load("recorde", 0)     # best score so far, 0 the first time
recorde_texto = game.label("Recorde: " + str(recorde), 240, 60, size=16)
upgrade.py (at the end)
# ❤️ Fim do jogo
if vidas <= 0:
    if pontos > recorde:
        game.save("recorde", pontos)   # remembered next time

Start your own frog game

Want to build one from scratch? Here's a small starter that uses the same ideas as Chá Preto, with the same Portuguese names: a frog, a falling fly, a scoreboard, two arrows you can tap, flies that speed up every 5 points, and some of the upgrades from above (sounds, the leaderboard line and tap to play again) already in. It's just over 40 lines, so there's lots of room to add lives, levels and lotus flowers of your own.

sapo.py
import game
import random

game.window(480, 360, background="#87CEEB")

sapo = game.sprite("🐸", 240, 250, size=50)
mosca = game.sprite("🪰", random.randint(30, 450), 0, size=35)
placar = game.label("Pontos: 0", 70, 30)

# Botões para celular: two arrows you can tap
esquerda = game.sprite("⬅️", 50, 320, size=40)
direita = game.sprite("➡️", 430, 320, size=40)

pontos = 0
velocidade_mosca = 2     # how far the fly falls each frame
velocidade_sapo = 8      # how far the frog moves each frame

while game.playing():
    if game.pressed("left"):
        sapo.x -= velocidade_sapo
    if game.pressed("right"):
        sapo.x += velocidade_sapo
    if esquerda.at_mouse() and game.clicked():
        sapo.x -= 40
    if direita.at_mouse() and game.clicked():
        sapo.x += 40

    mosca.y += velocidade_mosca

    if sapo.touches(mosca):
        pontos += 1
        placar.content = "Pontos: " + str(pontos)
        game.sound("coin")
        velocidade_mosca = 2 + pontos // 5     # a little faster every 5 points
        mosca.x = random.randint(30, 450)
        mosca.y = 0

    elif mosca.y > 370:
        game.sound("lose")
        game.submit_score(pontos)
        game.game_over("Fim do jogo! Pontos: " + str(pontos), retry=True)

    game.frame()

Open in the Playground Opens with this code already in the editor. Click the game first so it hears the arrow keys.

pontos // 5 is whole-number division: 0 to 4 points give 0, 5 to 9 give 1, and so on, so the fly speed goes up by one every 5 points. It's a shorter way to make levels than an if chain, and the if chain is easier to tune by hand. Both are good choices.

Obrigado, Nilsan!

Thank you for the frog. Go and play Chá Preto, see how far you get, then make a game in your own language and share it to the gallery.

Play Chá Preto