main.py 8.45 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 63
		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
64 65

	else:
Sam Moore's avatar
Sam Moore committed
66
		return ExternalAgent(name, colour)
Sam Moore's avatar
Sam Moore committed
67 68
			

Sam Moore's avatar
Sam Moore committed
69 70 71 72 73 74 75 76

# 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
77
	
Sam Moore's avatar
Sam Moore committed
78 79
	global turn_delay
	global agent_timeout
Sam Moore's avatar
Sam Moore committed
80
	global log_files
Sam Moore's avatar
Sam Moore committed
81
	global src_file
Sam Moore's avatar
Sam Moore committed
82
	global graphics_enabled
83
	global always_reveal_states
Sam Moore's avatar
Sam Moore committed
84
	global sleep_timeout
Sam Moore's avatar
Sam Moore committed
85

Sam Moore's avatar
Sam Moore committed
86
	max_moves = None
Sam Moore's avatar
Sam Moore committed
87
	src_file = None
Sam Moore's avatar
Sam Moore committed
88 89 90
	
	style = "quantum"
	colour = "white"
Sam Moore's avatar
Sam Moore committed
91 92 93 94 95 96 97

	# 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
98 99 100 101 102 103 104

	players = []
	i = 0
	while i < len(argv)-1:
		i += 1
		arg = argv[i]
		if arg[0] != '-':
Sam Moore's avatar
Sam Moore committed
105 106 107 108 109
			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
110 111 112 113 114 115 116 117 118 119 120 121 122
			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"
123 124
		elif arg[1] == '-' and arg[2:] == "reveal":
			always_reveal_states = True
Sam Moore's avatar
Sam Moore committed
125
		elif (arg[1] == '-' and arg[2:] == "graphics"):
Sam Moore's avatar
Sam Moore committed
126 127 128
			graphics_enabled = True
		elif (arg[1] == '-' and arg[2:] == "no-graphics"):
			graphics_enabled = False
Sam Moore's avatar
Sam Moore committed
129 130 131
		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
132
				src_file = sys.stdin
Sam Moore's avatar
Sam Moore committed
133
			else:
Sam Moore's avatar
Sam Moore committed
134
				f = arg[2:].split("=")[1]
Sam Moore's avatar
Sam Moore committed
135 136
				if f[0:7] == "http://":
					src_file = HttpReplay(f)
Sam Moore's avatar
Sam Moore committed
137
				else:
Sam Moore's avatar
Sam Moore committed
138
					src_file = FileReplay(f.split(":")[0])
139

Sam Moore's avatar
Sam Moore committed
140 141
					if len(f.split(":")) == 2:
						max_moves = int(f.split(":")[1])
142 143 144 145 146 147 148
						
		elif (arg[1] == '-' and arg[2:] == "server"):
			if len(arg[2:].split("=") <= 1):
				dedicated_server()
			else:
				client(arg[2:].split("=")[1])
			sys.exit(0)
Sam Moore's avatar
Sam Moore committed
149 150 151
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"):
			# Log file
			if len(arg[2:].split("=")) == 1:
Sam Moore's avatar
Sam Moore committed
152
				log_files.append(LogFile(sys.stdout))
Sam Moore's avatar
Sam Moore committed
153
			else:
Sam Moore's avatar
Sam Moore committed
154 155
				f = arg[2:].split("=")[1]
				if f[0] == '@':
Sam Moore's avatar
Sam Moore committed
156
					log_files.append(ShortLog(f[1:]))
Sam Moore's avatar
Sam Moore committed
157
				else:
Sam Moore's avatar
Sam Moore committed
158
					log_files.append(LogFile(open(f, "w", 0)))
Sam Moore's avatar
Sam Moore committed
159 160 161 162 163 164 165 166 167 168 169 170
		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:
171
				agent_timeout = float(arg[2:].split("=")[1])
Sam Moore's avatar
Sam Moore committed
172 173 174 175 176 177
		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])
Sam Moore's avatar
Sam Moore committed
178 179 180 181 182 183 184 185
				
		elif (arg[1] == '-' and arg[2:] == "help"):
			# Help
			os.system("less data/help.txt") # The best help function
			return 0


	# Create the board
Sam Moore's avatar
Sam Moore committed
186 187 188 189
	
	# Construct a GameThread! Make it global! Damn the consequences!
			
	if src_file != None:
190 191 192 193 194 195 196 197
		# 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
198
			players = [Player("dummy", "white"), Player("dummy", "black")]
199 200 201 202 203
		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
204
		game = ReplayThread(players, src_file, end=end, max_moves=max_moves)
Sam Moore's avatar
Sam Moore committed
205 206
	else:
		board = Board(style)
Sam Moore's avatar
Sam Moore committed
207
		board.max_moves = max_moves
Sam Moore's avatar
Sam Moore committed
208 209
		game = GameThread(board, players) 

Sam Moore's avatar
Sam Moore committed
210 211


Sam Moore's avatar
Sam Moore committed
212

Sam Moore's avatar
Sam Moore committed
213 214 215
	# Initialise GUI
	if graphics_enabled == True:
		try:
Sam Moore's avatar
Sam Moore committed
216
			graphics = GraphicsThread(game.board, grid_sz = [64,64]) # Construct a GraphicsThread!
Sam Moore's avatar
Sam Moore committed
217 218
			
			graphics.sleep_timeout = sleep_timeout
219

Sam Moore's avatar
Sam Moore committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
		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:
			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
238 239 240 241 242
	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
243
					continue
Sam Moore's avatar
Sam Moore committed
244 245
				players[i] = NetworkPlayer(old[i].colour, p.network, old[i])
		
Sam Moore's avatar
Sam Moore committed
246
	for p in players:
Sam Moore's avatar
Sam Moore committed
247 248 249 250 251 252 253
		debug(str(p))
		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
254
				
255 256
	
	# If using windows, select won't work; use horrible TimeoutPlayer hack
Sam Moore's avatar
Sam Moore committed
257 258 259 260 261
	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)
262

Sam Moore's avatar
Sam Moore committed
263 264 265 266 267 268 269
		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])
270 271 272 273


		

Sam Moore's avatar
Sam Moore committed
274

Sam Moore's avatar
Sam Moore committed
275

Sam Moore's avatar
Sam Moore committed
276

277 278
	log_init(game.board, players)
	
Sam Moore's avatar
Sam Moore committed
279 280 281 282
	
	if graphics != None:
		game.start() # This runs in a new thread
		graphics.run()
283 284 285 286
		if game.is_alive():
			game.join()
	

Sam Moore's avatar
Sam Moore committed
287
		error = game.error + graphics.error
Sam Moore's avatar
Sam Moore committed
288 289
	else:
		game.run()
Sam Moore's avatar
Sam Moore committed
290
		error = game.error
291
	
Sam Moore's avatar
Sam Moore committed
292

Sam Moore's avatar
Sam Moore committed
293 294
	for l in log_files:
		l.close()
Sam Moore's avatar
Sam Moore committed
295 296 297 298

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

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

Sam Moore's avatar
Sam Moore committed
301
	return error
302 303 304 305 306 307 308
		
		
	
		
	
		
		
Sam Moore's avatar
Sam Moore committed
309 310 311

# This is how python does a main() function...
if __name__ == "__main__":
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	try:
		sys.exit(main(sys.argv))
	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()

		sys.exit(102)