https://github.com/rodielm/poke-memory-game
https://github.com/rodielm/poke-memory-game
Last synced: 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/rodielm/poke-memory-game
- Owner: Rodielm
- License: mit
- Created: 2026-02-08T13:06:49.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-08T14:23:32.000Z (5 months ago)
- Last Synced: 2026-02-08T20:49:59.321Z (5 months ago)
- Language: JavaScript
- Size: 123 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Poke Memory Game
Juego de memoria con cartas de Pokemon construido con **React + Vite + Tailwind CSS**.
> Este README es una guia paso a paso para aprender a crear la app desde cero.
## Demo
[Jugar online](https://rodielm.github.io/poke-memory-game/)
## Que vas a aprender
- Crear un proyecto con Vite + React
- Manejar estado con `useState`
- Ejecutar efectos con `useEffect`
- Renderizado condicional y listas con `.map()`
- Animaciones CSS 3D (efecto flip de cartas)
- Estilos con Tailwind CSS
- Deploy a GitHub Pages
## Requisitos previos
- Node.js (v18+) o Docker
- Conocimientos basicos de HTML, CSS y JavaScript
---
## Paso 1: Crear el proyecto
```bash
npm create vite@latest poke-memory-game -- --template react
cd poke-memory-game
npm install
```
Esto genera la estructura base:
```
poke-memory-game/
├── public/ # Archivos estaticos (favicon, imagenes)
├── src/
│ ├── App.jsx # Componente principal
│ ├── main.jsx # Punto de entrada (monta React en el DOM)
│ └── index.css # Estilos globales
├── index.html # HTML base (Vite inyecta los scripts aqui)
├── vite.config.js # Configuracion de Vite
└── package.json # Dependencias y scripts
```
## Paso 2: Instalar dependencias
```bash
npm install lucide-react
npm install -D tailwindcss @tailwindcss/vite
```
| Paquete | Para que sirve |
|---------|---------------|
| `lucide-react` | Iconos como el boton de reinicio |
| `tailwindcss` | Framework CSS basado en clases utilitarias |
| `@tailwindcss/vite` | Plugin para integrar Tailwind con Vite |
## Paso 3: Configurar Tailwind y Vite
**`vite.config.js`** - Agregar el plugin de Tailwind:
```js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
```
**`src/index.css`** - Importar Tailwind:
```css
@import "tailwindcss";
```
Con esto ya puedes usar clases como `bg-blue-500`, `p-4`, `flex`, etc.
## Paso 4: Definir los datos de los Pokemon
En `src/App.jsx`, definimos un array con los Pokemon que usara el juego:
```jsx
const POKEMON_DATA = [
{ id: 1, name: 'Bulbasaur', image: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/1.png' },
{ id: 4, name: 'Charmander', image: '...' },
// ... 8 Pokemon en total
];
```
Usamos sprites directos de GitHub (PokeAPI) en vez de una API para evitar latencia y limites de peticiones.
## Paso 5: Crear el estado del juego
React usa **hooks** para manejar datos que cambian. Cada `useState` crea una variable reactiva:
```jsx
const PokemonMemoryGame = () => {
const [cards, setCards] = useState([]); // Las 16 cartas en el tablero
const [flipped, setFlipped] = useState([]); // Cartas volteadas (max 2)
const [matched, setMatched] = useState([]); // Cartas ya emparejadas
const [moves, setMoves] = useState(0); // Contador de movimientos
const [loading, setLoading] = useState(true); // Estado de carga
};
```
Cuando llamas a `setCards(nuevasCartas)`, React re-renderiza el componente con los nuevos datos.
## Paso 6: Inicializar el juego
```jsx
useEffect(() => {
initGame();
}, []); // [] = ejecutar solo al montar el componente
const initGame = () => {
// 1. Duplicar los pokemon para crear pares
const gameCards = [...POKEMON_DATA, ...POKEMON_DATA].map((pokemon, index) => ({
...pokemon,
uniqueId: index, // ID unico por carta (0-15)
pairId: pokemon.id // ID para emparejar (ej: ambas Pikachu = 25)
}));
// 2. Mezclar y resetear estado
setCards(shuffleArray(gameCards));
setFlipped([]);
setMatched([]);
setMoves(0);
};
```
**Conceptos clave:**
- `useEffect` con `[]` se ejecuta una sola vez al cargar
- Spread operator `[...array]` crea una copia del array
- `.map()` transforma cada elemento del array
## Paso 7: Algoritmo de mezcla (Fisher-Yates)
```jsx
const shuffleArray = (array) => {
const newArray = [...array];
for (let i = newArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArray[i], newArray[j]] = [newArray[j], newArray[i]];
}
return newArray;
};
```
Es el algoritmo estandar para mezclar arrays. Recorre de atras hacia adelante e intercambia cada posicion con una aleatoria.
## Paso 8: Logica de click en cartas
Esta es la logica central del juego:
```jsx
const handleCardClick = (uniqueId) => {
// Ignorar si: ya hay 2 volteadas, ya esta volteada, o ya fue emparejada
if (flipped.length === 2 || flipped.includes(uniqueId) || matched.includes(uniqueId)) {
return;
}
const newFlipped = [...flipped, uniqueId];
setFlipped(newFlipped);
if (newFlipped.length === 2) {
setMoves(moves + 1);
const firstCard = cards.find(c => c.uniqueId === newFlipped[0]);
const secondCard = cards.find(c => c.uniqueId === newFlipped[1]);
if (firstCard.pairId === secondCard.pairId) {
// Son pareja: guardar en matched despues de 600ms
setTimeout(() => {
setMatched([...matched, newFlipped[0], newFlipped[1]]);
setFlipped([]);
}, 600);
} else {
// No son pareja: voltear de regreso despues de 1s
setTimeout(() => setFlipped([]), 1000);
}
}
};
```
**Flujo:**
1. Click en carta 1 -> se voltea
2. Click en carta 2 -> se voltea + comparar
3. Si son iguales -> quedan volteadas (matched)
4. Si no -> se voltean de regreso despues de 1 segundo
## Paso 9: Animacion flip con CSS 3D
El efecto de voltear cartas usa transformaciones CSS 3D:
```jsx
{/* Contenedor con perspectiva (habilita el 3D) */}
{/* Carta que rota */}
{/* Cara trasera (Pokeball) */}
...
{/* Cara frontal (Pokemon) - pre-rotada 180deg */}
...
```
**Como funciona:**
- Ambas caras se superponen con `position: absolute`
- `backfaceVisibility: hidden` oculta cada cara cuando esta de espaldas
- La cara frontal empieza rotada 180deg, asi cuando el contenedor rota 180deg, queda al frente
## Paso 10: Deploy a GitHub Pages
### 1. Configurar `base` en Vite
```js
// vite.config.js
export default defineConfig({
base: '/poke-memory-game/', // Nombre de tu repositorio
plugins: [react(), tailwindcss()],
})
```
Esto es necesario porque GitHub Pages sirve desde `https://usuario.github.io/repo/`, no desde la raiz `/`.
### 2. Instalar gh-pages
```bash
npm install -D gh-pages
```
### 3. Agregar scripts en package.json
```json
{
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d dist"
}
}
```
### 4. Desplegar
```bash
npm run deploy
```
Esto hace build, crea la rama `gh-pages` y sube los archivos. Tu app estara en:
`https://tu-usuario.github.io/poke-memory-game/`
### 5. Configurar en GitHub
Ve a **Settings > Pages** del repositorio y selecciona:
- Source: **Deploy from a branch**
- Branch: **gh-pages** / **(root)**
---
## Ejecutar localmente
### Opcion 1: Node.js
```bash
npm install
npm run dev
```
### Opcion 2: Docker
```bash
docker compose up
```
La app estara en `http://localhost:5173` con hot-reload.
## Tecnologias
| Tecnologia | Version | Uso |
|-----------|---------|-----|
| React | 19 | UI con componentes |
| Vite | 7 | Bundler y dev server |
| Tailwind CSS | 4 | Estilos utilitarios |
| Lucide React | - | Iconos SVG |
| gh-pages | - | Deploy a GitHub Pages |
---
## Glosario: Conceptos clave
Referencia rapida de cada tecnologia y concepto utilizado en este proyecto.
### React
React es una **libreria de JavaScript para construir interfaces de usuario**. En vez de manipular el HTML directamente (como con `document.getElementById`), React te permite crear **componentes** — piezas reutilizables de UI que manejan su propio estado.
```jsx
// Sin React (vanilla JS)
document.getElementById('counter').innerText = count;
// Con React - declaras QUE quieres ver, React se encarga del COMO
const [count, setCount] = useState(0);
return
{count}
; // React actualiza el DOM automaticamente
```
**Conceptos de React usados en este proyecto:**
| Concepto | Que es | Donde se usa |
|----------|--------|-------------|
| **Componente** | Funcion que retorna JSX (HTML-like). Pieza reutilizable de UI | `PokemonMemoryGame` es el componente principal |
| **JSX** | Sintaxis que parece HTML pero es JavaScript. Se compila a `React.createElement()` | Todo lo que esta dentro de `return (...)` |
| **useState** | Hook que crea una variable reactiva. Cuando cambia, React re-renderiza | `cards`, `flipped`, `matched`, `moves`, `loading` |
| **useEffect** | Hook que ejecuta codigo cuando el componente se monta o cuando cambian sus dependencias | Inicializar el juego al cargar |
| **Props** | Datos que un componente padre pasa a un hijo | No se usan aqui (es un solo componente) |
| **Renderizado condicional** | Mostrar u ocultar JSX segun una condicion | `{isGameComplete &&
...}` |
| **Listas con .map()** | Renderizar multiples elementos a partir de un array | `{cards.map((card) => ...)}` |
| **key** | Identificador unico en listas para que React sepa que elemento cambio | `key={card.uniqueId}` |
### Vite
Vite es un **bundler y servidor de desarrollo** para aplicaciones web modernas. Es el reemplazo moderno de herramientas como Webpack o Create React App.
**Que hace Vite:**
- **En desarrollo:** Levanta un servidor local con **Hot Module Replacement (HMR)** — cuando guardas un archivo, el cambio se refleja instantaneamente en el navegador sin recargar la pagina
- **En produccion (`npm run build`):** Empaqueta todo tu codigo en archivos optimizados (minificados, con hashes) listos para subir a un servidor
**Por que Vite y no Create React App:**
- Vite es mucho mas rapido (usa ESModules nativos del navegador)
- Create React App fue deprecado oficialmente
- Configuracion minima — un solo archivo `vite.config.js`
```
npm run dev → Servidor local con HMR (desarrollo)
npm run build → Genera carpeta dist/ con archivos optimizados (produccion)
npm run preview → Sirve la carpeta dist/ localmente para probar el build
```
### Tailwind CSS
Tailwind es un **framework CSS basado en clases utilitarias**. En vez de escribir CSS en archivos separados, aplicas estilos directamente en el HTML con clases predefinidas.
```jsx
// CSS tradicional:
// .boton { background: blue; padding: 12px 24px; border-radius: 9999px; }
// Click
// Tailwind - estilos directamente en la clase:
Click
```
**Clases usadas en este proyecto:**
| Clase | CSS equivalente | Categoria |
|-------|----------------|-----------|
| `min-h-screen` | `min-height: 100vh` | Tamaño |
| `p-4` | `padding: 1rem` | Espaciado |
| `bg-blue-500` | `background-color: #3b82f6` | Color |
| `text-white` | `color: white` | Texto |
| `flex` | `display: flex` | Layout |
| `grid grid-cols-4` | `display: grid; grid-template-columns: repeat(4, 1fr)` | Grid |
| `rounded-xl` | `border-radius: 0.75rem` | Bordes |
| `shadow-lg` | `box-shadow: ...` | Sombras |
| `hover:scale-105` | `:hover { transform: scale(1.05) }` | Interactivo |
| `sm:p-8` | `@media (min-width: 640px) { padding: 2rem }` | Responsive |
| `animate-pulse` | Animacion de opacidad pulsante | Animacion |
| `transition-all` | `transition: all` | Transicion |
| `bg-white/90` | `background: rgba(255,255,255,0.9)` | Opacidad |
| `backdrop-blur-sm` | `backdrop-filter: blur(4px)` | Filtros |
**Ventajas:** No cambias entre archivos HTML y CSS, los estilos son predecibles, y el build final solo incluye las clases que usaste.
### npm (Node Package Manager)
npm es el **gestor de paquetes de Node.js**. Permite instalar y administrar librerias externas.
**Archivos clave:**
| Archivo | Que hace |
|---------|----------|
| `package.json` | Lista las dependencias y scripts del proyecto |
| `package-lock.json` | Fija las versiones exactas de cada dependencia (no editar manualmente) |
| `node_modules/` | Carpeta donde se instalan las dependencias (no se sube a git) |
**Comandos usados:**
```bash
npm install # Instala todas las dependencias de package.json
npm install lucide-react # Agrega una dependencia de produccion
npm install -D gh-pages # Agrega una dependencia de desarrollo (-D = devDependency)
npm run dev # Ejecuta el script "dev" definido en package.json
npm run build # Ejecuta el script "build"
npm run deploy # Ejecuta "predeploy" automaticamente, luego "deploy"
```
**dependencies vs devDependencies:**
- `dependencies`: Se necesitan para que la app funcione (React, Lucide)
- `devDependencies`: Solo se necesitan durante el desarrollo (Vite, Tailwind, ESLint, gh-pages)
### Docker
Docker permite **empaquetar una aplicacion con todo lo que necesita** (sistema operativo, runtime, dependencias) en un **contenedor**. Esto garantiza que la app funcione igual en cualquier maquina.
**Analogia:** Un contenedor es como una caja que incluye la app + todas sus herramientas. No importa en que computadora la abras, siempre funciona igual.
**Dockerfile** — La "receta" para construir la imagen:
```dockerfile
FROM node:22-bookworm-slim # 1. Partir de una imagen base con Node.js
RUN apt-get update && apt-get install -y \
git curl \ # 2. Instalar herramientas del sistema
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace # 3. Definir directorio de trabajo
COPY package.json package-lock.json ./ # 4. Copiar archivos de dependencias
RUN npm install # 5. Instalar dependencias (se cachea si no cambian)
COPY . . # 6. Copiar el resto del codigo
EXPOSE 5173 # 7. Documentar que puerto usa la app
CMD ["npm", "run", "dev", "--", "--host"] # 8. Comando por defecto al iniciar
```
**Conceptos clave de Docker:**
| Concepto | Que es |
|----------|--------|
| **Imagen** | Plantilla de solo lectura con el SO + app + dependencias. Se crea con `docker build` |
| **Contenedor** | Instancia en ejecucion de una imagen. Se crea con `docker run` |
| **Dockerfile** | Archivo con instrucciones para construir una imagen |
| **WORKDIR** | Directorio de trabajo dentro del contenedor |
| **COPY** | Copia archivos del host al contenedor |
| **EXPOSE** | Documenta que puerto usa la app (no lo abre, solo informativo) |
| **CMD** | Comando que se ejecuta al iniciar el contenedor |
### Docker Compose
Docker Compose es una herramienta para **definir y ejecutar aplicaciones multi-contenedor** con un solo archivo YAML. Aunque aqui usamos un solo servicio, simplifica mucho la configuracion.
```yaml
services:
app:
build: . # Construir imagen desde el Dockerfile actual
ports:
- "5173:5173" # Mapear puerto: host:contenedor
volumes:
- .:/workspace # Montar codigo local en el contenedor (hot-reload)
- node_modules:/workspace/node_modules # Volumen persistente para node_modules
command: npm run dev -- --host
volumes:
node_modules: # Volumen nombrado (persiste entre reinicios)
```
**Por que usamos volumes:**
- `.:/workspace` — Monta tu codigo local dentro del contenedor. Cuando editas un archivo en tu editor, el cambio se ve dentro del contenedor inmediatamente (asi funciona el hot-reload con Vite)
- `node_modules:/workspace/node_modules` — Volumen separado para que los `node_modules` del contenedor (Linux) no se mezclen con los de tu maquina (puede ser Mac/Windows)
**Comandos:**
```bash
docker compose up # Construir imagen (si no existe) + levantar contenedor
docker compose up --build # Forzar reconstruccion de la imagen
docker compose down # Detener y eliminar contenedores
docker compose exec app sh # Abrir terminal dentro del contenedor
```
### GitHub Pages
GitHub Pages es un **servicio gratuito de hosting para sitios estaticos** (HTML, CSS, JS) directamente desde un repositorio de GitHub.
**Como funciona en este proyecto:**
1. `npm run build` genera la carpeta `dist/` con archivos estaticos optimizados
2. `gh-pages -d dist` sube el contenido de `dist/` a la rama `gh-pages`
3. GitHub detecta la rama y sirve los archivos en `https://usuario.github.io/repo/`
**Importante:** Como la app se sirve desde un subdirectorio (`/poke-memory-game/` y no `/`), hay que configurar `base` en Vite para que las rutas de los assets sean correctas.
### ESLint
ESLint es un **linter** — una herramienta que analiza tu codigo JavaScript/JSX y detecta errores o malas practicas **sin ejecutarlo**.
```bash
npm run lint # Ejecutar ESLint en el proyecto
```
Ejemplo de lo que detecta: variables sin usar, imports duplicados, hooks de React usados incorrectamente.
### Lucide React
Lucide es una libreria de **iconos SVG** optimizados para React. Cada icono es un componente que puedes importar individualmente (tree-shakeable — solo se incluye lo que usas en el build final).
```jsx
import { RotateCcw } from 'lucide-react';
// Renderiza un icono SVG de 20x20px
```
## Autor
Rodiel - [GitHub](https://github.com/Rodielm)