3 # Dictionary that stores the unicode character representations of the different pieces
4 # Chess was clearly the reason why unicode was invented
5 # For some reason none of the pygame chess implementations I found used them!
6 piece_char = {"white" : {"king" : u'\u2654',
13 "black" : {"king" : u'\u265A',
21 images = {"white" : {}, "black" : {}}
22 small_images = {"white" : {}, "black" : {}}
24 # A thread to make things pretty
25 class GraphicsThread(StoppableThread):
26 def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]):
27 StoppableThread.__init__(self)
31 self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h))
32 pygame.display.set_caption(title)
33 self.grid_sz = grid_sz[:]
34 self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None}
36 self.lock = threading.RLock()
37 self.cond = threading.Condition()
40 l_size = 5*(self.grid_sz[0] / 8)
41 s_size = 3*(self.grid_sz[0] / 8)
42 for p in piece_types.keys():
44 images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0))})
45 small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0))})
48 images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size+1).render(piece_char["black"][p], True,(255,255,255))})
49 images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0)),(0,0))
50 small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size+1).render(piece_char["black"][p],True,(255,255,255))})
51 small_images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0)),(0,0))
57 # On the run from the world
60 while not self.stopped():
62 self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board
66 self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board
70 for event in pygame.event.get():
71 if event.type == pygame.QUIT:
72 if isinstance(game, GameThread):
74 game.final_result = "terminated"
78 elif event.type == pygame.MOUSEBUTTONDOWN:
79 self.mouse_down(event)
80 elif event.type == pygame.MOUSEBUTTONUP:
88 self.message("Game ends, result \""+str(game.final_result) + "\"")
91 # Wake up anyone who is sleeping
96 pygame.quit() # Time to say goodbye
98 # Mouse release event handler
99 def mouse_up(self, event):
100 if event.button == 3:
102 self.state["overlay"] = None
103 elif event.button == 2:
105 self.state["coverage"] = None
107 # Mouse click event handler
108 def mouse_down(self, event):
109 if event.button == 1:
110 m = [event.pos[i] / self.grid_sz[i] for i in range(2)]
111 if isinstance(game, GameThread):
113 p = game.state["turn"]
118 if isinstance(p, HumanPlayer):
120 s = self.board.grid[m[0]][m[1]]
121 select = self.state["select"]
123 if s != None and s.colour != p.colour:
124 self.message("Wrong colour") # Look at all this user friendliness!
127 # Notify human player of move
130 self.state["select"] = s
131 self.state["dest"] = None
140 if self.state["moves"] == None:
143 if not m in self.state["moves"]:
144 self.message("Illegal Move") # I still think last year's mouse interface was adequate
149 if self.state["dest"] == None:
151 self.state["dest"] = m
152 self.state["select"] = None
153 self.state["moves"] = None
156 elif event.button == 3:
157 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
158 if isinstance(game, GameThread):
160 p = game.state["turn"]
165 if isinstance(p, HumanPlayer):
167 self.state["overlay"] = self.board.probability_grid(self.board.grid[m[0]][m[1]])
169 elif event.button == 2:
170 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
171 if isinstance(game, GameThread):
173 p = game.state["turn"]
178 if isinstance(p, HumanPlayer):
180 self.state["coverage"] = self.board.coverage(m[0], m[1], None, self.state["select"])
185 square_img = pygame.Surface((self.grid_sz[0], self.grid_sz[1]),pygame.SRCALPHA) # A square image
186 # Draw square over the selected piece
188 select = self.state["select"]
190 mp = [self.grid_sz[i] * [select.x, select.y][i] for i in range(len(self.grid_sz))]
191 square_img.fill(pygame.Color(0,255,0,64))
192 self.window.blit(square_img, mp)
193 # If a piece is selected, draw all reachable squares
194 # (This quality user interface has been patented)
196 m = self.state["moves"]
198 square_img.fill(pygame.Color(255,0,0,128)) # Draw them in blood red
200 mp = [self.grid_sz[i] * move[i] for i in range(2)]
201 self.window.blit(square_img, mp)
202 # If a piece is overlayed, show all squares that it has a probability to reach
204 m = self.state["overlay"]
209 mp = [self.grid_sz[i] * [x,y][i] for i in range(2)]
210 square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple
211 self.window.blit(square_img, mp)
212 font = pygame.font.Font(None, 14)
213 text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0))
214 self.window.blit(text, mp)
216 # If a square is selected, highlight all pieces that have a probability to reach it
218 m = self.state["coverage"]
221 mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)]
222 square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue
223 self.window.blit(square_img, mp)
224 font = pygame.font.Font(None, 14)
225 text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0))
226 self.window.blit(text, mp)
227 # Draw a square where the mouse is
228 # This also serves to indicate who's turn it is
230 if isinstance(game, GameThread):
232 turn = game.state["turn"]
236 if isinstance(turn, HumanPlayer):
237 mp = [self.grid_sz[i] * int(pygame.mouse.get_pos()[i] / self.grid_sz[i]) for i in range(2)]
238 square_img.fill(pygame.Color(0,0,255,128))
239 if turn.colour == "white":
240 c = pygame.Color(255,255,255)
242 c = pygame.Color(0,0,0)
243 pygame.draw.rect(square_img, c, (0,0,self.grid_sz[0], self.grid_sz[1]), self.grid_sz[0]/10)
244 self.window.blit(square_img, mp)
246 # Message in a bottle
247 def message(self, string, pos = None, colour = None, font_size = 32):
248 font = pygame.font.Font(None, font_size)
250 colour = pygame.Color(0,0,0)
252 text = font.render(string, 1, colour)
255 s = pygame.Surface((text.get_width(), text.get_height()), pygame.SRCALPHA)
256 s.fill(pygame.Color(128,128,128))
258 tmp = self.window.get_size()
261 pos = (tmp[0] / 2 - text.get_width() / 2, tmp[1] / 3 - text.get_height())
263 pos = (pos[0]*text.get_width() + tmp[0] / 2 - text.get_width() / 2, pos[1]*text.get_height() + tmp[1] / 3 - text.get_height())
266 rect = (pos[0], pos[1], text.get_width(), text.get_height())
268 pygame.draw.rect(self.window, pygame.Color(0,0,0), pygame.Rect(rect), 1)
269 self.window.blit(s, pos)
270 self.window.blit(text, pos)
272 pygame.display.flip()
274 def getstr(self, prompt = None):
280 self.message(result, pos = (0, 1))
282 for event in pygame.event.get():
283 if event.type == pygame.KEYDOWN:
284 if chr(event.key) == '\r':
286 result += str(chr(event.key))