Self inflicted wounds using cx_freeze
[progcomp2013.git] / qchess / src / graphics.py
diff --git a/qchess/src/graphics.py b/qchess/src/graphics.py
new file mode 100644 (file)
index 0000000..c068900
--- /dev/null
@@ -0,0 +1,427 @@
+import pygame
+
+
+
+
+
+
+
+
+
+# A thread to make things pretty
+class GraphicsThread(StoppableThread):
+       def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]):
+               StoppableThread.__init__(self)
+               
+               self.board = board
+               pygame.init()
+               self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h))
+               pygame.display.set_caption(title)
+
+               #print "Initialised properly"
+               
+               self.grid_sz = grid_sz[:]
+               self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None}
+               self.error = 0
+               self.lock = threading.RLock()
+               self.cond = threading.Condition()
+
+               #print "Test font"
+               pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 32).render("Hello", True,(0,0,0))
+
+               #load_images()
+               create_images(grid_sz)
+
+               """
+               for c in images.keys():
+                       for p in images[c].keys():
+                               images[c][p] = images[c][p].convert(self.window)
+                               small_images[c][p] = small_images[c][p].convert(self.window)
+               """
+
+               
+       
+
+
+       # On the run from the world
+       def run(self):
+               
+               while not self.stopped():
+                       
+                       #print "Display grid"
+                       self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board
+
+                       #print "Display overlay"
+                       self.overlay()
+
+                       #print "Display pieces"
+                       self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board                
+
+                       pygame.display.flip()
+
+                       for event in pygame.event.get():
+                               if event.type == pygame.QUIT:
+                                       if isinstance(game, GameThread):
+                                               with game.lock:
+                                                       game.final_result = ""
+                                                       if game.state["turn"] != None:
+                                                               game.final_result = game.state["turn"].colour + " "
+                                                       game.final_result += "terminated"
+                                               game.stop()
+                                       self.stop()
+                                       break
+                               elif event.type == pygame.MOUSEBUTTONDOWN:
+                                       self.mouse_down(event)
+                               elif event.type == pygame.MOUSEBUTTONUP:
+                                       self.mouse_up(event)
+                                       
+
+                               
+                                                               
+                                               
+                                               
+               self.message("Game ends, result \""+str(game.final_result) + "\"")
+               time.sleep(1)
+
+               # Wake up anyone who is sleeping
+               self.cond.acquire()
+               self.cond.notify()
+               self.cond.release()
+
+               pygame.quit() # Time to say goodbye
+
+       # Mouse release event handler
+       def mouse_up(self, event):
+               if event.button == 3:
+                       with self.lock:
+                               self.state["overlay"] = None
+               elif event.button == 2:
+                       with self.lock:
+                               self.state["coverage"] = None   
+
+       # Mouse click event handler
+       def mouse_down(self, event):
+               if event.button == 1:
+                       m = [event.pos[i] / self.grid_sz[i] for i in range(2)]
+                       if isinstance(game, GameThread):
+                               with game.lock:
+                                       p = game.state["turn"]
+                       else:
+                                       p = None
+                                       
+                                       
+                       if isinstance(p, HumanPlayer):
+                               with self.lock:
+                                       s = self.board.grid[m[0]][m[1]]
+                                       select = self.state["select"]
+                               if select == None:
+                                       if s != None and s.colour != p.colour:
+                                               self.message("Wrong colour") # Look at all this user friendliness!
+                                               time.sleep(1)
+                                               return
+                                       # Notify human player of move
+                                       self.cond.acquire()
+                                       with self.lock:
+                                               self.state["select"] = s
+                                               self.state["dest"] = None
+                                       self.cond.notify()
+                                       self.cond.release()
+                                       return
+
+                               if select == None:
+                                       return
+                                               
+                                       
+                               if self.state["moves"] == None:
+                                       return
+
+                               if not m in self.state["moves"]:
+                                       self.message("Illegal Move") # I still think last year's mouse interface was adequate
+                                       time.sleep(2)
+                                       return
+                                               
+                               with self.lock:
+                                       if self.state["dest"] == None:
+                                               self.cond.acquire()
+                                               self.state["dest"] = m
+                                               self.state["select"] = None
+                                               self.state["moves"] = None
+                                               self.cond.notify()
+                                               self.cond.release()
+               elif event.button == 3:
+                       m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
+                       if isinstance(game, GameThread):
+                               with game.lock:
+                                       p = game.state["turn"]
+                       else:
+                               p = None
+                                       
+                                       
+                       if isinstance(p, HumanPlayer):
+                               with self.lock:
+                                       self.state["overlay"] = self.board.probability_grid(self.board.grid[m[0]][m[1]])
+
+               elif event.button == 2:
+                       m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
+                       if isinstance(game, GameThread):
+                               with game.lock:
+                                       p = game.state["turn"]
+                       else:
+                               p = None
+                       
+                       
+                       if isinstance(p, HumanPlayer):
+                               with self.lock:
+                                       self.state["coverage"] = self.board.coverage(m[0], m[1], None, self.state["select"])
+                               
+       # Draw the overlay
+       def overlay(self):
+
+               square_img = pygame.Surface((self.grid_sz[0], self.grid_sz[1]),pygame.SRCALPHA) # A square image
+               # Draw square over the selected piece
+               with self.lock:
+                       select = self.state["select"]
+               if select != None:
+                       mp = [self.grid_sz[i] * [select.x, select.y][i] for i in range(len(self.grid_sz))]
+                       square_img.fill(pygame.Color(0,255,0,64))
+                       self.window.blit(square_img, mp)
+               # If a piece is selected, draw all reachable squares
+               # (This quality user interface has been patented)
+               with self.lock:
+                       m = self.state["moves"]
+               if m != None:
+                       square_img.fill(pygame.Color(255,0,0,128)) # Draw them in blood red
+                       for move in m:
+                               mp = [self.grid_sz[i] * move[i] for i in range(2)]
+                               self.window.blit(square_img, mp)
+               # If a piece is overlayed, show all squares that it has a probability to reach
+               with self.lock:
+                       m = self.state["overlay"]
+               if m != None:
+                       for x in range(w):
+                               for y in range(h):
+                                       if m[x][y] > 0.0:
+                                               mp = [self.grid_sz[i] * [x,y][i] for i in range(2)]
+                                               square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple
+                                               self.window.blit(square_img, mp)
+                                               font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14)
+                                               text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0))
+                                               self.window.blit(text, mp)
+                               
+               # If a square is selected, highlight all pieces that have a probability to reach it
+               with self.lock:                         
+                       m = self.state["coverage"]
+               if m != None:
+                       for p in m:
+                               mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)]
+                               square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue
+                               self.window.blit(square_img, mp)
+                               font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), 14)
+                               text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0))
+                               self.window.blit(text, mp)
+                       # Draw a square where the mouse is
+               # This also serves to indicate who's turn it is
+               
+               if isinstance(game, GameThread):
+                       with game.lock:
+                               turn = game.state["turn"]
+               else:
+                       turn = None
+
+               if isinstance(turn, HumanPlayer):
+                       mp = [self.grid_sz[i] * int(pygame.mouse.get_pos()[i] / self.grid_sz[i]) for i in range(2)]
+                       square_img.fill(pygame.Color(0,0,255,128))
+                       if turn.colour == "white":
+                               c = pygame.Color(255,255,255)
+                       else:
+                               c = pygame.Color(0,0,0)
+                       pygame.draw.rect(square_img, c, (0,0,self.grid_sz[0], self.grid_sz[1]), self.grid_sz[0]/10)
+                       self.window.blit(square_img, mp)
+
+       # Message in a bottle
+       def message(self, string, pos = None, colour = None, font_size = 20):
+               #print "Drawing message..."
+               font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size)
+               if colour == None:
+                       colour = pygame.Color(0,0,0)
+               
+               text = font.render(string, 1, colour)
+       
+
+               s = pygame.Surface((text.get_width(), text.get_height()), pygame.SRCALPHA)
+               s.fill(pygame.Color(128,128,128))
+
+               tmp = self.window.get_size()
+
+               if pos == None:
+                       pos = (tmp[0] / 2 - text.get_width() / 2, tmp[1] / 3 - text.get_height())
+               else:
+                       pos = (pos[0]*text.get_width() + tmp[0] / 2 - text.get_width() / 2, pos[1]*text.get_height() + tmp[1] / 3 - text.get_height())
+               
+
+               rect = (pos[0], pos[1], text.get_width(), text.get_height())
+       
+               pygame.draw.rect(self.window, pygame.Color(0,0,0), pygame.Rect(rect), 1)
+               self.window.blit(s, pos)
+               self.window.blit(text, pos)
+
+               pygame.display.flip()
+
+       def getstr(self, prompt = None):
+               s = pygame.Surface((self.window.get_width(), self.window.get_height()))
+               s.blit(self.window, (0,0))
+               result = ""
+
+               while True:
+                       #print "LOOP"
+                       if prompt != None:
+                               self.message(prompt)
+                               self.message(result, pos = (0, 1))
+       
+                       pygame.event.pump()
+                       for event in pygame.event.get():
+                               if event.type == pygame.QUIT:
+                                       return None
+                               if event.type == pygame.KEYDOWN:
+                                       if event.key == pygame.K_BACKSPACE:
+                                               result = result[0:len(result)-1]
+                                               self.window.blit(s, (0,0)) # Revert the display
+                                               continue
+                               
+                                               
+                                       try:
+                                               if event.unicode == '\r':
+                                                       return result
+                                       
+                                               result += str(event.unicode)
+                                       except:
+                                               continue
+
+
+       # Function to pick a button
+       def SelectButton(self, choices, prompt = None, font_size=20):
+
+               #print "Select button called!"
+               self.board.display_grid(self.window, self.grid_sz)
+               if prompt != None:
+                       self.message(prompt)
+               font = pygame.font.Font(os.path.join(os.path.curdir, "data", "DejaVuSans.ttf"), font_size)
+               targets = []
+               sz = self.window.get_size()
+
+               
+               for i in range(len(choices)):
+                       c = choices[i]
+                       
+                       text = font.render(c, 1, pygame.Color(0,0,0))
+                       p = (sz[0] / 2 - (1.5*text.get_width())/2, sz[1] / 2 +(i-1)*text.get_height()+(i*2))
+                       targets.append((p[0], p[1], p[0] + 1.5*text.get_width(), p[1] + text.get_height()))
+
+               while True:
+                       mp =pygame.mouse.get_pos()
+                       for i in range(len(choices)):
+                               c = choices[i]
+                               if mp[0] > targets[i][0] and mp[0] < targets[i][2] and mp[1] > targets[i][1] and mp[1] < targets[i][3]:
+                                       font_colour = pygame.Color(255,0,0)
+                                       box_colour = pygame.Color(0,0,255,128)
+                               else:
+                                       font_colour = pygame.Color(0,0,0)
+                                       box_colour = pygame.Color(128,128,128)
+                               
+                               text = font.render(c, 1, font_colour)
+                               s = pygame.Surface((text.get_width()*1.5, text.get_height()), pygame.SRCALPHA)
+                               s.fill(box_colour)
+                               pygame.draw.rect(s, (0,0,0), (0,0,1.5*text.get_width(), text.get_height()), self.grid_sz[0]/10)
+                               s.blit(text, ((text.get_width()*1.5)/2 - text.get_width()/2 ,0))
+                               self.window.blit(s, targets[i][0:2])
+                               
+       
+                       pygame.display.flip()
+
+                       for event in pygame.event.get():
+                               if event.type == pygame.QUIT:
+                                       return None
+                               elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
+                                       for i in range(len(targets)):
+                                               t = targets[i]
+                                               if event.pos[0] > t[0] and event.pos[0] < t[2]:
+                                                       if event.pos[1] > t[1] and event.pos[1] < t[3]:
+                                                               return i
+                                               #print "Reject " + str(i) + str(event.pos) + " vs " + str(t)
+               
+
+       # Function to pick players in a nice GUI way
+       def SelectPlayers(self, players = []):
+
+
+               #print "SelectPlayers called"
+               
+               missing = ["white", "black"]
+               for p in players:
+                       missing.remove(p.colour)
+
+               for colour in missing:
+                       
+                       
+                       choice = self.SelectButton(["human", "agent", "network"],prompt = "Choose " + str(colour) + " player")
+                       if choice == 0:
+                               players.append(HumanPlayer("human", colour))
+                       elif choice == 1:
+                               if True:
+                                       import Tkinter
+                                       from tkFileDialog import askopenfilename
+                                       root = Tkinter.Tk() # Need a root to make Tkinter behave
+                                       root.withdraw() # Some sort of magic incantation
+                                       path = askopenfilename(parent=root, initialdir="../agents",title=
+'Choose an agent.')
+                                       if path == "":
+                                               return self.SelectPlayers()
+                                       players.append(make_player(path, colour))       
+                               else:
+                                       print "Exception was " + str(e.message)
+                                       p = None
+                                       while p == None:
+                                               self.board.display_grid(self.window, self.grid_sz)
+                                               pygame.display.flip()
+                                               path = self.getstr(prompt = "Enter path:")
+                                               if path == None:
+                                                       return None
+
+                                               if path == "":
+                                                       return self.SelectPlayers()
+
+                                               try:
+                                                       p = make_player(path, colour)
+                                               except:
+                                                       self.board.display_grid(self.window, self.grid_sz)
+                                                       pygame.display.flip()
+                                                       self.message("Invalid path!")
+                                                       time.sleep(1)
+                                                       p = None
+                                       players.append(p)
+                       elif choice == 2:
+                               address = ""
+                               while address == "":
+                                       self.board.display_grid(self.window, self.grid_sz)
+                                       
+                                       address = self.getstr(prompt = "Address? (leave blank for server)")
+                                       if address == None:
+                                               return None
+                                       if address == "":
+                                               address = None
+                                               continue
+                                       try:
+                                               map(int, address.split("."))
+                                       except:
+                                               self.board.display_grid(self.window, self.grid_sz)
+                                               self.message("Invalid IPv4 address!")
+                                               address = ""
+
+                               players.append(NetworkReceiver(colour, address))
+                       else:
+                               return None
+               #print str(self) + ".SelectPlayers returns " + str(players)
+               return players
+                       
+                               
+                       

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