Final Commit
[progcomp2013.git] / qchess / src / main.py
1 #!/usr/bin/python -u
2
3 # Do you know what the -u does? It unbuffers stdin and stdout
4 # I can't remember why, but last year things broke without that
5
6 """
7         UCC::Progcomp 2013 Quantum Chess game
8         @author Sam Moore [SZM] "matches"
9         @copyright The University Computer Club, Incorporated
10                 (ie: You can copy it for not for profit purposes)
11 """
12
13 # system python modules or whatever they are called
14 import sys
15 import os
16 import time
17
18 turn_delay = 0.5
19 sleep_timeout = None
20 [game, graphics] = [None, None]
21
22 def make_player(name, colour):
23         debug(name)
24         if name[0] == '@':
25                 if name[1:] == "human":
26                         return HumanPlayer(name, colour)
27                 s = name[1:].split(":")
28                 if s[0] == "network":
29                         ip = None
30                         port = 4562
31                         #print str(s)
32                         if len(s) > 1:
33                                 if s[1] != "":
34                                         ip = s[1]
35                         if len(s) > 2:
36                                 port = int(s[2])
37                                 
38                         if ip == None:
39                                 if colour == "black":
40                                         port += 1
41                         elif colour == "white":
42                                 port += 1
43                                                 
44                         return NetworkPlayer(colour, Network((ip, port)), None)
45                 if s[0] == "internal":
46
47                         import inspect
48                         internal_agents = inspect.getmembers(sys.modules[__name__], inspect.isclass)
49                         internal_agents = [x for x in internal_agents if issubclass(x[1], InternalAgent)]
50                         internal_agents.remove(('InternalAgent', InternalAgent)) 
51                         
52                         if len(s) != 2:
53                                 sys.stderr.write(sys.argv[0] + " : '@internal' should be followed by ':' and an agent name\n")
54                                 sys.stderr.write(sys.argv[0] + " : Choices are: " + str(map(lambda e : e[0], internal_agents)) + "\n")
55                                 return None
56
57                         for a in internal_agents:
58                                 if s[1] == a[0]:
59                                         return a[1](name, colour)
60                         
61                         sys.stderr.write(sys.argv[0] + " : Can't find an internal agent matching \"" + s[1] + "\"\n")
62                         sys.stderr.write(sys.argv[0] + " : Choices are: " + str(map(lambda e : e[0], internal_agents)) + "\n")
63                         return None
64                 if s[0] == "fifo":
65                         if len(s) > 1:
66                                 return FifoPlayer(s[1], colour)
67                         else:
68                                 return FifoPlayer(str(os.getpid())+"."+colour, colour)
69
70         else:
71                 return ExternalAgent(name, colour)
72                         
73
74
75 # The main function! It does the main stuff!
76 def main(argv):
77
78         # Apparently python will silently treat things as local unless you do this
79         # Anyone who says "You should never use a global variable" can die in a fire
80         global game
81         global graphics
82         
83         global turn_delay
84         global agent_timeout
85         global log_files
86         global src_file
87         global graphics_enabled
88         global always_reveal_states
89         global sleep_timeout
90
91
92         retry_illegal = False
93         server_addr = None
94
95         max_moves = None
96         src_file = None
97         
98         style = "quantum"
99         colour = "white"
100
101         # Get the important warnings out of the way
102         if platform.system() == "Windows":
103                 sys.stderr.write(sys.argv[0] + " : Warning - You are using " + platform.system() + "\n")
104                 if platform.release() == "Vista":
105                         sys.stderr.write(sys.argv[0] + " : God help you.\n")
106         
107
108         players = []
109         i = 0
110         while i < len(argv)-1:
111                 i += 1
112                 arg = argv[i]
113                 if arg[0] != '-':
114                         players.append(arg)
115                         continue
116
117                 # Option parsing goes here
118                 if arg[1] == '-' and arg[2:] == "classical":
119                         style = "classical"
120                 elif arg[1] == '-' and arg[2:] == "quantum":
121                         style = "quantum"
122                 elif arg[1] == '-' and arg[2:] == "reveal":
123                         always_reveal_states = True
124                 elif (arg[1] == '-' and arg[2:] == "graphics"):
125                         graphics_enabled = True
126                 elif (arg[1] == '-' and arg[2:] == "no-graphics"):
127                         graphics_enabled = False
128                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "file"):
129                         # Load game from file
130                         if len(arg[2:].split("=")) == 1:
131                                 src_file = sys.stdin
132                         else:
133                                 f = arg[2:].split("=")[1]
134                                 if f[0:7] == "http://":
135                                         src_file = HttpReplay(f)
136                                 else:
137                                         src_file = FileReplay(f.split(":")[0])
138
139                                         if len(f.split(":")) == 2:
140                                                 max_moves = int(f.split(":")[1])
141                                                 
142                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "server"):
143                         #debug("Server: " + str(arg[2:]))
144                         if len(arg[2:].split("=")) <= 1:
145                                 server_addr = True
146                         else:
147                                 server_addr = arg[2:].split("=")[1]
148                         
149                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "log"):
150                         # Log file
151                         if len(arg[2:].split("=")) == 1:
152                                 log_files.append(LogFile(sys.stdout,""))
153                         else:
154                                 f = arg[2:].split("=")[1]
155                                 if f == "":
156                                         log_files.append(LogFile(sys.stdout, ""))
157                                 elif f[0] == '@':
158                                         log_files.append(ShortLog(f[1:]))
159                                 else:
160                                         log_files.append(LogFile(open(f, "w", 0), f))
161                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "delay"):
162                         # Delay
163                         if len(arg[2:].split("=")) == 1:
164                                 turn_delay = 0
165                         else:
166                                 turn_delay = float(arg[2:].split("=")[1])
167
168                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "timeout"):
169                         # Timeout
170                         if len(arg[2:].split("=")) == 1:
171                                 agent_timeout = -1
172                         else:
173                                 agent_timeout = float(arg[2:].split("=")[1])
174                 elif (arg[1] == '-' and arg[2:].split("=")[0] == "blackout"):
175                         # Screen saver delay
176                         if len(arg[2:].split("=")) == 1:
177                                 sleep_timeout = -1
178                         else:
179                                 sleep_timeout = float(arg[2:].split("=")[1])
180                 elif (arg[1] == '-' and arg[2:] == "retry-illegal"):
181                         retry_illegal = not retry_illegal
182                 elif (arg[1] == '-' and arg[2:] == "help"):
183                         # Help
184                         os.system("less data/help.txt") # The best help function
185                         return 0
186                 
187         # Dedicated server?
188         
189         #debug("server_addr = " + str(server_addr))
190         
191         if server_addr != None:
192                 if server_addr == True:
193                         return dedicated_server()
194                 else:
195                         if len(players) > 1:
196                                 sys.stderr.write("Only a single player may be provided when --server is used\n")
197                                 return 1
198                         if len(players) == 1:
199                                 return client(server_addr, players[0])
200                         else:
201                                 return client(server_addr)
202                 
203         for i in xrange(len(players)):
204                 p = make_player(players[i], colour)
205                 if not isinstance(p, Player):
206                         sys.stderr.write(sys.argv[0] + " : Fatal error creating " + colour + " player\n")
207                         return 100
208                 players[i] = p
209                 if colour == "white":
210                         colour = "black"
211                 elif colour == "black":
212                         pass
213                 else:
214                         sys.stderr.write(sys.argv[0] + " : Too many players (max 2)\n")
215                 
216         # Create the board
217         
218         # Construct a GameThread! Make it global! Damn the consequences!
219                         
220         if src_file != None:
221                 # Hack to stop ReplayThread from exiting
222                 #if len(players) == 0:
223                 #       players = [HumanPlayer("dummy", "white"), HumanPlayer("dummy", "black")]
224
225                 # Normally the ReplayThread exits if there are no players
226                 # TODO: Decide which behaviour to use, and fix it
227                 end = (len(players) == 0)
228                 if end:
229                         players = [Player("dummy", "white"), Player("dummy", "black")]
230                 elif len(players) != 2:
231                         sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n")
232                         if graphics_enabled:
233                                 sys.stderr.write(sys.argv[0] + " : (You won't get a GUI, because --file was used, and the author is lazy)\n")
234                         return 44
235                 game = ReplayThread(players, src_file, end=end, max_moves=max_moves)
236         else:
237                 board = Board(style)
238                 board.max_moves = max_moves
239                 game = GameThread(board, players) 
240                 game.retry_illegal = retry_illegal
241
242
243
244
245         # Initialise GUI
246         if graphics_enabled == True:
247                 try:
248                         graphics = GraphicsThread(game.board, grid_sz = [64,64]) # Construct a GraphicsThread!
249                         
250                         graphics.sleep_timeout = sleep_timeout
251
252                 except Exception,e:
253                         graphics = None
254                         sys.stderr.write(sys.argv[0] + " : Got exception trying to initialise graphics\n"+str(e.message)+"\nDisabled graphics\n")
255                         graphics_enabled = False
256
257         # If there are no players listed, display a nice pretty menu
258         if len(players) != 2:
259                 if graphics != None:
260                         
261                         server_addr = graphics.SelectServer()
262                         if server_addr != None:
263                                 pygame.quit() # Time to say goodbye
264                                 if server_addr == True:
265                                         return dedicated_server()
266                                 else:
267                                         return client(server_addr)      
268                         
269                         players = graphics.SelectPlayers(players)
270                 else:
271                         sys.stderr.write(sys.argv[0] + " : Usage " + sys.argv[0] + " white black\n")
272                         return 44
273
274         # If there are still no players, quit
275         if players == None or len(players) != 2:
276                 sys.stderr.write(sys.argv[0] + " : Graphics window closed before players chosen\n")
277                 return 45
278
279         old = players[:]
280         for p in old:
281                 if isinstance(p, NetworkPlayer):
282                         for i in range(len(old)):
283                                 if old[i] == p or isinstance(old[i], NetworkPlayer):
284                                         continue
285                                 players[i] = NetworkPlayer(old[i].colour, p.network, old[i])
286                 
287         for p in players:
288                 #debug(str(p))
289                 if isinstance(p, NetworkPlayer):
290                         p.board = game.board
291                         if not p.network.connected:
292                                 if not p.network.server:
293                                         time.sleep(0.2)
294                                 p.network.connect()
295                                 
296         
297         # If using windows, select won't work; use horrible TimeoutPlayer hack
298         if agent_timeout > 0:
299                 if platform.system() == "Windows":
300                         for i in range(len(players)):
301                                 if isinstance(players[i], ExternalAgent) or isinstance(players[i], InternalAgent):
302                                         players[i] = TimeoutPlayer(players[i], agent_timeout)
303
304                 else:
305                         warned = False
306                         # InternalAgents get wrapped to an ExternalAgent when there is a timeout
307                         # This is not confusing at all.
308                         for i in range(len(players)):
309                                 if isinstance(players[i], InternalAgent):
310                                                 players[i] = ExternalWrapper(players[i])
311
312
313                 
314
315
316
317
318         log_init(game.board, players)
319         
320         
321         if graphics != None:
322                 game.start() # This runs in a new thread
323                 graphics.run()
324                 if game.is_alive():
325                         game.join()
326         
327
328                 error = game.error + graphics.error
329         else:
330                 game.run()
331                 error = game.error
332         
333
334         for l in log_files:
335                 l.close()
336
337         if src_file != None and src_file != sys.stdin:
338                 src_file.close()
339
340         sys.stdout.write(game.final_result + "\n")
341
342         return error
343                 
344                 
345         
346                 
347         
348                 
349                 
350
351 # This is how python does a main() function...
352 if __name__ == "__main__":
353         retcode = 0
354         try:
355                 retcode = main(sys.argv)
356         except KeyboardInterrupt:
357                 sys.stderr.write(sys.argv[0] + " : Got KeyboardInterrupt. Stopping everything\n")
358                 if isinstance(graphics, StoppableThread):
359                         graphics.stop()
360                         graphics.run() # Will clean up graphics because it is stopped, not run it (a bit dodgy)
361
362                 if isinstance(game, StoppableThread):
363                         game.stop()
364                         if game.is_alive():
365                                 game.join()
366                 retcode = 102
367         #except Exception, e:
368         #       sys.stderr.write(sys.argv[0] + " : " + e.message + "\n")
369         #       retcode = 103   
370                 
371         try:
372                 sys.stdout.close()
373         except:
374                 pass
375         try:
376                 sys.stderr.close()
377         except:
378                 pass
379         sys.exit(retcode)
380                 
381

UCC git Repository :: git.ucc.asn.au