Skip to main content

Event Overview

Pica y Fija uses Socket.IO for real-time bidirectional communication between clients and server. This page documents all events, their parameters, and usage examples.

Event Types

Client → Server

Events emitted by the client to request actions

Server → Client

Events emitted by the server to notify clients of state changes

Client → Server Events

These events are emitted by the client and handled by the server.

crearSala

Creates a new game room with specified options.
tiempoTurno
number
required
Turn time limit in seconds. Determines how long each player has to make a guess.
publica
boolean
required
Whether the room should be publicly listed. If true, other players can find it via buscarSala.
requiereAprobacion
boolean
required
Whether the host must approve players joining. Currently not enforced in the implementation.

Client Example

socket.emit('crearSala', {
    tiempoTurno: 60,
    publica: true,
    requiereAprobacion: false
});

Server Handler

server.js
socket.on('crearSala', ({ tiempoTurno, publica, requiereAprobacion }) => {
    const code = generateRoomCode();
    rooms[code] = {
        host: socket,
        players: [socket],
        secrets: {},
        turn: 0,
        turnCounts: [1, 1],
        options: { tiempoTurno, publica, requiereAprobacion },
        gameOver: false
    };

    socket.roomCode = code;
    socket.playerId = 0;
    socket.join(code);
    if (publica) publicRooms.push(code);

    socket.emit('salaCreada', { codigo: code });
    socket.emit('info', 'Sala creada. Esperando jugador...');
});

Response Events

  • salaCreada - Contains the generated room code
  • info - Confirmation message

buscarSala

Searches for an available public room and automatically joins it.
(no parameters)
void
This event takes no parameters.

Client Example

socket.emit('buscarSala');

Server Handler

server.js
socket.on('buscarSala', () => {
    const roomCode = publicRooms.find(code => rooms[code].players.length === 1);
    if (roomCode) {
        joinRoom(socket, roomCode);
    } else {
        socket.emit('info', 'No hay salas públicas disponibles');
    }
});

Behavior

  • Finds the first public room with only 1 player (waiting for opponent)
  • Automatically calls joinRoom() if found
  • Emits info event if no rooms available

Response Events

  • salaLista - If room found and joined successfully
  • info - Status messages

unirseSala

Joins a specific room by code.
codigo
string
required
The 6-character room code (e.g., “XK9P2L”)

Client Example

const roomCode = "XK9P2L";
socket.emit('unirseSala', roomCode);

Server Handler

server.js
socket.on('unirseSala', (codigo) => {
    const room = rooms[codigo];
    if (!room) {
        socket.emit('error', 'Sala no encontrada');
        return;
    }

    if (room.players.length >= 2) {
        socket.emit('error', 'La sala ya está llena');
        return;
    }

    joinRoom(socket, codigo);
});

Validation

  • Room must exist
  • Room must have fewer than 2 players

Response Events

  • error - If room not found or full
  • salaLista - If joined successfully
  • info - Player assignment confirmation

secret

Submits the player’s secret 4-digit number.
secret
string
required
A 4-digit number with all unique digits (e.g., “1234”, “9876”)

Client Example

const mySecret = "1234";
socket.emit('secret', mySecret);

Server Handler

server.js
socket.on('secret', secret => {
    const room = rooms[socket.roomCode];
    if (!room) return;

    if (!/^\d{4}$/.test(secret) || new Set(secret).size !== 4) {
        socket.emit('error', 'El número debe tener 4 cifras diferentes');
        return;
    }

    room.secrets[socket.playerId] = secret;
    socket.emit('info', 'Número secreto guardado');

    if (room.secrets[0] && room.secrets[1]) {
        io.to(socket.roomCode).emit('start');
        setTimeout(() => broadcastTurn(room), 200);
    }
});

Validation

  • Must be exactly 4 digits
  • All digits must be unique (checked via new Set(secret).size !== 4)

Behavior

  • Stores secret in room.secrets[playerId]
  • When both players submit secrets, emits start to begin the game
  • After 200ms delay, broadcasts the first turn

Response Events

  • error - If validation fails
  • info - Confirmation message
  • start - When both secrets are submitted (broadcast to room)
  • turn - First turn notification

guess

Submits a guess attempt to crack the opponent’s secret number.
guess
string
required
A 4-digit guess with all unique digits (e.g., “5678”)

Client Example

const myGuess = "5678";
socket.emit('guess', myGuess);

Server Handler

server.js
socket.on('guess', guess => {
    const room = rooms[socket.roomCode];
    if (!room) return;

    if (room.turn !== socket.playerId) {
        socket.emit('error', 'No es tu turno');
        return;
    }

    if (room.gameOver) {
        socket.emit('error', 'El juego ya terminó');
        return;
    }

    if (!/^\d{4}$/.test(guess) || new Set(guess).size !== 4) {
        socket.emit('error', 'El intento debe tener 4 cifras distintas');
        return;
    }

    const opponentId = socket.playerId === 0 ? 1 : 0;
    const opponentSecret = room.secrets[opponentId];
    if (!opponentSecret) {
        socket.emit('error', 'El oponente aún no ha elegido su número');
        return;
    }

    const result = getPicasFijas(opponentSecret, guess);
    const turnNumber = room.turnCounts[socket.playerId]++;

    const data = {
        jugadorId: socket.playerId,
        jugador: socket.playerId === 0 ? 'yo' : 'oponente',
        turno: turnNumber,
        intento: guess,
        fijas: result.fijas,
        picas: result.picas
    };

    room.players.forEach(p => p.emit('result', data));

    if (result.fijas === 4) {
        room.gameOver = true;
    } else {
        room.turn = opponentId;
        broadcastTurn(room);
    }
});

Validation

  • Must be player’s turn (room.turn === socket.playerId)
  • Game must not be over (room.gameOver === false)
  • Must be exactly 4 digits with no repeats
  • Opponent must have submitted their secret

Behavior

  1. Validates the guess format and turn
  2. Calculates picas and fijas using getPicasFijas()
  3. Broadcasts result to both players
  4. If 4 fijas, sets gameOver = true
  5. Otherwise, switches turn to opponent

Response Events

  • error - If validation fails
  • result - Guess outcome (broadcast to both players)
  • turn - Next turn notification (if game continues)

pasarTurnoPorTiempo

Forces the turn to switch to the opponent when time runs out.
(no parameters)
void
This event takes no parameters.

Client Example

// Called when the turn timer expires
socket.emit('pasarTurnoPorTiempo');

Server Handler

server.js
socket.on('pasarTurnoPorTiempo', () => {
    const room = rooms[socket.roomCode];
    if (!room) return;
    
    // Only the player with the current turn can force it
    if (room.turn !== socket.playerId) return;
    
    // Simply switch the turn
    room.turn = room.turn === 0 ? 1 : 0;
    broadcastTurn(room);
});

Behavior

  • Only the current turn holder can trigger this
  • Immediately switches turn to opponent
  • No guess is recorded

Response Events

  • turn - Next turn notification (broadcast to both players)

Server → Client Events

These events are emitted by the server to notify clients of state changes.

salaCreada

Sent to the room creator after successful room creation.
codigo
string
The generated 6-character room code (e.g., “XK9P2L”)

Server Code

server.js
socket.emit('salaCreada', { codigo: code });

Client Handler Example

socket.on('salaCreada', ({ codigo }) => {
    console.log(`Room created with code: ${codigo}`);
    displayRoomCode(codigo);
});

salaLista

Sent to both players when the room is full and ready to start.
jugador
number
Player number: 1 or 2
sala
string
The room code
tiempoTurno
number
Turn time limit in seconds

Server Code

server.js
room.players.forEach((player, idx) => {
    player.emit('salaLista', {
        jugador: idx + 1,
        sala: code,
        tiempoTurno: room.options.tiempoTurno
    });
});

Client Handler Example

socket.on('salaLista', ({ jugador, sala, tiempoTurno }) => {
    console.log(`You are Player ${jugador} in room ${sala}`);
    console.log(`Turn time limit: ${tiempoTurno} seconds`);
    showSecretInput();
});

start

Broadcast to both players when both secrets have been submitted.
(no data)
void
This event carries no data payload.

Server Code

server.js
io.to(socket.roomCode).emit('start');

Client Handler Example

socket.on('start', () => {
    console.log('Game starting!');
    hideSecretInput();
    showGameBoard();
});

turn

Notifies each player whether it’s their turn.
yourTurn
boolean
true if it’s this player’s turn, false otherwise

Server Code

server.js
function broadcastTurn(room) {
    room.players.forEach((s, i) => {
        s.emit('turn', { yourTurn: i === room.turn });
    });
}

Client Handler Example

socket.on('turn', ({ yourTurn }) => {
    if (yourTurn) {
        console.log('Your turn!');
        enableGuessInput();
        startTurnTimer();
    } else {
        console.log('Opponent\'s turn');
        disableGuessInput();
    }
});

result

Sent to both players after a guess is evaluated.
jugadorId
number
ID of the player who made the guess: 0 or 1
jugador
string
Perspective label: "yo" (me) or "oponente" (opponent)
turno
number
Turn number for the guessing player (1, 2, 3, …)
intento
string
The 4-digit guess that was made
fijas
number
Number of correct digits in correct positions (0-4)
picas
number
Number of correct digits in wrong positions (0-4)

Server Code

server.js
const data = {
    jugadorId: socket.playerId,
    jugador: socket.playerId === 0 ? 'yo' : 'oponente',
    turno: turnNumber,
    intento: guess,
    fijas: result.fijas,
    picas: result.picas
};

room.players.forEach(p => p.emit('result', data));

Client Handler Example

socket.on('result', ({ jugador, turno, intento, fijas, picas }) => {
    addToHistory(jugador, turno, intento, fijas, picas);
    
    if (fijas === 4) {
        if (jugador === 'yo') {
            showWinMessage();
        } else {
            showLoseMessage();
        }
    }
});

Win Condition

When fijas === 4, the game is over and the guessing player wins.

info

General information or status messages.
message
string
Human-readable status message in Spanish

Server Examples

server.js
socket.emit('info', 'Sala creada. Esperando jugador...');
socket.emit('info', 'Número secreto guardado');
socket.emit('info', 'Conectado como Jugador 1');
socket.emit('info', 'Tu oponente se ha desconectado');

Client Handler Example

socket.on('info', (message) => {
    console.log('[INFO]', message);
    showNotification(message, 'info');
});

error

Error messages for invalid actions.
message
string
Human-readable error message in Spanish

Server Examples

server.js
socket.emit('error', 'Sala no encontrada');
socket.emit('error', 'La sala ya está llena');
socket.emit('error', 'No es tu turno');
socket.emit('error', 'El número debe tener 4 cifras diferentes');
socket.emit('error', 'El juego ya terminó');

Client Handler Example

socket.on('error', (message) => {
    console.error('[ERROR]', message);
    showNotification(message, 'error');
});

Complete Client Example

Here’s a full client implementation showing all event handlers:
client.js
import { io } from 'socket.io-client';

const socket = io('http://localhost:9090');

// Create a room
function createRoom() {
    socket.emit('crearSala', {
        tiempoTurno: 60,
        publica: true,
        requiereAprobacion: false
    });
}

// Join a public room
function findPublicRoom() {
    socket.emit('buscarSala');
}

// Join by code
function joinRoom(code) {
    socket.emit('unirseSala', code);
}

// Submit secret
function submitSecret(secret) {
    socket.emit('secret', secret);
}

// Make a guess
function makeGuess(guess) {
    socket.emit('guess', guess);
}

// Handle turn timeout
function onTurnTimeout() {
    socket.emit('pasarTurnoPorTiempo');
}

// Event listeners
socket.on('salaCreada', ({ codigo }) => {
    console.log('Room created:', codigo);
});

socket.on('salaLista', ({ jugador, sala, tiempoTurno }) => {
    console.log(`Player ${jugador} in room ${sala}`);
});

socket.on('start', () => {
    console.log('Game starting!');
});

socket.on('turn', ({ yourTurn }) => {
    console.log(yourTurn ? 'Your turn' : 'Opponent turn');
});

socket.on('result', ({ jugador, turno, intento, fijas, picas }) => {
    console.log(`${jugador} - Turn ${turno}: ${intento}${fijas}F ${picas}P`);
});

socket.on('info', (message) => {
    console.log('[INFO]', message);
});

socket.on('error', (message) => {
    console.error('[ERROR]', message);
});

Event Flow Diagram

All events are real-time. The server broadcasts state changes immediately to maintain synchronization between players.

Build docs developers (and LLMs) love