aboutsummaryrefslogtreecommitdiff
path: root/gameserver/gameserver.js
diff options
context:
space:
mode:
Diffstat (limited to 'gameserver/gameserver.js')
-rwxr-xr-xgameserver/gameserver.js251
1 files changed, 251 insertions, 0 deletions
diff --git a/gameserver/gameserver.js b/gameserver/gameserver.js
new file mode 100755
index 0000000..ddc6f0b
--- /dev/null
+++ b/gameserver/gameserver.js
@@ -0,0 +1,251 @@
+#!/usr/bin/env node
+
+"use strict";
+
+var fs=require("fs"),
+ http=require("http"),
+ util=require("util"),
+ assert=require("assert"),
+ WebSocketServer=require("ws").Server,
+ Naampje=require("naampje").name;
+var HTTPPORT=83,WSPORT=8383;
+
+var conns=[],games=[];
+
+var genid=(function(){
+ var $=1;
+ return function(){return $++;};
+})();
+
+function findby(a,key,val){
+ for(var i=0;i<a.length;i++)if(a[i][key]==val)return i;
+ return -1;
+}
+
+function nickIsTaken(nick){
+ var i;
+ for(i=0;i<conns.length;i++)if(conns[i].nick==nick)return true;
+ return false;
+}
+
+function numberiseNick(nick){
+ var i;
+ for(i=0;;i++){
+ if(!nickIsTaken(nick+i))return nick+i;
+ }
+}
+
+function gennick(){
+ var i,nick;
+ for(i=0;i<10;i++){
+ nick=Naampje();
+ if(!nickIsTaken(nick))break;
+ }
+ if(i<10)return nick;
+ else return numberiseNick(nick);
+}
+
+function Connection(ws){
+ if(!(this instanceof Connection))return new Connection(ws);
+ this.id=genid();
+ this.nick=gennick();
+ this.ws=ws;
+ this.gameid=null;
+}
+
+function Game(c1,c2){
+ if(!(this instanceof Game))return new Game(c1,c2);
+ this.id=genid();
+ this.i1=c1.id;
+ this.i2=c2.id;
+ this.board=[1,1,1,1,1,
+ 0,0,0,0,0,
+ 0,0,3,0,0,
+ 0,0,0,0,0,
+ 2,2,2,2,2];
+ this.onturn=1; //1 or 2
+ this.firstmove=true; //in first move, neudir==-1
+}
+
+function saveFinishedBoard(bd){
+ var fname=process.hrtime();
+ if(fs.existsSync("winboards")){
+ if(!fs.lstatPath("winboards").isDirectory()){
+ console.log("winboards is not a directory. Bleh.");
+ return;
+ }
+ } else {
+ fs.mkdirSync("winboards");
+ }
+ fname="winboards/"+fname[0]+("000000000"+fname[1]).slice(-9)+".txt";
+ if(fs.existsSync(fname)){ //don't think this'll happen often
+ console.log("saveFinishedBoard: file '"+fname+"' already exists!");
+ return;
+ }
+ fs.writeFileSync(fname,bd.join(""));
+}
+
+var idxAfterSlide__xy_deltas=[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];
+function idxAfterSlide(bd,idx,dir){
+ var x=idx%5,y=~~(idx/5),x2,y2,d=idxAfterSlide__xy_deltas[dir],dx=d[0],dy=d[1];
+ while(true){
+ x2=x+dx; y2=y+dy;
+ if(x2<0||x2>=5||y2<0||y2>=5||bd[5*y2+x2]!=0)return 5*y+x;
+ x=x2; y=y2;
+ }
+}
+
+function applyMove(bd,mv){
+ var neuidx=-1,newidx,i;
+ bd=bd.slice();
+ for(i=0;i<25;i++)if(bd[i]==3){neuidx=i;break;}
+ assert(neuidx!=-1);
+ if(mv[0]!=-1){
+ newidx=idxAfterSlide(bd,neuidx,mv[0]);
+ bd[newidx]=3;
+ bd[neuidx]=0;
+ }
+ if(mv[1]!=-1&&mv[2]!=-1){
+ newidx=idxAfterSlide(bd,mv[1],mv[2]);
+ bd[newidx]=bd[mv[1]];
+ bd[mv[1]]=0;
+ }
+ return bd;
+}
+
+function moveIsLegal(bd,mv,forpl){
+ var neuidx=-1,newidx,i;
+ if(bd[mv[1]]!=forpl)return false;
+ for(i=0;i<25;i++)if(bd[i]==3){neuidx=i;break;}
+ assert(neuidx!=-1);
+ if(mv[0]!=-1){
+ newidx=idxAfterSlide(bd,neuidx,mv[0]);
+ if(newidx==neuidx)return false;
+ }
+ newidx=idxAfterSlide(applyMove(bd,[mv[0],-1,-1]),mv[1],mv[2]);
+ if(newidx==mv[1])return false;
+ return true;
+}
+
+function handleMessage(conn,msg){
+ var cmd=msg.split(" "),list,i,game,c2,mv;
+ switch(cmd[0]){
+ case "nick":
+ conn.ws.send("nick \""+conn.nick+"\"");
+ break;
+ /*case "list_free_users":
+ list=[];
+ for(i=0;i<conns.length;i++)if(conns[i].gameid==null)list.push(conns[i].nick);
+ conn.ws.send("list_free_users "+JSON.stringify(list));
+ break;*/
+ case "game_create":
+ c2=cmd.slice(1).join(" ");
+ if(conn.gameid!=null){
+ conn.ws.send("game_create [false,\"Already in a game\"]");
+ break;
+ }
+ if(c2==conn.nick){
+ conn.ws.send("game_create [false,\"Cannot create a game with yourself\"]");
+ break;
+ }
+ c2=findby(conns,"nick",c2);
+ if(c2==-1){
+ conn.ws.send("game_create [false,\"Non-existent nick\"]");
+ break;
+ }
+ c2=conns[c2];
+ if(c2.gameid!=null){
+ conn.ws.send("game_create [false,\"Other already in a game\"]");
+ break;
+ }
+ console.log("new game between "+conn.nick+" and "+c2.nick);
+ game=new Game(conn,c2);
+ conn.gameid=game.id;
+ c2.gameid=game.id;
+ games.push(game);
+ conn.ws.send("game_create true");
+ c2.ws.send("joined_game true");
+ conn.ws.send("your_turn false");
+ break;
+ case "game_info":
+ if(conn.gameid==null){
+ conn.ws.send("game_info null");
+ break;
+ }
+ game=games[findby(games,"id",conn.gameid)];
+ conn.ws.send("game_info "+JSON.stringify([
+ [
+ conns[findby(conns,"id",game.i1)].nick,
+ conns[findby(conns,"id",game.i2)].nick
+ ],
+ game.board,
+ game.onturn
+ ]));
+ break;
+ case "game_doturn": //neudir stoneidx stonedir
+ if(conn.gameid==null){
+ conn.ws.send("game_doturn [false,\"Not in a game\"]");
+ break;
+ }
+ game=games[findby(games,"id",conn.gameid)];
+ mv=[+cmd[1],+cmd[2],+cmd[3]];
+ if(mv[0]==undefined||isNaN(mv[0])||mv[0]%1!=0||
+ (game.firstmove ? mv[0]!=-1 : mv[0]<0||mv[0]>=8) ||
+ mv[1]==undefined||isNaN(mv[1])||mv[1]%1!=0||mv[1]<0||mv[1]>=25||
+ mv[2]==undefined||isNaN(mv[2])||mv[2]%1!=0||mv[2]<0||mv[2]>=8){
+ conn.ws.send("game_doturn [false,\"Invalid move\"]");
+ break;
+ }
+ if(!moveIsLegal(game.board,mv,game.onturn)){
+ conn.ws.send("game_doturn [false,\"Illegal move\"]");
+ break;
+ }
+ game.board=applyMove(game.board,mv);
+ console.log("new board:");
+ console.log(game.board);
+ game.onturn=3-game.onturn;
+ conn.ws.send("game_doturn "+JSON.stringify([true,mv]));
+ c2=conns[findby(conns,"id",conn.id==game.i1?game.i2:game.i1)];
+ c2.ws.send("your_turn "+JSON.stringify(mv));
+ game.firstmove=false;
+ break;
+ default:
+ conn.ws.send("error "+cmd[0]);
+ break;
+ }
+}
+
+new WebSocketServer({port:WSPORT}).on("connection",function(ws){
+ var conn=new Connection(ws);
+ conns.push(conn);
+ ws.on("message",function(msg){
+ console.log(msg,util.inspect(conn,{depth:0}));
+ handleMessage(conn,msg);
+ });
+ ws.on("open",function(){
+ //hi!
+ });
+ ws.on("close",function(){
+ if(conn.gameid!=null){
+ var gameidx=findby(games,"id",conn.gameid);
+ var game=games[gameidx];
+ var otherplayeridx=findby(conns,"id",game.i1==conn.id?game.i2:game.i1)
+ console.log("otherplayeridx="+otherplayeridx);
+ conns[otherplayeridx].gameid=null;
+ conn.gameid=null;
+ games.splice(gameidx,1);
+ }
+ });
+});
+console.log("Websocket server started on port "+WSPORT+".");
+
+http.createServer(function(req,res){
+ if(req.url=="/"){
+ res.writeHead(200,{"Content-Type":"text/html"});
+ res.end(String(fs.readFileSync("gameserver.html")));
+ } else {
+ res.writeHead(404,{"Content-Type":"text/plain"});
+ res.end("404 Not found");
+ }
+}).listen(HTTPPORT);
+console.log("HTTP server started on port "+HTTPPORT+".");