{"id":19321379,"url":"https://github.com/andre2l2/tic-tac-toe","last_synced_at":"2025-10-16T03:42:47.883Z","repository":{"id":112414269,"uuid":"296975291","full_name":"andre2l2/tic-tac-toe","owner":"andre2l2","description":"Jogo da velha. Feito com express e socket.io","archived":false,"fork":false,"pushed_at":"2023-02-21T16:04:42.000Z","size":2801,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-10T05:16:47.609Z","etag":null,"topics":["brasil","css","express","game","game-development","gamedev","html5","javascript","nodejs","socket-io"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andre2l2.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-20T00:39:25.000Z","updated_at":"2025-05-27T12:08:47.000Z","dependencies_parsed_at":"2023-03-13T13:27:58.014Z","dependency_job_id":null,"html_url":"https://github.com/andre2l2/tic-tac-toe","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/andre2l2/tic-tac-toe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andre2l2%2Ftic-tac-toe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andre2l2%2Ftic-tac-toe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andre2l2%2Ftic-tac-toe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andre2l2%2Ftic-tac-toe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andre2l2","download_url":"https://codeload.github.com/andre2l2/tic-tac-toe/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andre2l2%2Ftic-tac-toe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279147682,"owners_count":26114108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["brasil","css","express","game","game-development","gamedev","html5","javascript","nodejs","socket-io"],"created_at":"2024-11-10T01:36:56.650Z","updated_at":"2025-10-16T03:42:47.850Z","avatar_url":"https://github.com/andre2l2.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TIC-TAC-TOE\n \n## Como rodar em sua máquina:\n \n- Faça um clone deste repositório para sua máquina;\n- Execute os comandos abaixo.\n \n```terminal\n   % npm install\n   % npm run start   \n```\n \nApós isso você poderá abrir em: http://localhost:3333\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/demo.gif\"\u003e\n\u003c/p\u003e\n \n### Quais tecnologias usei\nEsse projeto foi feito com _HTML_, _CSS_, e _JavaScript_ no FrontEnd. No BackEnd utilizei _node.js_, _express_ e _socket.io_ para fazer a coneção com o BackEnd. O tutorial abaixo explica como fiz tudo passo a passo. __Boa leitura!__\n \n---\n \n## Como fazer:\n \nHá algum tempo atrás eu havia feito um artigo sobre o [efeito gavetas](https://github.com/andre2l2/colors), e desta vez, resolvi criar mais um artigo. E irei explicar como fiz um jogo de cerquilha, ou jogo da velha para os mais íntimos. Onde você poderá conectar dois jogadores e disputar uma partida do jogo da velha. Só para deixar claro, eu sou o __mestre__ desse jogo!\n \nNeste projeto eu utilizei _express_ e _socket.io_, para gerenciar o estado do jogo, para isso vamos começar a criar nosso FrontEnd, pois assim será mais intuitivo. Vamos codar!\n \nPrimeiro crie 3 arquivos, _index.html_, _script.js_ e _style.css_. Agora em nosso html iremos precisar apenas de uma div com o id hash. Pois dentro dela vamos injetar todo o html restante via javascript.\n \n```html\n   \u003cbody\u003e\n       \u003cdiv id=\"hash\"\u003e\u003c/div\u003e\n \n       \u003cscript src=\"./script.js\"\u003e\u003c/script\u003e\n   \u003c/body\u003e\n```\n \nAgora temos que centralizar essa div utilizando o nosso amado CSS \u003c3.\n \n```CSS\n   * {\n       /* Padronizar alguns comportamentos do navegador */\n       margin: 0;\n       padding: 0;\n       box-sizing: border-box;\n   }\n \n   body {\n       /* Define uma altura e largura em relação a tela do usuário */\n       height: 100vh;\n       width: 100vw;\n \n       /* Centraliza os filhos do html ao centro */\n       display: flex;\n       flex-direction: column;\n       justify-content: center;\n       align-items: center;\n   }\n```\n \nCom todo esse CSS pronto, vem a parte mais interessante em minha opinião, a logica do jogo. Vamos lá!\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"https://i.pinimg.com/originals/c6/f1/3b/c6f13b01a53d7152d7f235838efe5a09.gif\"\u003e\n\u003c/p\u003e\n \nTemos que começar injetando todo _html_ dentro da _div#hash_. Mas não podemos injetar diretamente, precisamos fazer um _for_, onde teremos um (3x3) três linhas por três colunas. Talvez seja um pouco abstrato pensar nisso, mas olhe para esse esboço:\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/hashGame.jpg\"\u003e\n\u003c/p\u003e\n \nPerceba que as linhas são as _tr_ (table row) e cada uma das celuas são o _td_ (table data).\n \nAgora precisamos criar uma variável chamada table, que receberá tudo o html. E a cada loop injetamos dentro da variável uma _tr_ e tres _td_.\n \n```javascript\n// Renderiza dentro da \u003cdiv #hash /\u003e\nfunction renderHashGame() {\n   let table = `\u003ctable\u003e`;\n \n   for (let r = 0; r \u003c 3; r++) {\n       table += `\u003ctr\u003e`;\n \n       for (let d = 0; d \u003c 3; d++) {\n           table += `\u003ctd\u003e`;\n \n           table += `\u003c/td\u003e`;\n       }\n \n       table += `\u003c/tr\u003e`;\n   }\n   table += `\u003c/table\u003e`;\n   document.querySelector('#hash').innerHTML = table;\n}\n```\n \nSe você tentar executá-lo no navegador, não verá nada. Mas muita calma! Só porque não está aparecendo não quer dizer que não esteja funcionando.\nDê uma olhada no seu dev tools e verá que apareceu uma tabela com varias _tr_ e _td_, como mostrado abaixo.\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/devtools.png\"\u003e\n\u003c/p\u003e\n \nLegal né? Agora vamos estilizar cada umas dessas td, fazendo um quadrado de _50px_ por _50px_, com a cor que preferir, eu irei fazer com um tipo de \"verde\".\n \n```css\n#hash td {\n   width: 50px;\n   height: 50px;\n   padding: 1px;\n \n   background-color: cadetblue;\n   cursor: pointer; /* Muda o cursor para um pontiro */\n}\n```\n \nAgora se salva-lo e recarregar a página verá algo parecido com a imagem abaixo. Mas ainda temos que criar a mecânica do jogo como a vez de cada jogador e os clicks.\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/gameExample.png\"\u003e\n\u003c/p\u003e\n \nVamos implementar agora a função de click a todos os nove _td_, usando o _querySelectorAll_ ele nos retorna um array com todas as _td_. Mas se clicar neles não ira acontecer nada, pois não programamos uma ação ainda, vamos apenas colocar um _console.log_ para vermos que deu certo.\n \n```javascript\n// adiciona o evento de click a todos os \u003ctd\u003e\nfunction addEvents() {\n   // Retorna um array com 9 itens contendo as td\n   const allTableData = document.querySelectorAll('#hash td');\n \n   // Faz um loop dentro do nosso array, pegando cada\n   // um dos valores(value) e adiciona um click.\n   allTableData.forEach((value, index) =\u003e {\n       value.addEventListener('click', () =\u003e {\n           console.log(index);\n       })\n   })\n}\n```\n \nAgora se abrir o console do dev tools, irá ver que, ao clicar em um dos itens aparecerá o número que ele representa. E é isto que queremos para colocar o X e o círculo em seu respectivo lugar.\n \nEntão vem a parte mais legal, que será onde vamos criar a função responsável pela parte de interagir com o jogo. Temos que criar duas variáveis onde guardaremos o estado de cada item, e uma flag _(bandeira)_, que será usada para saber a vez do jogador.\n \n```javascript\n// Array de propriedades os valores podem ser 0, 1 e 2.\nconst items = [\n   0, 0, 0,\n   0, 0, 0,\n   0, 0, 0\n]\n// Variavel responsavel pela vez de cada jogador\nlet flag = true;\n```\n \nA função abaixo é responsável por adicionar uma classe a cada uma das td.\n \n```javascript\nfunction toChangeArray(valIndex) {\n   const allTableData = document.querySelectorAll('#hash td');\n \n   // Verifica a flag se for true\n   if (flag \u0026\u0026 items[valIndex] === 0) {\n       items[valIndex] = 1;\n \n       // adiciona uma classe x\n       allTableData[valIndex].classList.add('x');\n       flag = false;\n \n   } else if (!flag \u0026\u0026 items[valIndex] === 0) {\n       items[valIndex] = 2;\n \n       // adiciona uma classe circule\n       allTableData[valIndex].classList.add('circule');\n       flag = true;\n   }\n}\n```\n \nMas para que essas classes funcione teremos que adicionar um pouco mais de css no nosso arquivo de estilo.\n \n```css\n.circule {\n   /* Adicionar um circulo no jogo */\n   background-repeat: no-repeat;\n   background-image: url(./svg/circule.svg);\n}\n \n/* Adicionar um x no jogo */\n.x {\n   /* Impede que o background se repita */\n   background-repeat: no-repeat;\n   background-image: url(./svg/x.svg);\n}\n```\n \nPerceba que usamos um _background-image_ que está disponível dentro da pasta svg que você também poderá usar.\nPronto! Agora se salvar e testar no seu navegador já está funcionando.\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/funcionalGame.gif\"\u003e\n\u003c/p\u003e\n \nTemos 60% do projeto pronto, agora iremos para o BackEnd em _node_. Vamos começar importando os módulos necessários. Lembre-se não esqueça de fazer o _npm init -y_ antes.\n \n```zsh\n% npm install express\n% npm install socket.io\n% npm install nodemon -D\n```\n \nTalvez o express você já o conheça, mas a sacada desse porjéto é o socket.io, ele é um _FrameWork_ focado para chat, mas funciona também em muitas outras coisas. Como trocar informações com o frontend e atualizar todos os navegadores conectados.\nBasicamente, quando o backend percebe alguma alteração, ele emite as informações para o frontend, assim todos os navegadores conectados serão atualizados simultaneamente. Legal né? Então vamos lá!\n \nCrie uma pasta para separar o BackEnd do FrontEnd com a seguinte arquitetura de pastas:\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/folder.png\"\u003e\n\u003c/p\u003e\n \nDentro do diretório _src_ coloquei um arquivo chamado _server.js_, mas existe muita gente que chama de _app.js_. Você pode chama-lo como quiser. Sim você pode chamar seu BackEnd de \"_jose.js_\" se quiser!\n \nAgora dentro do seu arquivo \"_jose.js_\", no meu caso é o _server_, temos que importar o express e o socket.io.\n \n```javascript\nconst express = require('express');\nconst server = express();\n \nserver.get('/', (req, res) =\u003e {\n   return res.send('test');\n})\n \nserver.listen(3333);\n```\n \nSe salvar e executar esse arquivo usando o _nodemon_ ou o próprio _node_, na url _http://localhost:3333_ verá um test, na tela do navegador. Mais agora precisamos configurar o _node_ para servir aquivos estáticos.\n \n```javascript\nconst express = require('express');\nconst server = express();\n \n// Configurações:\nserver.use(express.urlencoded({ extended: true }));\n// Configura a pasta pública do projeto\nserver.use(express.static('./public'));\n// Permite que o express use json\nserver.use(express.json());\n \n// Requisições\nserver.get('/', (req, res) =\u003e {\n   return res.render('../public/index');\n})\n \n// Porta\nserver.listen(3333);\n```\n \nAgora vem a parte mais legal, vamos usar o _socket.io_ com o FrontEnd. Vamos lá de novo!\n \n```javascript\nconst express = require('express');\nconst server = express();\n \nserver.use(express.urlencoded({ extended: true }));\n// Configura a pasta pública do projeto\nserver.use(express.static('./public'));\n// Permite que o express use json\nserver.use(express.json());\n \n// Requisições\nserver.get('/', (req, res) =\u003e {\n   return res.render('../public/index');\n})\n \n// Porta\nconst port = server.listen(3333);\n \n// Importa o socket.io\nconst io = require('socket.io')(port);\n \n// Cria uma conexão que ficará ouvidno a porta 3333\nio.on('connection', (socket) =\u003e {\n   socket.on('test', (data) =\u003e {\n       // Cria um emit para o frontend.\n       io.sockets.emit('test', {\n           position: data.position,\n       });\n   })\n})\n```\n \nPerceba que no _emit_ eu o chamei de test, recomendo que mude para game, ou um nome mais apropriado. Eu irei deixar assim, pois usarei apenas para testes.\n \n__Acabou!__ \n\nMais o menos. Precisamos voltar lá no FrontEnd e fazer algumas alterações. Temos que importar a _CDN_ do _socket.io_ para que possamos usar algumas funções.\n \n```html\n\u003cbody\u003e\n  \n   \u003cdiv id=\"hash\"\u003e\u003c/div\u003e\n \n   \u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js\"\u003e\u003c/script\u003e\n   \u003cscript src=\"./script.js\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n```\n \nAgora dentro do _script.js_ vamos criar uma função _emit_, bem parecido com a que fizemos no BackEnd, lembrado que ela deve ter o mesmo nome que colocamos. No meu caso foi _test_. \n \n```javascript\n// Faz uma conexão com o socket.io no BackEnd\nconst socket = io.connect('http://localhost:3333/');\n \n// Quando houver alteração no BackEnd\nsocket.on('test', (data) =\u003e {\n   toChangeArray(data.position);\n})\n```\n\nAgora coloque esses _scripts_ no topo de tudo, e caso tenha dúvidas você pode olhar o arquivo principal. Mas até aqui não tem muito segredo.\nE por fim, dentro da função de click, iremos enviar o _index_ (posição) do array para o BackEnd, para que ele atualize todos os navegadores.\n \n```javascript\nfunction addEvents() {\n   const allTableData = document.querySelectorAll('#hash td');\n \n   allTableData.forEach((value, index) =\u003e {\n       value.addEventListener('click', () =\u003e {\n           // Emite a posição para o servidor\n           socket.emit('test', { position: index });\n       })\n   })\n}\n```\n \n__Agora realmente acabou!__\n\nBasta salvar, e executar em dois navegadores diferentes, para ver o resultado.\n \n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"./img/demo.gif\"\u003e\n\u003c/p\u003e\n \n---\n \n## Como ajudar?\n \n- Erros gramaticais sempre acabam surgindo no README.md, e caso encontre algum pode mandar a correção por aqui mesmo. Ficarei muito feliz. Obrigado!\n \n## Desafios:\n- Fazer uma função que identifique quando um usuário ganhou.\n- Fazer um contador que mostra quantas vezes o usuário já ganhou.\n \n\u003e Não esqueça de fazer um fork e me mandar seu pull request, fechado?\n \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandre2l2%2Ftic-tac-toe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandre2l2%2Ftic-tac-toe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandre2l2%2Ftic-tac-toe/lists"}