aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--fiforead.cpp16
-rw-r--r--gameserver/gameserver.html18
-rwxr-xr-xgameserver/gameserver.js1
-rw-r--r--humanbot.cpp254
-rw-r--r--humanbot_index.html543
-rwxr-xr-xhumanbot_server.js74
7 files changed, 903 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index d4a5d5b..8e7121c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,9 @@ randino
photon
charm
+fiforead
+humanbot
+
viewcompetition
*.o
diff --git a/fiforead.cpp b/fiforead.cpp
new file mode 100644
index 0000000..6da0ae1
--- /dev/null
+++ b/fiforead.cpp
@@ -0,0 +1,16 @@
+#include <iostream>
+#include <fstream>
+#include <cassert>
+
+using namespace std;
+
+int main(int argc,char **argv){
+ if(argc!=2){
+ cerr<<"Pass fifo name as argument."<<endl;
+ return 1;
+ }
+ ifstream fifo(argv[1]);
+ assert(fifo.good());
+ while(!!fifo)cout<<(char)fifo.get()<<flush;
+ return 0;
+}
diff --git a/gameserver/gameserver.html b/gameserver/gameserver.html
index 7fc27fc..fdce5a8 100644
--- a/gameserver/gameserver.html
+++ b/gameserver/gameserver.html
@@ -39,6 +39,7 @@ function preinit(){
var spaceidx=msg.indexOf(" "),
cmd=msg.slice(0,spaceidx),
arg=JSON.parse(msg.slice(spaceidx+1));
+ var alerttext;
console.log(cmd,arg);
if(cmd=="nick"){
nick=arg;
@@ -96,10 +97,11 @@ function preinit(){
} else if(cmd=="game_win"){
bgcell(neuidx).classList.add("win");
if(arg==imPlayer){
- alert("You have won the game!");
+ alerttext="You have won the game!";
} else {
- alert("You have lost the game! Better luck next time.");
+ alerttext="You have lost the game! Better luck next time.";
}
+ setTimeout(function(){alert(alerttext);},canclick?500:1000);
} else alert("Unrecognised message "+msg);
});
ws.addEventListener("open",function(){
@@ -242,14 +244,18 @@ function enableClicking(en,disneu){
en=en?true:false;
disneu=disneu?true:false;
for(i=0;i<8;i++){
- document.getElementById("input_neudir"+i)[en&&!disneu?"removeAttribute":"setAttribute"]("disabled");
- document.getElementById("input_dir"+i)[en?"removeAttribute":"setAttribute"]("disabled");
+ if(en&&!disneu)document.getElementById("input_neudir"+i).removeAttribute("disabled");
+ else document.getElementById("input_neudir"+i).setAttribute("disabled","");
+ if(en)document.getElementById("input_dir"+i).removeAttribute("disabled");
+ else document.getElementById("input_dir"+i).setAttribute("disabled","");
}
l=document.getElementsByName("inputstoneradio");
for(i=0;i<l.length;i++){
- l[i][en&&(imPlayer==-1||board[i]==imPlayer)?"removeAttribute":"setAttribute"]("disabled");
+ if(en&&(imPlayer==-1||board[i]==imPlayer))l[i].removeAttribute("disabled");
+ else l[i].setAttribute("disabled","");
}
- document.getElementById("input_domovebtn")[en?"removeAttribute":"setAttribute"]("disabled");
+ if(en)document.getElementById("input_domovebtn").removeAttribute("disabled");
+ else document.getElementById("input_domovebtn").setAttribute("disabled","");
clickenabled=[en,disneu];
}
diff --git a/gameserver/gameserver.js b/gameserver/gameserver.js
index 8ab4bbc..a9233e9 100755
--- a/gameserver/gameserver.js
+++ b/gameserver/gameserver.js
@@ -225,6 +225,7 @@ function handleMessage(conn,msg){
c2.ws.send("other_turn "+JSON.stringify(mv));
won=checkWin(game.board,3-game.onturn); //player that just moved
if(won){
+ saveFinishedBoard(game.board);
conn.ws.send("game_win "+won);
c2.ws.send("game_win "+won);
games.splice(findby(games,"id",conn.gameid),1);
diff --git a/humanbot.cpp b/humanbot.cpp
new file mode 100644
index 0000000..5902e24
--- /dev/null
+++ b/humanbot.cpp
@@ -0,0 +1,254 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#define S (5)
+
+using namespace std;
+
+bool should_flip_board=false;
+
+struct Move;
+class Board;
+
+Move protocol_get_move(istream &s);
+void protocol_put_move(ostream &s,Move m);
+
+int index_deltas[8][2]={{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+
+struct Move{
+ int neudir,from,dir;
+ string str(void){
+ stringstream ss;
+ ss<<neudir<<' '<<from<<' '<<dir;
+ return ss.str();
+ }
+};
+
+class Board{
+public:
+ int grid[S*S];
+ int neuidx;
+ Board(void):neuidx(S*(S/2)+S/2){
+ memset(grid,0,S*S*sizeof(int));
+ grid[S*(S/2)+S/2]=3;
+ for(int i=0;i<S;i++){
+ grid[i]=1;
+ grid[S*(S-1)+i]=2;
+ }
+ }
+ int moved(int at,int dir){
+ int x=at%S,y=at/S,x2,y2;
+ while(true){
+ x2=x+index_deltas[dir][0];
+ y2=y+index_deltas[dir][1];
+ if(x2<0||x2>=S||y2<0||y2>=S||grid[S*y2+x2]!=0)return S*y+x;
+ x=x2;
+ y=y2;
+ }
+ }
+ bool move(int at,int dir){
+ int newat=moved(at,dir);
+ if(newat==at)return false;
+ grid[newat]=grid[at];
+ grid[at]=0;
+ if(grid[newat]==3)neuidx=newat;
+ return true;
+ }
+ void undomove(int at,int dir){
+ int movedidx=moved(at,dir);
+ movedidx+=S*index_deltas[dir][1]+index_deltas[dir][0];
+ grid[at]=grid[movedidx];
+ grid[movedidx]=0;
+ if(grid[at]==3)neuidx=at;
+ }
+ bool isvalid(Move mv){
+ int oldneuidx=neuidx;
+ //cerr<<"Called isvalid with move ";
+ //protocol_put_move(cerr,mv);
+ //print();
+ if(!move(oldneuidx,mv.neudir)){
+ //undomove(oldneuidx,mv.neudir);
+ return false;
+ }
+ if(!move(mv.from,mv.dir)){
+ //undomove(mv.from,mv.dir);
+ undomove(oldneuidx,mv.neudir);
+ return false;
+ }
+ undomove(mv.from,mv.dir);
+ undomove(oldneuidx,mv.neudir);
+ return true;
+ }
+ void print(void){
+ int x,y;
+ if(should_flip_board){
+ for(y=S-1;y>=0;y--){
+ for(x=S-1;x>=0;x--)cerr<<grid[S*y+x]<<' ';
+ cerr<<endl;
+ }
+ } else {
+ for(y=0;y<S;y++){
+ for(x=0;x<S;x++)cerr<<grid[S*y+x]<<' ';
+ cerr<<endl;
+ }
+ }
+ }
+};
+
+
+void restart_server(void);
+void kill_server(void);
+
+ofstream othermovefifo;
+ifstream mynewmovefifo;
+
+Move calculate_move(Board &bd){
+ cerr<<"c_m: restarting server..."<<endl;
+ restart_server();
+ cerr<<"c_m: Waiting for move from fifo..."<<endl;
+ Move mv=protocol_get_move(mynewmovefifo);
+ cerr<<"c_m: Got move from fifo."<<endl;
+ return mv;
+}
+
+long server_pid=-1;
+int server_port=-1;
+
+void setup_server(void){
+ unlink(".humanbot__.__othermove.fifo");
+ assert(mkfifo(".humanbot__.__othermove.fifo",0666)==0);
+
+ unlink(".humanbot__.__mynewmove.fifo");
+ assert(mkfifo(".humanbot__.__mynewmove.fifo",0666)==0);
+
+ server_port=42000+rand()%1000;
+
+ restart_server();
+
+ //only now can we do this, 'cause unix doesn't let a fifo exist with only one end
+ othermovefifo.open(".humanbot__.__othermove.fifo",ofstream::out|ofstream::app);
+ mynewmovefifo.open(".humanbot__.__mynewmove.fifo");
+ cerr<<"Opened fifo's!"<<endl;
+}
+
+void restart_server(void){
+ if(server_pid!=-1)kill_server();
+ pid_t cpid=fork();
+ assert(cpid!=-1);
+ if(cpid==0){ //child
+ cerr<<"Child!"<<endl;
+ server_pid=(long)getpid();
+ char *portstr;
+ asprintf(&portstr,"%d",server_port);
+ assert(!!portstr);
+ execl("/usr/bin/env","node","./humanbot_server.js",portstr,NULL);
+ perror("execlp");
+ exit(1);
+ }
+ cerr<<"Parent!"<<endl;
+}
+
+void kill_server(void){
+ kill(server_pid,SIGTERM); //stay friendly
+ usleep(100*1000); //100ms
+ kill(server_pid,SIGINT);
+ usleep(1000*1000); //1s
+}
+
+
+inline int flip_index(int idx){
+ return S*(S-1-idx/S)+S-1-idx%S;
+}
+inline int flip_dir(int dir){
+ return dir==-1?-1:(dir+4)%8;
+}
+
+Move protocol_get_move(istream &s){
+ Move m;
+ /*char c=s.peek();
+ if(c=='q'||c=='Q')exit(0);*/
+ cerr<<"Sleeping."<<endl;
+ system("sleep 10");
+ s>>m.neudir>>m.from>>m.dir;
+ if(should_flip_board){
+ m.neudir=flip_dir(m.neudir);
+ m.from=flip_index(m.from);
+ m.dir=flip_dir(m.dir);
+ }
+ cerr<<"Got: ";
+ protocol_put_move(cerr,m);
+ s.ignore(1024,'\n');
+ return m;
+}
+
+void protocol_put_move(ostream &s,Move m){
+ if(should_flip_board){
+ s<<flip_dir(m.neudir)<<' '<<flip_index(m.from)<<' '<<flip_dir(m.dir)<<endl;
+ } else {
+ s<<m.neudir<<' '<<m.from<<' '<<m.dir<<endl;
+ }
+}
+
+int main(void){
+ Board bd;
+ Move mv;
+ string line;
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ cerr<<"seed="<<(1000000*tv.tv_sec+tv.tv_usec)<<endl;
+ srand(1000000*tv.tv_sec+tv.tv_usec);
+ //srand(1430130052185291);
+ setup_server();
+ getline(cin,line);
+ if(line=="go"){
+ othermovefifo<<"go"<<endl;
+ should_flip_board=false;
+ //mv.neudir=-1; mv.from=1; mv.dir=3;
+ mv=calculate_move(bd);
+ bd.move(mv.from,mv.dir);
+ protocol_put_move(cout,mv);
+ } else {
+ if(line!="nogo")cerr<<"no0b "<<line<<" not in (go,nogo)"<<endl;
+ othermovefifo<<"nogo"<<endl;
+ should_flip_board=true;
+ mv=protocol_get_move(cin);
+ protocol_put_move(othermovefifo,mv);
+ bd.move(mv.from,mv.dir);
+ bd.print();
+ mv=calculate_move(bd);
+ if(!bd.isvalid(mv)){
+ cerr<<"calculate_move gave invalid move "<<mv.str()<<endl;
+ return 1;
+ }
+ bd.move(bd.neuidx,mv.neudir);
+ bd.move(mv.from,mv.dir);
+ protocol_put_move(cout,mv);
+ }
+ while(true){
+ bd.print();
+ mv=protocol_get_move(cin);
+ protocol_put_move(othermovefifo,mv);
+ bd.move(bd.neuidx,mv.neudir);
+ bd.move(mv.from,mv.dir);
+ bd.print();
+ mv=calculate_move(bd);
+ if(!bd.isvalid(mv)){
+ cerr<<"calculate_move gave invalid move "<<mv.str()<<endl;
+ return 1;
+ }
+ bd.move(bd.neuidx,mv.neudir);
+ bd.move(mv.from,mv.dir);
+ protocol_put_move(cout,mv);
+ }
+ return 0;
+}
diff --git a/humanbot_index.html b/humanbot_index.html
new file mode 100644
index 0000000..7d05c7a
--- /dev/null
+++ b/humanbot_index.html
@@ -0,0 +1,543 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Game</title>
+<script>
+var S=5;
+var PLAYERS=["Player 1","Player 2"];
+var MOVES=[];//[[-1,1,3],[7,23,0],[0,0,3]];
+
+
+var moveidx=0;
+var neuidxhist=[];
+
+var board=Array.apply(null,new Array(S*S)).map(function(){return 0;});
+var fgcells=board.map(function(){return null;});
+var neuidx;
+
+var canclick=false;
+
+var imPlayer=-1;
+
+var othermovefifo_interval=null;
+
+var clickenabled=[false,false];
+var input_neudir_choice=null,input_dir_choice=null,input_stone_choice=null;
+
+function init(){
+ var tr,td,y,x,e,span,
+ movelisttbody,
+ header=document.getElementById("header"),
+ status=document.getElementById("status"),
+ movelist=document.getElementById("movelist"),
+ bgtbody=document.getElementById("bgtbody");
+
+ document.title="Game: "+PLAYERS[0]+" vs "+PLAYERS[1];
+
+ header.appendChild(document.createTextNode("Game: "));
+ span=document.createElement("span");
+ span.classList.add("celltypeclr"); span.classList.add("celltypeclr_1");
+ span.appendChild(document.createTextNode(PLAYERS[0]));
+ header.appendChild(span);
+ header.appendChild(document.createTextNode(" vs "));
+ span=document.createElement("span");
+ span.classList.add("celltypeclr"); span.classList.add("celltypeclr_2");
+ span.appendChild(document.createTextNode(PLAYERS[1]));
+ header.appendChild(span);
+
+
+ e=document.createElement("table");
+ movelisttbody=document.createElement("tbody");
+ movelisttbody.id="movelisttbody";
+ tr=document.createElement("tr");
+ td=document.createElement("td"); td.innerHTML="Player"; tr.appendChild(td);
+ td=document.createElement("td"); td.innerHTML="Neutron"; tr.appendChild(td);
+ td=document.createElement("td"); td.innerHTML="Stone"; tr.appendChild(td);
+ movelisttbody.appendChild(tr);
+ e.appendChild(movelisttbody);
+ movelist.appendChild(e);
+
+ tr=document.createElement("tr");
+ td=document.createElement("td");
+ td.setAttribute("colspan","3");
+ td.innerHTML="&nbsp;";
+ tr.appendChild(td);
+ (function(tr){
+ tr.addEventListener("click",function(ev){
+ if(moveidx==0)return;
+ while(moveidx>0)prevmove(true);
+ setselectedmovelistline(0);
+ });
+ })(tr);
+ tr.classList.add("selected");
+ movelisttbody.appendChild(tr);
+
+ MOVES.forEach(function(mv,i){
+ addmovelistrow(mv,i);
+ });
+
+
+ var inputstonetbody=document.getElementById("inputstonetbody");
+ for(y=0;y<S;y++){
+ tr=document.createElement("tr");
+ for(x=0;x<S;x++){
+ td=document.createElement("td");
+ e=document.createElement("input");
+ e.setAttribute("type","radio");
+ e.setAttribute("name","inputstoneradio");
+ e.addEventListener("click",(function(x,y){return function(){
+ input_stone_choice=S*y+x;
+ };})(x,y));
+ td.appendChild(e);
+ tr.appendChild(td);
+ }
+ inputstonetbody.appendChild(tr);
+ }
+
+
+ for(y=0;y<S;y++){
+ tr=document.createElement("tr");
+ for(x=0;x<S;x++){
+ td=document.createElement("td");
+ td.classList.add("bgcell");
+ td.innerHTML=S*y+x;
+ tr.appendChild(td);
+ }
+ bgtbody.appendChild(tr);
+ }
+ for(x=0;x<S;x++){
+ board[x]=1;
+ e=makefgcell(x,1);
+ fgcells[x]=e;
+ document.body.appendChild(e);
+
+ board[S*(S-1)+x]=2;
+ e=makefgcell(S*(S-1)+x,2);
+ fgcells[S*(S-1)+x]=e;
+ document.body.appendChild(e);
+ }
+ neuidx=S*~~(S/2)+~~(S/2);
+ board[neuidx]=3;
+ e=makefgcell(neuidx,3);
+ fgcells[neuidx]=e;
+ document.body.appendChild(e);
+
+ setnextenabled(MOVES.length>0);
+ setprevenabled(false);
+ canclick=true;
+
+ othermovefifo_interval=setInterval(function(){
+ if(clickenabled[0]||clickenabled[1])return;
+ var xhr=new XMLHttpRequest();
+ xhr.addEventListener("readystatechange",function(){
+ if(this.readyState!=4)return;
+ console.log("othermove",this.status,this.responseText);
+ if(this.status!=200)return;
+ if(this.responseText=="go"){
+ enableClicking(true,true); //2nd true is to disable neutron dir
+ imPlayer=1;
+ } else if(this.responseText=="nogo"){
+ enableClicking(false);
+ imPlayer=2;
+ } else {
+ var mv=JSON.parse(this.responseText);
+ MOVES.push(mv);
+ addmovelistrow(mv);
+ nextmove();
+ enableClicking(true);
+ }
+ });
+ xhr.open("GET",location.protocol+"//"+location.host+"/othermove");
+ xhr.send();
+ },1500);
+
+ enableClicking(false);
+}
+
+function enableClicking(en,disneu){
+ var i,l;
+ en=en?true:false;
+ disneu=disneu?true:false;
+ for(i=0;i<8;i++){
+ document.getElementById("input_neudir"+i)[en&&!disneu?"removeAttribute":"setAttribute"]("disabled");
+ document.getElementById("input_dir"+i)[en?"removeAttribute":"setAttribute"]("disabled");
+ }
+ l=document.getElementsByName("inputstoneradio");
+ for(i=0;i<l.length;i++){
+ l[i][en&&(imPlayer==-1||board[i]==imPlayer)?"removeAttribute":"setAttribute"]("disabled");
+ }
+ document.getElementById("input_domovebtn")[en?"removeAttribute":"setAttribute"]("disabled");
+ clickenabled=[en,disneu];
+}
+
+function addmovelistrow(mv,i/*optional*/){
+ var tr,td;
+ var movelisttbody=document.getElementById("movelisttbody");
+ var i=i!=undefined?i:movelisttbody.children.length;
+ tr=document.createElement("tr");
+
+ td=document.createElement("td");
+ td.innerHTML=i%2+1;
+ tr.appendChild(td);
+
+ td=document.createElement("td");
+ if(mv[0]==-1)td.innerHTML="&nbsp;";
+ else td.innerHTML=arrowfor(mv[0]);
+ tr.appendChild(td);
+
+ td=document.createElement("td");
+ td.innerHTML=mv[1]+": "+arrowfor(mv[2]);
+ tr.appendChild(td);
+
+ tr.addEventListener("click",(function(target){return function(ev){
+ if(moveidx==target)return;
+ while(moveidx<target)nextmove(true);
+ while(moveidx>target)prevmove(true);
+ setselectedmovelistline(i+1);
+ };})(i+1));
+
+ movelisttbody.appendChild(tr);
+}
+
+function setselectedmovelistline(i){
+ var movelist=document.getElementById("movelist");
+ movelist.querySelectorAll("tr.selected")[0].classList.remove("selected");
+ document.getElementById("movelist").getElementsByTagName("tr")[i+1].classList.add("selected");
+}
+
+function arrowfor(dir){
+ return "&#x"+[2191,2197,2192,2198,2193,2199,2190,2196][dir]+";";
+}
+
+function bgcell(idx){
+ return document.getElementById("bgtbody").children[idx/S|0].children[idx%S];
+}
+function fgcellposition(idx){
+ var rect=bgcell(idx).getBoundingClientRect(),
+ scrolltop=window.pageYOffset||document.documentElement.scrollTop;
+ return [rect.left,rect.top+scrolltop];
+}
+function movefgcell(e,idx){
+ var pos=fgcellposition(idx);
+ e.style.left=pos[0]+"px";
+ e.style.top=pos[1]+"px";
+ e.cell_index=idx;
+}
+function makefgcell(idx,type){ //type in [1,2,3]
+ var pos=fgcellposition(idx),
+ e=document.createElement("div");
+ e.classList.add("fgcell");
+ e.classList.add("celltype_"+type);
+ e.setAttribute("style","left:"+pos[0]+"px;top:"+pos[1]+"px;");
+ e.cell_index=idx;
+ e.appendChild(document.createElement("div"));
+ return e;
+}
+
+var index_deltas=[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];
+function indexafterslide(from,dir,overshoot){
+ var x=from%S,y=~~(from/S),x2,y2;
+ while(true){
+ x2=x+index_deltas[dir][0];
+ y2=y+index_deltas[dir][1];
+ if(x2<0||x2>=S||y2<0||y2>=S||board[S*y2+x2]!=0){
+ if(overshoot)return S*y2+x2;
+ else return S*y+x;
+ }
+ x=x2;
+ y=y2;
+ }
+}
+
+function setnextenabled(en){
+ document.getElementById("nextmovebtn").disabled=!en;
+}
+function setprevenabled(en){
+ document.getElementById("prevmovebtn").disabled=!en;
+}
+
+function nextmove(notimeout){
+ var newidx;
+ if(moveidx==MOVES.length||!canclick)return false;
+
+ if(MOVES[moveidx][0]!=-1){
+ neuidxhist.push(neuidx);
+ newidx=indexafterslide(neuidx,MOVES[moveidx][0]);
+ movefgcell(fgcells[neuidx],newidx);
+ board[newidx]=board[neuidx];
+ board[neuidx]=0;
+ fgcells[newidx]=fgcells[neuidx];
+ fgcells[neuidx]=null;
+ neuidx=newidx;
+
+ if(moveidx==MOVES.length-1)bgcell(neuidx).classList.add("win");
+ }
+
+ newidx=indexafterslide(MOVES[moveidx][1],MOVES[moveidx][2]);
+ if(MOVES[moveidx][0]!=-1&&!notimeout){
+ setTimeout(
+ (function(e,idx){return function(){
+ movefgcell(e,idx);
+ canclick=true;
+ };})(fgcells[MOVES[moveidx][1]],newidx),
+ 500
+ );
+ canclick=false;
+ } else {
+ movefgcell(fgcells[MOVES[moveidx][1]],newidx);
+ }
+ board[newidx]=board[MOVES[moveidx][1]];
+ board[MOVES[moveidx][1]]=0;
+ fgcells[newidx]=fgcells[MOVES[moveidx][1]];
+ fgcells[MOVES[moveidx][1]]=null;
+
+ moveidx++;
+ setprevenabled(true);
+ if(moveidx==MOVES.length)setnextenabled(false);
+ if(!notimeout)setselectedmovelistline(moveidx);
+ return true;
+}
+function prevmove(notimeout){
+ var item,oldneuidx,newidx,winGlowIdxToRemove=null;
+ if(moveidx==0||!canclick)return false;
+ moveidx--;
+
+ if(MOVES[moveidx][0]!=-1&&moveidx==MOVES.length-1)winGlowIdxToRemove=neuidx;
+
+ newidx=indexafterslide(MOVES[moveidx][1],MOVES[moveidx][2],true);
+ board[MOVES[moveidx][1]]=board[newidx];
+ board[newidx]=0;
+ fgcells[MOVES[moveidx][1]]=fgcells[newidx];
+ fgcells[newidx]=null;
+ movefgcell(fgcells[MOVES[moveidx][1]],MOVES[moveidx][1]);
+
+ if(MOVES[moveidx][0]!=-1){
+ oldneuidx=neuidxhist.pop();
+ board[oldneuidx]=board[neuidx];
+ board[neuidx]=0;
+ fgcells[oldneuidx]=fgcells[neuidx];
+ fgcells[neuidx]=null;
+ if(notimeout){
+ movefgcell(fgcells[oldneuidx],oldneuidx);
+ if(winGlowIdxToRemove!=null)bgcell(winGlowIdxToRemove).classList.remove("win");
+ } else {
+ setTimeout(
+ (function(e,idx){return function(){
+ movefgcell(e,idx);
+ canclick=true;
+ if(winGlowIdxToRemove!=null)bgcell(winGlowIdxToRemove).classList.remove("win");
+ };})(fgcells[oldneuidx],oldneuidx),
+ 500
+ );
+ canclick=false;
+ }
+ neuidx=oldneuidx;
+ }
+
+ setnextenabled(true);
+ if(moveidx==0)setprevenabled(false);
+ if(!notimeout)setselectedmovelistline(moveidx);
+ return true;
+}
+
+
+function input_neudir(dir){
+ var i;
+ for(i=0;i<8;i++){
+ if(i==dir)document.getElementById("input_neudir"+i).classList.add("selected");
+ else document.getElementById("input_neudir"+i).classList.remove("selected");
+ }
+}
+
+function input_dir(dir){
+ var i;
+ for(i=0;i<8;i++){
+ if(i==dir)document.getElementById("input_dir"+i).classList.add("selected");
+ else document.getElementById("input_dir"+i).classList.remove("selected");
+ }
+}
+
+function input_domove(){
+ var i,l,mv=[-1,-1,-1];
+ for(i=0;i<8;i++){
+ if(document.getElementById("input_neudir"+i).classList.contains("selected")){
+ mv[0]=i;
+ break;
+ }
+ }
+ for(i=0;i<8;i++){
+ if(document.getElementById("input_dir"+i).classList.contains("selected")){
+ mv[2]=i;
+ break;
+ }
+ }
+ l=document.getElementsByName("inputstoneradio");
+ for(i=0;i<l.length;i++){
+ if(l[i].checked){
+ mv[1]=i;
+ break;
+ }
+ }
+ if(mv[0]==-1&&!clickenabled[1]){alert("Neutron direction not set!");return;}
+ if(mv[1]==-1){alert("Stone choice not set!");return;}
+ if(mv[2]==-1){alert("Stone direction not set!");return;}
+ var clickenabledcache=clickenabled;
+ enableClicking(false);
+ var xhr=new XMLHttpRequest();
+ xhr.addEventListener("readystatechange",function(){
+ if(this.readyState!=4)return;
+ console.log("mynewmove",this.status,this.responseText);
+ if(this.status!=200){
+ alert("Failed to submit move!");
+ enableClicking.apply(this,clickenabledcache);
+ } else {
+ MOVES.push(mv);
+ addmovelistrow(mv);
+ nextmove();
+ }
+ });
+ xhr.open("POST",location.protocol+"//"+location.host+"/mynewmove");
+ xhr.send(JSON.stringify(mv));
+}
+
+
+window.addEventListener("load",init,false);
+</script>
+<style>
+body{
+ font-family:Georgia,Times,Serif;
+ font-size:15px;
+}
+
+table#bgtab{
+ border-spacing:4px;
+}
+td.bgcell{
+ width:80px;
+ height:80px;
+ border:3px #ddd solid;
+ border-radius:12px;
+ transition:background-color 0.6s ease 0.2s, box-shadow 0.6s ease 0.2s, -webkit-box-shadow 0.6s ease 0.2s;
+
+ text-align:right;
+ vertical-align:bottom;
+ font-size:14px;
+}
+td.bgcell.win{
+ background-color:#8f8;
+ box-shadow:inset 0 0 6px #6e6;
+ -webkit-box-shadow:inset 0 0 6px #6e6;
+}
+div.fgcell{
+ position:absolute;
+ width:82px;
+ height:82px;
+ border:3px rgba(0,0,0,0) solid;
+ text-align:center;
+ transition:left 0.6s ease 0s, top 0.6s ease 0s;
+}
+
+div.fgcell > div{
+ height:75%;
+ margin:10px;
+ border-radius:30px;
+}
+
+h1#header{
+ letter-spacing:0.5px;
+}
+span.celltypeclr{
+ border-radius:5px;
+ padding:4px;
+}
+
+div#status{
+ font-size:16px;
+ font-weight:bold;
+}
+
+div.fgcell.celltype_1 > div{background-color:#ddd;}
+span.celltypeclr_1 {background-color:#ddd;}
+div.fgcell.celltype_2 > div{background-color:#2a2a2a;}
+span.celltypeclr_2 {background-color:#2a2a2a;color:#eee;}
+div.fgcell.celltype_3 > div{background-color:#11e;}
+
+div#movelist{
+ border:1px black solid;
+ width:500px;
+ height:200px;
+ overflow-y:scroll;
+}
+div#movelist table{
+ border-spacing:0;
+ width:100%;
+}
+div#movelist tr:first-child{
+ font-weight:bold;
+ background-color:#ccc;
+}
+div#movelist tr:first-child > td{
+ border-bottom:1px black solid;
+}
+div#movelist tr:nth-child(odd){background-color:#e4e4e4;}
+div#movelist tr:nth-child(even){background-color:#fff;}
+div#movelist tr:nth-child(n+2):hover{background-color:#bbf;cursor:pointer;}
+div#movelist tr.selected{
+ background-color:#afa;
+}
+
+input[type=button].selected{
+ background-color:#aaf;
+}
+</style>
+</head>
+<body>
+<h1 id="header"></h1>
+<table id="bgtab" style="display:inline-block"><tbody id="bgtbody"></tbody></table>
+<table id="inputneudirtab" style="display:inline-block"><tbody id="inputneudirtbody">
+ <tr>
+ <td><input type="button" id="input_neudir7" value="&#x2196;" onclick="input_neudir(7)"></td>
+ <td><input type="button" id="input_neudir0" value="&#x2191;" onclick="input_neudir(0)"></td>
+ <td><input type="button" id="input_neudir1" value="&#x2197;" onclick="input_neudir(1)"></td>
+ </tr>
+ <tr>
+ <td><input type="button" id="input_neudir6" value="&#x2190;" onclick="input_neudir(6)"></td>
+ <td>Neu</td>
+ <td><input type="button" id="input_neudir2" value="&#x2192;" onclick="input_neudir(2)"></td>
+ </tr>
+ <tr>
+ <td><input type="button" id="input_neudir5" value="&#x2199;" onclick="input_neudir(5)"></td>
+ <td><input type="button" id="input_neudir4" value="&#x2193;" onclick="input_neudir(4)"></td>
+ <td><input type="button" id="input_neudir3" value="&#x2198;" onclick="input_neudir(3)"></td>
+ </tr>
+</tbody></table>
+<table id="inputdirtab" style="display:inline-block;margin-left:20px"><tbody id="inputdirtbody">
+ <tr>
+ <td><input type="button" id="input_dir7" value="&#x2196;" onclick="input_dir(7)"></td>
+ <td style="text-align:center"><input type="button" id="input_dir0" value="&#x2191;" onclick="input_dir(0)"></td>
+ <td><input type="button" id="input_dir1" value="&#x2197;" onclick="input_dir(1)"></td>
+ </tr>
+ <tr>
+ <td><input type="button" id="input_dir6" value="&#x2190;" onclick="input_dir(6)"></td>
+ <td>
+ <table id="inputstonetab" style="display:inline-block"><tbody id="inputstonetbody"></tbody></table>
+ </td>
+ <td><input type="button" id="input_dir2" value="&#x2192;" onclick="input_dir(2)"></td>
+ </tr>
+ <tr>
+ <td><input type="button" id="input_dir5" value="&#x2199;" onclick="input_dir(5)"></td>
+ <td style="text-align:center"><input type="button" id="input_dir4" value="&#x2193;" onclick="input_dir(4)"></td>
+ <td><input type="button" id="input_dir3" value="&#x2198;" onclick="input_dir(3)"></td>
+ </tr>
+</tbody></table>
+<div style="display:inline-block;margin-left:10px">
+ <input type="button" id="input_domovebtn" value="Do move" onclick="input_domove()">
+ <br>
+</div>
+<br>
+<input type="button" id="prevmovebtn" onclick="prevmove()" value="&larr;">
+<input type="button" id="nextmovebtn" onclick="nextmove()" value="&rarr;"><br>
+<div id="movelist" style="margin-top:30px"></div>
+</body>
+</html>
diff --git a/humanbot_server.js b/humanbot_server.js
new file mode 100755
index 0000000..3f38f66
--- /dev/null
+++ b/humanbot_server.js
@@ -0,0 +1,74 @@
+#!/usr/bin/env node
+var http=require("http"),fs=require("fs");
+var PORT=+process.argv[2];
+var movequeue=[];
+var httpserver=http.createServer(function(req,res){
+ var reqbody="";
+ console.log("req: "+req.url);
+ if(req.url=="/othermove"){
+ if(movequeue.length){
+ res.writeHead(200,{"Content-Type":"application/json"});
+ res.end(movequeue.shift());
+ } else {
+ res.writeHead(503,{"Content-Type":"text/plain"});
+ res.end("No move to unqueue");
+ }
+ } else if(req.url=="/mynewmove"){
+ req.on("data",function(data){reqbody+=data;});
+ req.on("end",function(){
+ var mv;
+ console.log("mynewmove: reqbody = "+reqbody);
+ try {mv=JSON.parse(reqbody);}
+ catch(e){mv=null;}
+ if(mv==null||
+ !Array.isArray(mv)||
+ mv.length!=3||
+ !mv.map(function(v){
+ return typeof(v)=="number"&&v%1==0;
+ }).reduce(function(a,b){return a&&b;})
+ ){
+ res.writeHead(400,{"Content-Type":"text/plain"});
+ res.end("400 Bad request");
+ return;
+ }
+ mynewmovefifo.write(mv.join(" ")+"\n",function(){
+ console.log("server: ACTUALLY written new move to fifo");
+ });
+ res.writeHead(200,{"Content-Type":"text/plain"});
+ res.end("true");
+ });
+ } else if(req.url=="/"){
+ res.writeHead(200,{"Content-Type":"text/html"});
+ res.end(String(fs.readFileSync("humanbot_index.html")));
+ } else {
+ res.writeHead(404,{"Content-Type":"text/plain"});
+ res.end("Nothing here...");
+ }
+});
+var httpserverlisteninterval=setInterval(function(){
+ try {
+ httpserver.listen(PORT);
+ console.log("Server running at port "+PORT+". (http://localhost:"+PORT+")");
+ clearInterval(httpserverlisteninterval);
+ } catch(e){}
+},10);
+
+var othermovefifo,mynewmovefifo,othermovefifo_buffer="";
+othermovefifo=fs.createReadStream(".humanbot__.__othermove.fifo");
+othermovefifo.on("data",function(data){
+ var idx,v;
+ othermovefifo_buffer+=data;
+ idx=othermovefifo_buffer.indexOf("\n");
+ if(idx!=-1){
+ v=othermovefifo_buffer.slice(0,idx);
+ if(v!="go"&&v!="nogo")v="["+v.replace(/ /g,",")+"]";
+ movequeue.push(v);
+ othermovefifo_buffer=othermovefifo_buffer.slice(idx+1);
+ }
+});
+othermovefifo.on("end",function(){
+ console.log("othermovefifo closed. Exiting.");
+ process.exit();
+});
+mynewmovefifo=fs.createWriteStream(".humanbot__.__mynewmove.fifo",{flags:"a"});
+console.log("Fifo's opened.");