diff --git a/agents/bishop.py b/agents/bishop/bishop.py
similarity index 93%
rename from agents/bishop.py
rename to agents/bishop/bishop.py
index ea96c2ce5e745e35030ea68f15e6d7a52d8d1516..88f3c49640d0219f3bf9a19c1a5cdfc01dace424 100755
--- a/agents/bishop.py
+++ b/agents/bishop/bishop.py
@@ -2,6 +2,8 @@
 
 from qchess import *
 
+graphics_enabled = True
+
 """
 	This is a wrapper to AgentBishop, which can now be found directly in qchess as one of the internal agents
 	As well as wrapping, it will also show AgentBishop's thought processes in graphics, which is kind of cool
@@ -106,10 +108,17 @@ class AgentBishop_Graphics(GraphicsThread):
 
 if __name__ == "__main__":
 	
+	if sys.argv[1] == "--no-graphics":
+		graphics_enabled = False
+
 	colour = sys.stdin.readline().strip("\r\n")
 	agent = AgentBishop(sys.argv[0], colour)
-	graphics = AgentBishop_Graphics(agent.board, "Agent Bishop ("+agent.colour+") DEBUG")
-	graphics.start()
+
+	if graphics_enabled:
+		graphics = AgentBishop_Graphics(agent.board, "Agent Bishop ("+agent.colour+") DEBUG")
+		graphics.start()
 	run_agent(agent)
-	graphics.stop()
-	graphics.join()
+	
+	if graphics_enabled:
+		graphics.stop()
+		graphics.join()
diff --git a/agents/bishop/data b/agents/bishop/data
new file mode 120000
index 0000000000000000000000000000000000000000..75a080af8fdf43fc66d55d1102ce8d663b397ea0
--- /dev/null
+++ b/agents/bishop/data
@@ -0,0 +1 @@
+../../qchess/data/
\ No newline at end of file
diff --git a/agents/bishop/info b/agents/bishop/info
new file mode 100644
index 0000000000000000000000000000000000000000..9ac962d5dc0979948c77cae6882104420457608a
--- /dev/null
+++ b/agents/bishop/info
@@ -0,0 +1,5 @@
+bishop.py --no-graphics
+Sam Moore
+python
+A wrapper to qchess.AgentBishop (allows it to run as an external agent). See qchess/src/agent_bishop.py.
+This agent uses a min/max probability based scoring algorithm with a recursion depth of a couple of turns.
diff --git a/agents/bishop/qchess.py b/agents/bishop/qchess.py
new file mode 120000
index 0000000000000000000000000000000000000000..35c6d6e8a5edf5bdf5173254d57a1c5500b7b59b
--- /dev/null
+++ b/agents/bishop/qchess.py
@@ -0,0 +1 @@
+../../qchess/qchess.py
\ No newline at end of file
diff --git a/agents/data b/agents/data
deleted file mode 120000
index 09607df28f17d87cfce921cc2fae54ababf05297..0000000000000000000000000000000000000000
--- a/agents/data
+++ /dev/null
@@ -1 +0,0 @@
-../qchess/data/
\ No newline at end of file
diff --git a/agents/qchess.py b/agents/qchess.py
deleted file mode 120000
index 385f472e964b1596589e1e1b56e0fc755c399e21..0000000000000000000000000000000000000000
--- a/agents/qchess.py
+++ /dev/null
@@ -1 +0,0 @@
-../qchess/qchess.py
\ No newline at end of file
diff --git a/agents/sample/data b/agents/sample/data
new file mode 120000
index 0000000000000000000000000000000000000000..75a080af8fdf43fc66d55d1102ce8d663b397ea0
--- /dev/null
+++ b/agents/sample/data
@@ -0,0 +1 @@
+../../qchess/data/
\ No newline at end of file
diff --git a/agents/sample/info b/agents/sample/info
new file mode 100644
index 0000000000000000000000000000000000000000..77784fcf300f2c66a1049b317bebca9615841aaf
--- /dev/null
+++ b/agents/sample/info
@@ -0,0 +1,4 @@
+sample.py --no-debug
+Sam Moore
+python
+Sample agent; makes random moves. Basically a copy of the internal agent that makes random moves. With comments.
diff --git a/agents/sample/qchess.py b/agents/sample/qchess.py
new file mode 120000
index 0000000000000000000000000000000000000000..35c6d6e8a5edf5bdf5173254d57a1c5500b7b59b
--- /dev/null
+++ b/agents/sample/qchess.py
@@ -0,0 +1 @@
+../../qchess/qchess.py
\ No newline at end of file
diff --git a/agents/sample.py b/agents/sample/sample.py
similarity index 92%
rename from agents/sample.py
rename to agents/sample/sample.py
index 34ce85895fc9bb3b3db51cde9daec3e52c199bbb..55572a2184e8f6c2e7f5aae0be412e764c9aed2f 100755
--- a/agents/sample.py
+++ b/agents/sample/sample.py
@@ -6,6 +6,8 @@
 from qchess import * # This is normally considered bad practice in python, but good practice in UCC::Progcomp
 import random # For the example which makes random moves
 
+debug = False
+
 # The first thing to do is pick a cool name...
 class AgentSample(InternalAgent): 
 	def __init__(self, name, colour):
@@ -19,15 +21,17 @@ class AgentSample(InternalAgent):
 		#TODO: Any extra initialisation
 		
 		# You should print debug messages like this:
-		sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Initialised agent\n")
-		# I would appreciate it if you removed them before submitting an entry though.
+		if debug:
+			sys.stderr.write(sys.argv[0] + " : Initialised agent\n")
+		
 
 	# Must return [x,y] of selected piece
 	# Your agent will call select(), followed by get_move() and so on
 	# TODO: Implement
 	def select(self):
 		# debug message
-		sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Selecting piece...\n")
+		if debug:
+			sys.stderr.write(sys.argv[0] + " : Selecting piece...\n")
 		
 
 		# Here is a random choice algorithm to help you start
@@ -73,7 +77,8 @@ class AgentSample(InternalAgent):
 	# TODO: Implement this
 	def get_move(self):	
 		# debug message
-		sys.stderr.write(sys.argv[0] + " : " + str(self) + " : Moving piece ("+str(self.choice)+")\n")
+		if debug:
+			sys.stderr.write(sys.argv[0] + " : Moving piece ("+str(self.choice)+")\n")
 		# As an example we will just pick a random move for the piece previously chosen in select()
 
 		# Note that whichever piece was previously selected will have collapsed into a classical state
@@ -92,6 +97,14 @@ class AgentSample(InternalAgent):
 # Look at qchess/src/agent_bishop.py for a more effective (but less explained) agent
 
 if __name__ == "__main__":
+
+	# Parse arguments here
+	for i in range(len(sys.argv)):
+		if sys.argv[i] == "--debug":
+			debug = True
+		elif sys.argv[i] == "--no-debug":
+			debug = False
+
 	colour = sys.stdin.readline().strip("\r\n")
 	agent = AgentSample(sys.argv[0], colour) # Change the class name here
 	run_agent(agent) # This is provided by qchess. It calls the functions of your agent as required during the game.
diff --git a/clean.sh b/clean.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f3301c4887ec5837886ce3744a98e79163a9ba09
--- /dev/null
+++ b/clean.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# This script can be used to remove all results
+# For testing purposes
+
+. vars
+
+rm -f web/current.log
+
+rm -rf $results_dir
+
+for s in *.result; do
+	rm -f $s
+done
+
+cd $agent_dir
+for s in $(find . | grep score.dat); do
+	rm -f $s
+done
+		
+
+
diff --git a/qchess/build/exe.linux-x86_64-2.7.zip b/qchess/build/exe.linux-x86_64-2.7.zip
index 5b5221480b15bbb71407f309567a312495212f1b..5b7b25c2d1d9cbe441122fb15abf506e432ab7df 100644
Binary files a/qchess/build/exe.linux-x86_64-2.7.zip and b/qchess/build/exe.linux-x86_64-2.7.zip differ
diff --git a/qchess/build/exe.win32-2.7.zip b/qchess/build/exe.win32-2.7.zip
index 9d1041ba3ab84dcf734950128645afac6cff72f3..925060464a16664742e08d19fbf802a95e28cef2 100644
Binary files a/qchess/build/exe.win32-2.7.zip and b/qchess/build/exe.win32-2.7.zip differ
diff --git a/qchess/qchess.py b/qchess/qchess.py
index ced16ce2b582299f87e1cf8268b240bf1910c474..ced6001689cd5f7c24f84928540bcba60c5434d9 100755
--- a/qchess/qchess.py
+++ b/qchess/qchess.py
@@ -1400,6 +1400,43 @@ class HttpReplay():
 			
 	def close(self):
 		self.getter.stop()
+
+class FileReplay():
+	def __init__(self, filename):
+		self.f = open(filename, "r", 0)
+		self.filename = filename
+		self.mod = os.path.getmtime(filename)
+		self.count = 0
+	
+	def readline(self):
+		line = self.f.readline()
+		
+		while line == "":
+			mod2 = os.path.getmtime(self.filename)
+			if mod2 > self.mod:
+				#sys.stderr.write("File changed!\n")
+				self.mod = mod2
+				self.f.close()
+				self.f = open(self.filename, "r", 0)
+				
+				new_line = self.f.readline()
+				
+				if " ".join(new_line.split(" ")[0:3]) != "# Short log":
+					for i in range(self.count):
+						new_line = self.f.readline()
+						#sys.stderr.write("Read back " + str(i) + ": " + str(new_line) + "\n")
+					new_line = self.f.readline()
+				else:
+					self.count = 0
+				
+				line = new_line
+
+		self.count += 1
+		return line
+
+	def close(self):
+		self.f.close()
+		
 						
 def log(s):
 	for l in log_files:
@@ -1593,6 +1630,8 @@ class ReplayThread(GameThread):
 			if self.stopped():
 				break
 			
+			if len(line) <= 0:
+				continue
 					
 
 			if line[0] == '#':
@@ -1640,7 +1679,10 @@ class ReplayThread(GameThread):
 				self.board.update_select(x, y, int(tokens[2]), tokens[len(tokens)-1])
 				if isinstance(graphics, GraphicsThread):
 					with graphics.lock:
-						graphics.state["moves"] = self.board.possible_moves(target)
+						if target.current_type != "unknown":
+							graphics.state["moves"] = self.board.possible_moves(target)
+						else:
+							graphics.state["moves"] = None
 					time.sleep(turn_delay)
 			else:
 				self.board.update_move(x, y, x2, y2)
@@ -1798,7 +1840,7 @@ class GraphicsThread(StoppableThread):
 		#print "Test font"
 		pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 32).render("Hello", True,(0,0,0))
 
-		#load_images()
+		#create_images(grid_sz)
 		create_images(grid_sz)
 
 		"""
@@ -2319,7 +2361,9 @@ def main(argv):
 		elif arg[1] == '-' and arg[2:] == "reveal":
 			always_reveal_states = True
 		elif (arg[1] == '-' and arg[2:] == "graphics"):
-			graphics_enabled = not graphics_enabled
+			graphics_enabled = True
+		elif (arg[1] == '-' and arg[2:] == "no-graphics"):
+			graphics_enabled = False
 		elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"):
 			# Load game from file
 			if len(arg[2:].split("=")) == 1:
@@ -2329,7 +2373,7 @@ def main(argv):
 				if f[0:7] == "http://":
 					src_file = HttpReplay(f)
 				else:
-					src_file = open(f.split(":")[0], "r", 0)
+					src_file = FileReplay(f.split(":")[0])
 
 					if len(f.split(":")) == 2:
 						max_moves = int(f.split(":")[1])
@@ -2480,6 +2524,8 @@ def main(argv):
 	if src_file != None and src_file != sys.stdin:
 		src_file.close()
 
+	sys.stdout.write(game.final_result + "\n")
+
 	return error
 
 # This is how python does a main() function...
@@ -2500,4 +2546,4 @@ if __name__ == "__main__":
 		sys.exit(102)
 
 # --- main.py --- #
-# EOF - created from make on Thu Jan 31 13:37:15 WST 2013
+# EOF - created from make on Tue Feb 12 17:06:37 WST 2013
diff --git a/qchess/src/game.py b/qchess/src/game.py
index 16f26d72a8699d78f170aa87ac0f5191829f80d4..4eebc76af62acbee1f8113fa9f476383300ebc46 100644
--- a/qchess/src/game.py
+++ b/qchess/src/game.py
@@ -180,6 +180,8 @@ class ReplayThread(GameThread):
 			if self.stopped():
 				break
 			
+			if len(line) <= 0:
+				continue
 					
 
 			if line[0] == '#':
@@ -227,7 +229,10 @@ class ReplayThread(GameThread):
 				self.board.update_select(x, y, int(tokens[2]), tokens[len(tokens)-1])
 				if isinstance(graphics, GraphicsThread):
 					with graphics.lock:
-						graphics.state["moves"] = self.board.possible_moves(target)
+						if target.current_type != "unknown":
+							graphics.state["moves"] = self.board.possible_moves(target)
+						else:
+							graphics.state["moves"] = None
 					time.sleep(turn_delay)
 			else:
 				self.board.update_move(x, y, x2, y2)
diff --git a/qchess/src/log.py b/qchess/src/log.py
index 1ef8145bb43ba337e90263edb93ac37ecc4f8f06..af0c2a79106adf67a71d4a18e0f8a5ab8d56a63e 100644
--- a/qchess/src/log.py
+++ b/qchess/src/log.py
@@ -131,6 +131,43 @@ class HttpReplay():
 			
 	def close(self):
 		self.getter.stop()
+
+class FileReplay():
+	def __init__(self, filename):
+		self.f = open(filename, "r", 0)
+		self.filename = filename
+		self.mod = os.path.getmtime(filename)
+		self.count = 0
+	
+	def readline(self):
+		line = self.f.readline()
+		
+		while line == "":
+			mod2 = os.path.getmtime(self.filename)
+			if mod2 > self.mod:
+				#sys.stderr.write("File changed!\n")
+				self.mod = mod2
+				self.f.close()
+				self.f = open(self.filename, "r", 0)
+				
+				new_line = self.f.readline()
+				
+				if " ".join(new_line.split(" ")[0:3]) != "# Short log":
+					for i in range(self.count):
+						new_line = self.f.readline()
+						#sys.stderr.write("Read back " + str(i) + ": " + str(new_line) + "\n")
+					new_line = self.f.readline()
+				else:
+					self.count = 0
+				
+				line = new_line
+
+		self.count += 1
+		return line
+
+	def close(self):
+		self.f.close()
+		
 						
 def log(s):
 	for l in log_files:
diff --git a/qchess/src/main.py b/qchess/src/main.py
index cbc07f79db30d13c04de20ac2324e392c100f033..748a4592688d48ee0a85165a2e784242282ae39f 100644
--- a/qchess/src/main.py
+++ b/qchess/src/main.py
@@ -109,7 +109,9 @@ def main(argv):
 		elif arg[1] == '-' and arg[2:] == "reveal":
 			always_reveal_states = True
 		elif (arg[1] == '-' and arg[2:] == "graphics"):
-			graphics_enabled = not graphics_enabled
+			graphics_enabled = True
+		elif (arg[1] == '-' and arg[2:] == "no-graphics"):
+			graphics_enabled = False
 		elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"):
 			# Load game from file
 			if len(arg[2:].split("=")) == 1:
@@ -119,7 +121,7 @@ def main(argv):
 				if f[0:7] == "http://":
 					src_file = HttpReplay(f)
 				else:
-					src_file = open(f.split(":")[0], "r", 0)
+					src_file = FileReplay(f.split(":")[0])
 
 					if len(f.split(":")) == 2:
 						max_moves = int(f.split(":")[1])
@@ -270,6 +272,8 @@ def main(argv):
 	if src_file != None and src_file != sys.stdin:
 		src_file.close()
 
+	sys.stdout.write(game.final_result + "\n")
+
 	return error
 
 # This is how python does a main() function...
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2067aca2bdbc156406302418046238e25826209b
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+
+# This script runs a single round of the competition
+
+. vars
+
+agents=""
+
+
+
+# Copy messages to a log file
+if [ "$BASH_ARGV" != "_worker_" ]; then
+	# Get the round number
+	mkdir -p $results_dir
+	cd $results_dir
+	round=$(ls | grep "round" | sed -e "s:round::g" | sort -n | head --lines=1)
+	if [ "$round" == "" ]; then round=0; fi
+	round=$(( $round + 1 ))
+	mkdir -p round$round
+	cd $root_dir
+	exec $0 "$@" _worker_ 2>&1 | tee $results_dir/round$round/run.log
+	exit $?
+else
+	cd $results_dir
+	round=$(ls | grep "round" | sed -e "s:round::g" | sort -n | head --lines=1)
+fi
+
+echo "Start at $(date)"
+
+# Setup the swarm
+if [ "$swarm_hosts" != "" ]; then
+	swarm --daemon
+	for h in $swarm_hosts; do
+		swarm -c "#ABSORB $h#"
+	done
+	swarm -c "#.*# cd $root_dir; mkdir -p $results_dir/round$round"
+fi
+
+
+
+cd $root_dir/$agent_dir
+count=0
+for f in $(ls); do
+	if [ -d $f ]; then
+		info_file=$(ls $f | grep -w "info")
+		if [ "$info_file" == "" ]; then
+			echo "Skipping agent $f (missing info file)"
+		else
+			count=$(( $count + 1 ))
+			agents="$agents $f"
+		fi
+	fi
+done
+echo "Found $count agents in $agent_dir/"
+
+
+
+# Add all the games to swarm
+cd $root_dir
+
+if [ "$agents" == "" ]; then
+	echo "No agents to run round $round with" 1>&2
+	rm -rf "$results_dir/round$round"
+	exit 1
+fi
+
+echo "Start round $round"
+
+game=0
+for a in $agents; do
+	runa="$agent_dir/$a/$(head --lines=1 $agent_dir/$a/info)"
+	for b in $agents; do
+		if [ "$a" == "$b" ]; then continue; fi
+		for ((i=1;i<=$games_per_pair;++i)); do
+			runb="$agent_dir/$b/$(head --lines=1 $agent_dir/$b/info)"
+			f="${a}_${b}_${i}"
+
+			game=$(( $game + 1))	
+			l="$results_dir/round$round/$f.log"
+			err="$results_dir/round$round/$f.err"
+
+			echo "Game #$game: $a .vs. $b ($i of $games_per_pair)"
+			if [ "$swarm_hosts" != "" ]; then
+				swarm -c "$qchess --no-graphics \"$runa\" \"$runb\" --log=$l --log=@web/current.log 2>$err" -o $f.result
+			else
+				$qchess --no-graphics "$runa" "$runb" --log=$l --log=@web/current.log 1> $f.result 2> $err
+				if [ "$(wc -l $err | awk '{print $1}')" == "0" ]; then rm $err; fi
+			fi
+		done
+	done
+done
+
+
+
+if [ "$swarm_hosts" != "" ]; then
+	swarm -c "#BARRIER BLOCK#" # Wait for all games to finish
+
+	#Copy over log files (start before updating scores as the scp may take some time)
+	for h in "local $swarm_hosts"; do
+		cmd="
+		if [ \"\$(hostname)\" != \"$webserver\" ]; then
+			cd $root_dir/$results_dir/round$round;
+			for i in *.log *.err; do
+				if [ \"\$(wc -l \$i | awk '{print \$1}')\" != 0 ]; then
+					scp \$i $webserver/$root_dir/$results_dir/round$round/\$i
+				fi
+			done
+		fi"
+		swarm -c "#$h:.* \$# $cmd" # Execute once on each host
+	done
+fi
+
+echo "Completed $games games with $count agents"
+
+# A bash function. Yes, they do exist.
+function update_score
+{
+	if [ -e $agent_dir/$1/score.dat ]; then
+		score=$(tail --lines=1 $agent_dir/$1/score.dat | awk '{print $1}')
+	else
+		score=0
+	fi
+	
+
+	score=$(( $score + $3 ))
+	echo "$3 $score $2 $4 $5" >> $agent_dir/$1/score.dat
+	return $score
+}
+
+# Go through results
+for f in *.result; do
+	log=round$round/$(echo $f.log | sed -e "s:.result::g")
+	white=$(echo $f | tr '_' '\t' | awk '{print $1}')
+	black=$(echo $f | tr '_' '\t' | awk '{print $2}')
+	if [ "$(cat $f)" == "white" ]; then
+		update_score $white $black $win_score WIN $log
+		update_score $black $white $loss_score LOSS $log
+	elif [ "$(cat $f)" == "black" ]; then
+		update_score $white $black $loss_score LOSS $log
+		update_score $black $white $win_score WIN $log
+	elif [ "$(cat $f)" == "DRAW" ]; then
+		update_score $white $black $draw_score DRAW $log
+		update_score $black $white $draw_score DRAW $log
+	else
+		echo "Unrecognised result \"$(cat $f)\" in $f" 1>&2
+	fi
+
+	rm $f
+done
+
+echo "Updated scores"
+
+#Close the swarm
+if [ "$swarm_hosts" != "" ]; then
+	swarm -c "#BARRIER BLOCK#"
+	swarm -c "#.*# exit"
+fi
+
+echo "Finished at $(date)"
+
diff --git a/vars b/vars
new file mode 100644
index 0000000000000000000000000000000000000000..981395dcc9fd0c1713a90b9354d40a53f8e26509
--- /dev/null
+++ b/vars
@@ -0,0 +1,12 @@
+root_dir=$(pwd)
+results_dir="web/results"
+agent_dir="agents"
+qchess="qchess/qchess.py"
+webserver=$(hostname) # Where log files need to be copied
+swarm_hosts=""
+
+games_per_pair=1
+win_score=3
+loss_score=1
+draw_score=1
+illegal_score=0