3 [w,h] = [8,8] # Width and height of board(s)
5 # Class to represent a quantum chess board
7 # Initialise; if master=True then the secondary piece types are assigned
8 # Otherwise, they are left as unknown
9 # So you can use this class in Agent programs, and fill in the types as they are revealed
10 def __init__(self, style="agent"):
12 self.pieces = {"white" : [], "black" : []}
13 self.grid = [[None] * w for _ in range(h)] # 2D List (you can get arrays in python, somehow, but they scare me)
14 self.unrevealed_types = {"white" : piece_types.copy(), "black" : piece_types.copy()}
15 self.king = {"white" : None, "black" : None} # We need to keep track of the king, because he is important
16 for c in ["black", "white"]:
17 del self.unrevealed_types[c]["unknown"]
19 # Add all the pieces with known primary types
22 s = ["black", "white"][i]
26 c.append(Piece(s, 0, y, ["rook"]))
27 c.append(Piece(s, 1, y, ["knight"]))
28 c.append(Piece(s, 2, y, ["bishop"]))
29 k = Piece(s, 3, y, ["king", "king"]) # There can only be one ruler!
30 k.types_revealed[1] = True
31 k.current_type = "king"
34 c.append(Piece(s, 4, y, ["queen"])) # Apparently he may have multiple wives though.
35 c.append(Piece(s, 5, y, ["bishop"]))
36 c.append(Piece(s, 6, y, ["knight"]))
37 c.append(Piece(s, 7, y, ["rook"]))
46 c.append(Piece(s, x, y, ["pawn"]))
49 types_left.update(piece_types)
50 del types_left["king"] # We don't want one of these randomly appearing (although it might make things interesting...)
51 del types_left["unknown"] # We certainly don't want these!
54 self.grid[piece.x][piece.y] = piece
56 if len(piece.types) > 1:
58 if style == "agent": # Assign placeholder "unknown" secondary type
59 piece.types.append("unknown")
62 elif style == "quantum":
63 # The master allocates the secondary types
64 choice = types_left.keys()[random.randint(0, len(types_left.keys())-1)]
65 types_left[choice] -= 1
66 if types_left[choice] <= 0:
67 del types_left[choice]
68 piece.types.append(choice)
69 elif style == "classical":
70 piece.types.append(piece.types[0])
71 piece.current_type = piece.types[0]
72 piece.types_revealed[1] = True
76 newboard = Board(master = False)
77 newpieces = newboard.pieces["white"] + newboard.pieces["black"]
78 mypieces = self.pieces["white"] + self.pieces["black"]
80 for i in range(len(mypieces)):
81 newpieces[i].init_from_copy(mypieces[i])
84 def display_grid(self, window = None, grid_sz = [80,80]):
86 return # I was considering implementing a text only display, then I thought "Fuck that"
88 # The indentation is getting seriously out of hand...
92 c = pygame.Color(200,200,200)
94 c = pygame.Color(64,64,64)
95 pygame.draw.rect(window, c, (x*grid_sz[0], y*grid_sz[1], (x+1)*grid_sz[0], (y+1)*grid_sz[1]))
97 def display_pieces(self, window = None, grid_sz = [80,80]):
100 for p in self.pieces["white"] + self.pieces["black"]:
101 p.draw(window, grid_sz)
103 # Draw the board in a pygame window
104 def display(self, window = None):
105 self.display_grid(window)
106 self.display_pieces(window)
114 if self.grid[x][y] == None:
116 if (self.grid[x][y].x != x or self.grid[x][y].y != y):
117 raise Exception(sys.argv[0] + ": MISMATCH " + str(self.grid[x][y]) + " should be at " + str(x) + "," + str(y))
119 # Select a piece on the board (colour is the colour of whoever is doing the selecting)
120 def select(self, x,y, colour=None):
121 if not self.on_board(x, y): # Get on board everyone!
122 raise Exception("BOUNDS")
124 piece = self.grid[x][y]
126 raise Exception("EMPTY")
128 if colour != None and piece.colour != colour:
129 raise Exception("COLOUR")
131 # I'm not quite sure why I made this return a string, but screw logical design
132 return str(x) + " " + str(y) + " " + str(piece.select()) + " " + str(piece.current_type)
135 # Update the board when a piece has been selected
136 # "type" is apparently reserved, so I'll use "state"
137 def update_select(self, x, y, type_index, state):
138 piece = self.grid[x][y]
139 if piece.types[type_index] == "unknown":
140 if not state in self.unrevealed_types[piece.colour].keys():
141 raise Exception("SANITY: Too many " + piece.colour + " " + state + "s")
142 self.unrevealed_types[piece.colour][state] -= 1
143 if self.unrevealed_types[piece.colour][state] <= 0:
144 del self.unrevealed_types[piece.colour][state]
146 piece.types[type_index] = state
147 piece.types_revealed[type_index] = True
148 piece.current_type = state
150 if len(self.possible_moves(piece)) <= 0:
151 piece.deselect() # Piece can't move; deselect it
153 # Update the board when a piece has been moved
154 def update_move(self, x, y, x2, y2):
155 piece = self.grid[x][y]
156 self.grid[x][y] = None
157 taken = self.grid[x2][y2]
159 if taken.current_type == "king":
160 self.king[taken.colour] = None
161 self.pieces[taken.colour].remove(taken)
162 self.grid[x2][y2] = piece
166 # If the piece is a pawn, and it reaches the final row, it becomes a queen
167 # I know you are supposed to get a choice
168 # But that would be effort
169 if piece.current_type == "pawn" and ((piece.colour == "white" and piece.y == 0) or (piece.colour == "black" and piece.y == h-1)):
170 if self.style == "classical":
171 piece.types[0] = "queen"
172 piece.types[1] = "queen"
174 piece.types[piece.choice] = "queen"
175 piece.current_type = "queen"
177 piece.deselect() # Uncollapse (?) the wavefunction!
180 # Update the board from a string
181 # Guesses what to do based on the format of the string
182 def update(self, result):
183 #print "Update called with \"" + str(result) + "\""
184 # String always starts with 'x y'
186 s = result.split(" ")
187 [x,y] = map(int, s[0:2])
189 raise Exception("GIBBERISH \""+ str(result) + "\"") # Raise expectations
191 piece = self.grid[x][y]
193 raise Exception("EMPTY")
195 # If a piece is being moved, the third token is '->'
196 # We could get away with just using four integers, but that wouldn't look as cool
198 # Last two tokens are the destination
200 [x2,y2] = map(int, s[3:])
202 raise Exception("GIBBERISH \"" + str(result) + "\"") # Raise the alarm
204 # Move the piece (take opponent if possible)
205 self.update_move(x, y, x2, y2)
208 # Otherwise we will just assume a piece has been selected
210 type_index = int(s[2]) # We need to know which of the two types the piece is in; that's the third token
211 state = s[3] # The last token is a string identifying the type
213 raise Exception("GIBBERISH \"" + result + "\"") # Throw a hissy fit
216 self.update_select(x, y, type_index, state)
220 # Gets each piece that could reach the given square and the probability that it could reach that square
221 # Will include allied pieces that defend the attacker
222 def coverage(self, x, y, colour = None, reject_allied = True):
226 pieces = self.pieces["white"] + self.pieces["black"]
228 pieces = self.pieces[colour]
231 prob = self.probability_grid(p, reject_allied)[x][y]
233 result.update({p : prob})
242 # Associates each square with a probability that the piece could move into it
243 # Look, I'm doing all the hard work for you here...
244 def probability_grid(self, p, reject_allied = True):
246 result = [[0.0] * w for _ in range(h)]
247 if not isinstance(p, Piece):
250 if p.current_type != "unknown":
251 #sys.stderr.write(sys.argv[0] + ": " + str(p) + " moves " + str(self.possible_moves(p, reject_allied)) + "\n")
252 for point in self.possible_moves(p, reject_allied):
253 result[point[0]][point[1]] = 1.0
257 for i in range(len(p.types)):
260 if t == "unknown" or p.types_revealed[i] == False:
262 for t2 in self.unrevealed_types[p.colour].keys():
263 total_types += self.unrevealed_types[p.colour][t2]
265 for t2 in self.unrevealed_types[p.colour].keys():
266 prob2 = float(self.unrevealed_types[p.colour][t2]) / float(total_types)
268 for point in self.possible_moves(p, reject_allied):
269 result[point[0]][point[1]] += prob2 * prob
273 for point in self.possible_moves(p, reject_allied):
274 result[point[0]][point[1]] += prob
277 p.current_type = "unknown"
280 def prob_is_type(self, p, state):
283 for i in range(len(p.types)):
288 if t == "unknown" or p.types_revealed[i] == False:
290 for t2 in self.unrevealed_types[p.colour].keys():
291 total_prob += self.unrevealed_types[p.colour][t2]
292 for t2 in self.unrevealed_types[p.colour].keys():
294 result += prob * float(self.unrevealed_types[p.colour][t2]) / float(total_prob)
298 # Get all squares that the piece could move into
299 # This is probably inefficient, but I looked at some sample chess games and they seem to actually do things this way
300 # reject_allied indicates whether squares occupied by allied pieces will be removed
301 # (set to false to check for defense)
302 def possible_moves(self, p, reject_allied = True):
308 if p.current_type == "unknown":
309 raise Exception("SANITY: Piece state unknown")
310 # The below commented out code causes things to break badly
315 # result += self.possible_moves(p)
316 #p.current_type = "unknown"
319 if p.current_type == "king":
320 result = [[p.x-1,p.y],[p.x+1,p.y],[p.x,p.y-1],[p.x,p.y+1], [p.x-1,p.y-1],[p.x-1,p.y+1],[p.x+1,p.y-1],[p.x+1,p.y+1]]
321 elif p.current_type == "queen":
322 for d in [[-1,0],[1,0],[0,-1],[0,1],[-1,-1],[-1,1],[1,-1],[1,1]]:
323 result += self.scan(p.x, p.y, d[0], d[1])
324 elif p.current_type == "bishop":
325 for d in [[-1,-1],[-1,1],[1,-1],[1,1]]: # There's a reason why bishops move diagonally
326 result += self.scan(p.x, p.y, d[0], d[1])
327 elif p.current_type == "rook":
328 for d in [[-1,0],[1,0],[0,-1],[0,1]]:
329 result += self.scan(p.x, p.y, d[0], d[1])
330 elif p.current_type == "knight":
331 # I would use two lines, but I'm not sure how python likes that
332 result = [[p.x-2, p.y-1], [p.x-2, p.y+1], [p.x+2, p.y-1], [p.x+2,p.y+1], [p.x-1,p.y-2], [p.x-1, p.y+2],[p.x+1,p.y-2],[p.x+1,p.y+2]]
333 elif p.current_type == "pawn":
334 if p.colour == "white":
336 # Pawn can't move forward into occupied square
337 if self.on_board(p.x, p.y-1) and self.grid[p.x][p.y-1] == None:
338 result = [[p.x,p.y-1]]
339 for f in [[p.x-1,p.y-1],[p.x+1,p.y-1]]:
340 if not self.on_board(f[0], f[1]):
342 if self.grid[f[0]][f[1]] != None: # Pawn can take diagonally
345 # Slightly embarrassing if the pawn jumps over someone on its first move...
346 if self.grid[p.x][p.y-1] == None and self.grid[p.x][p.y-2] == None:
347 result.append([p.x, p.y-2])
349 # Vice versa for the black pawn
350 if self.on_board(p.x, p.y+1) and self.grid[p.x][p.y+1] == None:
351 result = [[p.x,p.y+1]]
353 for f in [[p.x-1,p.y+1],[p.x+1,p.y+1]]:
354 if not self.on_board(f[0], f[1]):
356 if self.grid[f[0]][f[1]] != None:
357 #sys.stderr.write(sys.argv[0] + " : "+str(p) + " can take " + str(self.grid[f[0]][f[1]]) + "\n")
360 if self.grid[p.x][p.y+1] == None and self.grid[p.x][p.y+2] == None:
361 result.append([p.x, p.y+2])
363 #sys.stderr.write(sys.argv[0] + " : possible_moves for " + str(p) + " " + str(result) + "\n")
365 # Remove illegal moves
366 # Note: The result[:] creates a copy of result, so that the result.remove calls don't fuck things up
367 for point in result[:]:
369 if (point[0] < 0 or point[0] >= w) or (point[1] < 0 or point[1] >= h):
370 result.remove(point) # Remove locations outside the board
372 g = self.grid[point[0]][point[1]]
374 if g != None and (g.colour == p.colour and reject_allied == True):
375 result.remove(point) # Remove allied pieces
381 # Scans in a direction until it hits a piece, returns all squares in the line
382 # (includes the final square (which contains a piece), but not the original square)
383 def scan(self, x, y, vx, vy):
391 if not self.on_board(xx, yy):
395 g = self.grid[xx][yy]
403 # I typed the full statement about 30 times before writing this function...
404 def on_board(self, x, y):
405 return (x >= 0 and x < w) and (y >= 0 and y < h)
409 # A thread that runs the game
410 class GameThread(StoppableThread):
411 def __init__(self, board, players):
412 StoppableThread.__init__(self)
414 self.players = players
415 self.state = {"turn" : None} # The game state
416 self.error = 0 # Whether the thread exits with an error
417 self.lock = threading.RLock() #lock for access of self.state
418 self.cond = threading.Condition() # conditional for some reason, I forgot
419 self.final_result = ""
421 # Run the game (run in new thread with start(), run in current thread with run())
424 while not self.stopped():
426 for p in self.players:
428 self.state["turn"] = p # "turn" contains the player who's turn it is
431 [x,y] = p.select() # Player selects a square
435 result = self.board.select(x, y, colour = p.colour)
436 for p2 in self.players:
437 p2.update(result) # Inform players of what happened
441 target = self.board.grid[x][y]
442 if isinstance(graphics, GraphicsThread):
444 graphics.state["moves"] = self.board.possible_moves(target)
445 graphics.state["select"] = target
447 time.sleep(turn_delay)
450 if len(self.board.possible_moves(target)) == 0:
451 #print "Piece cannot move"
453 if isinstance(graphics, GraphicsThread):
455 graphics.state["moves"] = None
456 graphics.state["select"] = None
457 graphics.state["dest"] = None
461 [x2,y2] = p.get_move() # Player selects a destination
468 result = self.board.update_move(x, y, x2, y2)
469 for p2 in self.players:
470 p2.update(str(x) + " " + str(y) + " -> " + str(x2) + " " + str(y2)) # Inform players of what happened
472 if isinstance(graphics, GraphicsThread):
474 graphics.state["moves"] = [[x2,y2]]
476 time.sleep(turn_delay)
478 if isinstance(graphics, GraphicsThread):
480 graphics.state["select"] = None
481 graphics.state["dest"] = None
482 graphics.state["moves"] = None
484 # Commented out exception stuff for now, because it makes it impossible to tell if I made an IndentationError somewhere
486 #result = "ILLEGAL " + e.message
487 #sys.stderr.write(result + "\n")
491 # self.final_result = self.state["turn"].colour + " " + "ILLEGAL"
493 if self.board.king["black"] == None:
494 if self.board.king["white"] == None:
496 self.final_result = "DRAW"
499 self.final_result = "white"
501 elif self.board.king["white"] == None:
503 self.final_result = "black"
511 for p2 in self.players:
512 p2.quit(self.final_result)
519 def opponent(colour):
520 if colour == "white":
525 # +++ graphics.py +++ #
528 # Dictionary that stores the unicode character representations of the different pieces
529 # Chess was clearly the reason why unicode was invented
530 # For some reason none of the pygame chess implementations I found used them!
531 piece_char = {"white" : {"king" : u'\u2654',
534 "bishop" : u'\u2657',
535 "knight" : u'\u2658',
538 "black" : {"king" : u'\u265A',
541 "bishop" : u'\u265D',
542 "knight" : u'\u265E',
546 images = {"white" : {}, "black" : {}}
547 small_images = {"white" : {}, "black" : {}}
549 # A thread to make things pretty
550 class GraphicsThread(StoppableThread):
551 def __init__(self, board, title = "UCC::Progcomp 2013 - QChess", grid_sz = [80,80]):
552 StoppableThread.__init__(self)
556 self.window = pygame.display.set_mode((grid_sz[0] * w, grid_sz[1] * h))
557 pygame.display.set_caption(title)
558 self.grid_sz = grid_sz[:]
559 self.state = {"select" : None, "dest" : None, "moves" : None, "overlay" : None, "coverage" : None}
561 self.lock = threading.RLock()
562 self.cond = threading.Condition()
565 l_size = 5*(self.grid_sz[0] / 8)
566 s_size = 3*(self.grid_sz[0] / 8)
567 for p in piece_types.keys():
569 images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0))})
570 small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0))})
573 images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", l_size+1).render(piece_char["black"][p], True,(255,255,255))})
574 images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", l_size).render(piece_char[c][p], True,(0,0,0)),(0,0))
575 small_images[c].update({p : pygame.font.Font("data/DejaVuSans.ttf", s_size+1).render(piece_char["black"][p],True,(255,255,255))})
576 small_images[c][p].blit(pygame.font.Font("data/DejaVuSans.ttf", s_size).render(piece_char[c][p],True,(0,0,0)),(0,0))
582 # On the run from the world
585 while not self.stopped():
587 self.board.display_grid(window = self.window, grid_sz = self.grid_sz) # Draw the board
591 self.board.display_pieces(window = self.window, grid_sz = self.grid_sz) # Draw the board
593 pygame.display.flip()
595 for event in pygame.event.get():
596 if event.type == pygame.QUIT:
597 if isinstance(game, GameThread):
599 game.final_result = "terminated"
603 elif event.type == pygame.MOUSEBUTTONDOWN:
604 self.mouse_down(event)
605 elif event.type == pygame.MOUSEBUTTONUP:
613 self.message("Game ends, result \""+str(game.final_result) + "\"")
616 # Wake up anyone who is sleeping
621 pygame.quit() # Time to say goodbye
623 # Mouse release event handler
624 def mouse_up(self, event):
625 if event.button == 3:
627 self.state["overlay"] = None
628 elif event.button == 2:
630 self.state["coverage"] = None
632 # Mouse click event handler
633 def mouse_down(self, event):
634 if event.button == 1:
635 m = [event.pos[i] / self.grid_sz[i] for i in range(2)]
636 if isinstance(game, GameThread):
638 p = game.state["turn"]
643 if isinstance(p, HumanPlayer):
645 s = self.board.grid[m[0]][m[1]]
646 select = self.state["select"]
648 if s != None and s.colour != p.colour:
649 self.message("Wrong colour") # Look at all this user friendliness!
652 # Notify human player of move
655 self.state["select"] = s
656 self.state["dest"] = None
665 if self.state["moves"] == None:
668 if not m in self.state["moves"]:
669 self.message("Illegal Move") # I still think last year's mouse interface was adequate
674 if self.state["dest"] == None:
676 self.state["dest"] = m
677 self.state["select"] = None
678 self.state["moves"] = None
681 elif event.button == 3:
682 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
683 if isinstance(game, GameThread):
685 p = game.state["turn"]
690 if isinstance(p, HumanPlayer):
692 self.state["overlay"] = self.board.probability_grid(self.board.grid[m[0]][m[1]])
694 elif event.button == 2:
695 m = [event.pos[i] / self.grid_sz[i] for i in range(len(event.pos))]
696 if isinstance(game, GameThread):
698 p = game.state["turn"]
703 if isinstance(p, HumanPlayer):
705 self.state["coverage"] = self.board.coverage(m[0], m[1], None, self.state["select"])
710 square_img = pygame.Surface((self.grid_sz[0], self.grid_sz[1]),pygame.SRCALPHA) # A square image
711 # Draw square over the selected piece
713 select = self.state["select"]
715 mp = [self.grid_sz[i] * [select.x, select.y][i] for i in range(len(self.grid_sz))]
716 square_img.fill(pygame.Color(0,255,0,64))
717 self.window.blit(square_img, mp)
718 # If a piece is selected, draw all reachable squares
719 # (This quality user interface has been patented)
721 m = self.state["moves"]
723 square_img.fill(pygame.Color(255,0,0,128)) # Draw them in blood red
725 mp = [self.grid_sz[i] * move[i] for i in range(2)]
726 self.window.blit(square_img, mp)
727 # If a piece is overlayed, show all squares that it has a probability to reach
729 m = self.state["overlay"]
734 mp = [self.grid_sz[i] * [x,y][i] for i in range(2)]
735 square_img.fill(pygame.Color(255,0,255,int(m[x][y] * 128))) # Draw in purple
736 self.window.blit(square_img, mp)
737 font = pygame.font.Font(None, 14)
738 text = font.render("{0:.2f}".format(round(m[x][y],2)), 1, pygame.Color(0,0,0))
739 self.window.blit(text, mp)
741 # If a square is selected, highlight all pieces that have a probability to reach it
743 m = self.state["coverage"]
746 mp = [self.grid_sz[i] * [p.x,p.y][i] for i in range(2)]
747 square_img.fill(pygame.Color(0,255,255, int(m[p] * 196))) # Draw in pale blue
748 self.window.blit(square_img, mp)
749 font = pygame.font.Font(None, 14)
750 text = font.render("{0:.2f}".format(round(m[p],2)), 1, pygame.Color(0,0,0))
751 self.window.blit(text, mp)
752 # Draw a square where the mouse is
753 # This also serves to indicate who's turn it is
755 if isinstance(game, GameThread):
757 turn = game.state["turn"]
761 if isinstance(turn, HumanPlayer):
762 mp = [self.grid_sz[i] * int(pygame.mouse.get_pos()[i] / self.grid_sz[i]) for i in range(2)]
763 square_img.fill(pygame.Color(0,0,255,128))
764 if turn.colour == "white":
765 c = pygame.Color(255,255,255)
767 c = pygame.Color(0,0,0)
768 pygame.draw.rect(square_img, c, (0,0,self.grid_sz[0], self.grid_sz[1]), self.grid_sz[0]/10)
769 self.window.blit(square_img, mp)
771 # Message in a bottle
772 def message(self, string, pos = None, colour = None, font_size = 32):
773 font = pygame.font.Font(None, font_size)
775 colour = pygame.Color(0,0,0)
777 text = font.render(string, 1, colour)
780 s = pygame.Surface((text.get_width(), text.get_height()), pygame.SRCALPHA)
781 s.fill(pygame.Color(128,128,128))
783 tmp = self.window.get_size()
786 pos = (tmp[0] / 2 - text.get_width() / 2, tmp[1] / 3 - text.get_height())
788 pos = (pos[0]*text.get_width() + tmp[0] / 2 - text.get_width() / 2, pos[1]*text.get_height() + tmp[1] / 3 - text.get_height())
791 rect = (pos[0], pos[1], text.get_width(), text.get_height())
793 pygame.draw.rect(self.window, pygame.Color(0,0,0), pygame.Rect(rect), 1)
794 self.window.blit(s, pos)
795 self.window.blit(text, pos)
797 pygame.display.flip()
799 def getstr(self, prompt = None):
805 self.message(result, pos = (0, 1))
807 for event in pygame.event.get():
808 if event.type == pygame.KEYDOWN:
809 if chr(event.key) == '\r':
811 result += str(chr(event.key))
812 # --- graphics.py --- #
816 # Do you know what the -u does? It unbuffers stdin and stdout
817 # I can't remember why, but last year things broke without that
820 UCC::Progcomp 2013 Quantum Chess game
821 @author Sam Moore [SZM] "matches"
822 @copyright The University Computer Club, Incorporated
823 (ie: You can copy it for not for profit purposes)
826 # system python modules or whatever they are called
832 [game, graphics] = [None, None]
835 # The main function! It does the main stuff!
838 # Apparently python will silently treat things as local unless you do this
839 # But (here's the fun part), only if you actually modify the variable.
840 # For example, all those 'if graphics_enabled' conditions work in functions that never say it is global
841 # Anyone who says "You should never use a global variable" can die in a fire
845 # Magical argument parsing goes here
847 players = [HumanPlayer("saruman", "white"), AgentRandom("sabbath", "black")]
849 players = [AgentPlayer(argv[1], "white"), HumanPlayer("shadow", "black"), ]
851 players = [AgentPlayer(argv[1], "white"), AgentPlayer(argv[2], "black")]
853 # Construct the board!
854 board = Board(style = "quantum")
855 game = GameThread(board, players) # Construct a GameThread! Make it global! Damn the consequences!
858 graphics = GraphicsThread(board, grid_sz = [64,64]) # Construct a GraphicsThread! I KNOW WHAT I'M DOING! BEAR WITH ME!
859 game.start() # This runs in a new thread
861 # print "Run game in main thread"
862 # game.run() # Run game in the main thread (no need for joining)
864 #except Exception, e:
870 return game.error + graphics.error
873 # This is how python does a main() function...
874 if __name__ == "__main__":
875 sys.exit(main(sys.argv))
880 # I know using non-abreviated strings is inefficient, but this is python, who cares?
881 # Oh, yeah, this stores the number of pieces of each type in a normal chess game
882 piece_types = {"pawn" : 8, "bishop" : 2, "knight" : 2, "rook" : 2, "queen" : 1, "king" : 1, "unknown" : 0}
884 # Class to represent a quantum chess piece
886 def __init__(self, colour, x, y, types):
887 self.colour = colour # Colour (string) either "white" or "black"
888 self.x = x # x coordinate (0 - 8), none of this fancy 'a', 'b' shit here
889 self.y = y # y coordinate (0 - 8)
890 self.types = types # List of possible types the piece can be (should just be two)
891 self.current_type = "unknown" # Current type
892 self.choice = -1 # Index of the current type in self.types (-1 = unknown type)
893 self.types_revealed = [True, False] # Whether the types are known (by default the first type is always known at game start)
897 self.last_state = None
898 self.move_pattern = None
902 def init_from_copy(self, c):
903 self.colour = c.colour
906 self.types = c.types[:]
907 self.current_type = c.current_type
908 self.choice = c.choice
909 self.types_revealed = c.types_revealed[:]
911 self.last_state = None
912 self.move_pattern = None
916 # Make a string for the piece (used for debug)
918 return str(self.current_type) + " " + str(self.types) + " at " + str(self.x) + ","+str(self.y)
920 # Draw the piece in a pygame surface
921 def draw(self, window, grid_sz = [80,80]):
923 # First draw the image corresponding to self.current_type
924 img = images[self.colour][self.current_type]
925 rect = img.get_rect()
926 offset = [-rect.width/2,-3*rect.height/4]
927 window.blit(img, (self.x * grid_sz[0] + grid_sz[0]/2 + offset[0], self.y * grid_sz[1] + grid_sz[1]/2 + offset[1]))
930 # Draw the two possible types underneath the current_type image
931 for i in range(len(self.types)):
932 if self.types_revealed[i] == True:
933 img = small_images[self.colour][self.types[i]]
935 img = small_images[self.colour]["unknown"] # If the type hasn't been revealed, show a placeholder
938 rect = img.get_rect()
939 offset = [-rect.width/2,-rect.height/2]
942 target = (self.x * grid_sz[0] + grid_sz[0]/5 + offset[0], self.y * grid_sz[1] + 3*grid_sz[1]/4 + offset[1])
944 target = (self.x * grid_sz[0] + 4*grid_sz[0]/5 + offset[0], self.y * grid_sz[1] + 3*grid_sz[1]/4 + offset[1])
946 window.blit(img, target) # Blit shit
948 # Collapses the wave function!
950 if self.current_type == "unknown":
951 self.choice = random.randint(0,1)
952 self.current_type = self.types[self.choice]
953 self.types_revealed[self.choice] = True
956 # Uncollapses (?) the wave function!
958 #print "Deselect called"
959 if (self.x + self.y) % 2 != 0:
960 if (self.types[0] != self.types[1]) or (self.types_revealed[0] == False or self.types_revealed[1] == False):
961 self.current_type = "unknown"
964 self.choice = 0 # Both the two types are the same
966 # The sad moment when you realise that you do not understand anything about a subject you studied for 4 years...
968 # +++ player.py +++ #
973 # A player who can't play
975 def __init__(self, name, colour):
979 # Player that runs from another process
980 class AgentPlayer(Player):
981 def __init__(self, name, colour):
982 Player.__init__(self, name, colour)
983 self.p = subprocess.Popen(name, stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=sys.stderr)
985 self.p.stdin.write(colour + "\n")
987 raise Exception("UNRESPONSIVE")
992 self.p.stdin.write("SELECTION?\n")
993 line = self.p.stdout.readline().strip("\r\n ")
995 # raise Exception("UNRESPONSIVE")
997 result = map(int, line.split(" "))
999 raise Exception("GIBBERISH \"" + str(line) + "\"")
1002 def update(self, result):
1003 #print "Update " + str(result) + " called for AgentPlayer"
1005 self.p.stdin.write(result + "\n")
1007 # raise Exception("UNRESPONSIVE")
1012 self.p.stdin.write("MOVE?\n")
1013 line = self.p.stdout.readline().strip("\r\n ")
1015 raise Exception("UNRESPONSIVE")
1017 result = map(int, line.split(" "))
1019 raise Exception("GIBBERISH \"" + str(line) + "\"")
1022 def quit(self, final_result):
1024 self.p.stdin.write("QUIT " + final_result + "\n")
1028 # So you want to be a player here?
1029 class HumanPlayer(Player):
1030 def __init__(self, name, colour):
1031 Player.__init__(self, name, colour)
1033 # Select your preferred account
1035 if isinstance(graphics, GraphicsThread):
1036 # Basically, we let the graphics thread do some shit and then return that information to the game thread
1037 graphics.cond.acquire()
1038 # We wait for the graphics thread to select a piece
1039 while graphics.stopped() == False and graphics.state["select"] == None:
1040 graphics.cond.wait() # The difference between humans and machines is that humans sleep
1041 select = graphics.state["select"]
1044 graphics.cond.release()
1045 if graphics.stopped():
1047 return [select.x, select.y]
1049 # Since I don't display the board in this case, I'm not sure why I filled it in...
1051 sys.stdout.write("SELECTION?\n")
1053 p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
1055 sys.stderr.write("ILLEGAL GIBBERISH\n")
1057 # It's your move captain
1059 if isinstance(graphics, GraphicsThread):
1060 graphics.cond.acquire()
1061 while graphics.stopped() == False and graphics.state["dest"] == None:
1062 graphics.cond.wait()
1063 graphics.cond.release()
1065 return graphics.state["dest"]
1069 sys.stdout.write("MOVE?\n")
1071 p = map(int, sys.stdin.readline().strip("\r\n ").split(" "))
1073 sys.stderr.write("ILLEGAL GIBBERISH\n")
1076 # Are you sure you want to quit?
1077 def quit(self, final_result):
1078 sys.stdout.write("QUIT " + final_result + "\n")
1080 # Completely useless function
1081 def update(self, result):
1082 if isinstance(graphics, GraphicsThread):
1085 sys.stdout.write(result + "\n")
1088 # Player that makes random moves
1089 class AgentRandom(Player):
1090 def __init__(self, name, colour):
1091 Player.__init__(self, name, colour)
1094 self.board = Board(style = "agent")
1098 self.choice = self.board.pieces[self.colour][random.randint(0, len(self.board.pieces[self.colour])-1)]
1100 # Check that the piece has some possibility to move
1101 tmp = self.choice.current_type
1102 if tmp == "unknown": # For unknown pieces, try both types
1103 for t in self.choice.types:
1106 self.choice.current_type = t
1107 all_moves += self.board.possible_moves(self.choice)
1109 all_moves = self.board.possible_moves(self.choice)
1110 self.choice.current_type = tmp
1111 if len(all_moves) > 0:
1113 return [self.choice.x, self.choice.y]
1116 moves = self.board.possible_moves(self.choice)
1117 move = moves[random.randint(0, len(moves)-1)]
1120 def update(self, result):
1121 #sys.stderr.write(sys.argv[0] + " : Update board for AgentRandom\n")
1122 self.board.update(result)
1125 def quit(self, final_result):
1127 # --- player.py --- #
1128 # +++ thread_util.py +++ #
1131 # A thread that can be stopped!
1132 # Except it can only be stopped if it checks self.stopped() periodically
1133 # So it can sort of be stopped
1134 class StoppableThread(threading.Thread):
1136 threading.Thread.__init__(self)
1137 self._stop = threading.Event()
1143 return self._stop.isSet()
1144 # --- thread_util.py --- #