Anatomia

Como um robô é montado

Um robô é um arquivo de texto com algumas instruções. Esta página explica cada pedaço desse arquivo, sem pressupor que você já saiba Python.

As duas linhas que todo robô tem

as duas primeiras linhas
from robot import Robot


class MeuRobo(Robot):
    ...

A primeira linha diz: "pegue o robô básico que já vem pronto no jogo". Esse robô básico já sabe andar, girar, atirar e enxergar, então você não precisa ensinar nada disso.

A segunda linha diz: "o meu robô é uma versão personalizada daquele robô básico". Aquele (Robot) entre parênteses é o que dá ao seu robô todos os comandos do jogo. Sem ele, o seu arquivo é só um texto qualquer.

MeuRobo é o nome que vai aparecer em cima do tanque na arena. Troque pelo nome do seu time.

Sobre a indentação

Em Python, os espaços no começo da linha têm significado. Tudo que pertence ao seu robô fica deslocado quatro espaços para a direita, e tudo que pertence a um comando específico fica deslocado mais quatro. O PyCharm faz isso sozinho quando você aperta Enter depois de uma linha terminada em dois-pontos. Se aparecer o erro IndentationError, é sinal de que uma linha ficou fora do lugar.

O ciclo de vida: quem chama o quê

Você nunca "roda" o seu robô diretamente. Quem manda é o jogo: ele chama certos comandos do seu robô em certos momentos. Seu trabalho é preencher esses comandos.

init() uma única vez run() enfileira os comandos O jogo executa a fila, um passinho por quadro 5 px ou 5 graus de cada vez quando a fila esvazia, run() é chamado de novo sensors() chamado a cada quadro, o tempo todo onHitWall(), onTargetSpotted(), ... chamados só quando aquilo acontece Vida chega a zero → onRobotDeath() e o robô sai da arena quem deu o último dano ganha uma eliminação
Quem chama cada parte do seu robô, e quando.

O detalhe que mais confunde

run() não roda a cada instante

Comandos como move(100) não acontecem na hora: eles entram numa fila. O jogo executa essa fila devagar, um pedacinho por quadro. Só quando a fila termina é que o run() é chamado de novo, do começo.

Consequência prática: nunca coloque um while True: dentro do run(). O run() já é o laço do robô: quem repete é o jogo. Um laço infinito ali dentro congela a partida.

O stop(): fazendo uma coisa depois da outra

Os comandos que você enfileira acontecem ao mesmo tempo, a não ser que você separe. O stop() é o separador.

Sem stop() no meio

ao mesmo tempo
self.move(100)
self.turn(90)
self.stop()

Anda e gira simultaneamente: o robô faz uma curva.

Com stop() no meio

um depois do outro
self.move(100)
self.stop()
self.turn(90)
self.stop()

Anda em linha reta, para, e só então gira no lugar.

Isso vale para qualquer combinação: andar enquanto gira o radar, virar o corpo enquanto a arma aponta para outro lado, e assim por diante.

Os 11 comandos obrigatórios

O jogo espera encontrar estes onze comandos no seu robô. Eles podem estar vazios, mas precisam existir.

ComandoQuando o jogo chama
init(self)Uma vez, quando o robô entra na arena. É onde se definem cores e ajustes iniciais.
run(self)Toda vez que a fila de comandos esvazia. É o cérebro do robô.
sensors(self)A cada quadro. Serve para observar a arena.
onHitWall(self)Quando o robô bate numa parede.
onRobotHit(self, ...)Quando o seu robô trombou em outro.
onHitByRobot(self, ...)Quando outro robô trombou no seu.
onHitByBullet(self, ...)Quando o robô leva um tiro.
onBulletHit(self, ...)Quando um tiro seu acerta alguém.
onBulletMiss(self, ...)Quando um tiro seu sai da arena sem acertar.
onTargetSpotted(self, ...)Quando o radar enxerga um inimigo.
onRobotDeath(self)Quando a vida do robô chega a zero.

A página de eventos explica cada um em detalhe, com as informações que o jogo entrega junto.

Se faltar um deles, a arena fecha

Quando o jogo precisa avisar o seu robô de alguma coisa e o comando correspondente não existe (ou existe mas dá erro), o programa inteiro é encerrado no meio da partida, para todo mundo. Por isso todo robô deste site começa com os onze comandos escritos, mesmo os vazios.

Como deixar um comando vazio

Em Python não existe comando vazio de verdade: é preciso escrever pass, que significa "não faça nada aqui".

um comando que não faz nada
def onBulletMiss(self, bulletId):
    pass

O self que aparece em todo comando é o jeito do robô se referir a si mesmo. É por isso que os comandos são sempre escritos como self.move(100): leia como "eu, ande 100".

O robô mínimo, completo

Este é o menor robô válido do jogo. Ele não faz nada além de escolher as cores, mas entra na arena sem quebrar nada e é o ponto de partida certo.

Robots/RoboMinimo.py
from robot import Robot


class RoboMinimo(Robot):

    def init(self):
        """Roda uma unica vez, quando o robo entra na arena."""
        # Cores em RGB: (vermelho, verde, azul), de 0 a 255
        self.setColor(66, 185, 193)          # o corpo do tanque
        self.setGunColor(255, 255, 255)      # o cano da arma
        self.setRadarColor(255, 204, 77)     # o radar
        self.setBulletsColor(66, 185, 193)   # os tiros

        # Deixa o campo de visao do radar visivel. Otimo para entender
        # o que o robo enxerga enquanto voce testa.
        self.radarVisible(True)

    def run(self):
        """O cerebro do robo. Escreva aqui o que ele deve fazer."""
        pass

    def sensors(self):
        """Roda a cada quadro. Use para observar a arena."""
        pass

    def onHitWall(self):
        """Bati numa parede."""
        pass

    def onRobotHit(self, robotId, robotName):
        """EU bati em outro robo."""
        pass

    def onHitByRobot(self, robotId, robotName):
        """OUTRO robo bateu em mim."""
        pass

    def onHitByBullet(self, bulletBotId, bulletBotName, bulletPower):
        """Levei um tiro."""
        pass

    def onBulletHit(self, botId, bulletId):
        """Meu tiro acertou alguem."""
        pass

    def onBulletMiss(self, bulletId):
        """Meu tiro saiu da arena sem acertar ninguem."""
        pass

    def onTargetSpotted(self, botId, botName, botPos):
        """Meu radar viu um inimigo."""
        pass

    def onRobotDeath(self):
        """Minha vida chegou a zero."""
        pass

As frases entre """ são comentários de documentação: o jogo ignora, servem só para você lembrar do que se trata. As linhas que começam com # também são ignoradas.

O próximo passo

Com o esqueleto no lugar, comece a preencher. A ordem que costuma funcionar melhor é:

  1. Fazer ele andar. Coloque um self.move(80) no run() e veja o que acontece.
  2. Fazer ele não bater na parede. Preencha o onHitWall().
  3. Fazer ele enxergar. Gire o radar no run().
  4. Fazer ele atirar. Coloque um self.fire(3) no onTargetSpotted().
  5. Fazer ele mirar direito. Aí sim entram as contas, e o RoboCacador mostra como.