player.py 10.1 KB
Newer Older
Sam Moore's avatar
Sam Moore committed
1
import subprocess
Sam Moore's avatar
Sam Moore committed
2 3
import select
import platform
4
import re
5

Sam Moore's avatar
Sam Moore committed
6 7
agent_timeout = -1.0 # Timeout in seconds for AI players to make moves
			# WARNING: Won't work for windows based operating systems
Sam Moore's avatar
Sam Moore committed
8

Sam Moore's avatar
Sam Moore committed
9 10
if platform.system() == "Windows":
	agent_timeout = -1 # Hence this
Sam Moore's avatar
Sam Moore committed
11 12 13 14 15 16 17

# A player who can't play
class Player():
	def __init__(self, name, colour):
		self.name = name
		self.colour = colour

Sam Moore's avatar
Sam Moore committed
18
	def update(self, result):
Sam Moore's avatar
Sam Moore committed
19
		return result
Sam Moore's avatar
Sam Moore committed
20

Sam Moore's avatar
Sam Moore committed
21 22
	def reset_board(self, s):
		pass
Sam Moore's avatar
Sam Moore committed
23 24 25 26 27 28
	
	def __str__(self):
		return self.name + "<"+str(self.colour)+">"

	def base_player(self):
		return self
Sam Moore's avatar
Sam Moore committed
29 30 31 32 33 34 35 36 37 38 39 40
	


def open_fifo(name, mode, timeout=None):
	if timeout == None:
		return open(name, mode)
	
	
	class Worker(threading.Thread):
		def __init__(self):
			threading.Thread.__init__(self)
			self.result = None
Sam Moore's avatar
Sam Moore committed
41
			self.exception = None
Sam Moore's avatar
Sam Moore committed
42 43 44

			
		def run(self):		
Sam Moore's avatar
Sam Moore committed
45 46 47 48 49
			try:
				self.result = open(name, mode)
			except Exception, e:
				self.exception = e
				self.result = None
Sam Moore's avatar
Sam Moore committed
50 51 52 53 54 55 56 57 58
		

	w = Worker()
	w.start()
	
	start = time.time()
	while time.time() - start < timeout:
		if w.is_alive() == False:
			w.join()
Sam Moore's avatar
Sam Moore committed
59 60
			if w.exception != None:
				raise w.exception
Sam Moore's avatar
Sam Moore committed
61 62 63 64 65 66
			return w.result
		time.sleep(0.1)
	
	
	if w.is_alive():
		#sys.stderr.write("FIFO_TIMEOUT!\n")
Sam Moore's avatar
Sam Moore committed
67 68 69 70 71 72 73 74
		# Recursive to deal with possible race condition
		try:
			if mode == "r":
				f = open_fifo(name, "w", 1)
			else:
				f = open_fifo(name, "r", 1)
		except:
			pass
Sam Moore's avatar
Sam Moore committed
75 76 77 78 79 80 81 82 83 84 85
			
		#sys.stderr.write("Opened other end!\n")
		while w.is_alive():
			time.sleep(0.1)
			
		w.join()
		f.close()
		w.result.close()
		raise Exception("FIFO_TIMEOUT")
	else:
		w.join()
Sam Moore's avatar
Sam Moore committed
86 87
		if w.exception != None:
			raise w.exception
Sam Moore's avatar
Sam Moore committed
88 89
		return w.result
	
Sam Moore's avatar
Sam Moore committed
90

Sam Moore's avatar
Sam Moore committed
91 92
# Player that runs through a fifo
class FifoPlayer(Player):
Sam Moore's avatar
Sam Moore committed
93 94 95
	
	timeout = 300
	
Sam Moore's avatar
Sam Moore committed
96 97 98 99
	def __init__(self, name, colour):
		Player.__init__(self, name, colour)
		os.mkfifo(self.name+".in")
		os.mkfifo(self.name+".out")
100 101 102 103 104 105 106 107 108

		try:
			self.fifo_out = open_fifo(self.name+".out","w", FifoPlayer.timeout)
		except:
			raise Exception("FIFO_TIMEOUT")
		else:
			self.fifo_out.write("START "+colour+"\n")
			self.fifo_out.close()

Sam Moore's avatar
Sam Moore committed
109 110 111 112 113 114
		
		
		
		
	def update(self, result):
		sys.stderr.write("update fifo called\n")
Sam Moore's avatar
Sam Moore committed
115 116 117 118 119 120 121 122
		try:
			self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
		except:
			raise Exception("FIFO_TIMEOUT")
		else:
			self.fifo_out.write(result +"\n")
			self.fifo_out.close()
			return result
Sam Moore's avatar
Sam Moore committed
123 124 125
		
	def select(self):
		sys.stderr.write("select fifo called\n")
Sam Moore's avatar
Sam Moore committed
126 127 128 129 130 131 132 133 134 135 136 137 138
		try:
			self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
		except:
			#sys.stderr.write("TIMEOUT\n")
			raise Exception("FIFO_TIMEOUT")
		else:
			
			self.fifo_out.write("SELECT?\n")
			self.fifo_out.close()
			self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
			s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
			self.fifo_in.close()
			return s
Sam Moore's avatar
Sam Moore committed
139 140 141
	
	def get_move(self):
		sys.stderr.write("get_move fifo called\n")
Sam Moore's avatar
Sam Moore committed
142 143 144 145 146 147 148 149 150 151 152
		try:
			self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
		except:
			raise Exception("FIFO_TIMEOUT")
		else:
			self.fifo_out.write("MOVE?\n")
			self.fifo_out.close()
			self.fifo_in = open_fifo(self.name+".in", "r", FifoPlayer.timeout)
			s = map(int, self.fifo_in.readline().strip(" \r\n").split(" "))
			self.fifo_in.close()
			return s
Sam Moore's avatar
Sam Moore committed
153 154
	
	def quit(self, result):
Sam Moore's avatar
Sam Moore committed
155 156 157 158 159 160 161 162 163 164 165 166
		try:
			self.fifo_out = open_fifo(self.name+".out", "w", FifoPlayer.timeout)
		except:
			os.remove(self.name+".in")
			os.remove(self.name+".out")
			#raise Exception("FIFO_TIMEOUT")
			
		else:
			self.fifo_out.write(result + "\n")
			self.fifo_out.close()
			os.remove(self.name+".in")
			os.remove(self.name+".out")
Sam Moore's avatar
Sam Moore committed
167

Sam Moore's avatar
Sam Moore committed
168
# Player that runs from another process
Sam Moore's avatar
Sam Moore committed
169
class ExternalAgent(Player):
Sam Moore's avatar
Sam Moore committed
170 171


Sam Moore's avatar
Sam Moore committed
172 173
	def __init__(self, name, colour):
		Player.__init__(self, name, colour)
Sam Moore's avatar
Sam Moore committed
174
		self.p = subprocess.Popen(name,bufsize=0,stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True,universal_newlines=True)
Sam Moore's avatar
Sam Moore committed
175 176 177 178 179 180 181 182 183
		
		self.send_message(colour)

	def send_message(self, s):
		if agent_timeout > 0.0:
			ready = select.select([], [self.p.stdin], [], agent_timeout)[1]
		else:
			ready = [self.p.stdin]
		if self.p.stdin in ready:
Sam Moore's avatar
Sam Moore committed
184
			#sys.stderr.write("Writing \'" + s + "\' to " + str(self.p) + "\n")
Sam Moore's avatar
Sam Moore committed
185 186 187 188 189
			try:
				self.p.stdin.write(s + "\n")
			except:
				raise Exception("UNRESPONSIVE")
		else:
Sam Moore's avatar
Sam Moore committed
190
			raise Exception("TIMEOUT")
Sam Moore's avatar
Sam Moore committed
191 192 193 194 195 196 197

	def get_response(self):
		if agent_timeout > 0.0:
			ready = select.select([self.p.stdout], [], [], agent_timeout)[0]
		else:
			ready = [self.p.stdout]
		if self.p.stdout in ready:
Sam Moore's avatar
Sam Moore committed
198
			#sys.stderr.write("Reading from " + str(self.p) + " 's stdout...\n")
Sam Moore's avatar
Sam Moore committed
199
			try:
200
				result = self.p.stdout.readline().strip(" \t\r\n")
Sam Moore's avatar
Sam Moore committed
201 202
				#sys.stderr.write("Read \'" + result + "\' from " + str(self.p) + "\n")
				return result
Sam Moore's avatar
Sam Moore committed
203 204 205
			except: # Exception, e:
				raise Exception("UNRESPONSIVE")
		else:
Sam Moore's avatar
Sam Moore committed
206
			raise Exception("TIMEOUT")
Sam Moore's avatar
Sam Moore committed
207 208

	def select(self):
Sam Moore's avatar
Sam Moore committed
209 210 211

		self.send_message("SELECTION?")
		line = self.get_response()
Sam Moore's avatar
Sam Moore committed
212 213
		
		try:
214 215
			m = re.match("\s*(\d+)\s+(\d+)\s*", line)
			result = map(int, [m.group(1), m.group(2)])
Sam Moore's avatar
Sam Moore committed
216 217 218 219 220 221
		except:
			raise Exception("GIBBERISH \"" + str(line) + "\"")
		return result

	def update(self, result):
		#print "Update " + str(result) + " called for AgentPlayer"
Sam Moore's avatar
Sam Moore committed
222
		self.send_message(result)
Sam Moore's avatar
Sam Moore committed
223
		return result
Sam Moore's avatar
Sam Moore committed
224 225 226

	def get_move(self):
		
Sam Moore's avatar
Sam Moore committed
227 228 229
		self.send_message("MOVE?")
		line = self.get_response()
		
Sam Moore's avatar
Sam Moore committed
230
		try:
231 232 233
			m = re.match("\s*(\d+)\s+(\d+)\s*", line)
			result = map(int, [m.group(1), m.group(2)])

Sam Moore's avatar
Sam Moore committed
234 235 236 237
		except:
			raise Exception("GIBBERISH \"" + str(line) + "\"")
		return result

Sam Moore's avatar
Sam Moore committed
238 239 240 241 242 243
	def reset_board(self, s):
		self.send_message("BOARD")
		for line in s.split("\n"):
			self.send_message(line.strip(" \r\n"))
		self.send_message("END BOARD")

Sam Moore's avatar
Sam Moore committed
244 245
	def quit(self, final_result):
		try:
Sam Moore's avatar
Sam Moore committed
246
			self.send_message("QUIT " + final_result)
Sam Moore's avatar
Sam Moore committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
		except:
			self.p.kill()

# So you want to be a player here?
class HumanPlayer(Player):
	def __init__(self, name, colour):
		Player.__init__(self, name, colour)
		
	# Select your preferred account
	def select(self):
		if isinstance(graphics, GraphicsThread):
			# Basically, we let the graphics thread do some shit and then return that information to the game thread
			graphics.cond.acquire()
			# We wait for the graphics thread to select a piece
			while graphics.stopped() == False and graphics.state["select"] == None:
				graphics.cond.wait() # The difference between humans and machines is that humans sleep
			select = graphics.state["select"]
			
			
			graphics.cond.release()
			if graphics.stopped():
				return [-1,-1]
			return [select.x, select.y]
		else:
			# Since I don't display the board in this case, I'm not sure why I filled it in...
			while True:
				sys.stdout.write("SELECTION?\n")
				try:
					p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
judge's avatar
judge committed
276
					return p
Sam Moore's avatar
Sam Moore committed
277 278 279
				except:
					sys.stderr.write("ILLEGAL GIBBERISH\n")
					continue
judge's avatar
judge committed
280

Sam Moore's avatar
Sam Moore committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	# It's your move captain
	def get_move(self):
		if isinstance(graphics, GraphicsThread):
			graphics.cond.acquire()
			while graphics.stopped() == False and graphics.state["dest"] == None:
				graphics.cond.wait()
			graphics.cond.release()
			
			return graphics.state["dest"]
		else:

			while True:
				sys.stdout.write("MOVE?\n")
				try:
					p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
judge's avatar
judge committed
296
					return p
Sam Moore's avatar
Sam Moore committed
297 298 299 300 301 302
				except:
					sys.stderr.write("ILLEGAL GIBBERISH\n")
					continue

	# Are you sure you want to quit?
	def quit(self, final_result):
Sam Moore's avatar
Sam Moore committed
303 304
		if graphics == None:		
			sys.stdout.write("QUIT " + final_result + "\n")
Sam Moore's avatar
Sam Moore committed
305 306 307 308 309 310 311

	# Completely useless function
	def update(self, result):
		if isinstance(graphics, GraphicsThread):
			pass
		else:
			sys.stdout.write(result + "\n")	
Sam Moore's avatar
Sam Moore committed
312
		return result
Sam Moore's avatar
Sam Moore committed
313 314


Sam Moore's avatar
Sam Moore committed
315 316
# Default internal player (makes random moves)
class InternalAgent(Player):
Sam Moore's avatar
Sam Moore committed
317 318 319 320 321 322
	def __init__(self, name, colour):
		Player.__init__(self, name, colour)
		self.choice = None

		self.board = Board(style = "agent")

Sam Moore's avatar
Sam Moore committed
323 324 325 326 327


	def update(self, result):
		
		self.board.update(result)
Sam Moore's avatar
Sam Moore committed
328 329
		#self.board.verify()
		return result
Sam Moore's avatar
Sam Moore committed
330

Sam Moore's avatar
Sam Moore committed
331 332 333
	def reset_board(self, s):
		self.board.reset_board(s)

Sam Moore's avatar
Sam Moore committed
334 335 336 337 338 339 340
	def quit(self, final_result):
		pass

class AgentRandom(InternalAgent):
	def __init__(self, name, colour):
		InternalAgent.__init__(self, name, colour)

Sam Moore's avatar
Sam Moore committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	def select(self):
		while True:
			self.choice = self.board.pieces[self.colour][random.randint(0, len(self.board.pieces[self.colour])-1)]
			all_moves = []
			# Check that the piece has some possibility to move
			tmp = self.choice.current_type
			if tmp == "unknown": # For unknown pieces, try both types
				for t in self.choice.types:
					if t == "unknown":
						continue
					self.choice.current_type = t
					all_moves += self.board.possible_moves(self.choice)
			else:
				all_moves = self.board.possible_moves(self.choice)
			self.choice.current_type = tmp
			if len(all_moves) > 0:
				break
		return [self.choice.x, self.choice.y]

	def get_move(self):
		moves = self.board.possible_moves(self.choice)
		move = moves[random.randint(0, len(moves)-1)]
		return move


Sam Moore's avatar
Sam Moore committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
# Terrible, terrible hacks

def run_agent(agent):
	#sys.stderr.write(sys.argv[0] + " : Running agent " + str(agent) + "\n")
	while True:
		line = sys.stdin.readline().strip(" \r\n")
		if line == "SELECTION?":
			#sys.stderr.write(sys.argv[0] + " : Make selection\n")
			[x,y] = agent.select() # Gets your agent's selection
			#sys.stderr.write(sys.argv[0] + " : Selection was " + str(agent.choice) + "\n")
			sys.stdout.write(str(x) + " " + str(y) + "\n")				
		elif line == "MOVE?":
			#sys.stderr.write(sys.argv[0] + " : Make move\n")
			[x,y] = agent.get_move() # Gets your agent's move
			sys.stdout.write(str(x) + " " + str(y) + "\n")
		elif line.split(" ")[0] == "QUIT":
			#sys.stderr.write(sys.argv[0] + " : Quitting\n")
			agent.quit(" ".join(line.split(" ")[1:])) # Quits the game
			break
Sam Moore's avatar
Sam Moore committed
385 386 387 388 389 390 391 392
		elif line.split(" ")[0] == "BOARD":
			s = ""
			line = sys.stdin.readline().strip(" \r\n")
			while line != "END BOARD":
				s += line + "\n"
				line = sys.stdin.readline().strip(" \r\n")
			agent.board.reset_board(s)
			
Sam Moore's avatar
Sam Moore committed
393 394 395 396 397 398 399 400 401
		else:
			agent.update(line) # Updates agent.board
	return 0


# Sort of works?

class ExternalWrapper(ExternalAgent):
	def __init__(self, agent):
402
		run = "python -u -c \"import sys;import os;from qchess import *;agent = " + agent.__class__.__name__ + "('" + agent.name + "','"+agent.colour+"');sys.stdin.readline();sys.exit(run_agent(agent))\""
Sam Moore's avatar
Sam Moore committed
403 404
		# str(run)
		ExternalAgent.__init__(self, run, agent.colour)
405

Sam Moore's avatar
Sam Moore committed
406
	
407