def run(self): while True: try: if self.check_win(): self.won() SCREEN.fill(WHITE) SCREEN.blit(BG, (INDENT_BOARD, INDENT_BOARD)) self.board.draw_board(self.colour_check) self.colour_check = (self.colour_check + 1) % FLASH_RATE self.score.draw() self.board.PLAYER_FIELD.draw() OUTPUT = self.board.ROLL_BUTTON.click() if OUTPUT is not None: self.board.dice_object.dice.roll_dice_gif(OUTPUT, self.IN, 900, 230) self.board.dice_object.display_dice(900, 230, self.connection.current_dice) pygame.display.update() for event in pygame.event.get(): if event.type == QUIT: self.terminate() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_a: self.board.move_piece(1, 1) if event.key == pygame.K_s: self.board.move_piece(4, 6) if event.key == pygame.K_d: self.board.move_piece(8, 1) if event.key == pygame.K_f: self.board.move_piece(12, 1) if event.key == pygame.K_g: self.board.move_piece(2, 1) if event.key == pygame.K_h: self.board.move_piece(3, 1) elif event.type == pygame.MOUSEBUTTONDOWN: if self.connection.my_player.turn_token: x, y = event.pos print(x, y) for num in range(self.connection.my_player.low_range, self.connection.my_player.low_range + 4): #e.g for "red" - range(0, 4), for "green" - range(4, 8) piece = self.connection.my_player.my_pieces[num - self.connection.my_player.low_range] #gets index 0-3 to use my_pieces. pos = piece.get_position() if piece.image.get_width() == 64: if pos is not None and piece.image.get_rect(topleft=(coOrds[pos][0]-7, coOrds[pos][1]-25)).collidepoint(x, y): #If you clicked a piece, move them (if you rolled) self.click_piece(num, piece) break elif piece.image.get_rect(topleft=(self.board.home_coords[num])).collidepoint(x, y) and self.connection.my_player.roll == 6: #If you clicked a piece in home and you rolled 6, move them out. self.board.move_piece(num, self.connection.my_player.roll) self.connection.send_out(num, self.connection.my_player.start) self.connection.my_player.turnstaken += 1 #Player sent piece out, increase turnstaken self.connection.my_player.roll = 0 #reset the dice so it can't be reused print("piece sent out - rolls:", self.connection.my_player.rollsleft, "-turnstaken:", self.connection.my_player.turnstaken) if self.connection.my_player.turnstaken >= 3: _thread.start_new_thread(self.connection.end_turn, ()) print("Home", piece.get_steps_from_start()) break else: if piece.image.get_rect(topleft=(coOrds[pos][0], coOrds[pos][1])).collidepoint(x, y): #If you clicked a piece, move them (if you rolled) self.click_piece(num, piece) break self.clock.tick(FPS) except pygame.error: continue
def draw(self): pygame.draw.rect(SCREEN, self._c, (self._x, self._y, self._w, self._h), self._s) small_text = pygame.font.Font("freesansbold.ttf", 20) text_surf, text_rect = self.text_objects(self._msg, small_text) text_rect.center = ((self._x + (self._w/2)), (self._y+(self._h/2))) SCREEN.blit(text_surf, text_rect)
def draw_arrows_and_stars(self): """Draws the arrows and stars on the board. It also calls the draw_boxes function. The arrows are drawn using pygames draw function. The arrow images are blit on to the board. """ pygame.draw.polygon(SCREEN, RED, (CENTRE, TOP_LEFT, BOTTOM_LEFT), 0) pygame.draw.polygon(SCREEN, GREEN, (CENTRE, TOP_LEFT, TOP_RIGHT), 0) pygame.draw.polygon(SCREEN, YELLOW, (CENTRE, TOP_RIGHT, BOTTOM_RIGHT), 0) pygame.draw.polygon(SCREEN, BLUE, (CENTRE, BOTTOM_LEFT, BOTTOM_RIGHT), 0) pygame.draw.polygon(SCREEN, BLACK, (CENTRE, TOP_LEFT, BOTTOM_LEFT), 1) pygame.draw.polygon(SCREEN, BLACK, (CENTRE, TOP_LEFT, TOP_RIGHT), 1) pygame.draw.polygon(SCREEN, BLACK, (CENTRE, TOP_RIGHT, BOTTOM_RIGHT), 1) pygame.draw.polygon(SCREEN, BLACK, (CENTRE, BOTTOM_LEFT, BOTTOM_RIGHT), 1) self.draw_boxes() #Print Arrows on SCREEN SCREEN.blit( UP_ARROW, (BOX_SIZE * 7 + INDENT_BOARD, BOX_SIZE * 14 + INDENT_BOARD)) SCREEN.blit(DOWN_ARROW, (BOX_SIZE * 7 + INDENT_BOARD, INDENT_BOARD)) SCREEN.blit(RIGHT_ARROW, (INDENT_BOARD, BOX_SIZE * 7 + INDENT_BOARD)) SCREEN.blit( LEFT_ARROW, (BOX_SIZE * 14 + INDENT_BOARD, BOX_SIZE * 7 + INDENT_BOARD))
def draw(self): mouse = pygame.mouse.get_pos() if self._x + self._w > mouse[0] > self._x and \ self._y + self._h > mouse[1] > self._y: pygame.draw.rect(SCREEN, self._ac, (self._x, self._y, self._w, self._h), self._s) else: pygame.draw.rect(SCREEN, self._c, (self._x, self._y, self._w, self._h), self._s) small_text = pygame.font.Font("freesansbold.ttf", 20) text_surf, text_rect = self.text_objects(self._msg, small_text) text_rect.center = ((self._x + (self._w/2)), (self._y+(self._h/2))) SCREEN.blit(text_surf, text_rect) return None
def draw_boxes(self): """Draws rectangles for each position on the board using the boc_pos list. It also blits star images on the board. """ box_pos = [[0, -1, -2, -3, -4, -5], [-11, -12, -13, -14, -15, 13], [-6, -7, -8, -9, -10, 26], [39, -16, -17, -18, -19, -20], [ 51, 1, 2, 3, 4, 18, 19, 20, 21, 22, 23, 50, 24, 49, 48, 47, 46, 45, 44, 30, 29, 28, 27, 26, 25, 11, 10, 9, 8, 7, 6, 5, 43, 42, 41, 40, 38, 37, 12, 14, 15, 16, 17, 31, 32, 33, 34, 35, 36 ], [8, 34, 21, 47]] for item in box_pos[0]: pygame.draw.rect( SCREEN, RED, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 0) pygame.draw.rect( SCREEN, BLACK, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 1) for item in box_pos[1]: pygame.draw.rect( SCREEN, GREEN, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 0) pygame.draw.rect( SCREEN, BLACK, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 1) for item in box_pos[2]: pygame.draw.rect( SCREEN, YELLOW, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 0) pygame.draw.rect( SCREEN, BLACK, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 1) for item in box_pos[3]: pygame.draw.rect( SCREEN, BLUE, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 0) pygame.draw.rect( SCREEN, BLACK, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 1) for item in box_pos[4]: pygame.draw.rect( SCREEN, BLACK, (coOrds[item][0], coOrds[item][1], BOX_SIZE, BOX_SIZE), 1) for item in box_pos[5]: SCREEN.blit(STAR, (coOrds[item][0], coOrds[item][1]))
def draw_pieces(self, home_coords, check=0): """Draws the pieces on the board. It goes through all the pieces on the board and draw it in their position. If there is a conflict their size changes. The pieces flash is they are movable depending on the dice roll. """ piece_in = {} temp = None for num in range(16): piece = self.ALL_PIECES[num] piece_pos = piece.get_position() # If pieces are movable flash orange. if piece.colour == self.current_player and piece.movable and check > FPS * 2: temp = piece.image if piece.image.get_width() != 64: piece.image = ORANGE_PIECE_32 else: piece.image = ORANGE_PIECE # If piece in home. if piece_pos is None: SCREEN.blit(piece.image, self.home_coords[num]) else: if piece.image.get_width() != 64: if piece.get_position() not in piece_in: piece_in[piece.get_position()] = 0 SCREEN.blit( piece.image, (coOrds[piece_pos][0], coOrds[piece_pos][1])) piece_in[piece.get_position()] += 1 elif piece_in[piece.get_position()] == 1: SCREEN.blit( piece.image, (coOrds[piece_pos][0] + 20, coOrds[piece_pos][1])) piece_in[piece.get_position()] += 1 else: SCREEN.blit(piece.image, (coOrds[piece_pos][0] + 10, coOrds[piece_pos][1] + 15)) else: SCREEN.blit( piece.image, (coOrds[piece_pos][0] - 7, coOrds[piece_pos][1] - 25)) if temp: piece.image = temp temp = None
def draw(self): # Draws the button # If the mouse is within the boundaries of the button it changes # to its active colour 'ac' mouse = pygame.mouse.get_pos() if self._x + self._w > mouse[0] > self._x and \ self._y + self._h > mouse[1] > self._y: pygame.draw.rect(SCREEN, self._ac, (self._x, self._y, self._w, self._h), self._s) else: pygame.draw.rect(SCREEN, self._c, (self._x, self._y, self._w, self._h), self._s) small_text = pygame.font.Font("freesansbold.ttf", 20) text_surf, text_rect = self.text_objects(self._msg, small_text) text_rect.center = ((self._x + (self._w / 2)), (self._y + (self._h / 2))) SCREEN.blit(text_surf, text_rect) return None
def draw_genie(self): """Draws the genie on the board depending on who has the genie. If no-one has the genie, a lamp is placed in the middle. """ if self.genie_owner == "red": SCREEN.blit( GENIE_SMALL, (self.home_coords[0][0] + 57, self.home_coords[0][1] + 89)) elif self.genie_owner == "green": SCREEN.blit( GENIE_SMALL, (self.home_coords[4][0] + 57, self.home_coords[4][1] + 89)) elif self.genie_owner == "yellow": SCREEN.blit( GENIE_SMALL, (self.home_coords[8][0] + 57, self.home_coords[8][1] + 89)) elif self.genie_owner == "blue": SCREEN.blit( GENIE_SMALL, (self.home_coords[12][0] + 57, self.home_coords[12][1] + 89)) else: SCREEN.blit(LAMP_SMALL, (CENTRE[0] - 37, CENTRE[1] - 37))
def show_start_screen(self): FPSCLOCK = pygame.time.Clock() title_font = pygame.font.SysFont("Arial", 100) colours = [RED, GREEN, YELLOW, BLUE] index = 0 while True: SCREEN.fill(WHITE) title_surf = title_font.render('Ludo!', True, colours[index]) title_surf_rect = title_surf.get_rect() title_surf_rect.center = (BOARD_WIDTH / 2, BOARD_HEIGHT / 2) SCREEN.blit(title_surf, title_surf_rect) if self.connection.my_player is not None: pygame.event.get() return if pygame.event.get(QUIT): self.terminate() return index = (index + 1) % 4 pygame.display.update() FPSCLOCK.tick(5)
def show_start_screen(self): """Shows the start screen whent game first starts. It shows the word LUDO in flashing colours. Once the player connects to the server, the screen goes away.""" FPSCLOCK = pygame.time.Clock() title_font = pygame.font.SysFont("Arial", 100) colours = [c.RED, c.GREEN, c.YELLOW, c.BLUE] index = 0 while True: SCREEN.fill(c.WHITE) title_surf = title_font.render('Ludo!', True, colours[index]) title_surf_rect = title_surf.get_rect() title_surf_rect.center = (c.BOARD_WIDTH / 2, c.BOARD_HEIGHT / 2) SCREEN.blit(title_surf, title_surf_rect) if self.connection.my_player is not None: pygame.event.get() return if pygame.event.get(pygame.QUIT): self.terminate() index = (index + 1) % 4 pygame.display.update() FPSCLOCK.tick(5)
def endScreen(all_pieces): pygame.display.set_caption("End Screen") clock = pygame.time.Clock() font = pygame.font.Font(None, 100) x = ScoreBoard(all_pieces) while not pygame.event.get(QUIT): SCREEN.fill(WHITE) text = font.render("Final Score:", True, GREEN) text_rect = text.get_rect() text_x = SCREEN.get_width() / 2 - text_rect.width / 2 text_y = SCREEN.get_height() / 2 - text_rect.height / 2 SCREEN.blit(text, [text_x, text_y]) x.draw(x=text_x, y=text_y + 80, w=200) font = pygame.font.SysFont("Comicsansms", 100) SCREEN.blit(text, [text_x, text_y]) pygame.display.flip() pygame.quit() exit(0)
def run(self): """This is the main game method. It draws the board, pieces and the buttons. It also shows the dice rolling animation. """ while True: try: SCREEN.fill(c.WHITE) SCREEN.blit(c.BG, (c.INDENT_BOARD, c.INDENT_BOARD)) self.board.draw_board(self.colour_check) self.colour_check = (self.colour_check + 1) % c.FLASH_RATE self.draw_scoreboard(self.all_pieces, 900, 500, 100, 30) self.board.PLAYER_FIELD.draw() OUTPUT = self.board.ROLL_BUTTON.click() if OUTPUT is not None: self.board.dice_object.dice.roll_dice_gif(OUTPUT, self.IN, 900, 230) self.board.dice_object.display_dice(900, 230, self.connection.current_dice) # draw remain time if not self.p.empty(): message = self.p.get() # receive a data and reset the timer if message != "time": self.text = self.font.render(message, True, (0, 128, 0)) SCREEN.blit(self.text, (900, 20)) pygame.display.update() for event in pygame.event.get(): if event.type == pygame.QUIT: self.terminate() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_a: self.board.move_piece(1, 1) if event.key == pygame.K_s: self.board.move_piece(4, 6) if event.key == pygame.K_d: self.board.move_piece(8, 1) if event.key == pygame.K_f: self.board.move_piece(12, 1) if event.key == pygame.K_g: self.board.move_piece(2, 1) if event.key == pygame.K_h: self.board.move_piece(3, 1) elif event.type == pygame.MOUSEBUTTONDOWN: if self.connection.my_player.turn_token is True and self.connection.my_player.diceroll_token is False: x, y = event.pos print(x, y) for num in range(self.connection.my_player.low_range, self.connection.my_player.low_range + 4): #e.g for "red" - range(0, 4), for "green" - range(4, 8) piece = self.connection.my_player.my_pieces[num - self.connection.my_player.low_range] #gets index 0-3 to use my_pieces. pos = piece.get_position() if piece.movable: if piece.image.get_width() == 64: if pos is not None and piece.image.get_rect(topleft=(coOrds[pos][0]-7, coOrds[pos][1]-25)).collidepoint(x, y): #If you clicked a piece, move them (if you rolled) self.click_piece(num, piece) break elif piece.image.get_rect(topleft=(self.board.home_coords[num])).collidepoint(x, y) and self.connection.my_player.roll == 6: #If you clicked a piece in home and you rolled 6, move them out. self.board.move_piece(num, self.connection.my_player.roll) self.connection.send_out(num, self.connection.my_player.start) self.connection.end_roll() print("Home", piece.get_steps_from_start()) break else: if piece.image.get_rect(topleft=(coOrds[pos][0], coOrds[pos][1])).collidepoint(x, y): #If you clicked a piece, move them (if you rolled) self.click_piece(num, piece) break self.clock.tick(c.FPS) except pygame.error as e: print(e) continue
def connection_handler(self): """This function controls all data received from the server, and updates the client-side program accoridng to the received JSON messages. When referring to JSON message comments, if the symbols <> are used, it implies that the data is dynamic, and what will be in there depends on the player colour, roll of the dice etc.""" colors = ["red", "green", "yellow", "blue"] while True: data = self.sock.recv(4096).decode() # decodes received data. print(data) msg = json.loads(data) self.q.put("already push a button" ) # tell the time out function to reset the time # Start implies it is the first message of the game. # The message comes in the form {"start":True,"Colour":<colour>} if "start" in msg: names = msg["names"] self.my_player = Player(msg["Colour"], names[colors.index(msg["Colour"])], self.ALL_PIECES, names) self.board.my_player = self.my_player print(self.my_player.name, self.my_player.colour) # Messages come of the form {"turn_token":True,"Colour":<colour>}. # This tells all games which player's turn it is. if "turnToken" in msg: # If msg["Colour"] is this client's colour, then it is their turn. if msg["Colour"] == self.my_player.colour: self.board.PLAYER_FIELD.set_msg("MY TURN") self.my_player.turn_token = True self.my_player.diceroll_token = True else: self.board.PLAYER_FIELD.set_msg(msg["Colour"] + "'s turn") self.current_player = msg["Colour"] self.board.current_player = msg["Colour"] # This message is a response to pressing the "ROLL" button. # It comes in the form {"dicenum":<number between 1-6>,"Colour":<colour>} if "dicenum" in msg: roll = msg["dicenum"] self.my_player.roll = roll # Assigns value of dice roll to self genie_status = msg[ "genie_result"] # genie_status is either "take", "return" or None if genie_status == "take" and self.board.genie_owner is None: # If you roll to take the genie and no one currently has it SCREEN.blit(GENIE_BIG, (950, 50)) self.board.genie_owner = msg["Colour"] # Take the genie for num in range((LOW_RANGES[msg["Colour"]]), (LOW_RANGES[msg["Colour"]]) + 4): self.ALL_PIECES[num].genie = True elif genie_status == "return" and self.board.genie_owner == msg[ "Colour"]: # If you roll to give back the genie and you own it SCREEN.blit(LAMP_BIG, (950, 50)) self.board.genie_owner = None # The genie goes back to the centre for num in range((LOW_RANGES[msg["Colour"]]), (LOW_RANGES[msg["Colour"]]) + 4): self.ALL_PIECES[num].genie = False self.current_dice = ROLL_TO_IMG[ roll] # updates the dice image. # If the dicenum is for this player, then react accordingly. if msg["Colour"] == self.my_player.colour: self.pieces_playable() # This message is broadcast by the server if a player sends out a piece from their home. # It comes in the form {"Sendout":<piece-number>,"pos":<startposition>} if "Sendout" in msg: piece = msg["Sendout"] pos = msg["pos"] self.ALL_PIECES[piece].set_position(pos) self.board.check_conflict(self.ALL_PIECES[piece]) if self.my_player.roll == 6: self.my_player.diceroll_token == True # This message is broadcast if a player moves a piece. # As the player moves it's own pieces, they only react to other if "Movement" in msg and msg["Colour"] != self.my_player.colour: # It comes in the form {"Movement":<piecenum>, # "Moveforward":<number-of-steps-to-move>,"Colour":<colour>} # player's movements. steps = msg["Moveforward"] num = msg["Movement"] self.board.move_piece(num, steps) if self.my_player.roll == 6: self.my_player.diceroll_token == True if "Player_Won" in msg: print(msg)
def connection_handler(self): """This function controls all data received from the server, and updates the client-side program accoridng to the received JSON messages. When referring to JSON message comments, if the symbols <> are used, it implies that the data is dynamic, and what will be in there depends on the player colour, roll of the dice etc.""" while True: data = self.sock.recv(4096).decode() # decodes received data. print(data) msg = json.loads(data) self.q.put("already push a button" ) # tell the time out function to reset the time # Start implies it is the first message of the game. # The message comes in the form {"start":True,"Colour":<colour>} if "start" in msg: self.my_player = Player(msg["Colour"], "", self.ALL_PIECES) self.board.my_player = self.my_player print(self.my_player.name, self.my_player.colour) # Messages come of the form {"turn_token":True,"Colour":<colour>}. # This tells all games which player's turn it is. if "turnToken" in msg: # If msg["Colour"] is this client's colour, then it is their turn. if msg["Colour"] == self.my_player.colour: self.board.PLAYER_FIELD.set_msg("MY TURN") self.my_player.turn_token = True self.my_player.diceroll_token = True self.my_player.rollsleft = 1 print("rolls:", self.my_player.rollsleft, "-turnstaken:", self.my_player.turnstaken) else: self.board.PLAYER_FIELD.set_msg(msg["Colour"] + "'s turn") self.current_player = msg["Colour"] self.board.current_player = msg["Colour"] # This message is a response to pressing the "ROLL" button. # It comes in the form {"dicenum":<number between 1-6>,"Colour":<colour>} if "dicenum" in msg: roll = msg["dicenum"] genie_status = msg[ "genie_result"] # genie_status is either "take", "return" or None if genie_status == "take" and self.board.genie_owner is None: # If you roll to take the genie and no one currently has it SCREEN.blit(GENIE_BIG, (950, 50)) self.board.genie_owner = msg["Colour"] # Take the genie for num in range((LOW_RANGES[msg["Colour"]]), (LOW_RANGES[msg["Colour"]]) + 4): self.ALL_PIECES[num].genie = True elif genie_status == "return" and self.board.genie_owner == msg[ "Colour"]: # If you roll to give back the genie and you own it SCREEN.blit(LAMP_BIG, (950, 50)) self.board.genie_owner = None # The genie goes back to the centre for num in range((LOW_RANGES[msg["Colour"]]), (LOW_RANGES[msg["Colour"]]) + 4): self.ALL_PIECES[num].genie = False self.current_dice = ROLL_TO_IMG[ roll] # updates the dice image. # If the dicenum is for this player, then react accordingly. if msg["Colour"] == self.my_player.colour: self.pieces_playable() # This message is broadcast by the server if a player sends out a piece from their home. # It comes in the form {"Sendout":<piece-number>,"pos":<startposition>} if "Sendout" in msg: piece = msg["Sendout"] pos = msg["pos"] self.ALL_PIECES[piece].set_position(pos) self.board.check_conflict(self.ALL_PIECES[piece]) if roll == 6: self.my_player.rollsleft += 1 # This message is broadcast if a player moves a piece. # As the player moves it's own pieces, they only react to other if "Movement" in msg and msg["Colour"] != self.my_player.colour: # It comes in the form {"Movement":<piecenum>, # "Moveforward":<number-of-steps-to-move>,"Colour":<colour>} # player's movements. steps = msg["Moveforward"] num = msg["Movement"] self.board.move_piece(num, steps) if roll == 6: self.my_player.rollsleft += 1 if "Player_Won" in msg: print(msg) if msg == "time is running out" and self.my_player.turn_token == True: # this is the time out function # either randomly pick a pieces or roll a print("success") print(self.my_player.rollsleft) if self.my_player.rollsleft == 1: self.board.dice_object.roll_dice() print("here") continue # send out a message and jump to the next loop self.pieces_playable() print(len(self.my_player.movable_pieces_array)) if len(self.my_player.movable_pieces_array) != 0: i = self.my_player.movable_pieces_array[randint( 0, len(self.my_player.movable_pieces_array) - 1)] print(i) self.my_player.turnstaken += 1 if self.ALL_PIECES[i].position == None: print("from home") self.send_out(i, self.my_player.start) self.my_player.roll = 0 self.ALL_PIECES[i].set_position(self.my_player.start) self.ALL_PIECES[i].steps_from_start = 0 print("piece sent out - rolls:", self.my_player.rollsleft, "-turnstaken:", self.my_player.turnstaken) if self.my_player.turnstaken >= 3: _thread.start_new_thread(self.end_turn, ()) print("endturn") else: self.board.move_piece(i, self.my_player.roll) print("from board") if self.my_player.roll != 0: self.my_player.turnstaken += 1 # Player moved piece, increase turnstaken print("piece moved after update rolls:", self.my_player.rollsleft, "-turnstaken:", self.my_player.turnstaken) self.send_movement(i, self.my_player.roll) if self.my_player.turnstaken == 3 or self.my_player.rollsleft == 0: # End turn if player has no rolls left, or they've already taken 3 turns. _thread.start_new_thread(self.end_turn, ()) else: self.my_player.roll = 0 continue else: _thread.start_new_thread(self.end_turn, ())
def display_dice(self, x, y, img): SCREEN.blit(img, (x, y))