summaryrefslogtreecommitdiff
path: root/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'index.js')
-rwxr-xr-xindex.js248
1 files changed, 248 insertions, 0 deletions
diff --git a/index.js b/index.js
new file mode 100755
index 0000000..d2432ac
--- /dev/null
+++ b/index.js
@@ -0,0 +1,248 @@
+#!/usr/bin/env node
+
+"use strict";
+
+var app=require("express")(),
+ http=require("http").Server(app),
+ io=require("socket.io")(http),
+ fs=require("fs"),
+ spawn=require("child_process").spawn,
+ mkdirp=require("mkdirp"),
+ C=require("./common.js");
+
+
+var HTTPPORT=8090;
+
+var uniqid=(function(){
+ var id=0;
+ return function(){return id++;};
+})();
+
+app.get("/",function(req,res){
+ res.sendFile(__dirname+"/index.html");
+});
+
+["index.html","common.js"].forEach(function(fname){
+ app.get("/"+fname,function(req,res){
+ res.sendFile(__dirname+"/"+fname);
+ });
+});
+
+function Game(_np){
+ if(!(this instanceof Game))return new Game(_np);
+ this.bd=emptyboard();
+ this.np=np;
+ this.onturn=0;
+}
+Game.prototype.applymove=function(player,pos){
+ if(this.onturn!=player)return false;
+ var x=pos%C.W,y=~~(pos/C.W);
+ if(this.bd[y][x].n&&this.bd[y][x].c!=player)return false;
+ this.bd[y][x].c=player;
+ this.bd[y][x].n++;
+ this.bd=C.stabilise(this.bd);
+ do this.onturn=(this.onturn+1)%this.np;
+ while C.countballs(this.bd,this.onturn)==0;
+ return true;
+};
+Game.prototype.checkwin=function(){
+ if(C.countballs(this.bd)<this.np)return -1;
+ return C.checkwin(this.bd);
+};
+
+var connlist=[]; //sorted on id, because uniqid is strictly increasing
+var rooms={}; //{<roomid>: {ids:[id's], game:Game}}
+
+function findid(id){
+ var lo=0,mid,hi=connlist.length-1;
+ if(connlist[lo].id==id)return lo;
+ if(connlist[hi].id==id)return hi;
+ while(lo<hi){
+ mid=lo+(hi-lo)/2|0;
+ if(connlist[mid].id==id)return mid;
+ if(connlist[mid].id<id)lo=mid+1;
+ else hi=mid-1;
+ }
+ if(hi<lo||connlist[lo].id!=id)return -1; //not found
+ return lo;
+}
+
+function findname(name){
+ var i;
+ for(i=0;i<connlist.length;i++)if(connlist[i].name==name)return i;
+ return -1;
+}
+
+io.on("connection",function(conn){
+ var obj={
+ id:uniqid(),
+ name:"user",
+ conn:conn,
+ rooms:[],
+ pending:[]
+ };
+ obj.name+=obj.id;
+ connlist.push(obj);
+ conn.emit("me",[obj.id,obj.name]);
+ io.emit("userlist",connlist.map(function(o){return [o.id,o.name];}));
+
+ conn.on("disconnect",function(){
+ var idx=findid(obj.id);
+ if(idx==-1)return; //WAT
+ obj.pending.forEach(function(pendid){
+ io.to(pendid).emit("roomdestroy",pendid);
+ rooms[pendid].ids.forEach(function(id){
+ var o=connlist[findid(id)];
+ o.conn.leave(pendid);
+ o.rooms.splice(o.rooms.indexOf(pendid),1);
+ });
+ delete rooms[pendid];
+ });
+ obj.rooms.forEach(function(roomid){
+ io.to(roomid).emit("roomdestroy",roomid);
+ rooms[roomid].ids.forEach(function(id){
+ var o=connlist[findid(id)];
+ o.conn.leave(roomid);
+ o.rooms.splice(o.rooms.indexOf(roomid),1);
+ });
+ delete rooms[roomid];
+ });
+ connlist.splice(idx,1);
+ io.emit("userlist",connlist.map(function(o){return [o.id,o.name];}));
+ });
+ conn.on("me",function(cb){
+ cb([obj.id,obj.name]);
+ });
+ conn.on("setnick",function(nick){
+ var i;
+ if(C.validatenick(nick)){
+ if(findname(nick)!=-1){
+ i=1;
+ while(findname(nick+"_"+i)!=-1)i++;
+ nick+="_"+i;
+ }
+ obj.name=nick;
+ conn.emit("me",[obj.id,obj.name]);
+ io.emit("userlist",connlist.map(function(o){return [o.id,o.name];}));
+ } else conn.emit("message","That's an invalid nick!");
+ });
+ conn.on("userlist",function(cb){
+ cb(connlist.map(function(o){return [o.id,o.name];}));
+ });
+ conn.on("rooms",function(cb){
+ cb(obj.rooms);
+ });
+ conn.on("createroom",function(ids){ //give all id's, except your own
+ var myidx=findid(obj.id);
+ var indices=ids.map(function(id){return findid(id);}).filter(function(id){return id!=-1;});
+ if(indices.length==0){
+ conn.emit("message","Room too small; can only create rooms with 2 or more people");
+ return;
+ }
+ var roomid="room_"+uniqid();
+ rooms[roomid]={ids:[obj.id],game:null};
+ connlist[myidx].rooms.push(roomid);
+ conn.emit("room",roomid,[[obj.id,obj.name]]);
+ conn.join(roomid);
+ indices.forEach(function(idx){
+ connlist[idx].conn.emit("roominvite",roomid,[obj.id,obj.name]);
+ connlist[idx].pending.push(roomid);
+ });
+ });
+ conn.on("inviteaccept",function(roomid){
+ var idx=obj.pending.indexOf(roomid);
+ if(idx!=-1)obj.pending.splice(idx,1);
+ if(idx==-1||rooms[roomid]==undefined){
+ conn.emit("message","Cannot accept non-existent invitation");
+ return;
+ }
+ obj.rooms.push(roomid);
+ conn.emit("room",roomid,rooms[roomid].ids.map(function(id){
+ return [id,connlist[findid(id)].name];
+ }));
+ conn.join(roomid);
+ io.to(roomid).emit("roomjoin",roomid,[obj.id,obj.name]);
+ rooms[roomid].ids.push(obj.id);
+ });
+ function guardroomid(roomid){
+ if(obj.rooms.indexOf(roomid)==-1){
+ conn.emit("message","No member of that room!");
+ return false;
+ }
+ return true;
+ }
+ conn.on("roomdestroy",function(roomid){
+ if(!guardroomid(roomid))return;
+ io.to(roomid).emit("roomdestroy",roomid);
+ rooms[roomid].ids.forEach(function(id){
+ var o=connlist[findid(id)];
+ o.conn.leave(roomid);
+ o.rooms.splice(o.rooms.indexOf(roomid),1);
+ });
+ delete rooms[roomid];
+ });
+ conn.on("roomusers",function(roomid,cb){
+ if(!guardroomid(roomid)){
+ cb(false);
+ return;
+ }
+ cb(rooms[roomid].ids.map(function(id){
+ return [id,connlist[findid(id)].name];
+ }));
+ });
+ conn.on("roomchat",function(roomid,message){
+ if(!guardroomid(roomid))return;
+ io.to(roomid).emit("roomchat",roomid,[obj.id,obj.name],message);
+ });
+ conn.on("roomcreategame",function(roomid){
+ if(!guardroomid(roomid))return;
+ if(rooms[roomid].game!=null){
+ conn.emit("message","Room already has a running game");
+ return;
+ }
+ rooms[roomid].game=new Game(rooms[roomid].ids.length);
+ io.to(roomid).emit("roomcreategame",roomid);
+ connlist[findid(rooms[roomid].ids[0])].conn.emit("roomgameturn",roomid,0);
+ });
+ conn.on("roomhasgame",function(roomid,cb){
+ if(!guardroomid(roomid))return;
+ cb(rooms[roomid].game!=null);
+ });
+ function guardroomgame(roomid){
+ if(rooms[roomid].game==null){
+ conn.emit("message","No game in that room!");
+ return false;
+ }
+ return true;
+ }
+ conn.on("roomgameusers",function(roomid,cb){
+ if(!guardroomid(roomid)||!guardroomgame(roomid)){
+ cb(false);
+ return;
+ }
+ cb(rooms[roomid].ids.map(function(id){
+ return [id,connlist[findid(id)].name];
+ }));
+ });
+ conn.on("roomgameboard",function(roomid,cb){
+ if(!guardroomid(roomid)||!guardroomgame(roomid)){
+ cb(false);
+ return;
+ }
+ cb(rooms[roomid].game.bd);
+ });
+ conn.on("roomgameplace",function(roomid,pos){
+ if(!guardroomid(roomid)||!guardroomgame(roomid))return;
+ var playeridx=rooms[roomid].ids.indexOf(obj.id);
+ if(!rooms[roomid].game.applymove(pos,playeridx)){
+ conn.emit("message","Invalid move!");
+ return;
+ }
+ io.to(roomid).emit("roomgameplace",roomid,pos,playeridx);
+ connlist[findid(rooms[roomid].ids[rooms[roomid].game.onturn])].conn.emit("roomgameturn",roomid,rooms[roomid].game.onturn);
+ });
+});
+
+http.listen(HTTPPORT,function(){
+ console.log("Listening on http://localhost:"+HTTPPORT);
+});