diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rwxr-xr-x | competition.py | 34 | ||||
-rw-r--r-- | viewcompetition.cpp | 125 | ||||
-rw-r--r-- | viewcompetition.css | 76 | ||||
-rw-r--r-- | viewcompetition.js | 255 |
5 files changed, 416 insertions, 77 deletions
@@ -1,10 +1,13 @@ competitions/ playerlogs/ +gamevisuals/ gluon neutrino randino photon +viewcompetition + *.o diff --git a/competition.py b/competition.py index ab72d20..2582288 100755 --- a/competition.py +++ b/competition.py @@ -12,7 +12,7 @@ Options: -C Do not write a Competition log -h Help. What you're looking at -q Quiet. Don't print so much - -v View the competition afterwards using ./viewcompetition + -V Do not view the competition, so don't write an html file """ import os,sys,subprocess,shlex,re,time @@ -115,7 +115,7 @@ def haswon(board,lastplayer): fname="" quiet=False -viewcompetition=False +viewcompetition=True complog=True if len(sys.argv)==1: #no args fname="competition.txt" @@ -129,7 +129,7 @@ else: print(__doc__) sys.exit(0) elif c=="q": quiet=True - elif c=="v": viewcompetition=True + elif c=="V": viewcompetition=False else: print("Unrecognised flag '"+c+"'.") print(__doc__) @@ -179,12 +179,12 @@ elif not os.path.isdir("playerlogs"): sys.exit(1) try: - logfname="playerlogs/"+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+"_white_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+".txt" - p1errlog=open(logfname,mode="w") - logfname="playerlogs/"+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+"_black_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+".txt" - p2errlog=open(logfname,mode="w") + plogfname="playerlogs"+os.path.sep+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+"_white_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+".txt" + p1errlog=open(plogfname,mode="w") + plogfname="playerlogs"+os.path.sep+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+"_black_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+".txt" + p2errlog=open(plogfname,mode="w") except: - print("Error: could not open log file '"+logfname+"'.") + print("Error: could not open log file '"+plogfname+"'.") sys.exit(1) #Start programs @@ -222,7 +222,7 @@ elif not os.path.isdir("competitions"): sys.exit(1) try: - logfname="competitions/game_"+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+"_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+".txt" + logfname="competitions"+os.path.sep+"game_"+re.sub(r"[^a-zA-Z0-9 ]","",p1fname)+"_vs_"+re.sub(r"[^a-zA-Z0-9 ]","",p2fname)+".txt" logfile=open(logfname,mode="w") logfile.write("P1: "+p1fname+"\nP2: "+p2fname+"\n") except: @@ -299,6 +299,8 @@ while True: if not quiet: print("P"+str(player)+": "+movestr(move)+" ",end="") sys.stdout.flush() + if complog: + logfile.write("P"+str(player)+": "+movestr(move)+"\n") won=haswon(board,player) @@ -315,8 +317,6 @@ while True: except: pass sys.exit(1) else: raise e - if complog: - logfile.write("P"+str(player)+": "+movestr(move)+"\n") nummoves+=1 if won!=0: @@ -350,4 +350,14 @@ try: p2proc.terminate() except: pass if viewcompetition: - os.system("."+os.path.sep+"viewcompetition "+logfname) + if not os.path.exists("gamevisuals"): + try: + os.mkdir("gamevisuals") + except: + print("Error: could not create log directory 'gamevisuals'.") + sys.exit(1) + elif not os.path.isdir("gamevisuals"): + #Apparently, there's a file named "gamevisuals". Bastard. + print("Error: an existing file prohibits creation of log directory 'gamevisuals'.") + sys.exit(1) + os.system("."+os.path.sep+"viewcompetition "+logfname+" >gamevisuals"+os.path.sep+logfname[logfname.index(os.path.sep)+1:]+".html") diff --git a/viewcompetition.cpp b/viewcompetition.cpp index 40ba715..3f15c29 100644 --- a/viewcompetition.cpp +++ b/viewcompetition.cpp @@ -1,111 +1,106 @@ #include <iostream> #include <fstream> -#include <limits> -#include <cstdint> -#include <cassert> -#ifdef _WIN32 -#include <windows.h> -#else -#include <unistd.h> -#endif - -#define SYMBOLFOR(p) ((p)==P1?'X':(p)==P2?'O':(p)==PNONE?'.':'?') - -#define PNONE (0) -#define P1 (1) -#define P2 (2) +#include <vector> +#include <sstream> using namespace std; -uint8_t board[81]={PNONE}; - -void dosleep(int ms){ -#ifdef _WIN32 - Sleep(ms); -#else - usleep(ms*1e3); -#endif -} - -void printboard(void){ - int x,y; - cout<<"+-------+-------+-------+"<<endl; - for(y=0;y<9;y++){ - cout<<"| "; - for(x=0;x<9;x++){ - cout<<SYMBOLFOR(board[9*y+x])<<' '; - if(x%3==2)cout<<"| "; - } - cout<<endl; - if(y%3==2)cout<<"+-------+-------+-------+"<<endl; +struct Move{ + int neudir,from,dir; + string str(void){ + stringstream ss; + ss<<neudir<<' '<<from<<' '<<dir; + return ss.str(); } -} + string json(void){ + stringstream ss; + ss<<'['<<neudir<<','<<from<<','<<dir<<']'; + return ss.str(); + } +}; + +const char *html_string[5]={ +"<!DOCTYPE html>\n\ +<html>\n\ +<head>\n\ +<meta charset=\"utf-8\">\n\ +<title>Game</title>\n\ +<script>\n\ +var S=" , ";\n\ +var PLAYERS=[" , "];\n\ +var MOVES=[" , "null];MOVES.pop();\n\ +var WINNER=" , ";\n\ +</script>\n\ +<script src=\"../viewcompetition.js\"></script>\n\ +<link rel=\"stylesheet\" type=\"text/css\" href=\"../viewcompetition.css\">\n\ +</head>\n\ +<body>\n\ +<h1 id=\"header\"></h1>\n\ +<table id=\"bgtab\"><tbody id=\"bgtbody\"></tbody></table><br>\n\ +<input type=\"button\" id=\"prevmovebtn\" onclick=\"prevmove()\" value=\"←\">\n\ +<input type=\"button\" id=\"nextmovebtn\" onclick=\"nextmove()\" value=\"→\"><br>\n\ +<div id=\"movelist\" style=\"margin-top:30px\"></div>\n\ +<div id=\"status\" style=\"margin-top:50px\"></div>\n\ +</body>\n\ +</html>\n"}; int main(int argc,char **argv){ if(argc==1){ - cout<<"Pass the file name of the competition log as a command-line parameter."<<endl; + cerr<<"Pass the file name of the competition log as a command-line parameter."<<endl; return 1; } else if(argc>2){ - cout<<"Multiple command-line arguments were passed."<<endl; + cerr<<"Multiple command-line arguments were passed."<<endl; return 1; } ifstream in(argv[1]); if(!in.good()){ - cout<<"Cannot open file '"<<argv[1]<<"'."<<endl; + cerr<<"Cannot open file '"<<argv[1]<<"'."<<endl; return 1; } string p1name,p2name; - int player,x,y; + int player; char c; + Move mv; + cout<<html_string[0]; + cout<<5; + cout<<html_string[1]; in.get(); in.get(); in.get(); in.get(); //"P1: " getline(in,p1name); in.get(); in.get(); in.get(); in.get(); //"P2: " getline(in,p2name); - cout<<"\x1B[2J\x1B[1;1HCompetition between:\n" - "P1 (X): "<<p1name<<"\n" - "P2 (O): "<<p2name<<endl; + cout<<'"'<<p1name<<"\",\""<<p2name<<"\""; + cout<<html_string[2]; if(!in.good()){ - cout<<"Error in log file format."<<endl; + cerr<<"Error in log file format."<<endl; return 1; } - cout<<"\x1B[4;27HPress enter to play next move"<<flush; - cout<<"\x1B[4;1H"<<flush; - printboard(); while(in.good()){ c=in.get(); //"P" for a move line or a win line, "T" for a tie line if(c=='P'){ player=in.get()-'0'; c=in.get(); //":" for a move line, " " for a win line if(c==':'){ - in.get(); - x=in.get()-'0'; - in.get(); //" " - y=in.get()-'0'; + in>>mv.neudir>>mv.from>>mv.dir; in.ignore(numeric_limits<streamsize>::max(),'\n'); - board[9*y+x]=player; - cout<<"\x1B["<<(5+y/3+y)<<';'<<(3+x/3*2+x*2)<<'H'<<SYMBOLFOR(player) - <<"\x1B[5;27H"<<SYMBOLFOR(player)<<": "<<x<<' '<<y - <<"\x1B[7;27H"<<SYMBOLFOR(haswonsmall(0))<<SYMBOLFOR(haswonsmall(1))<<SYMBOLFOR(haswonsmall(2)) - <<"\x1B[8;27H"<<SYMBOLFOR(haswonsmall(3))<<SYMBOLFOR(haswonsmall(4))<<SYMBOLFOR(haswonsmall(5)) - <<"\x1B[9;27H"<<SYMBOLFOR(haswonsmall(6))<<SYMBOLFOR(haswonsmall(7))<<SYMBOLFOR(haswonsmall(8))<<flush; - c=cin.get(); - if(c!='\n')cin.ignore(numeric_limits<streamsize>::max(),'\n'); + cout<<mv.json()<<','; } else if(c==' '){ - cout<<"\x1B[17;1HThe victor is player "<<player<<"!"<<endl; + cout<<html_string[3]; + cout<<player; + cout<<html_string[4]<<flush; in.close(); - dosleep(1500); return 0; } else { - cout<<"\x1B[17;1HError in log file format."<<endl; + cerr<<"Error in log file format."<<endl; return 1; } } else if(c=='T'){ - cout<<"\x1B[17;1HThe game resulted in a tie."<<endl; + cout<<html_string[3]; + cout<<-1; + cout<<html_string[4]<<flush; in.close(); - dosleep(1500); return 0; } } - cout<<"\x1B[17;1HLog file ended prematurely."<<endl; + cerr<<"Log file ended prematurely."<<endl; return 1; } diff --git a/viewcompetition.css b/viewcompetition.css new file mode 100644 index 0000000..0fc53f5 --- /dev/null +++ b/viewcompetition.css @@ -0,0 +1,76 @@ +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; +} +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;} diff --git a/viewcompetition.js b/viewcompetition.js new file mode 100644 index 0000000..ab05c3c --- /dev/null +++ b/viewcompetition.js @@ -0,0 +1,255 @@ +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; + +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); + + status.appendChild(document.createTextNode("Game won by ")); + span=document.createElement("span"); + span.classList.add("celltypeclr"); span.classList.add("celltypeclr_"+WINNER); + span.appendChild(document.createTextNode(PLAYERS[WINNER-1])); + status.appendChild(span); + status.appendChild(document.createTextNode(".")); + + + e=document.createElement("table"); + movelisttbody=document.createElement("tbody"); + 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=" "; + tr.appendChild(td); + tr.addEventListener("click",function(ev){ + if(moveidx==0)return; + while(moveidx>0)prevmove(true); + }); + movelisttbody.appendChild(tr); + + MOVES.forEach(function(mv,i){ + var tr,td; + 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=" "; + 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); + };})(i+1)); + + movelisttbody.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(true); + setprevenabled(false); + canclick=true; +} + +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(); + return [rect.left,rect.top+document.documentElement.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.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 displaycurrentmove(){ + +} + +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); + 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); + return true; +} + +window.addEventListener("load",init,false); |