Javascript – Jogo da Memória

Javascript – Jogo da Memória

Uma forma muito agradável de aprender javascript é usando a linguagem para criar coisas interessantes e/ou divertidas.

E uma proposta do fórum scriptbrasil foi realizar a segunda etapa de um torneio de javascript criando um jogo da memória.

Dessa vez eu não me sai tão bem quanto a primeira etapa, que era a de criar um formulário de validação em javascript, que eu já postei aqui no blog.

Este meu novo código ficou em terceiro lugar no torneio, mas mesmo não sendo considerado o melhor do torneio, ainda assim pode ser interessante para muita gente aprender alguns comandos ou pegar algumas idéias. Já que são utlizados diversos recursos da linguagem e o código está bem comentado, facilitando o entendimento do mesmo.

Veja como ficou o jogo clicando aqui.

Veja o código do jogo abaixo:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="pt">
<head><title>Torneio Javascript - ScriptBrasil - por Mestre SAM</title>

<meta name="copyright" 		content="Copyright 2009 by Mario SAM">
<meta name="description" 	content="Torneio Javascript ScriptBrasil">

<style type="text/css">
<!--
body { margin: 0px auto; font: 11px Verdana, Arial; }
ul { font: 16px Verdana, Arial, Helvetica, sans-serif; list-style-type: circle; text-align: left; margin-left: 150px; }
li { padding: 20px; }
li a { text-decoration: none; font-weight: bold; color: #0000ff; }
li a:hover { color: #ff0000; font-size: 20px; }
td { text-align: center; font-weight: bold; font-size: 18px; border: 1px solid #ffffff; }
._blank { background-color: #ffffff; font-size: 8px; }
td._0 { width: 25%; height: 95px; }
td._1 { width: 20%; height: 80px; }
td._2 { width: 15%; height: 60px; }
div#bloco { width: 900px; margin: 10px auto; padding: 15px; background-color: #efefef; border: 2px solid #333333; }
div#titulo { width: 100%; text-align: center; background-color: #333333; border: 1px solid #000000; color:#ffffff; }
div#titulo a { color:#ffffff; text-decoration: none; }
div#conteudo { width: 100%; margin-top: 10px; margin-bottom: 10px; }
div#_marcador { width: 20%; float:left; text-align: right; }
div#_tempo h5 { font: 23px Arial, Verdana; color: #333333; padding: 0px; margin: 0px; display: inline; }
div#_tempo h6 { font: 33px Arial, Verdana; color: #333333; padding: 0px; margin: 0px; display: inline; }
div#_score p { font: 50px Arial, Verdana; color: #0000ff; }
div#_pontos { width: 25%; float:right; }
div#_pontos h2 { font: 18px Verdana, Arial; color: #333333; font-weight: bold; margin-top: 0px; }
div#_novo a { text-decoration: none; background-color: #dcdcdc; color: #333333; padding: 5px; }
div#_novo a:hover { background-color: #8b8b8b; color: #ffffff; }
div#_clear { clear: both; }
div#rodape { width: 100%; background-color: #dcdcdc; border: 1px solid #000000; }
div._blank { width: 100%; height: 100%; font-size: 12px; font-weight: normal; font-style: italic; background-color: #ffffff; border: 1px solid #ffffff; }
div._blank:hover { background-color: #efefef; }
div#opcoes_de_jogo { display: none; }
div#opcoes_de_jogo h3 { margin-top: 80px; }
div#_tabela { width: 50%; float: left; min-height: 360px; height: auto !important; height: 360px; border: 1px solid #000000; background-color: #ffffff; text-align: center; }
div#_tabela h2 { font: 15px Verdana, Arial; margin-top: 50px; }
-->
</style>

<script type="text/javascript">
//evita explosoes de erro de scripts.
function blockError() { return true; }
window.onerror = blockError;

//variaveis globais de controle do jogo.
var doc = document;
var cronometro = '';
var iniciando = "";
var pontos = 0; var acertos = 0; var cliqueUm = 0; var cliqueDois = 0; var nomeUm = 0; var nomeDois = 0; var min=0; var seg=0; var tempoJogo = 0;

//funcao que embaralha todo o array.
shuffle = function(o) {
for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};

/**
 * PREPARA PARTIDA
 *
 * :: Verifica nivel de jogo e embaralha as cartas na tela, em seguida esconde jogo.
 */
function novoJogo() {
//limpa jogos anteriores.
clearTimeout( iniciando );
clearTimeout( cronometro );
cliqueUm=0; cliqueDois=0; nomeUm=0; nomeDois=0; pontos=0; acertos=0; min=0; seg=0; tempoJogo=0;

//inicializa as variaveis.
var sel = doc.getElementById('nivel').value;
var col = 4;
var ind = 0;
var ext = '';
var img = '';
var im0 = new Array("1","2","3","4","5","6","7","8","1","2","3","4","5","6","7","8");
var im1 = new Array("1","2","3","4","5","6","7","8","9","10","11","12","1","2","3","4","5","6","7","8","9","10","11","12");
var im2 = new Array("1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18");
var cor = new Array("","d5f3d8","f3f0d5","f3d5d7","ffd06b","a5965a","d4d4d4","ef83ff","a983ff","aafffe","f4ffaa","a2a1a1","7cc696","c67c7c","a97cc6","7ca9c6","abc67c","9724b0","24a4b0");

//exibe msg temporaria para preparar jogo.
doc.getElementById('_tabela').innerHTML = 'Carregando jogo...';

//verifica o nivel de jogo selecionado.
switch ( parseInt(sel) ) {
case 2: col=6; img=shuffle(im2); break; //nivel dificil
case 1: col=5; img=shuffle(im1); img[24]=img[12]; img[12]='vazio'; break; //nivel normal (tem um remajanamento pq eh impar)
case 0: col=4; img=shuffle(im0); break; //nivel facil
}

//monta saida.
ext += '<table width="100%" cellspacing="0" cellpadding="0" border="0">';
for ( i=0; i<col; i++ ) {
ext += '<tr>';
for ( j=0; j<col; j++ ) {
ext += '<td id="_td_'+ind+'" class="_'+sel+'" style="background-color:#'+cor[img[ind]]+';">'+img[ind]+'<\/td>';
ind++;
}
ext += '<\/tr>';
}
ext += '<\/table>';

//exibe resultado na tela.
doc.getElementById('_tabela').innerHTML = ext;

//oculta o jogo pra comecar a brincadeira.
iniciando = setTimeout("escondeJogo()",3000);
cronometro = setInterval("tempo()",1000);
}

/**
 * COMECANDO A BRINCADEIRA
 *
 * :: esconde as cartas coloridas atras de um carta branca para iniciar jogo.
 */
function escondeJogo() {
//pega todos os tds da tela.
var tds = document.getElementsByTagName("td");

//percorre toda tabela escondendo as cartas.
for ( i=0; i<tds.length; i++) {
tds[i].innerHTML = "<div class='_blank' onclick='javascript: abrirQuadro("+tds[i].innerHTML+","+i+");'><p style='font-size:9px;'>MestreSAM<\/p><\/div>";
//BUG: o IE6 nao aceita (escrita) innerHTML dentro de tables, uma alternativa seria adaptar o codigo abaixo.
//tds[i].appendChild( document.createTextNode("<div class='_blank' onclick='javascript: abrirQuadro("+tds[i].innerHTML+","+i+");'><p style='font-size:9px;'>MestreSAM<\/p><\/div>") );
}
}

/**
 * USUARIO JOGANDO
 *
 * :: sempre que uma carta for aberta vai verificar acerto/erro, somar pontos e verifica fim de jogo.
 * -op: numero que deve aparecer dentro da carta.
 * -nome: identifica a td (posicao numerica) que possue a carta oculta.
 */
function abrirQuadro( op, nome ) {
//guarda o valor das duas cartas.
if ( cliqueUm > 0 ) { cliqueDois=op; nomeDois=nome; } else { cliqueUm=op; nomeUm=nome; }

//devolve o numero pra carta. BUG: o numero sumiu pq foi usado innerHTML pra ocultar a carta.
doc.getElementById( '_td_'+nome ).innerHTML = op;

//verifica ao abrir a segunda carta.
if ( cliqueDois > 0 ) {
//se as cartas sao iguais.
if ( cliqueUm == cliqueDois ) {
pontos = parseInt(pontos)+10;
acertos = parseInt(acertos)+1;

//se as cartas estao erradas fecha quadro.
} else {
pontos = parseInt(pontos)-3;
setTimeout("opcaoErrada("+nomeUm+","+nomeDois+","+cliqueUm+","+cliqueDois+")",1000);
}

//limpa variavel de cartas selecionadas.
cliqueUm = 0; cliqueDois = 0; nomeUm = 0; nomeDois = 0;

//exibe pontuacao do jogo atual.
doc.getElementById("_score").innerHTML = "<p>"+pontos+"<\/p>";

//verifica se usuario fechou o jogo.
var sel = doc.getElementById('nivel').value;
if ( sel==0 && acertos==8 || sel==1 && acertos==12 || sel==2 && acertos==18 ) {
setTimeout("fimDeJogo()",1000);
}
}
}

/**
 * DUPLA DE CARTAS INVALIDAS
 *
 * :: eh preciso esconder novamente as cartas que foram clicadas e nao sao iguais.
 * - op1: identifica a td (posicao numerica) que deve volta a ser oculta. primeira carta aberta.
 * - op2: identifica a td (posicao numerica) que deve volta a ser oculta. segunda carta aberta.
 * - v1: o valor numerico que havia dentro da carta aberta. primeira carta aberta.
 * - v2: o valor numerico que havia dentro da carta aberta. segunda carta aberta.
 */
function opcaoErrada( op1, op2, v1, v2 ) {
//oculta as duas faces de cartas erradas.
doc.getElementById( '_td_'+op1 ).innerHTML = "<div class='_blank' onclick='javascript: abrirQuadro("+v1+","+op1+");'><p style='font-size:9px;'>MestreSAM<\/p><\/div>";
doc.getElementById( '_td_'+op2 ).innerHTML = "<div class='_blank' onclick='javascript: abrirQuadro("+v2+","+op2+");'><p style='font-size:9px;'>MestreSAM<\/p><\/div>";
}

/**
 * CRONOMETRO DO JOGO
 *
 * :: exibe, controle e armazena tempo decorrido de jogo.
 */
function tempo() {
saida = '';

//tempo que vai ser salvo no cookie.
tempoJogo="";
if ( min < 10 ) { tempoJogo += "0"+min; } else { tempoJogo += min; }
if ( seg < 10 ) { tempoJogo += "0"+seg; } else { tempoJogo += seg; }

//adiciona 1 segundo ao tempo decorrido.
seg = parseInt(seg)+1;

//se ja se passaram 59 segundos, zera segundo e sobe 1 minuto.
if ( parseInt(seg) > 59 ) {
seg = 0;
min = parseInt(min)+1;
}

//se ja se passaram 3 minutos de jogo dar game over.
if ( min=="03" && seg=="00" ) vocePerdeu();

//preparar numeros decimais.
saida += "<h5>";
if ( min < 10 ) { saida += "0"+min; } else { saida += min; }
saida += ":<\/h5><h6>";
if ( seg < 10 ) { saida += "0"+seg; } else { saida += seg; }
saida += "<\/h6>";

//exibir tempo na tela.
doc.getElementById("_tempo").innerHTML = saida;
}

/**
 * GAME OVER
 *
 * :: se usuario deixar a tela ou o tempo de jogo esgotar o jogo termina.
 */
function vocePerdeu() {
//se nao tem nenhuma mensagem de jogo finalizado.
var d = doc.getElementById('_tabela').innerHTML;

if ( d.indexOf("Parab") == -1 && d.indexOf("INICIE") == -1 ) {
//para o cronometro.
clearInterval( cronometro );

//prepara mensagem de saida.
var msg = "<h2>Game Over<\/h2>";

//verifica por que deu game over.
if ( ( min=="03" && seg=="00" ) ) {
msg += "<p>Tempo Esgotado<\/p>";
} else {
msg += "<p>Jogo Abandonado<\/p>";
}

//exibe saida na tela.
doc.getElementById('_tabela').innerHTML = msg;
}
}

/**
 * JOGO FINALIZADO
 *
 * :: todas as cartas foram abertas, verifica pontuacao e possivel recorde e encerra partida.
 */
function fimDeJogo() {
//para o cronometro.
clearInterval( cronometro );

//saida de recorde.
var rec = 9999;

//prepara mensagem de saida.
var msg = "<h2>Parab&eacute;ns!<\/h2>";

//verifica o nivel que foi jogado.
var sel = doc.getElementById('nivel').value;

//mensagens de fim de jogo para quem jogou no facil.
if ( sel==0 && pontos==80 )
msg += "<p>Excelente! Voc&ecirc; est&aacute; pronto(a) para o pr&oacute;ximo n&iacute;vel.<\/p>";
else if ( sel==0 && pontos>70 )
msg += "<p>Grande Jogador! Quero ver jogar bem assim no pr&oacute;ximo n&iacute;vel.<\/p>";
else if ( sel==0 && pontos>55 )
msg += "<p>Muito Bom! Voc&ecirc; esta entre os melhores.<\/p>";
else if ( sel==0 && pontos>30 )
msg += "<p>Bom bom bom n&atilde;o t&aacute;! Mas ta bom ta bom.<\/p>";
else if ( sel==0 && pontos<30 )
msg += "<p>Continue Jogando! Vamos melhorar essa mem&oacute;ria de peixe.<\/p>";

//mensagens de fim de jogo para quem jogou no normal.
if ( sel==1 && pontos==120 )
msg += "<p>Finalmente! Voc&ecirc; atingiu um outro n&iacute;vel de memoriza&ccedil;&atilde;o.<\/p>";
else if ( sel==1 && pontos>100 )
msg += "<p>Excelente! N&atilde;o &eacute; todo mundo que consegue isso.<\/p>";
else if ( sel==1 && pontos>70 )
msg += "<p>Muito Bom! Voc&ecirc; esta entre os melhores.<\/p>";
else if ( sel==1 && pontos>40 )
msg += "<p>Voc&ecirc; pode fazer melhor que isso! Tenho certeza.<\/p>";
else if ( sel==1 && pontos<40 )
msg += "<p>Lembre-se! Este &eacute; um jogo que testa a sua mem&oacute;ria.<\/p>";

//mensagens de fim de jogo para quem jogou no dificil.
if ( sel==2 && pontos==180 )
msg += "<p>Cabuloso! Impressionante, vai trabalhar na NASA.<\/p>";
else if ( sel==2 && pontos>140 )
msg += "<p>Uau! Praticamente um Chuck Noris.<\/p>";
else if ( sel==2 && pontos>100 )
msg += "<p>Muito Bom! Voc&ecirc; esta entre os melhores.<\/p>";
else if ( sel==2 && pontos>50 )
msg += "<p>Voc&ecirc; esta melhorando! Tente novamente.<\/p>";
else if ( sel==2 && pontos<50 )
msg += "<p>Foi avisado! Este n&iacute;vel &eacute; para os melhores.<\/p>";

//verifica recorde.
var cookie = valorDoCookie();
var lista = cookie.split(",");

//salva o recorde pro nivel facil.
if ( sel==0 ) {
for ( i=0; i<3; i++ ) { //apenas os 3 primeiros sao nivel juninho.
var valores = lista[i].split("|");
var nome  = valores[0];
var point = valores[1];
var tempo = valores[2];

//se bateu o recorde de pontos.
if ( pontos >= point && rec == 9999 ) {
//se o recorde for igual comparar tempo decorrido.
if ( pontos == point ) {
if ( parseInt(tempoJogo) < parseInt(tempo) ) {
rec = i;
}
}
if ( pontos > point ) {
rec = i;
}
}
}
}
//salva o recorde no nivel normal.
if ( sel==1 ) {
for ( i=3; i<6; i++ ) { //do terceiro ao sexto sao nivel jogador.
var valores = lista[i].split("|");
var nome  = valores[0];
var point = valores[1];
var tempo = valores[2];

//se bateu o recorde de pontos.
if ( pontos >= point && rec == 9999 ) {
//se o recorde for igual comparar tempo decorrido.
if ( pontos == point ) {
if ( parseInt(tempoJogo) < parseInt(tempo) ) {
rec = i;
}
}
if ( pontos > point ) {
rec = i;
}
}
}
}
//salva o recorde no nivel dificil.
if ( sel==2 ) {
for ( i=6; i<9; i++ ) { //apenas os 3 ultimos sao nivel viciado.
var valores = lista[i].split("|");
var nome  = valores[0];
var point = valores[1];
var tempo = valores[2];

//se bateu o recorde de pontos.
if ( pontos >= point && rec == 9999 ) {
//se o recorde for igual comparar tempo decorrido.
if ( pontos == point ) {
if ( parseInt(tempoJogo) < parseInt(tempo) ) {
rec = i;
}
}
if ( pontos > point ) {
rec = i;
}
}
}
}

//pedir nome do recordista.
if ( rec < 99 ) {
msg += '<p><b>SUAS INICIAIS:<\/b>&nbsp;<input type="text" name="novo_recorde" id="novo_recorde" value="" maxlength="3" size="4" style="text-transform: uppercase;">&nbsp;<input type="button" name="btn_gravar" value="salvar" onclick="javascript:gravarRecorde('+rec+');"><\/p>';
}

//exibe saida na tela.
doc.getElementById('_tabela').innerHTML = msg;
}

/**
 * SALVA EM COOKIE O NOVO RECORDE
 *
 * :: pega os recordes do cookie e verifica em que posicao deve ser incluido o novo recorde.
 * - pos: a posicao do array(virtual) em que o novo recorde deve ser inserido.
 */
function gravarRecorde( pos ) {
var saida = '';
var cookie = valorDoCookie();
var lista = cookie.split(",");

//verifica a ordem que deve ser incluida o novo recorde.
for ( i=0; i<lista.length; i++ ) {
if ( pos == i ) {
//possivel remanejamento de posicoes.
if ( pos==0 || pos==1 || pos==2 ) {
if ( pos==1 ) lista[2] = lista[1];
if ( pos==0 ) { lista[2] = lista[1]; lista[1] = lista[0]; }
} else if ( pos==3 || pos==4 || pos==5 ) {
if ( pos==4 ) lista[5] = lista[4];
if ( pos==3 ) { lista[5] = lista[4]; lista[4] = lista[3]; }
} else if ( pos==6 || pos==7 || pos==8 ) {
if ( pos==7 ) lista[8] = lista[7];
if ( pos==6 ) { lista[8] = lista[7]; lista[7] = lista[6]; }
}
//adiciona novo recorde.
lista[i] = doc.getElementById('novo_recorde').value+'|'+pontos+'|'+tempoJogo;
}

//continua guardando os demais recordes remaneijados.
if ( saida != '' ) saida += ',';
saida += lista[i];
}

//salva o recorde.
var d = new Date();
    d.setTime(d.getTime()+(30*24*60*60*1000));
criarCookie( "c_rank_jm", saida, d.toGMTString() );

//exibe na tela o recorde recem salvo.
exibeMenu();
}

/**
 * GUARDA OS RECORDES DO JOGO EM COOKIE
 *
 * :: grava efetivamente um cookie no navegador com os pontos.
 * - nome: nome de identificacao do cookie para futuras verificacoes.
 * - valor: conteudo que sera armazenado no cookie.
 * - expirar: tempo de vida do cookie no navegador.
 */
function criarCookie( nome, valor, expirar ) {
    document.cookie = nome + "=" + valor + "; expires="+expirar;
}

/**
 * EXIBE O RANK DOS MELHORES JOGADORES NA TELA
 *
 * :: depois de pegar o valor do cookie quebra a string e exibe formatado na tela o ranking.
 */
function carregarPontuacao() {
//se o cookie existe no navegador.
if ( document.cookie.match(eval("/" + 'c_rank_jm' + "=/")) ) {
//recupera o valor.
var valorCookie = valorDoCookie();

//quebra valores.
var saida = '';
var lista = valorCookie.split(",");
for ( i=0; i<lista.length; i++ ) {
var valores = lista[i].split("|");
if ( i==0 ) saida += '<h2>RANKING JUNINHO<\/h2>';
if ( i==3 ) saida += '<hr><h2>RANKING JOGADOR<\/h2>';
if ( i==6 ) saida += '<hr><h2>RANKING VICIADO<\/h2>';
if ( valores[2]==undefined ) { criarCookie('c_rank_jm','',-1); exibeMenu(); } //correcao BUG cookie nao criado.
saida += '<p>'+valores[0].toUpperCase()+' - '+valores[1]+' - '+valores[2].substring(0,2)+":"+valores[2].substring(2,4)+'</p>';
}
saida += "<a href=\"#\" onclick=\"javascript: criarCookie('c_rank_jm','',-1); exibeMenu();\">reiniciar placar</a>";

//mostra rank na tela.
doc.getElementById('_pontos').innerHTML = saida;
} else {
var d = new Date(); //obtem uma data de criacao do cookie.
d.setTime(d.getTime()+(30*24*60*60*1000));
var valor = valorDoCookie(); //valor de instalacao.
criarCookie( "c_rank_jm", valor, d.toGMTString() );
//cria um cookie e volta pro metodo pra exibir pontuacao.
carregarPontuacao();
}
}

/**
 * INFORMA O VALOR DO RANK QUE ESTA EM COOKIE
 *
 * :: recupera o valor armazenado no cookie ou cria um novo e devolve resultado em string.
 */
function valorDoCookie() {
var valorCookie = document.cookie.replace(eval("/^.*?" + 'c_rank_jm' + "=([^\\s;]*).*$/"), "$1");
if ( valorCookie == "" ) {
valorCookie = "AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000,AAA|0|0000";
}
return valorCookie;
}

/**
 * PREPARA TELA PARA SELECIONAR NOVO JOGO
 *
 * :: apenas reinicia as opcoes da tela para iniciar um novo jogo.
 */
function exibeMenu() {
clearInterval( cronometro );

carregarPontuacao();
doc.getElementById('_tempo').innerHTML = "<h5>00:<\/h5><h6>00<\/h6>";
doc.getElementById('_score').innerHTML = "<p>00<\/p>";
doc.getElementById('_tabela').innerHTML = doc.getElementById('opcoes_de_jogo').innerHTML;
}
</script>
</head>

<!-- se o cara abandonar o jogo = game over (pra evitar print screen!) -->
<body onblur="javascript: vocePerdeu();" onload="javascript: exibeMenu();">

<div id="bloco">
<div id="titulo">
<h1>&oslash;&nbsp;&Oslash;&nbsp;JOGO DA MEM&Oacute;RIA&nbsp;&Oslash;&nbsp;&oslash;</h1>
<p><a href="http://www.mariosam.com">por Mario SAM <i>(MestreSAM)</i></a></p>
</div>
<div id="conteudo">
<div id="_tabela"></div>
<div id="_marcador">
<div id="_tempo"></div>
<div id="_score"></div>
<div id="_novo"><a href="#" onclick="javascript: exibeMenu();">NOVO JOGO</a></div>
</div>
<div id="_pontos"></div>
<div id="_clear"></div>
</div>
<div id="rodape">
<p>&nbsp;Este jogo da mem&oacute;ria ficou em terceiro lugar no torneio de javascript segunda etapa do f&oacute;rum ScriptBrasil. (obs.: N&atilde;o roda no IE6)</p>
</div>
</div>

<input type="hidden" name="nivel" id="nivel" value="">

<div id="opcoes_de_jogo">
<h3>INICIE UM NOVO JOGO:</h3>
<ul>
<li><a href="#" onclick="javascript:doc.getElementById('nivel').value='0';novoJogo();" title="Pra quem quer apenas brincar!">Juninho</a></li>
<li><a href="#" onclick="javascript:doc.getElementById('nivel').value='1';novoJogo();" title="Levando a brincadeira mais a serio!">O Jogador</a></li>
<li><a href="#" onclick="javascript:doc.getElementById('nivel').value='2';novoJogo();" title="Nivel Einstein. Cuidado! Risco de perder a memoria!">Viciado</a></li>
</ul>
</div>

</body>
</html>

Eu sei que não é o melhor código do mundo, se fosse assim eu teria tirado o primeiro lugar no torneio. :P

Mas espero que seja ao menos interessante para a maioria.

Sucesso!



Sobre o Autor

MarioSAM é faixa preta 2Dan de Taekwondo. Entusiasta do marketing online. Especulador do mercado financeiro. Projetista web com certificação SCJP da SUN. Com 10 anos de experiência em desenvolvimento de sistemas.