2939e8901f5d7e26ba0677a727a19be763613f1b
[progcomp2013.git] / qchess / graphics.py
1 import pygame
2
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',
7                          "queen" : u'\u2655',
8                          "rook" : u'\u2656',
9                          "bishop" : u'\u2657',
10                          "knight" : u'\u2658',
11                          "pawn" : u'\u2659',
12                          "unknown" : '?'},
13                 "black" : {"king" : u'\u265A',
14                          "queen" : u'\u265B',
15                          "rook" : u'\u265C',
16                          "bishop" : u'\u265D',
17                          "knight" : u'\u265E',
18                          "pawn" : u'\u265F',
19                          "unknown" : '?'}}
20
21 images = {"white" : {}, "black" : {}}
22 small_images = {"white" : {}, "black" : {}}
23
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)
28                 
29                 self.board = board
30                 pygame.init()
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}
35                 self.error = 0
36                 self.lock = threading.RLock()
37                 self.cond = threading.Condition()
38
39                 # Get the font sizes
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():
43                         c = "black"
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))})
46                         c = "white"
47
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))
52
53                 
54         
55
56
57         # On the run from the world
58         def run(self):
59                 
60                 while not self.stopped():
61                         
62                         self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board
63
64                         self.overlay()
65
66                         self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board                
67
68                         pygame.display.flip()
69
70                         for event in pygame.event.get():
71                                 if event.type == pygame.QUIT:
72                                         if isinstance(game, GameThread):
73                                                 with game.lock:
74                                                         game.final_result = "terminated"
75                                                 game.stop()
76                                         self.stop()
77                                         break
78                                 elif event.type == pygame.MOUSEBUTTONDOWN:
79                                         self.mouse_down(event)
80                                 elif event.type == pygame.MOUSEBUTTONUP:
81                                         self.mouse_up(event)
82                                         
83
84                                 
85                                                                 
86                                                 
87                                                 
88                 self.message("Game ends, result \""+str(game.final_result) + "\"")
89                 time.sleep(1)
90
91                 # Wake up anyone who is sleeping
92                 self.cond.acquire()
93                 self.cond.notify()
94                 self.cond.release()
95
96                 pygame.quit() # Time to say goodbye
97
98         # Mouse release event handler
99         def mouse_up(self, event):
100                 if event.button == 3:
101                         with self.lock:
102                                 self.state["overlay"] = None
103                 elif event.button == 2:
104                         with self.lock:
105                                 self.state["coverage"] = None   
106
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):
112                                 with game.lock:
113                                         p = game.state["turn"]
114                         else:
115                                         p = None
116                                         
117                                         
118                         if isinstance(p, HumanPlayer):
119                                 with self.lock:
120                                         s = self.board.grid[m[0]][m[1]]
121                                         select = self.state["select"]
122                                 if select == None:
123                                         if s != None and s.colour != p.colour:
124                                                 self.message("Wrong colour") # Look at all this user friendliness!
125                                                 time.sleep(1)
126                                                 return
127                                         # Notify human player of move
128                                         self.cond.acquire()
129                                         with self.lock:
130                                                 self.state["select"] = s
131                                                 self.state["dest"] = None
132                                         self.cond.notify()
133                                         self.cond.release()
134                                         return
135
136                                 if select == None:
137                                         return
138                                                 
139                                         
140                                 if self.state["moves"] == None:
141                                         return
142
143                                 if not m in self.state["moves"]:
144                                         self.message("Illegal Move") # I still think last year's mouse interface was adequate
145                                         time.sleep(2)
146                                         return
147                                                 
148                                 with self.lock:
149                                         if self.state["dest"] == None:
150                                                 self.cond.acquire()
151                                                 self.state["dest"] = m
152                                                 self.state["select"] = None
153                                                 self.state["moves"] = None
154                                                 self.cond.notify()
155                                                 self.cond.release()
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):
159                                 with game.lock:
160                                         p = game.state["turn"]
161                         else:
162                                 p = None
163                                         
164                                         
165                         if isinstance(p, HumanPlayer):
166                                 with self.lock:
167                                         self.state["overlay"] = self.board.probability_grid(self.board.grid[m[0]][m[1]])
168
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):
172                                 with game.lock:
173                                         p = game.state["turn"]
174                         else:
175                                 p = None
176                         
177                         
178                         if isinstance(p, HumanPlayer):
179                                 with self.lock:
180                                         self.state["coverage"] = self.board.coverage(m[0], m[1], None, self.state["select"])
181                                 
182         # Draw the overlay
183         def overlay(self):
184
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
187                 with self.lock:
188                         select = self.state["select"]
189                 if select != None:
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)
195                 with self.lock:
196                         m = self.state["moves"]
197                 if m != None:
198                         square_img.fill(pygame.Color(255,0,0,128)) # Draw them in blood red
199                         for move in m:
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
203                 with self.lock:
204                         m = self.state["overlay"]
205                 if m != None:
206                         for x in range(w):
207                                 for y in range(h):
208                                         if m[x][y] > 0.0:
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)
215                                 
216                 # If a square is selected, highlight all pieces that have a probability to reach it
217                 with self.lock:                         
218                         m = self.state["coverage"]
219                 if m != None:
220                         for p in m:
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
229                 
230                 if isinstance(game, GameThread):
231                         with game.lock:
232                                 turn = game.state["turn"]
233                 else:
234                         turn = None
235
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)
241                         else:
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)
245
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)
249                 if colour == None:
250                         colour = pygame.Color(0,0,0)
251                 
252                 text = font.render(string, 1, colour)
253         
254
255                 s = pygame.Surface((text.get_width(), text.get_height()), pygame.SRCALPHA)
256                 s.fill(pygame.Color(128,128,128))
257
258                 tmp = self.window.get_size()
259
260                 if pos == None:
261                         pos = (tmp[0] / 2 - text.get_width() / 2, tmp[1] / 3 - text.get_height())
262                 else:
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())
264                 
265
266                 rect = (pos[0], pos[1], text.get_width(), text.get_height())
267         
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)
271
272                 pygame.display.flip()
273
274         def getstr(self, prompt = None):
275                 result = ""
276                 while True:
277                         #print "LOOP"
278                         if prompt != None:
279                                 self.message(prompt)
280                                 self.message(result, pos = (0, 1))
281         
282                         for event in pygame.event.get():
283                                 if event.type == pygame.KEYDOWN:
284                                         if chr(event.key) == '\r':
285                                                 return result
286                                         result += str(chr(event.key))

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