No crescente mundo da Inteligência Artificial, uma das formas de explorar o poder das máquinas é através de algoritmos inteligentes para tomada de decisão. Hoje, vamos embarcar em um projeto prático e divertido: construir um jogo da velha para Arduino com uma estratégia de IA que garante que o computador nunca perca!
O jogo da velha, nosso conhecido “tic-tac-toe”, é um excelente campo de testes para entender como implementar lógica de decisão em sistemas embarcados. Nesta jornada, vamos utilizar uma abordagem específica da IA para criar um oponente virtual no seu Arduino que joga de forma inteligente, sempre buscando a melhor jogada para evitar a derrota e, quando possível, alcançar a vitória. Prepare-se para ter um adversário que, no mínimo, sempre empatará com você!
Neste guia passo a passo, vamos desvendar a estratégia por trás dessa “inteligência” imbatível, mostrando como implementar o algoritmo no seu código Arduino e como criar uma interface simples para jogar através do Monitor Serial. Este projeto é perfeito para quem quer aprender sobre os fundamentos da IA, algoritmos de busca e como aplicar esses conceitos na programação do seu microcontrolador Arduino. Vamos nessa?
No vasto campo da Inteligência Artificial, diferentes abordagens são empregadas para dotar máquinas com a capacidade de “pensar” e tomar decisões. Em jogos, duas categorias principais de IA se destacam: a determinística e a estocástica.
A IA determinística opera sob um conjunto fixo de regras, onde cada ação em um determinado estado sempre levará ao mesmo resultado. Não há espaço para aleatoriedade; a lógica e os cálculos ditam o comportamento. Imagine um robô seguindo um mapa predefinido: para cada instrução (“vire à esquerda”, “avance 5 passos”), a ação e o resultado são sempre os mesmos.
Em contraste, a IA estocástica incorpora elementos de aleatoriedade em seu processo de tomada de decisão. Para a mesma situação, a IA pode escolher ações diferentes com certas probabilidades. Essa abordagem é útil em jogos com informações incompletas (como pôquer, onde as cartas dos oponentes são desconhecidas) ou para introduzir imprevisibilidade e tornar o jogo contra a IA mais parecido com jogar contra um humano. Um exemplo seria um personagem de videogame que, ao avistar o jogador, tem uma probabilidade de 70% de correr para atacá-lo e 30% de se esconder.
Para o nosso projeto do jogo da velha inteligente no Arduino, adotaremos uma abordagem determinística, utilizando o renomado algoritmo Minimax. A escolha por uma IA determinística se deve à natureza perfeitamente conhecida do jogo da velha. Todas as informações estão sempre visíveis para ambos os jogadores, e não há elementos de sorte envolvidos. Nessa situação, uma estratégia baseada em regras fixas e na análise exaustiva de todas as possibilidades pode levar a uma jogabilidade perfeita por parte da IA. O Minimax nos permitirá explorar todas as ramificações do jogo para garantir que o Arduino sempre faça a melhor jogada possível.
O Algoritmo Minimax Explicado
O coração do nosso projeto é o algoritmo minimax – uma técnica de tomada de decisão usada em jogos de soma zero com dois jogadores (como xadrez, damas e jogo da velha). No jogo da velha, esse algoritmo permite que o computador analise todas as jogadas possíveis e suas consequências até o final do jogo, escolhendo sempre o movimento que maximiza suas chances de vitória.
Para entender o minimax, imagine que você está jogando xadrez e planejando vários movimentos à frente: “Se eu mover esta peça, meu oponente provavelmente responderá assim, então eu farei aquilo, e ele fará isso…” – O minimax formaliza exatamente esse processo de pensamento.
Vamos ilustrar com uma analogia:
Imagine que você está em um labirinto com vários caminhos possíveis. Alguns levam a tesouros (+10 pontos), outros a armadilhas (-10 pontos), e outros simplesmente a saídas neutras (0 pontos). O minimax é como se você pudesse ver todos os caminhos possíveis de uma vez e escolher aquele que garante o maior valor, considerando que há um adversário tentando te direcionar para o pior caminho possível.

Fonte: Autor
A lógica seguida pelo modelo é parecida com essa estrutura onde a cada jogada o algoritmo analisa as possibilidades e toma a melhor decisão, no caso buscando ganhar +10 (vitória), mas se não for possível busca o 0 (empate). Lembrando que essa imagem é só uma representação do fluxo do processo a quantidade de possibilidades analisadas a cada jogada é bem maior.
No contexto do jogo da velha:
Essa abordagem é possível no jogo da velha porque o espaço de possibilidades é relativamente pequeno. O jogo tem apenas 9 posições, resultando em um máximo de 9! (fatorial de 9) = 362.880 estados possíveis. Na prática, considerando as simetrias e estados impossíveis, o número real é muito menor, cerca de 765 estados finais únicos.
Para que o nosso jogo fique ainda mais interessante teremos 3 níveis de dificuldade fácil, médio e difícil e para construir a dificuldade cada nível terá acesso a diferentes níveis da árvore de possibilidades. Ou seja, conforme a dificuldade aumenta a IA consegue ver mais possibilidades a frente, simulando melhor assim o pensamento humano. Assim o nível fácil consegue ver poucos passos a frente, o médio vê alguns passos a mais e o avançado vê todos.
Para este projeto, você precisará de:
Código:
/*******************************************************************************************
* Jogo da Velha com Níveis de Dificuldade para Arduino
* Interface via Monitor Serial
* Este código implementa um jogo da velha com IA usando Minimax com diferentes profundidades
* Níveis:
* 1 - Fácil: Minimax com profundidade limitada (pensamento raso)
* 2 - Médio: Minimax com profundidade intermediária
* 3 - Difícil: Minimax completo (pensamento profundo)
*******************************************************************************************/
// Representação do tabuleiro (0 = vazio, 1 = jogador, 2 = Arduino)
int tabuleiro[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
// Combinações vencedoras
const int VITORIAS[8][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // linhas
{0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // colunas
{0, 4, 8}, {2, 4, 6} // diagonais
};
// Símbolos do tabuleiro
char simbolos[3] = {' ', 'X', 'O'};
// Variáveis de controle
bool turnoJogador = true;
bool jogoAtivo = false;
int dificuldade = 1; // 1-Fácil, 2-Médio, 3-Difícil
bool jogadorComeca = true;
bool primeiraPartida = true; //variável para controlar se é a primeira partida entre todas
// Pontuação
int jogadorPontos = 0;
int arduinoPontos = 0;
int empates = 0;
void setup() {
Serial.begin(9600);
while (!Serial) { ; }
// Inicializar o gerador de números aleatórios para decidir quem começa a primeira partida
randomSeed(analogRead(0));
exibirMenuInicial();
}
void loop() {
if (Serial.available() > 0) {
String entrada = Serial.readStringUntil('\n');
entrada.trim();
if (entrada.length() > 0) {
processarEntrada(entrada[0]);
}
}
}
void processarEntrada(char entrada) {
if (!jogoAtivo) {
switch(entrada) {
case '1': iniciarNovoJogo(); break;
case '2': exibirInstrucoes(); break;
case '3': selecionarDificuldade(); break;
default: Serial.println("Opção inválida. Digite 1, 2 ou 3.");
}
} else if (jogoAtivo && turnoJogador) {
if (entrada >= '1' && entrada <= '9') {
int posicao = entrada - '1';
if (tabuleiro[posicao] == 0) {
fazerJogada(posicao, 1); // Jogador (X)
verificarFimDeJogo();
if (jogoAtivo) {
//pausa para o jogador ver sua jogada
delay(1500);
turnoJogador = false;
Serial.println("\nArduino está pensando...");
delay(500);
realizarJogadaAI();
}
} else {
Serial.println("Posição ocupada! Escolha outra (1-9):");
}
} else {
Serial.println("Entrada inválida! Digite um número de 1 a 9:");
}
}
}
// Funções de interface
void exibirMenuInicial() {
Serial.println("\n\n==================================");
Serial.println("* JOGO DA VELHA INTELIGENTE *");
Serial.println("==================================");
Serial.println("1. Iniciar novo jogo");
Serial.println("2. Instruções");
Serial.println("3. Selecionar dificuldade");
Serial.println("==================================");
Serial.println("Digite uma opção:");
}
void exibirInstrucoes() {
Serial.println("\n\n==================================");
Serial.println("* INSTRUÇÕES *");
Serial.println("==================================");
Serial.println("- Você será X e o Arduino será O");
Serial.println("- Digite um número de 1 a 9 para jogar");
Serial.println("\n 1 | 2 | 3 \n ---------\n 4 | 5 | 6 \n ---------\n 7 | 8 | 9 ");
Serial.println("\nNíveis de dificuldade:");
Serial.println("1: Fácil (pensamento raso)");
Serial.println("2: Médio (pensamento moderado)");
Serial.println("3: Difícil (pensamento profundo)");
Serial.println("==================================");
}
void selecionarDificuldade() {
Serial.println("\nSelecione a dificuldade:");
Serial.println("1. Fácil");
Serial.println("2. Médio");
Serial.println("3. Difícil");
Serial.println("Digite o número (1-3):");
while (Serial.available() == 0) { delay(100); }
char entrada = Serial.readStringUntil('\n')[0];
if (entrada >= '1' && entrada <= '3') {
dificuldade = entrada - '0';
Serial.print("Dificuldade definida como: ");
Serial.println(dificuldade);
} else {
Serial.println("Opção inválida. Mantendo dificuldade atual.");
}
exibirMenuInicial();
}
// Funções do jogo
void iniciarNovoJogo() {
for (int i = 0; i < 9; i++) tabuleiro[i] = 0;
jogoAtivo = true;
// Lógica para determinar quem começa
if (primeiraPartida) {
// Na primeira partida, escolha aleatória de quem começa
jogadorComeca = random(2) == 0; // 50% de chance para cada um
primeiraPartida = false; // Não é mais a primeira partida
} else {
// Nas partidas seguintes, alterna quem começa
jogadorComeca = !jogadorComeca;
}
turnoJogador = jogadorComeca;
Serial.println("\n\n* NOVO JOGO INICIADO *");
if (turnoJogador) {
Serial.println("* Você começa! *");
} else {
Serial.println("* Arduino começa! *");
}
exibirTabuleiro();
if (!turnoJogador) {
// Adicionamos uma pequena pausa para o jogador ver o tabuleiro inicial
delay(1000);
Serial.println("\nArduino está pensando...");
delay(500);
realizarJogadaAI();
} else {
Serial.println("\nSua vez! Digite um número (1-9):");
}
}
void exibirTabuleiro() {
Serial.println("\n TABULEIRO");
Serial.println("=============");
for (int i = 0; i < 9; i += 3) {
Serial.print(" ");
for (int j = 0; j < 3; j++) {
Serial.print(" ");
if (tabuleiro[i+j] == 0) Serial.print(i+j+1);
else Serial.print(simbolos[tabuleiro[i+j]]);
if (j < 2) Serial.print(" |");
}
Serial.println();
if (i < 6) Serial.println(" ---+---+---");
}
Serial.println("=============");
}
void fazerJogada(int posicao, int jogador) {
tabuleiro[posicao] = jogador;
if (jogador == 1) Serial.println("\nVocê jogou:");
exibirTabuleiro();
}
void realizarJogadaAI() {
// Variável para armazenar a posição da melhor jogada encontrada pelo algoritmo
int melhorJogada = -1;
// Variável para armazenar a pontuação da melhor jogada
int melhorPontuacao = -1000;
// Calcula a melhor jogada baseado na dificuldade
int profundidadeMaxima;
switch(dificuldade) {
case 1: profundidadeMaxima = 1; break; // Fácil: pensamento raso
case 2: profundidadeMaxima = 4; break; // Médio: pensamento moderado
case 3: profundidadeMaxima = 8; break; // Difícil: pensamento profundo
default: profundidadeMaxima = 8;
}
// Percorre todas as 9 posições do tabuleiro procurando jogadas possíveis
for (int i = 0; i < 9; i++) {
if (tabuleiro[i] == 0) {
tabuleiro[i] = 2;
int pontuacao = minimax(tabuleiro, 0, false, profundidadeMaxima);
tabuleiro[i] = 0;
// Atualiza a melhor jogada se a pontuação atual for superior
if (pontuacao > melhorPontuacao) {
melhorPontuacao = pontuacao;
melhorJogada = i;
}
}
}
// Faz a jogada do Arduino
if (melhorJogada != -1) {
Serial.println("Arduino jogou:");
fazerJogada(melhorJogada, 2);
verificarFimDeJogo();
if (jogoAtivo) {
turnoJogador = true;
Serial.println("\nSua vez! Digite um número (1-9):");
}
}
}
int minimax(int tabuleiroAtual[], int profundidade, bool maximizando, int profundidadeMaxima) {
// Verifica se atingiu a profundidade máxima ou fim do jogo
int resultado = verificarVencedorSimulado(tabuleiroAtual);
if (resultado != 0 || profundidade >= profundidadeMaxima) {
if (resultado == 2) return 10 - profundidade;
else if (resultado == 1) return profundidade - 10;
else return 0;
}
// se maximizando for true busca a jogada que da maior score de pontuação
if (maximizando) {
int melhorPontuacao = -1000;
for (int i = 0; i < 9; i++) {
if (tabuleiroAtual[i] == 0) {
tabuleiroAtual[i] = 2;
int pontuacao = minimax(tabuleiroAtual, profundidade+1, false, profundidadeMaxima);
tabuleiroAtual[i] = 0;
melhorPontuacao = max(melhorPontuacao, pontuacao);
}
}
return melhorPontuacao;
}
// simula a vez do jogador para entender o que ele jogará
else {
int melhorPontuacao = 1000;
for (int i = 0; i < 9; i++) {
if (tabuleiroAtual[i] == 0) {
tabuleiroAtual[i] = 1;
int pontuacao = minimax(tabuleiroAtual, profundidade+1, true, profundidadeMaxima);
tabuleiroAtual[i] = 0;
melhorPontuacao = min(melhorPontuacao, pontuacao);
}
}
return melhorPontuacao;
}
}
void verificarFimDeJogo() {
// chama a funçaõ que valida o estado do tabuleiro final
int resultado = verificarVencedorSimulado(tabuleiro);
// dependendo do resultado imprime as saídas possiveis acrescentando as pontuações ao placar
if (resultado != 0) {
jogoAtivo = false;
exibirTabuleiro();
if (resultado == 1) {
Serial.println("\n*** PARABÉNS! VOCÊ VENCEU! ***");
jogadorPontos++;
} else if (resultado == 2) {
Serial.println("\n*** O ARDUINO VENCEU! ***");
arduinoPontos++;
} else {
Serial.println("\n*** EMPATE! ***");
empates++;
}
// exibe a pontuação e a telça de fim do jogo
exibirPontuacao();
exibirFimDeJogo();
}
}
int verificarVencedorSimulado(int tabuleiro[]) {
//valida se alguém venceu
for (int i = 0; i < 8; i++) {
if (tabuleiro[VITORIAS[i][0]] != 0 &&
tabuleiro[VITORIAS[i][0]] == tabuleiro[VITORIAS[i][1]] &&
tabuleiro[VITORIAS[i][0]] == tabuleiro[VITORIAS[i][2]]) {
return tabuleiro[VITORIAS[i][0]];
}
}
// verifica se o jogo esta em andamento
for (int i = 0; i < 9; i++) {
if (tabuleiro[i] == 0) return 0;
}
return 3; // Empate
}
void exibirPontuacao() {
//exibiçaõ da tela de pontos
Serial.println("\n PONTUAÇÃO");
Serial.println("=============");
Serial.print("Jogador (X): "); Serial.println(jogadorPontos);
Serial.print("Arduino (O): "); Serial.println(arduinoPontos);
Serial.print("Empates: "); Serial.println(empates);
Serial.println("=============");
}
void exibirFimDeJogo() {
//Exibição da tela de fim de partida
Serial.println("\n==================================");
Serial.println("* FIM DE JOGO *");
Serial.println("==================================");
Serial.println("1. Jogar novamente");
Serial.println("2. Instruções");
Serial.println("3. Alterar dificuldade");
Serial.println("==================================");
}Este código implementa um jogo da velha onde você joga contra o Arduino através do Monitor Serial. A principal característica é que o Arduino possui diferentes níveis de “inteligência” (dificuldade) para jogar contra você.
1. Variáveis Globais e Constantes:
tabuleiro[9]: Um array de inteiros que representa o tabuleiro 3×3. Os índices de 0 a 8 correspondem às posições:0 | 1 | 2
--+---+--
3 | 4 | 5
--+---+--
6 | 7 | 8
Os valores nas posições indicam: 0 para vazio, 1 para o jogador humano (‘X’), e 2 para o Arduino (‘O’). É inicializado vazio.
VITORIAS[8][3]: Uma constante que armazena as 8 combinações de índices do tabuleiro que resultam em uma vitória (as 3 linhas, as 3 colunas e as 2 diagonais). Usado para verificar o estado do jogo.simbolos[3]: Um array de caracteres para exibir o tabuleiro: um espaço para vazio, ‘X’ para o jogador (1) e ‘O’ para o Arduino (2).turnoJogador: Um booleano que indica de quem é a vez de jogar. true para o jogador, false para o Arduino.jogoAtivo: Um booleano que indica se há um jogo em andamento. true quando um jogo começa, false quando termina (vitória, derrota ou empate) ou quando o menu é exibido.dificuldade: Um inteiro que armazena o nível de dificuldade selecionado (1, 2 ou 3).jogadorComeca: Um booleano usado para alternar quem começa a cada novo jogo.primeiraPartida: Uma variável para definir se é a primeira partida do jogo.jogadorPontos, arduinoPontos, empates: Variáveis para manter o placar das partidas.2. Função setup():
Esta é a função de inicialização do Arduino, executada uma vez quando o programa inicia. Configura a comunicação serial e imediatamente chama a função para exibir o menu principal do jogo. No setup temos o randomSeed(analogRead(0)) que é usado internamente para definir o tipo de aleatoriedade em jogadorComeca = random(2) == 0 na função iniciarNovoJogo() assim definimos aleatoriamente quem começa a primeira partida do jogo.
4. Função loop():
Esta é a função principal do Arduino, executada repetidamente. Ela monitora a porta serial. Se algum dado for recebido, lê a linha completa, remove espaços e passa o primeiro caractere lido para a função processarEntrada.
5. Função processarEntrada(char entrada):
Esta função é o coração do tratamento de entrada.
jogoAtivo é false. Se for, significa que o usuário está no menu inicial ou final, e a entrada (‘1’, ‘2’ ou ‘3’) é interpretada como uma opção de menu.jogoAtivo for true E for a vez do turnoJogador, a entrada é esperada ser um número de ‘1’ a ‘9’.tabuleiro está vazia.fazerJogada).verificarFimDeJogo).delay e chama realizarJogadaAI.6. Funções de Interface (Display):
exibirMenuInicial():
Imprime o menu principal do jogo no Monitor Serial.
exibirInstrucoes():
Explica as regras básicas do jogo, mostra como as posições correspondem aos números de 1 a 9 e detalha os níveis de dificuldade.
selecionarDificuldade():
Exibe as opções de dificuldade, espera (bloqueando o programa com o while) por uma entrada do usuário. Valida a entrada e atualiza a variável global dificuldade. Em seguida, volta para o menu inicial.
exibirTabuleiro():
Formata e imprime o estado atual do tabuleiro no Monitor Serial, usando os simbolos (‘X’, ‘O’, ou o número da posição vazia).
exibirPontuacao():
Exibe o placar atual do jogo.
exibirFimDeJogo():
Exibe o menu de opções que aparece após o término de uma partida.
7. Funções de Lógica do Jogo:
iniciarNovoJogo():
Prepara o tabuleiro para um novo jogo, reseta a variável jogoAtivo, alterna quem começa a próxima rodada e exibe as informações iniciais. Se for a vez do Arduino, já chama a função para ele jogar.
fazerJogada(int posicao, int jogador):
Esta função executa uma jogada simples: preenche a posição especificada no tabuleiro com o valor do jogador (1 ou 2) e exibe o tabuleiro atualizado.
verificarFimDeJogo():
Verifica se o jogo terminou chamando verificarVencedorSimulado no tabuleiro principal. Se terminou, define jogoAtivo como false, exibe o tabuleiro final, anuncia o resultado, atualiza a pontuação e mostra os menus de pontuação e fim de jogo.
verificarVencedorSimulado(int tabuleiro[]):
Esta função é uma utilidade que verifica o estado de qualquer tabuleiro (passado como argumento, seja o tabuleiro real ou uma cópia usada no Minimax) para determinar se há um vencedor ou um empate. Retorna 1 se o jogador 1 vence, 2 se o jogador 2 vence, 3 se for empate, e 0 se o jogo ainda estiver em andamento.
8. Funções de IA (Minimax):
realizarJogadaAI():
Esta função é responsável por calcular e executar a jogada do Arduino.
profundidadeMaxima para a busca Minimax com base na variável dificuldade.minimax para avaliar a pontuação desse estado do tabuleiro. A chamada inicial para minimax usa false para o parâmetro maximizando, pois depois da jogada do Arduino, o próximo a jogar (na simulação) será o jogador humano, que tenta minimizar o score do Arduino. A profundidade inicial é 0.tabuleiro[i] = 0) para que o tabuleiro volte ao estado original antes de simular a próxima jogada possível (isso é o backtracking essencial para a recursão do Minimax).minimax com a melhorPontuacao encontrada até agora e atualiza melhorPontuacao e melhorJogada se a pontuação atual for melhor para o Arduino.melhorJogada encontrada chamando fazerJogada.minimax(int tabuleiroAtual[], int profundidade, bool maximizando, int profundidadeMaxima):
Esta é a implementação do algoritmo Minimax. É uma função recursiva que explora o “árvore do jogo” (todas as jogadas possíveis a partir de um determinado estado).
tabuleiroAtual[]: Uma cópia do tabuleiro para simular jogadas.profundidade: O nível atual na árvore de busca (começa em 0).maximizando: Um booleano; true significa que é o turno do jogador que tenta maximizar o score (o Arduino), false significa que é o turno do jogador que tenta minimizar o score (o jogador humano).profundidadeMaxima: O limite de profundidade da busca.verificarVencedorSimulado retorna algo diferente de 0).profundidadeMaxima) definida pela dificuldade é atingida.10 - profundidade. Quanto menor a profundidade (mais rápido a vitória), maior o score.profundidade - 10. Quanto menor a profundidade (mais rápido a derrota), menor (mais negativo) o score. Isso faz com que o Arduino evite estados onde o jogador ganha rapidamente dentro do limite de busca.0.maximizando é true (vez do Arduino na simulação): O objetivo é encontrar a jogada que leva ao maior score possível. Ele itera sobre as posições vazias, simula a jogada do Arduino, chama minimax recursivamente para o próximo nível (profundidade + 1) onde o jogador humano estará minimizando (false), e escolhe o máximo score retornado.maximizando é false (vez do Jogador na simulação): O objetivo é encontrar a jogada que leva ao menor score possível (do ponto de vista do Arduino). Ele itera sobre as posições vazias, simula a jogada do jogador, chama minimax recursivamente para o próximo nível (profundidade + 1) onde o Arduino estará maximizando (true), e escolhe o mínimo score retornado.tabuleiroAtual[i] = 0) após a chamada recursiva para não afetar as outras ramificações da árvore de busca (backtracking).Em resumo, o código funciona da seguinte forma:
processarEntrada direciona a entrada para as funções apropriadas, dependendo se um jogo está ativo ou se o usuário está interagindo com o menu.realizarJogadaAI), ele usa o algoritmo Minimax para decidir a melhor jogada.minimax) explora as possíveis sequências de jogadas até uma certa profundidade (definida pela dificuldade) ou até que um vencedor/empate seja alcançado na simulação. Ele atribui pontuações aos estados finais e usa a recursão para “propagá-las” de volta, assumindo que ambos os jogadores jogam otimamente (o Arduino maximiza seu score, o jogador minimiza o score do Arduino).realizarJogadaAI escolhe a jogada real que leva ao estado inicial simulado com a melhor pontuação Minimax encontrada.
Parabéns! Você acaba de implementar um jogo da velha com uma IA determinística que joga perfeitamente utilizando o algoritmo minimax. Este projeto ilustra conceitos fundamentais de algoritmos de busca, teoria dos jogos e tomada de decisão – blocos de construção essenciais para compreender sistemas de IA mais complexos.
O conhecimento adquirido pode ser estendido para jogos mais complexos e outros domínios de tomada de decisão. Aqui estão algumas ideias para expandir este projeto:
Além de IAs deterministicas temos outras soluções, mas é bom se atentar aos limites computacionais do arduino, ou sistema que esteja desenvolvendo, alguns modelos podem requerer bastante processamento e treinamento.
Estudante de Engenharia da Computação, especializado em curiosidades aparentemente aleatórias e desenvolvimento de software. Se eu não estiver pedalando agora estou estudando ou tentando aproveitar a energia dos raios.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!