aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <hallo@tomsmeding.nl>2015-04-26 20:21:51 +0200
committertomsmeding <hallo@tomsmeding.nl>2015-04-26 20:21:51 +0200
commit6264573c8537fda9df1b35a74019eabcc3ee8899 (patch)
tree2a96cda3e9e9a698d88289da15d1e33c9602c8a0
Initial
-rw-r--r--.gitignore9
-rw-r--r--Makefile15
-rwxr-xr-xcompetition.py353
-rwxr-xr-xfullcomp.sh36
-rwxr-xr-xfullcompMT.sh93
-rwxr-xr-xfullcompstats.py42
-rwxr-xr-xgluon.cc45
-rwxr-xr-xhiggs.cc117
-rwxr-xr-xhiggs.h31
-rwxr-xr-xneutrino.cc86
-rw-r--r--randino.cpp169
-rw-r--r--viewcompetition.cpp111
12 files changed, 1107 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2800b41
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+competitions/
+playerlogs/
+
+gluon
+neutrino
+randino
+
+*.o
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4476c41
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+.PHONY: all
+
+all: neutrino randino gluon
+
+neutrino: neutrino.cc higgs.o
+ g++ -Wall -std=c++11 -O2 -o neutrino neutrino.cc higgs.o
+
+gluon: gluon.cc higgs.o
+ g++ -Wall -std=c++11 -O2 -o gluon gluon.cc higgs.o
+
+randino: randino.cpp
+ g++ -Wall -std=c++11 -O2 -o randino randino.cpp
+
+higgs.o: higgs.cc
+ g++ -Wall -std=c++11 -O2 -c -o higgs.o higgs.cc
diff --git a/competition.py b/competition.py
new file mode 100755
index 0000000..ab72d20
--- /dev/null
+++ b/competition.py
@@ -0,0 +1,353 @@
+#!/usr/bin/env python3
+"""
+Usage: ./competition.py [options] [competitionfile]
+
+The <competitionfile> should contain two lines, which are the two commands to
+execute as players.
+
+The script writes a competitionlog to competitions/game_p1_vs_p2.txt (with p1
+and p2 replaced by their respective commands).
+
+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
+"""
+
+import os,sys,subprocess,shlex,re,time
+
+S=5
+PNONE=0
+PP1=1
+PP2=2
+PNEU=3
+
+#Function definitions
+
+class Board:
+ def __init__(self):
+ self._neuidx=S*(S//2)+S//2
+ self._grid=[0]*(S*S)
+ self._grid[self._neuidx]=PNEU
+ for i in range(S):
+ self._grid[i]=PP1
+ self._grid[S*(S-1)+i]=PP2
+
+ def move(self,at,to):
+ if at<0 or at>=S*S: raise Exception("Invalid at")
+ if to<0 or to>=S*S: raise Exception("Invalid to")
+ if self._grid[at]==PNONE: raise Exception("Nothing to move")
+ if self._grid[to]!=PNONE: raise Exception("Place taken")
+ self._grid[to]=self._grid[at]
+ self._grid[at]=PNONE
+ if self._grid[to]==PNEU:
+ self._neuidx=to
+
+ def undomove(self,at,to):
+ self._grid[at]=self._grid[to]
+ self._grid[to]=PNONE
+ if self._grid[at]==PNEU:
+ self._neuidx=at
+
+ def get(self,idx): return self._grid[idx]
+
+ def neu(self): return self._neuidx
+
+ def pr(self):
+ print("+"+"-+"*S)
+ for y in range(S):
+ print("|",end="")
+ for x in range(S):
+ print(self._grid[S*y+x],end=(" " if x<S-1 else "|"))
+ print("")
+ print("+"+"-+"*S)
+
+
+INDEXDELTAS=[(0,-1),(1,-1),(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1)]
+def indexAfterSlide(board,idx,dir):
+ x,y=idx%S,idx//S
+ dx,dy=INDEXDELTAS[dir]
+ while True:
+ x2,y2=x+dx,y+dy
+ if x2<0 or x2>=S or y2<0 or y2>=S or board.get(S*y2+x2)!=PNONE:
+ return S*y+x
+ x,y=x2,y2
+
+def parsemove(line):
+ match=re.match(r"^([0-7]|-1) ([0-9]+) ([0-7])$",line)
+ if not match: return False
+ try: return [int(match.group(i)) for i in range(1,4)] #neudir, from, dir
+ except: return False
+
+def isvalid(board,player,move):
+ if move[1]<0 or move[1]>=S*S: return False
+ if board.get(move[1])!=[PP1,PP2][player-1]: return False
+ if move[0]==-1 and not isFirstMove: return False
+ try:
+ if move[0]!=-1:
+ oldneu=board.neu()
+ newneuidx=indexAfterSlide(board,oldneu,move[0])
+ if newneuidx==oldneu: return False
+ board.move(board.neu(),newneuidx)
+ newidx=indexAfterSlide(board,move[1],move[2])
+ if newidx==move[1]:
+ if move[0]!=-1: board.undomove(oldneu,newneuidx)
+ return False
+ board.move(move[1],newidx)
+ except Exception as e:
+ return False
+ board.undomove(move[1],newidx)
+ if move[0]!=-1: board.undomove(oldneu,newneuidx)
+ return True
+
+def movestr(mv):return " ".join([str(e) for e in mv])
+
+def haswon(board,lastplayer):
+ if board.neu()<S: return 1
+ if board.neu()>=S*(S-1): return 2
+ for dir in range(8):
+ newidx=indexAfterSlide(board,board.neu(),dir)
+ if newidx!=board.neu(): return 0
+ return lastplayer
+
+#Getting entries and flags
+
+fname=""
+quiet=False
+viewcompetition=False
+complog=True
+if len(sys.argv)==1: #no args
+ fname="competition.txt"
+else:
+ for arg in sys.argv[1:]: #skip script name
+ if len(arg)>1 and arg[0]=="-":
+ for c in arg[1:]: #skip "-"
+ if c=="C":
+ complog=False
+ elif c=="h":
+ print(__doc__)
+ sys.exit(0)
+ elif c=="q": quiet=True
+ elif c=="v": viewcompetition=True
+ else:
+ print("Unrecognised flag '"+c+"'.")
+ print(__doc__)
+ sys.exit(1)
+ elif fname=="":
+ fname=arg
+ else:
+ print("Unrecognised argument '"+arg+"'; the competition file name was already given as '"+fname+"'.")
+ sys.exit(1)
+
+if fname=="-":
+ if not quiet: print("Getting entries from stdin.")
+ p1fname=""
+ p2fname=""
+ while p1fname=="": p1fname=input().strip()
+ while p2fname=="": p2fname=input().strip()
+else:
+ if fname=="": fname="competition.txt"
+ if not quiet: print("Getting entries from file '"+fname+"'.")
+ try:
+ f=open(fname,mode="r")
+ except:
+ print("Could not open file '"+fname+"'.")
+ sys.exit(1)
+
+ p1fname=f.readline().strip()
+ if p1fname=="":
+ print("Too few lines in file.");
+ sys.exit(1)
+ p2fname=f.readline().strip()
+ if p2fname=="":
+ print("Too few lines in file.");
+ sys.exit(1)
+ f.close()
+
+#Set up player logs
+
+if not os.path.exists("playerlogs"):
+ try:
+ os.mkdir("playerlogs")
+ except:
+ print("Error: could not create log directory 'playerlogs'.")
+ sys.exit(1)
+elif not os.path.isdir("playerlogs"):
+ #Apparently, there's a file named "playerlogs". Bastard.
+ print("Error: an existing file prohibits creation of log directory '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")
+except:
+ print("Error: could not open log file '"+logfname+"'.")
+ sys.exit(1)
+
+#Start programs
+
+if not quiet:
+ print("Running this competition with:")
+ print("P1 (X): '"+p1fname+"'")
+ print("P2 (O): '"+p2fname+"'")
+
+try:
+ p1proc=subprocess.Popen(shlex.split(p1fname),stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=p1errlog,bufsize=1,shell=True) #line-buffered
+except Exception as e:
+ print("Could not execute command '"+p1fname+"'.")
+ raise e
+(p1in,p1out)=(p1proc.stdin,p1proc.stdout)
+
+try:
+ p2proc=subprocess.Popen(shlex.split(p2fname),stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=p2errlog,bufsize=1,shell=True) #line-buffered
+except Exception as e:
+ print("Could not execute command '"+p2fname+"'.")
+ raise e
+(p2in,p2out)=(p2proc.stdin,p2proc.stdout)
+
+#Set up competition log
+
+if not os.path.exists("competitions"):
+ try:
+ os.mkdir("competitions")
+ except:
+ print("Error: could not create log directory 'competitions'.")
+ sys.exit(1)
+elif not os.path.isdir("competitions"):
+ #Apparently, there's a file named "competitions". Bastard.
+ print("Error: an existing file prohibits creation of log directory '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"
+ logfile=open(logfname,mode="w")
+ logfile.write("P1: "+p1fname+"\nP2: "+p2fname+"\n")
+except:
+ print("Error: could not open log file '"+logfname+"'.")
+ sys.exit(1)
+
+#Start competition
+
+board=Board()
+nummoves=0
+endgame=False
+
+isFirstMove=True
+
+p1totaltime=0
+p2totaltime=0
+
+p1in.write(b"go\n");
+try:
+ p1in.flush()
+except IOError as e:
+ if e.errno==os.errno.EINVAL:
+ print("Program P1 quit prematurely.")
+ try: p2proc.terminate()
+ except: pass
+ sys.exit(1)
+ else: raise e
+p2in.write(b"nogo\n");
+try:
+ p2in.flush()
+except IOError as e:
+ if e.errno==os.errno.EINVAL:
+ print("Program P2 quit prematurely.")
+ try: p1proc.terminate()
+ except: pass
+ sys.exit(1)
+ else: raise e
+while True:
+ for (pAin,pAout,pBin,pBout,player) in [(p1in,p1out,p2in,p2out,1),(p2in,p2out,p1in,p1out,2)]:
+ while True:
+ line=pAout.readline().decode("ascii").strip()
+ if len(line)>0:
+ break
+ time.sleep(0.1)
+ timeoutfail=False
+ if player==1:
+ p1totaltime+=0.1
+ if p1totaltime>=50:
+ timeoutfail=True
+ else:
+ p2totaltime+=0.1
+ if p2totaltime>=50:
+ timeoutfail=True
+ if timeoutfail:
+ print("P"+str(player)+" timed out!")
+ try: p1proc.terminate()
+ except: pass
+ try: p2proc.terminate()
+ except: pass
+ sys.exit(1)
+ move=parsemove(line)
+ if move==False or not isvalid(board,player,move):
+ print("move =",move,"player =",player)
+ print("P"+str(player)+" made an invalid move: '"+line+"'.")
+ endgame=True
+ break
+ if not isFirstMove:
+ newidx=indexAfterSlide(board,board.neu(),move[0])
+ #print("moving",board.neu(),newidx)
+ board.move(board.neu(),newidx)
+ newidx=indexAfterSlide(board,move[1],move[2])
+ board.move(move[1],newidx)
+
+ if not quiet:
+ print("P"+str(player)+": "+movestr(move)+" ",end="")
+ sys.stdout.flush()
+
+ won=haswon(board,player)
+
+ if not won: #writing the move to the next player if it was a winning move is _quite_ useless.
+ pBin.write((movestr(move)+"\n").encode("ascii"))
+ try:
+ pBin.flush()
+ except IOError as e:
+ if e.errno==os.errno.EINVAL:
+ print("Program P"+str(3-player)+" quit prematurely.")
+ try:
+ if player==1: p2proc.terminate()
+ else: p1proc.terminate()
+ except: pass
+ sys.exit(1)
+ else: raise e
+ if complog:
+ logfile.write("P"+str(player)+": "+movestr(move)+"\n")
+
+ nummoves+=1
+ if won!=0:
+ if not quiet:
+ print("")
+ board.pr()
+ print("")
+ print("P"+str(won)+" has won this game!")
+ if complog:
+ logfile.write("P"+str(won)+" won\n")
+ endgame=True
+ break
+ if not quiet:
+ print("")
+ board.pr()
+ print("")
+ isFirstMove=False
+ if endgame:
+ break
+
+#Clean up
+
+if complog:
+ logfile.close()
+p1errlog.close()
+p2errlog.close()
+
+try: p1proc.terminate()
+except: pass
+try: p2proc.terminate()
+except: pass
+
+if viewcompetition:
+ os.system("."+os.path.sep+"viewcompetition "+logfname)
diff --git a/fullcomp.sh b/fullcomp.sh
new file mode 100755
index 0000000..c588bd5
--- /dev/null
+++ b/fullcomp.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+BINARIES="./stttfirst ./stttrandom ./stttrecur ./stttswag"
+
+if [[ ! -e competitions ]]; then
+ mkdir competitions || exit 1
+fi
+
+find competitions -type f -delete
+
+for p1 in $BINARIES; do
+ for p2 in $BINARIES; do
+ if [[ $p1 == $p2 ]]; then
+ continue
+ fi
+ printf "%s\n%s\n" $p1 $p2 >competition.txt
+ ./competition.py $@
+ status=$?
+ if [[ $status != 0 ]]; then
+ echo "$p1 - $p2 : ERROR $status (0-0)"
+ continue
+ fi
+ p1pretty=$(echo "$p1" | sed 's/[^a-zA-Z0-9 ]//g')
+ p2pretty=$(echo "$p2" | sed 's/[^a-zA-Z0-9 ]//g')
+ complogfile="competitions/game_${p1pretty}_vs_${p2pretty}.txt"
+ lastline=$(tail -n1 $complogfile)
+ if [[ "$lastline" == "P1 won" ]]; then
+ echo "$p1 - $p2 : WIN - LOSS (3-1)"
+ elif [[ "$lastline" == "P2 won" ]]; then
+ echo "$p1 - $p2 : LOSS - WIN (1-3)"
+ elif [[ "$lastline" == "Tie" ]]; then
+ echo "$p1 - $p1 : TIE (1-1)"
+ fi
+ done
+done
+
+./fullcompstats.py
diff --git a/fullcompMT.sh b/fullcompMT.sh
new file mode 100755
index 0000000..5214f25
--- /dev/null
+++ b/fullcompMT.sh
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+BINARIES="./stttfirst ./stttrandom ./stttrecur ./stttswag"
+
+if [[ ! -e competitions ]]; then
+ mkdir competitions || exit 1
+fi
+if [[ ! -e competitionstubs ]]; then
+ mkdir competitionstubs || exit 1
+fi
+
+find competitions/ -type f -delete
+find competitionstubs/ -type f -delete
+
+FIFONAME=fullcompMT_output_fifo.fifo
+[[ -e $FIFONAME ]] && rm $FIFONAME
+mkfifo fullcompMT_output_fifo.fifo
+
+tail -F $FIFONAME &
+TAILPID=$!
+
+trap "rm $FIFONAME; kill $TAILPID; ./fullcompstats.py" EXIT
+
+
+DATE_FMT="%Y-%m-%d %H:%M:%S"
+
+
+
+#SOURCE: competition.sh, by Wilmer van der Gaast
+# Don't count the number of logical cores/threads. Although a competition
+# finishes slightly faster when using all (hyper)threads, it's only a 10-20%
+# improvement instead of the ~100% you'd expect. You just get threads tied
+# up waiting for execution units very often. Nice when testing, but it ruins
+# the time limits/etc.
+if [ -z "$num_cores" ] && [ -e /proc/cpuinfo ]; then
+ num_cores=$(grep ^'core id\b' /proc/cpuinfo | sort | uniq | wc -l)
+fi
+
+if [ -z "$num_cores" ] || [ "$num_cores" -lt "1" ]; then
+ # OS X, src:
+ # http://stackoverflow.com/questions/1715580/how-to-discover-number-of-cores-on-mac-os-x
+ # http://www.opensource.apple.com/source/xnu/xnu-792.13.8/libkern/libkern/sysctl.h
+ num_cores=$(sysctl -n hw.physicalcpu || echo 0)
+fi
+
+if [ -z "$num_cores" ] || [ "$num_cores" -lt "1" ]; then
+ num_cores=2
+ echo "Couldn't figure out number of cores, will guess $num_cores."
+else
+ echo "Number of cores (w/o Hyper-Threading): $num_cores."
+fi
+
+
+
+if [[ isatty ]]; then
+ function green {
+ printf '\x1B[33m%s\x1B[0m' $1
+ }
+else
+ function green {
+ printf '%s' $1
+ }
+fi
+
+
+
+for p1 in $BINARIES; do
+ for p2 in $BINARIES; do
+ [[ $p1 == $p2 ]] && continue
+ p1pretty=$(echo "$p1" | sed 's/[^a-zA-Z0-9 ]//g')
+ p2pretty=$(echo "$p2" | sed 's/[^a-zA-Z0-9 ]//g')
+ COMPFILE="competitionstubs/game_${p1pretty}_vs_${p2pretty}.sh"
+ cat >"$COMPFILE" << EOF
+#!/usr/bin/env bash
+printf "%s\n%s\n" $p1 $p2 | ./competition.py -q -
+status=$?
+if [[ \$status != 0 ]]; then
+ echo \$(date +"$DATE_FMT") "$p1 - $p2 : ERROR $status (0-0)" >$FIFONAME
+ exit 1
+fi
+lastline=\$(tail -n1 "competitions/game_${p1pretty}_vs_${p2pretty}.txt")
+if [[ "\$lastline" == "P1 won" ]]; then
+ echo \$(date +"$DATE_FMT") $(green "$p1") "- $p2 : WIN - LOSS (3-1)" >$FIFONAME
+elif [[ "\$lastline" == "P2 won" ]]; then
+ echo \$(date +"$DATE_FMT") "$p1 -" $(green $p2) ": LOSS - WIN (1-3)" >$FIFONAME
+elif [[ "\$lastline" == "Tie" ]]; then
+ echo \$(date +"$DATE_FMT") "$p1" $(green -) "$p2 : TIE (1-1)" >$FIFONAME
+fi
+EOF
+ chmod +x $COMPFILE
+ done
+done
+
+find competitionstubs -type f -name 'game_*' | xargs -P$num_cores -n1 -- bash
diff --git a/fullcompstats.py b/fullcompstats.py
new file mode 100755
index 0000000..38e713f
--- /dev/null
+++ b/fullcompstats.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+from os import listdir
+from os.path import isfile,join
+from re import match
+from sys import exit
+files=[fnm for fnm in listdir("competitions") if isfile(join("competitions",fnm)) and fnm[:5]=="game_"]
+scores=dict()
+playernames=[]
+for fname in files:
+ f=open(join("competitions",fname))
+ lines=f.readlines()
+ p1name=lines[0][4:].strip()
+ p2name=lines[1][4:].strip()
+ result=lines[-1].strip()
+ if not match(r"P. won|Tie",result):
+ print("(Unterminated log file "+fname+")")
+ continue #both get 0 score
+ try:
+ playernames.index(p1name)
+ except:
+ playernames.append(p1name)
+ scores[p1name]=0
+ try:
+ playernames.index(p2name)
+ except:
+ playernames.append(p2name)
+ scores[p2name]=0
+ if result=="Tie":
+ scores[p1name]+=1
+ scores[p2name]+=1
+ elif result=="P1 won":
+ scores[p1name]+=3
+ scores[p2name]+=1
+ elif result=="P2 won":
+ scores[p1name]+=1
+ scores[p2name]+=3
+scores=sorted(scores.items(),key=lambda item:-item[1]) #sort reverse on scores
+maxplayerlen=max(6,max([len(sc[0]) for sc in scores]))+1
+print("PLAYER "+" "*(maxplayerlen-7)+"| SCORE")
+print("-"*maxplayerlen+"+------")
+for score in scores:
+ print(score[0]+" "*(maxplayerlen-len(score[0]))+"| "+str(score[1]))
diff --git a/gluon.cc b/gluon.cc
new file mode 100755
index 0000000..86b78bd
--- /dev/null
+++ b/gluon.cc
@@ -0,0 +1,45 @@
+#include <iostream>
+#include <vector>
+#include <climits>
+#include <cstdlib>
+#include <ctime>
+#include "higgs.h"
+
+using namespace std;
+
+Move importMove() {
+ Move move;
+ cin >> move.ndir >> move.p >> move.dir;
+ return move;
+}
+
+void exportMove( Move move ) {
+ cout << move.ndir << " " << move.p << " " << move.dir << endl;
+}
+
+int main() {
+ Board board;
+ Move move;
+ string input;
+ srand( time( NULL ) );
+
+ cin >> input;
+ if( input == "go" ) {
+ //board.print();
+ move.ndir = -1;
+ move.p = S-1;
+ move.dir = 4;
+ board.doMove( move );
+ exportMove( move );
+ }
+ while( 1 ) {
+ //board.print();
+ board.doMove( importMove() );
+
+ //board.print();
+ vector<Move> move_list = board.generateMoves();
+ move = move_list[ rand() % move_list.size() ];
+ board.doMove( move );
+ exportMove( move );
+ }
+} \ No newline at end of file
diff --git a/higgs.cc b/higgs.cc
new file mode 100755
index 0000000..237346a
--- /dev/null
+++ b/higgs.cc
@@ -0,0 +1,117 @@
+#include <iostream>
+#include <vector>
+#include <climits>
+#include "higgs.h"
+
+using namespace std;
+
+int getIndex(int x, int y) {
+ return x + y*S;
+}
+
+int getX(int i) {
+ return i % S;
+}
+
+int getY(int i) {
+ return i / S;
+}
+
+void Board::doMove( Move move ) {
+ if( move.ndir != -1 ) {
+ int new_neutron = pushPiece( neutron, move.ndir );
+ square[new_neutron] = -1;
+ square[neutron] = 0;
+ neutron = new_neutron;
+ }
+ int new_proton = pushPiece( move.p, move.dir );
+ square[new_proton] = square[move.p];
+ square[move.p] = 0;
+ proton[square[new_proton] - 1] = new_proton;
+ move_count++;
+}
+
+void Board::undoMove( Move move, int n ) {
+ int new_proton = pushPiece( move.p, move.dir, true );
+ square[move.p] = square[new_proton];
+ square[new_proton] = 0;
+ proton[square[move.p]-1] = move.p;
+ if( move.ndir != -1 ) {
+ square[neutron] = 0;
+ square[n] = -1;
+ neutron = n;
+ }
+ move_count--;
+}
+
+vector<Move> Board::generateMoves() {
+ vector<Move> move_list;
+ Move move;
+ int n;
+ for( move.ndir = 0; move.ndir < 8; move.ndir++ ) {
+ n = pushPiece( neutron, move.ndir );
+ if( n != neutron ) {
+ square[n] = -1;
+ square[neutron] = 0;
+ for( int i = 0; i < S; i++ ) {
+ move.p = proton[i+S*(move_count % 2)];
+ for( move.dir = 0; move.dir < 8; move.dir++ ) {
+ if( pushPiece( move.p, move.dir ) != move.p )
+ move_list.push_back( move );
+ }
+ }
+ square[neutron] = -1;
+ square[n] = 0;
+ }
+ }
+ return move_list;
+}
+
+int Board::neutronWin() {
+ return ( getY(neutron) == 0 ) - ( getY(neutron) == S-1 );
+}
+
+int Board::pushPiece( int p, int d, bool e ) {
+ int dx = ( d > 0 && d < 4 ) - ( d > 4 && d < 8 );
+ int dy = ( d > 2 && d < 6 ) - ( d > 6 || d < 2 );
+ int x = getX( p );
+ int y = getY( p );
+ do {
+ x += dx;
+ y += dy;
+ } while( x >= 0 && x < S && y >= 0 && y < S && square[getIndex(x,y)] == 0 );
+ if( !e ) {
+ x -= dx;
+ y -= dy;
+ }
+ return getIndex(x,y);
+}
+
+void Board::print() {
+ for( int i = 0; i < SS; i++ ) {
+ if( i % S == 0 )
+ cerr << "|";
+ if( square[i] == 0 )
+ cerr << " .";
+ else if( square[i] == -1 )
+ cerr << " N";
+ else if( square[i] <= S )
+ cerr << " +";
+ else
+ cerr << " -";
+ if( i % S == S-1 )
+ cerr << " |\n";
+ }
+}
+
+Board::Board() {
+ for( int x = 0; x < S; x++ ) {
+ square[proton[x] = getIndex(x,0)] = x + 1;
+ square[proton[S+x] = getIndex(x,S-1)] = S + 1 + x;
+ }
+ for( int i = S; i < (S-1)*S; i++ )
+ square[i] = 0;
+ neutron = getIndex( S / 2, S / 2);
+ square[neutron] = -1;
+ move_count = 0;
+}
diff --git a/higgs.h b/higgs.h
new file mode 100755
index 0000000..c201955
--- /dev/null
+++ b/higgs.h
@@ -0,0 +1,31 @@
+#include <vector>
+#include <climits>
+
+#define S 5
+#define SS 25
+
+int getIndex(int x, int y);
+int getX(int i);
+int getY(int i);
+
+struct Move {
+ int ndir;
+ int p;
+ int dir;
+};
+
+class Board {
+public:
+ int square[SS];
+ int proton[2*S];
+ int neutron;
+ int move_count;
+public:
+ void doMove( Move move );
+ void undoMove( Move move, int n );
+ std::vector<Move> generateMoves();
+ int neutronWin();
+ int pushPiece( int p, int d, bool e = false );
+ void print();
+ Board();
+};
diff --git a/neutrino.cc b/neutrino.cc
new file mode 100755
index 0000000..2d8edac
--- /dev/null
+++ b/neutrino.cc
@@ -0,0 +1,86 @@
+#include <iostream>
+#include <vector>
+#include <climits>
+#include "higgs.h"
+
+using namespace std;
+
+
+int evaluate( const Board& board ) {
+ int score = 0;
+ for( int i = 0; i < S; ++i ) {
+ score += getY( board.proton[i] )*getY( board.proton[i] );
+ score -= (S - 1 - getY( board.proton[i+S] ))*(S-1-getY( board.proton[i+S] ));
+ }
+ score += S*(getY( board.neutron ) - S/2);
+ return (board.move_count % 2 == 0) ? score : -score;
+}
+
+int minimax( Board& board, Move& best_move, int depth, int alpha = INT_MIN/2, int beta = INT_MAX/2 ) {
+ if( board.neutronWin() != 0 ) {
+ if( board.neutronWin()*(2*( board.move_count % 2 ) - 1 ) > 0 )
+ return INT_MIN/2;
+ else
+ return INT_MAX/2;
+ } else if( depth == 0 )
+ return evaluate( board );
+ else {
+ int global_score = INT_MIN;
+ int local_score;
+ int prev_neutron;
+ Move best_move_recursive;
+ vector<Move> move_list = board.generateMoves();
+ if( move_list.size() == 0 )
+ return INT_MAX/2;
+ for( Move& move : move_list ) {
+ prev_neutron = board.neutron;
+ board.doMove( move );
+ local_score = -minimax( board, best_move_recursive, depth-1, -beta, -alpha );
+ board.undoMove( move, prev_neutron );
+ if( local_score > global_score ) {
+ global_score = local_score;
+ best_move = move;
+ }
+ if( local_score > alpha )
+ alpha = local_score;
+ if( beta <= alpha )
+ break;
+ }
+ return global_score;
+ }
+}
+
+Move importMove() {
+ Move move;
+ cin >> move.ndir >> move.p >> move.dir;
+ return move;
+}
+
+void exportMove( Move move ) {
+ cout << move.ndir << " " << move.p << " " << move.dir << endl;
+}
+
+int main() {
+ Board board;
+ Move move;
+ string input;
+
+ cin >> input;
+ if( input == "go" ) {
+ //board.print();
+ move.ndir = -1;
+ move.p = S-1;
+ move.dir = 4;
+ board.doMove( move );
+ exportMove( move );
+ }
+ while( 1 ) {
+ //board.print();
+ board.doMove( importMove() );
+
+ //board.print();
+ minimax( board, move, 5 );
+ board.doMove( move );
+ exportMove( move );
+ }
+} \ No newline at end of file
diff --git a/randino.cpp b/randino.cpp
new file mode 100644
index 0000000..e9b5abb
--- /dev/null
+++ b/randino.cpp
@@ -0,0 +1,169 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <sys/time.h>
+
+#define S (5)
+
+using namespace std;
+
+int index_deltas[8][2]={{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+
+class Move{
+public:
+ 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(0){
+ 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;
+ 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;
+ }
+};
+
+
+Move calculate_move(Board &bd){
+ vector<Move> poss;
+ Move m;
+ int nd,fr,d;
+ int newneu,newidx;
+ for(nd=0;nd<8;nd++){
+ newneu=bd.moved(bd.neuidx,nd);
+ if(newneu==bd.neuidx)continue;
+ for(fr=0;fr<S*S;fr++){
+ if(bd.grid[fr]!=1)continue;
+ for(d=0;d<8;d++){
+ newidx=bd.moved(fr,d);
+ if(newidx==fr)continue;
+ m.neudir=nd;
+ m.from=fr;
+ m.dir=d;
+ poss.push_back(m);
+ }
+ }
+ }
+ return poss[rand()%poss.size()];
+}
+
+
+bool should_flip_board=false;
+
+inline int flip_index(int idx){
+ return S*(S-1-idx/S)+S-1-idx%S;
+}
+inline int flip_dir(int dir){
+ return (dir+4)%8;
+}
+
+Move protocol_get_move(void){
+ Move m;
+ cin>>m.neudir>>m.from>>m.dir;
+ if(should_flip_board){
+ m.from=flip_index(m.from);
+ m.dir=flip_dir(m.dir);
+ }
+ return m;
+}
+
+void protocol_put_move(Move m){
+ if(should_flip_board){
+ cout<<m.neudir<<' '<<flip_index(m.from)<<' '<<flip_dir(m.dir)<<endl;
+ } else {
+ cout<<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);
+ getline(cin,line);
+ if(line=="go"){
+ should_flip_board=false;
+ mv.neudir=-1; mv.from=0; mv.dir=4;
+ protocol_put_move(mv);
+ } else {
+ if(line!="nogo")cerr<<"no0b "<<line<<" not in (go,nogo)"<<endl;
+ should_flip_board=true;
+ mv=protocol_get_move();
+ bd.move(mv.from,mv.dir);
+ 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(mv);
+ }
+ while(true){
+ mv=protocol_get_move();
+ bd.move(mv.from,mv.dir);
+ 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(mv);
+ }
+ return 0;
+}
diff --git a/viewcompetition.cpp b/viewcompetition.cpp
new file mode 100644
index 0000000..40ba715
--- /dev/null
+++ b/viewcompetition.cpp
@@ -0,0 +1,111 @@
+#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)
+
+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;
+ }
+}
+
+int main(int argc,char **argv){
+ if(argc==1){
+ cout<<"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;
+ return 1;
+ }
+ ifstream in(argv[1]);
+ if(!in.good()){
+ cout<<"Cannot open file '"<<argv[1]<<"'."<<endl;
+ return 1;
+ }
+ string p1name,p2name;
+ int player,x,y;
+ char c;
+ 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;
+ if(!in.good()){
+ cout<<"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.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');
+ } else if(c==' '){
+ cout<<"\x1B[17;1HThe victor is player "<<player<<"!"<<endl;
+ in.close();
+ dosleep(1500);
+ return 0;
+ } else {
+ cout<<"\x1B[17;1HError in log file format."<<endl;
+ return 1;
+ }
+ } else if(c=='T'){
+ cout<<"\x1B[17;1HThe game resulted in a tie."<<endl;
+ in.close();
+ dosleep(1500);
+ return 0;
+ }
+ }
+ cout<<"\x1B[17;1HLog file ended prematurely."<<endl;
+ return 1;
+}