#!/usr/bin/env node const naampje=require("naampje"); const PORT=15937; // parseInt("cap",36) // Game: {id: Int, size: Int, board: Board, onturn: Int, players: [Player]} // 'onturn' is index into 'players', or -1 if game is finished // Player: {id: Int, name: String, socket: SocketIO_Socket, games: [Game]} // Board: [(0,1,-1) * (size*size)] const games=new Map(); // game_id => Game const players=new Map(); // player_id => Player {let id=0; function uniqid(){return id++;}} function mkPlayer(socket){ const id=uniqid(); let name; while(true){try {name=naampje.name();} catch(e){continue;} break;} return {id, name, socket, games: []}; } function mkGame(size,players){ const id=uniqid(); const board=new Array(size*size).fill(0); return {id, size, board, onturn: 0, players}; } function safePlayer(p){ return {id: p.id, name: p.name, games: p.games.map(g=>g.id)}; } function safeGame(g){ return {id: g.id, size: g.size, board: g.board, onturn: g.onturn, players: g.players.map(safePlayer)}; } function playerLeavesGame(player,game){ game.onturn=-1; const newp=[]; for(const p of game.players){ if(p!=player){ p.socket.emit("game_leave",safeGame(game),safePlayer(player)); newp.push(p); } } if(newp.length==0)games.delete(game.id); else game.players=newp; } // Returns winning player (0 or 1), or -1 if none yet, or 2 if both function gameBoardFinished(game){ const B=game.board,S=game.size; const win=[false,false]; for(let i=0;i0&&!flags[at-1]){if(B[at-1]==B[i])queue.push(at-1); else if(B[at-1]==0){nb0=true; break;}} if(y>0&&!flags[at-S]){if(B[at-S]==B[i])queue.push(at-S); else if(B[at-S]==0){nb0=true; break;}} if(xconsole.log(`Listening on port ${PORT}`)); app.get("/",(req,res)=>{ res.sendFile(__dirname+"/index.html"); }); io.on("connection",(socket)=>{ const player=mkPlayer(socket); console.log(`connect ${player.id}`); players.set(player.id,player); for(const p of players.values()){ if(p!=player)p.socket.emit("player_list_change"); } socket.on("my_info",(cb)=>{ if(typeof cb!="function")return; cb(safePlayer(player)); }); socket.on("player_info",(pi,cb)=>{ if(typeof cb!="function")return; const p=players.get(pi); if(!p)cb(null); else cb(safePlayer(p)); }); socket.on("list_players",(cb)=>{ if(typeof cb!="function")return; const l=[]; for(const p of players.values()){ l.push(safePlayer(p)); } cb(l); }); socket.on("disconnect",()=>{ console.log(`disconnect ${player.id}`); for(const g of player.games)playerLeavesGame(player,g); players.delete(player.id); for(const p of players.values()){ p.socket.emit("player_list_change"); } }); socket.on("create_game",(otherid,size)=>{ const otherp=players.get(+otherid); if(!otherp){ socket.emit("err",`Player with id ${otherid} not found`); return; } if(typeof size!="number"||isNaN(size)||size<5||size>99||size%1!=0)return; const arr=Math.random()<0.5?[player,otherp]:[otherp,player]; const game=mkGame(size,arr); player.games.push(game); otherp.games.push(game); games.set(game.id,game); for(const p of game.players){ p.socket.emit("game_create",safeGame(game)); } game.players[0].socket.emit("game_turn",safeGame(game)); game.players[1].socket.emit("game_other_turn",safeGame(game)); }); socket.on("leave_game",(gi)=>{ const g=games.get(gi); if(!g)return; if(player.games.indexOf(g)==-1)return; playerLeavesGame(player,g); }); socket.on("game_move",(gi,idx)=>{ const g=games.get(gi); if(!g)return; if(player.games.indexOf(g)==-1)return; if(typeof idx!="number"||isNaN(idx)||idx<0||idx>=g.size*g.size||idx%1!=0){ socket.emit("err","Invalid position sent"); return; } if(g.players[g.onturn]!=player){ socket.emit("err","Not on turn"); return; } if(g.board[idx]!=0){ socket.emit("err","Position already taken"); return; } g.board[idx]=[1,-1][g.onturn]; let win=gameBoardFinished(g); if(win==2)win=g.onturn; if(win==-1){ g.onturn=1-g.onturn; g.players[g.onturn].socket.emit("game_turn",safeGame(g)); g.players[1-g.onturn].socket.emit("game_other_turn",safeGame(g)); } else { for(const p of g.players){ p.socket.emit("game_win",safeGame(g),safePlayer(g.players[win])); } } }); });