犯人は踊る オンライン(部屋番号マッチ)

▼ クライアント画面

名前:
部屋番号:

▼ サーバーコード(Node.js 用)

※この部分は HTML 内に書いてあるだけで、実際には server.js にコピーして使います。

/* ===== server.js ===== */

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, { cors: { origin: '*' } });

app.use(express.static('public'));

const rooms = {};

function createRoomIfNotExists(roomCode) {
  if (!rooms[roomCode]) {
    rooms[roomCode] = {
      players: {},
      deck: [],
      discard: [],
      turnOrder: [],
      currentTurnIndex: 0,
      started: false,
    };
  }
}

function generateRoomCode() {
  return Math.floor(1000 + Math.random() * 9000).toString();
}

function createSimpleDeck() {
  const cards = [];
  cards.push({ type: '犯人' });
  for (let i = 0; i < 4; i++) cards.push({ type: '探偵' });
  for (let i = 0; i < 5; i++) cards.push({ type: 'アリバイ' });
  for (let i = 0; i < 10; i++) cards.push({ type: '一般人' });
  for (let i = cards.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [cards[i], cards[j]] = [cards[j], cards[i]];
  }
  return cards;
}

function dealCards(room, cardsPerPlayer = 4) {
  const ids = Object.keys(room.players);
  ids.forEach(id => room.players[id].hand = []);
  for (let i = 0; i < cardsPerPlayer; i++) {
    ids.forEach(id => {
      const card = room.deck.pop();
      if (card) room.players[id].hand.push(card);
    });
  }
}

function getPublicState(roomCode) {
  const room = rooms[roomCode];
  return {
    players: Object.values(room.players).map(p => ({
      name: p.name,
      handCount: p.hand.length,
    })),
    discard: room.discard,
    turnPlayerName:
      room.turnOrder.length > 0
        ? room.players[room.turnOrder[room.currentTurnIndex]].name
        : null,
    started: room.started,
  };
}

io.on('connection', socket => {
  socket.on('createRoom', ({ name }, callback) => {
    const roomCode = generateRoomCode();
    createRoomIfNotExists(roomCode);
    const room = rooms[roomCode];

    room.players[socket.id] = { name, hand: [] };
    room.turnOrder.push(socket.id);

    socket.join(roomCode);
    callback({ roomCode, state: getPublicState(roomCode) });
    io.to(roomCode).emit('stateUpdate', getPublicState(roomCode));
  });

  socket.on('joinRoom', ({ roomCode, name }, callback) => {
    createRoomIfNotExists(roomCode);
    const room = rooms[roomCode];

    room.players[socket.id] = { name, hand: [] };
    room.turnOrder.push(socket.id);

    socket.join(roomCode);
    callback({ ok: true, state: getPublicState(roomCode) });
    io.to(roomCode).emit('stateUpdate', getPublicState(roomCode));
  });

  socket.on('startGame', ({ roomCode }) => {
    const room = rooms[roomCode];
    room.deck = createSimpleDeck();
    room.discard = [];
    room.currentTurnIndex = 0;
    room.started = true;

    dealCards(room);

    Object.keys(room.players).forEach(id => {
      io.to(id).emit('handUpdate', room.players[id].hand);
    });

    io.to(roomCode).emit('stateUpdate', getPublicState(roomCode));
  });

  socket.on('playCard', ({ roomCode, cardIndex }) => {
    const room = rooms[roomCode];
    const currentId = room.turnOrder[room.currentTurnIndex];
    if (socket.id !== currentId) return;

    const player = room.players[socket.id];
    const [card] = player.hand.splice(cardIndex, 1);
    room.discard.push({ ...card, playedBy: player.name });

    io.to(socket.id).emit('handUpdate', player.hand);

    room.currentTurnIndex =
      (room.currentTurnIndex + 1) % room.turnOrder.length;

    io.to(roomCode).emit('stateUpdate', getPublicState(roomCode));
  });
});

server.listen(3000, () => {
  console.log("http://localhost:3000");
});