1 graphics_enabled = True
5 os.environ["SDL_VIDEO_ALLOW_SCREENSAVER"] = "1"
7 graphics_enabled = False
13 # A thread to make things pretty
14 class GraphicsThread(StoppableThread):
15 def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]):
16 StoppableThread.__init__(self)
20 self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h))
21 pygame.display.set_caption(title)
23 #print "Initialised properly"
25 self.grid_sz = grid_sz[:]
26 self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None}
28 self.lock = threading.RLock()
29 self.cond = threading.Condition()
30 self.sleep_timeout = None
31 self.last_event = time.time()
35 pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 32).render("Hello", True,(0,0,0))
38 create_images(grid_sz)
41 for c in images.keys():
42 for p in images[c].keys():
43 images[c][p] = images[c][p].convert(self.window)
44 small_images[c][p] = small_images[c][p].convert(self.window)
51 # On the run from the world
54 while not self.stopped():
56 if self.sleep_timeout == None or (time.time() - self.last_event) < self.sleep_timeout:
59 self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board
61 #print "Display overlay"
64 #print "Display pieces"
65 self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board
68 elif pygame.mouse.get_focused() and not self.blackout:
69 os.system("xset dpms force off")
71 self.window.fill((0,0,0))
75 for event in pygame.event.get():
76 self.last_event = time.time()
77 if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_q):
78 if isinstance(game, GameThread):
80 game.final_result = ""
81 if game.state["turn"] != None:
82 game.final_result = game.state["turn"].colour + " "
83 game.final_result += "terminated"
87 elif event.type == pygame.MOUSEBUTTONDOWN:
88 self.mouse_down(event)
90 elif event.type == pygame.MOUSEBUTTONUP:
100 self.message("Game ends, result \""+str(game.final_result) + "\"")
103 # Wake up anyone who is sleeping
108 pygame.quit() # Time to say goodbye
110 # Mouse release event handler
111 def mouse_up(self, event):
112 if event.button == 3:
114 self.state["overlay"] = None
115 elif event.button == 2:
117 self.state["coverage"] = None
119 # Mouse click event handler
120 def mouse_down(self, event):
121 if event.button == 1:
122 m = [event.pos[i] / self.grid_sz[i] for i in range(2)]
123 if isinstance(game, GameThread):
125 p = game.state["turn"]
130 if isinstance(p, HumanPlayer):
132 s = self.board.grid[m[0]][m[1]]
133 select = self.state["select"]
135 if s != None and s.colour != p.colour:
136 self.message("Wrong colour") # Look at all this user friendliness!
139 # Notify human player of move
142 self.state["select"] = s
143 self.state["dest"] = None
152 if self.state["moves"] == None:
155 if not m in self.state["moves"]:
156 self.message("Illegal Move") # I still think last year's mouse interface was adequate
161 if self.state["dest"] == None:
163 self.state["dest"] = m
164 self.state["select"] = None
165 self.state["moves"] = None
168 elif event.button == 3:
169 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
170 if isinstance(game, GameThread):
172 p = game.state["turn"]
177 if isinstance(p, HumanPlayer):
179 self.state["overlay"] = self.board.probability_grid(self.board.grid[m[0]][m[1]])
181 elif event.button == 2:
182 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
183 if isinstance(game, GameThread):
185 p = game.state["turn"]
190 if isinstance(p, HumanPlayer):
192 self.state["coverage"] = self.board.coverage(m[0], m[1], None, self.state["select"])
197 square_img = pygame.Surface((self.grid_sz[0], self.grid_sz[1]),pygame.SRCALPHA) # A square image
198 # Draw square over the selected piece
200 select = self.state["select"]
202 mp = [self.grid_sz[i] * [select.x, select.y][i] for i in range(len(self.grid_sz))]
203 square_img.fill(pygame.Color(0,255,0,64))
204 self.window.blit(square_img, mp)
205 # If a piece is selected, draw all reachable squares
206 # (This quality user interface has been patented)
208 m = self.state["moves"]
210 square_img.fill(pygame.Color(255,0,0,128)) # Draw them in blood red
212 mp = [self.grid_sz[i] * move[i] for i in range(2)]
213 self.window.blit(square_img, mp)
214 # If a piece is overlayed, show all squares that it has a probability to reach
216 m = self.state["overlay"]
221 mp = [self.grid_sz[i] * [x,y][i] for i in range(2)]
222 square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple
223 self.window.blit(square_img, mp)
224 font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14)
225 text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0))
226 self.window.blit(text, mp)
228 # If a square is selected, highlight all pieces that have a probability to reach it
230 m = self.state["coverage"]
233 mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)]
234 square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue
235 self.window.blit(square_img, mp)
236 font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14)
237 text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0))
238 self.window.blit(text, mp)
239 # Draw a square where the mouse is
240 # This also serves to indicate who's turn it is
242 if isinstance(game, GameThread):
244 turn = game.state["turn"]
248 if isinstance(turn, HumanPlayer):
249 mp = [self.grid_sz[i] * int(pygame.mouse.get_pos()[i] / self.grid_sz[i]) for i in range(2)]
250 square_img.fill(pygame.Color(0,0,255,128))
251 if turn.colour == "white":
252 c = pygame.Color(255,255,255)
254 c = pygame.Color(0,0,0)
255 pygame.draw.rect(square_img, c, (0,0,self.grid_sz[0], self.grid_sz[1]), self.grid_sz[0]/10)
256 self.window.blit(square_img, mp)
258 # Message in a bottle
259 def message(self, string, pos = None, colour = None, font_size = 20):
260 #print "Drawing message..."
261 font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size)
263 colour = pygame.Color(0,0,0)
265 text = font.render(string, 1, colour)
268 s = pygame.Surface((text.get_width(), text.get_height()), pygame.SRCALPHA)
269 s.fill(pygame.Color(128,128,128))
271 tmp = self.window.get_size()
274 pos = (tmp[0] / 2 - text.get_width() / 2, tmp[1] / 3 - text.get_height())
276 pos = (pos[0]*text.get_width() + tmp[0] / 2 - text.get_width() / 2, pos[1]*text.get_height() + tmp[1] / 3 - text.get_height())
279 rect = (pos[0], pos[1], text.get_width(), text.get_height())
281 pygame.draw.rect(self.window, pygame.Color(0,0,0), pygame.Rect(rect), 1)
282 self.window.blit(s, pos)
283 self.window.blit(text, pos)
285 pygame.display.flip()
287 def getstr(self, prompt = None):
288 s = pygame.Surface((self.window.get_width(), self.window.get_height()))
289 s.blit(self.window, (0,0))
296 self.message(result, pos = (0, 1))
299 for event in pygame.event.get():
300 if event.type == pygame.QUIT:
302 if event.type == pygame.KEYDOWN:
303 if event.key == pygame.K_BACKSPACE:
304 result = result[0:len(result)-1]
305 self.window.blit(s, (0,0)) # Revert the display
310 if event.unicode == '\r':
313 result += str(event.unicode)
318 # Function to pick a button
319 def SelectButton(self, choices, prompt = None, font_size=20):
321 #print "Select button called!"
322 self.board.display_grid(self.window, self.grid_sz)
325 font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size)
327 sz = self.window.get_size()
330 for i in range(len(choices)):
333 text = font.render(c, 1, pygame.Color(0,0,0))
334 p = (sz[0] / 2 - (1.5*text.get_width())/2, sz[1] / 2 +(i-1)*text.get_height()+(i*2))
335 targets.append((p[0], p[1], p[0] + 1.5*text.get_width(), p[1] + text.get_height()))
338 mp =pygame.mouse.get_pos()
339 for i in range(len(choices)):
341 if mp[0] > targets[i][0] and mp[0] < targets[i][2] and mp[1] > targets[i][1] and mp[1] < targets[i][3]:
342 font_colour = pygame.Color(255,0,0)
343 box_colour = pygame.Color(0,0,255,128)
345 font_colour = pygame.Color(0,0,0)
346 box_colour = pygame.Color(128,128,128)
348 text = font.render(c, 1, font_colour)
349 s = pygame.Surface((text.get_width()*1.5, text.get_height()), pygame.SRCALPHA)
351 pygame.draw.rect(s, (0,0,0), (0,0,1.5*text.get_width(), text.get_height()), self.grid_sz[0]/10)
352 s.blit(text, ((text.get_width()*1.5)/2 - text.get_width()/2 ,0))
353 self.window.blit(s, targets[i][0:2])
356 pygame.display.flip()
358 for event in pygame.event.get():
359 if event.type == pygame.QUIT:
361 elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
362 for i in range(len(targets)):
364 if event.pos[0] > t[0] and event.pos[0] < t[2]:
365 if event.pos[1] > t[1] and event.pos[1] < t[3]:
367 #print "Reject " + str(i) + str(event.pos) + " vs " + str(t)
370 # Function to pick players in a nice GUI way
371 def SelectPlayers(self, players = []):
374 #print "SelectPlayers called"
376 missing = ["white", "black"]
378 missing.remove(p.colour)
380 for colour in missing:
383 choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player")
385 players.append(HumanPlayer("human", colour))
388 internal_agents = inspect.getmembers(sys.modules[__name__], inspect.isclass)
389 internal_agents = [x for x in internal_agents if issubclass(x[1], InternalAgent)]
390 internal_agents.remove(('InternalAgent', InternalAgent))
391 if len(internal_agents) > 0:
392 choice2 = self.SelectButton(["internal", "external"], prompt="Type of agent")
397 agent = internal_agents[self.SelectButton(map(lambda e : e[0], internal_agents), prompt="Choose internal agent")]
398 players.append(agent[1](agent[0], colour))
402 from tkFileDialog import askopenfilename
403 root = Tkinter.Tk() # Need a root to make Tkinter behave
404 root.withdraw() # Some sort of magic incantation
405 path = askopenfilename(parent=root, initialdir="../agents",title=
408 return self.SelectPlayers()
409 players.append(make_player(path, colour))
414 self.board.display_grid(self.window, self.grid_sz)
415 pygame.display.flip()
416 path = self.getstr(prompt = "Enter path:")
421 return self.SelectPlayers()
424 p = make_player(path, colour)
426 self.board.display_grid(self.window, self.grid_sz)
427 pygame.display.flip()
428 self.message("Invalid path!")
435 self.board.display_grid(self.window, self.grid_sz)
437 address = self.getstr(prompt = "Address? (leave blank for server)")
444 map(int, address.split("."))
446 self.board.display_grid(self.window, self.grid_sz)
447 self.message("Invalid IPv4 address!")
450 players.append(NetworkReceiver(colour, address))
453 #print str(self) + ".SelectPlayers returns " + str(players)