main.py 9.71 KB
Newer Older
Sam Moore's avatar
Sam Moore committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#!/usr/bin/python -u

# Do you know what the -u does? It unbuffers stdin and stdout
# I can't remember why, but last year things broke without that

"""
	UCC::Progcomp 2013 Quantum Chess game
	@author Sam Moore [SZM] "matches"
	@copyright The University Computer Club, Incorporated
		(ie: You can copy it for not for profit purposes)
"""

# system python modules or whatever they are called
import sys
import os
import time

turn_delay = 0.5
Sam Moore's avatar
Sam Moore committed
19
sleep_timeout = None
Sam Moore's avatar
Sam Moore committed
20 21
[game, graphics] = [None, None]

Sam Moore's avatar
Sam Moore committed
22 23 24 25 26 27
def make_player(name, colour):
	if name[0] == '@':
		if name[1:] == "human":
			return HumanPlayer(name, colour)
		s = name[1:].split(":")
		if s[0] == "network":
Sam Moore's avatar
Sam Moore committed
28 29
			ip = None
			port = 4562
30
			#print str(s)
Sam Moore's avatar
Sam Moore committed
31
			if len(s) > 1:
32 33 34 35
				if s[1] != "":
					ip = s[1]
			if len(s) > 2:
				port = int(s[2])
Sam Moore's avatar
Sam Moore committed
36 37 38
				
			if ip == None:
				if colour == "black":
39
					port += 1
Sam Moore's avatar
Sam Moore committed
40
			elif colour == "white":
41
				port += 1
Sam Moore's avatar
Sam Moore committed
42 43
						
			return NetworkPlayer(colour, Network((ip, port)), None)
Sam Moore's avatar
Sam Moore committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
		if s[0] == "internal":

			import inspect
			internal_agents = inspect.getmembers(sys.modules[__name__], inspect.isclass)
			internal_agents = [x for x in internal_agents if issubclass(x[1], InternalAgent)]
			internal_agents.remove(('InternalAgent', InternalAgent)) 
			
			if len(s) != 2:
				sys.stderr.write(sys.argv[0] + " : '@internal' should be followed by ':' and an agent name\n")
				sys.stderr.write(sys.argv[0] + " : Choices are: " + str(map(lambda e : e[0], internal_agents)) + "\n")
				return None

			for a in internal_agents:
				if s[1] == a[0]:
					return a[1](name, colour)
			
			sys.stderr.write(sys.argv[0] + " : Can't find an internal agent matching \"" + s[1] + "\"\n")
			sys.stderr.write(sys.argv[0] + " : Choices are: " + str(map(lambda e : e[0], internal_agents)) + "\n")
			return None
Sam Moore's avatar
Sam Moore committed
63 64 65 66 67
		if s[0] == "fifo":
			if len(s) > 1:
				return FifoPlayer(s[1], colour)
			else:
				return FifoPlayer(str(os.getpid())+"."+colour, colour)
Sam Moore's avatar
Sam Moore committed
68 69

	else:
Sam Moore's avatar
Sam Moore committed
70
		return ExternalAgent(name, colour)
Sam Moore's avatar
Sam Moore committed
71 72
			

Sam Moore's avatar
Sam Moore committed
73 74 75 76 77 78 79 80

# The main function! It does the main stuff!
def main(argv):

	# Apparently python will silently treat things as local unless you do this
	# Anyone who says "You should never use a global variable" can die in a fire
	global game
	global graphics
Sam Moore's avatar
Sam Moore committed
81
	
Sam Moore's avatar
Sam Moore committed
82 83
	global turn_delay
	global agent_timeout
Sam Moore's avatar
Sam Moore committed
84
	global log_files
Sam Moore's avatar
Sam Moore committed
85
	global src_file
Sam Moore's avatar
Sam Moore committed
86
	global graphics_enabled
87
	global always_reveal_states
Sam Moore's avatar
Sam Moore committed
88
	global sleep_timeout
Sam Moore's avatar
Sam Moore committed
89

90

judge's avatar
judge committed
91
	retry_illegal = False
92 93
	server_addr = None

Sam Moore's avatar
Sam Moore committed
94
	max_moves = None
Sam Moore's avatar
Sam Moore committed
95
	src_file = None
Sam Moore's avatar
Sam Moore committed
96 97 98
	
	style = "quantum"
	colour = "white"
Sam Moore's avatar
Sam Moore committed
99 100 101 102 103 104 105

	# Get the important warnings out of the way
	if platform.system() == "Windows":
		sys.stderr.write(sys.argv[0] + " : Warning - You are using " + platform.system() + "\n")
		if platform.release() == "Vista":
			sys.stderr.write(sys.argv[0] + " : God help you.\n")
	
Sam Moore's avatar
Sam Moore committed
106 107 108 109 110 111 112

	players = []
	i = 0
	while i < len(argv)-1:
		i += 1
		arg = argv[i]
		if arg[0] != '-':
Sam Moore's avatar
Sam Moore committed
113 114 115 116 117
			p = make_player(arg, colour)
			if not isinstance(p, Player):
				sys.stderr.write(sys.argv[0] + " : Fatal error creating " + colour + " player\n")
				return 100
			players.append(p)
Sam Moore's avatar
Sam Moore committed
118 119 120 121 122 123 124 125 126 127 128 129 130
			if colour == "white":
				colour = "black"
			elif colour == "black":
				pass
			else:
				sys.stderr.write(sys.argv[0] + " : Too many players (max 2)\n")
			continue

		# Option parsing goes here
		if arg[1] == '-' and arg[2:] == "classical":
			style = "classical"
		elif arg[1] == '-' and arg[2:] == "quantum":
			style = "quantum"
131 132
		elif arg[1] == '-' and arg[2:] == "reveal":
			always_reveal_states = True
Sam Moore's avatar
Sam Moore committed
133
		elif (arg[1] == '-' and arg[2:] == "graphics"):
Sam Moore's avatar
Sam Moore committed
134 135 136
			graphics_enabled = True
		elif (arg[1] == '-' and arg[2:] == "no-graphics"):
			graphics_enabled = False
Sam Moore's avatar
Sam Moore committed
137 138 139
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"):
			# Load game from file
			if len(arg[2:].split("=")) == 1:
Sam Moore's avatar
Sam Moore committed
140
				src_file = sys.stdin
Sam Moore's avatar
Sam Moore committed
141
			else:
Sam Moore's avatar
Sam Moore committed
142
				f = arg[2:].split("=")[1]
Sam Moore's avatar
Sam Moore committed
143 144
				if f[0:7] == "http://":
					src_file = HttpReplay(f)
Sam Moore's avatar
Sam Moore committed
145
				else:
Sam Moore's avatar
Sam Moore committed
146
					src_file = FileReplay(f.split(":")[0])
147

Sam Moore's avatar
Sam Moore committed
148 149
					if len(f.split(":")) == 2:
						max_moves = int(f.split(":")[1])
150
						
151 152 153 154
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "server"):
			#debug("Server: " + str(arg[2:]))
			if len(arg[2:].split("=")) <= 1:
				server_addr = True
155
			else:
156 157
				server_addr = arg[2:].split("=")[1]
			
Sam Moore's avatar
Sam Moore committed
158 159 160
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"):
			# Log file
			if len(arg[2:].split("=")) == 1:
161
				log_files.append(LogFile(sys.stdout,""))
Sam Moore's avatar
Sam Moore committed
162
			else:
Sam Moore's avatar
Sam Moore committed
163
				f = arg[2:].split("=")[1]
164 165 166
				if f == "":
					log_files.append(LogFile(sys.stdout, ""))
				elif f[0] == '@':
Sam Moore's avatar
Sam Moore committed
167
					log_files.append(ShortLog(f[1:]))
Sam Moore's avatar
Sam Moore committed
168
				else:
169
					log_files.append(LogFile(open(f, "w", 0), f))
Sam Moore's avatar
Sam Moore committed
170 171 172 173 174 175 176 177 178 179 180 181
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "delay"):
			# Delay
			if len(arg[2:].split("=")) == 1:
				turn_delay = 0
			else:
				turn_delay = float(arg[2:].split("=")[1])

		elif (arg[1] == '-' and arg[2:].split("=")[0] == "timeout"):
			# Timeout
			if len(arg[2:].split("=")) == 1:
				agent_timeout = -1
			else:
182
				agent_timeout = float(arg[2:].split("=")[1])
Sam Moore's avatar
Sam Moore committed
183 184 185 186 187 188
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "blackout"):
			# Screen saver delay
			if len(arg[2:].split("=")) == 1:
				sleep_timeout = -1
			else:
				sleep_timeout = float(arg[2:].split("=")[1])
judge's avatar
judge committed
189 190
		elif (arg[1] == '-' and arg[2:] == "retry-illegal"):
			retry_illegal = not retry_illegal
Sam Moore's avatar
Sam Moore committed
191 192 193 194
		elif (arg[1] == '-' and arg[2:] == "help"):
			# Help
			os.system("less data/help.txt") # The best help function
			return 0
195 196 197 198 199 200 201 202 203
		
	# Dedicated server?
	
	#debug("server_addr = " + str(server_addr))
	
	if server_addr != None:
		if server_addr == True:
			return dedicated_server()
		else:
Sam Moore's avatar
Sam Moore committed
204 205 206 207 208 209 210
			if len(players) > 1:
				sys.stderr.write("Only a single player may be provided when --server is used\n")
				return 1
			if len(players) == 1:
				return client(server_addr, players[0].name)
			else:
				return client(server_addr)
211
		
Sam Moore's avatar
Sam Moore committed
212 213

	# Create the board
Sam Moore's avatar
Sam Moore committed
214 215 216 217
	
	# Construct a GameThread! Make it global! Damn the consequences!
			
	if src_file != None:
218 219 220 221 222 223 224 225
		# Hack to stop ReplayThread from exiting
		#if len(players) == 0:
		#	players = [HumanPlayer("dummy", "white"), HumanPlayer("dummy", "black")]

		# Normally the ReplayThread exits if there are no players
		# TODO: Decide which behaviour to use, and fix it
		end = (len(players) == 0)
		if end:
Sam Moore's avatar
Sam Moore committed
226
			players = [Player("dummy", "white"), Player("dummy", "black")]
227 228 229 230 231
		elif len(players) != 2:
			sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n")
			if graphics_enabled:
				sys.stderr.write(sys.argv[0] + " : (You won't get a GUI, because --file was used, and the author is lazy)\n")
			return 44
Sam Moore's avatar
Sam Moore committed
232
		game = ReplayThread(players, src_file, end=end, max_moves=max_moves)
Sam Moore's avatar
Sam Moore committed
233 234
	else:
		board = Board(style)
Sam Moore's avatar
Sam Moore committed
235
		board.max_moves = max_moves
Sam Moore's avatar
Sam Moore committed
236
		game = GameThread(board, players) 
judge's avatar
judge committed
237
		game.retry_illegal = retry_illegal
Sam Moore's avatar
Sam Moore committed
238

Sam Moore's avatar
Sam Moore committed
239 240


Sam Moore's avatar
Sam Moore committed
241

Sam Moore's avatar
Sam Moore committed
242 243 244
	# Initialise GUI
	if graphics_enabled == True:
		try:
Sam Moore's avatar
Sam Moore committed
245
			graphics = GraphicsThread(game.board, grid_sz = [64,64]) # Construct a GraphicsThread!
Sam Moore's avatar
Sam Moore committed
246 247
			
			graphics.sleep_timeout = sleep_timeout
248

Sam Moore's avatar
Sam Moore committed
249 250 251 252 253 254 255 256
		except Exception,e:
			graphics = None
			sys.stderr.write(sys.argv[0] + " : Got exception trying to initialise graphics\n"+str(e.message)+"\nDisabled graphics\n")
			graphics_enabled = False

	# If there are no players listed, display a nice pretty menu
	if len(players) != 2:
		if graphics != None:
257 258 259
			
			server_addr = graphics.SelectServer()
			if server_addr != None:
Sam Moore's avatar
Sam Moore committed
260
				pygame.quit() # Time to say goodbye
261 262 263 264 265
				if server_addr == True:
					return dedicated_server()
				else:
					return client(server_addr)	
			
Sam Moore's avatar
Sam Moore committed
266 267 268 269 270 271 272 273 274 275
			players = graphics.SelectPlayers(players)
		else:
			sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n")
			return 44

	# If there are still no players, quit
	if players == None or len(players) != 2:
		sys.stderr.write(sys.argv[0] + " : Graphics window closed before players chosen\n")
		return 45

Sam Moore's avatar
Sam Moore committed
276 277 278 279 280
	old = players[:]
	for p in old:
		if isinstance(p, NetworkPlayer):
			for i in range(len(old)):
				if old[i] == p or isinstance(old[i], NetworkPlayer):
Sam Moore's avatar
Sam Moore committed
281
					continue
Sam Moore's avatar
Sam Moore committed
282 283
				players[i] = NetworkPlayer(old[i].colour, p.network, old[i])
		
Sam Moore's avatar
Sam Moore committed
284
	for p in players:
285
		#debug(str(p))
Sam Moore's avatar
Sam Moore committed
286 287 288 289 290 291
		if isinstance(p, NetworkPlayer):
			p.board = game.board
			if not p.network.connected:
				if not p.network.server:
					time.sleep(0.2)
				p.network.connect()
Sam Moore's avatar
Sam Moore committed
292
				
293 294
	
	# If using windows, select won't work; use horrible TimeoutPlayer hack
Sam Moore's avatar
Sam Moore committed
295 296 297 298 299
	if agent_timeout > 0:
		if platform.system() == "Windows":
			for i in range(len(players)):
				if isinstance(players[i], ExternalAgent) or isinstance(players[i], InternalAgent):
					players[i] = TimeoutPlayer(players[i], agent_timeout)
300

Sam Moore's avatar
Sam Moore committed
301 302 303 304 305 306 307
		else:
			warned = False
			# InternalAgents get wrapped to an ExternalAgent when there is a timeout
			# This is not confusing at all.
			for i in range(len(players)):
				if isinstance(players[i], InternalAgent):
						players[i] = ExternalWrapper(players[i])
308 309 310 311


		

Sam Moore's avatar
Sam Moore committed
312

Sam Moore's avatar
Sam Moore committed
313

Sam Moore's avatar
Sam Moore committed
314

315 316
	log_init(game.board, players)
	
Sam Moore's avatar
Sam Moore committed
317 318 319 320
	
	if graphics != None:
		game.start() # This runs in a new thread
		graphics.run()
321 322 323 324
		if game.is_alive():
			game.join()
	

Sam Moore's avatar
Sam Moore committed
325
		error = game.error + graphics.error
Sam Moore's avatar
Sam Moore committed
326 327
	else:
		game.run()
Sam Moore's avatar
Sam Moore committed
328
		error = game.error
329
	
Sam Moore's avatar
Sam Moore committed
330

Sam Moore's avatar
Sam Moore committed
331 332
	for l in log_files:
		l.close()
Sam Moore's avatar
Sam Moore committed
333 334 335 336

	if src_file != None and src_file != sys.stdin:
		src_file.close()

Sam Moore's avatar
Sam Moore committed
337 338
	sys.stdout.write(game.final_result + "\n")

Sam Moore's avatar
Sam Moore committed
339
	return error
340 341 342 343 344 345 346
		
		
	
		
	
		
		
Sam Moore's avatar
Sam Moore committed
347 348 349

# This is how python does a main() function...
if __name__ == "__main__":
350
	retcode = 0
351
	try:
352
		retcode = main(sys.argv)
353 354 355 356 357 358 359 360 361 362
	except KeyboardInterrupt:
		sys.stderr.write(sys.argv[0] + " : Got KeyboardInterrupt. Stopping everything\n")
		if isinstance(graphics, StoppableThread):
			graphics.stop()
			graphics.run() # Will clean up graphics because it is stopped, not run it (a bit dodgy)

		if isinstance(game, StoppableThread):
			game.stop()
			if game.is_alive():
				game.join()
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
		retcode = 102
	#except Exception, e:
	#	sys.stderr.write(sys.argv[0] + " : " + e.message + "\n")
	#	retcode = 103	
		
	try:
    		sys.stdout.close()
	except:
    		pass
	try:
    		sys.stderr.close()
	except:
    		pass
	sys.exit(retcode)
		
378