main.py 8.39 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 28 29 30 31
def make_player(name, colour):
	if name[0] == '@':
		if name[1:] == "human":
			return HumanPlayer(name, colour)
		s = name[1:].split(":")
		if s[0] == "network":
			address = None
			if len(s) > 1:
				address = s[1]
			return NetworkReceiver(colour, address)
Sam Moore's avatar
Sam Moore committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
		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
52 53

	else:
Sam Moore's avatar
Sam Moore committed
54
		return ExternalAgent(name, colour)
Sam Moore's avatar
Sam Moore committed
55 56
			

Sam Moore's avatar
Sam Moore committed
57 58 59 60 61 62 63 64

# 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
65
	
Sam Moore's avatar
Sam Moore committed
66 67
	global turn_delay
	global agent_timeout
Sam Moore's avatar
Sam Moore committed
68
	global log_files
Sam Moore's avatar
Sam Moore committed
69
	global src_file
Sam Moore's avatar
Sam Moore committed
70
	global graphics_enabled
71
	global always_reveal_states
Sam Moore's avatar
Sam Moore committed
72
	global sleep_timeout
Sam Moore's avatar
Sam Moore committed
73

Sam Moore's avatar
Sam Moore committed
74
	max_moves = None
Sam Moore's avatar
Sam Moore committed
75
	src_file = None
Sam Moore's avatar
Sam Moore committed
76 77 78
	
	style = "quantum"
	colour = "white"
Sam Moore's avatar
Sam Moore committed
79 80 81 82 83 84 85

	# 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
86 87 88 89 90 91 92

	players = []
	i = 0
	while i < len(argv)-1:
		i += 1
		arg = argv[i]
		if arg[0] != '-':
Sam Moore's avatar
Sam Moore committed
93 94 95 96 97
			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
98 99 100 101 102 103 104 105 106 107 108 109 110
			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"
111 112
		elif arg[1] == '-' and arg[2:] == "reveal":
			always_reveal_states = True
Sam Moore's avatar
Sam Moore committed
113
		elif (arg[1] == '-' and arg[2:] == "graphics"):
Sam Moore's avatar
Sam Moore committed
114 115 116
			graphics_enabled = True
		elif (arg[1] == '-' and arg[2:] == "no-graphics"):
			graphics_enabled = False
Sam Moore's avatar
Sam Moore committed
117 118 119
		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
120
				src_file = sys.stdin
Sam Moore's avatar
Sam Moore committed
121
			else:
Sam Moore's avatar
Sam Moore committed
122
				f = arg[2:].split("=")[1]
Sam Moore's avatar
Sam Moore committed
123 124
				if f[0:7] == "http://":
					src_file = HttpReplay(f)
Sam Moore's avatar
Sam Moore committed
125
				else:
Sam Moore's avatar
Sam Moore committed
126
					src_file = FileReplay(f.split(":")[0])
127

Sam Moore's avatar
Sam Moore committed
128 129
					if len(f.split(":")) == 2:
						max_moves = int(f.split(":")[1])
130

Sam Moore's avatar
Sam Moore committed
131 132 133
		elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"):
			# Log file
			if len(arg[2:].split("=")) == 1:
Sam Moore's avatar
Sam Moore committed
134
				log_files.append(LogFile(sys.stdout))
Sam Moore's avatar
Sam Moore committed
135
			else:
Sam Moore's avatar
Sam Moore committed
136 137
				f = arg[2:].split("=")[1]
				if f[0] == '@':
Sam Moore's avatar
Sam Moore committed
138
					log_files.append(ShortLog(f[1:]))
Sam Moore's avatar
Sam Moore committed
139
				else:
Sam Moore's avatar
Sam Moore committed
140
					log_files.append(LogFile(open(f, "w", 0)))
Sam Moore's avatar
Sam Moore committed
141 142 143 144 145 146 147 148 149 150 151 152
		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:
153
				agent_timeout = float(arg[2:].split("=")[1])
Sam Moore's avatar
Sam Moore committed
154 155 156 157 158 159
		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
160 161 162 163 164 165 166 167
				
		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
168 169 170 171
	
	# Construct a GameThread! Make it global! Damn the consequences!
			
	if src_file != None:
172 173 174 175 176 177 178 179
		# 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
180
			players = [Player("dummy", "white"), Player("dummy", "black")]
181 182 183 184 185
		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
186
		game = ReplayThread(players, src_file, end=end, max_moves=max_moves)
Sam Moore's avatar
Sam Moore committed
187 188
	else:
		board = Board(style)
Sam Moore's avatar
Sam Moore committed
189
		board.max_moves = max_moves
Sam Moore's avatar
Sam Moore committed
190 191
		game = GameThread(board, players) 

Sam Moore's avatar
Sam Moore committed
192 193


Sam Moore's avatar
Sam Moore committed
194

Sam Moore's avatar
Sam Moore committed
195 196 197
	# Initialise GUI
	if graphics_enabled == True:
		try:
Sam Moore's avatar
Sam Moore committed
198
			graphics = GraphicsThread(game.board, grid_sz = [64,64]) # Construct a GraphicsThread!
Sam Moore's avatar
Sam Moore committed
199 200
			
			graphics.sleep_timeout = sleep_timeout
201

Sam Moore's avatar
Sam Moore committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
		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


	# Wrap NetworkSender players around original players if necessary
	for i in range(len(players)):
		if isinstance(players[i], NetworkReceiver):
			players[i].board = board # Network players need direct access to the board
			for j in range(len(players)):
				if j == i:
					continue
				if isinstance(players[j], NetworkSender) or isinstance(players[j], NetworkReceiver):
					continue
				players[j] = NetworkSender(players[j], players[i].address)
				players[j].board = board

	# Connect the networked players
	for p in players:
		if isinstance(p, NetworkSender) or isinstance(p, NetworkReceiver):
			if graphics != None:
				graphics.board.display_grid(graphics.window, graphics.grid_sz)
				graphics.message("Connecting to " + p.colour + " player...")
			p.connect()

241 242
	
	# If using windows, select won't work; use horrible TimeoutPlayer hack
Sam Moore's avatar
Sam Moore committed
243 244 245 246 247
	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)
248

Sam Moore's avatar
Sam Moore committed
249 250 251 252 253 254 255
		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])
256 257 258 259


		

Sam Moore's avatar
Sam Moore committed
260

Sam Moore's avatar
Sam Moore committed
261

Sam Moore's avatar
Sam Moore committed
262

263 264
	log_init(game.board, players)
	
Sam Moore's avatar
Sam Moore committed
265 266 267 268
	
	if graphics != None:
		game.start() # This runs in a new thread
		graphics.run()
269 270 271 272
		if game.is_alive():
			game.join()
	

Sam Moore's avatar
Sam Moore committed
273
		error = game.error + graphics.error
Sam Moore's avatar
Sam Moore committed
274 275
	else:
		game.run()
Sam Moore's avatar
Sam Moore committed
276
		error = game.error
277
	
Sam Moore's avatar
Sam Moore committed
278

Sam Moore's avatar
Sam Moore committed
279 280
	for l in log_files:
		l.close()
Sam Moore's avatar
Sam Moore committed
281 282 283 284

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

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

Sam Moore's avatar
Sam Moore committed
287
	return error
Sam Moore's avatar
Sam Moore committed
288 289 290

# This is how python does a main() function...
if __name__ == "__main__":
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	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)