diff --git a/assets.py b/assets.py index 1c9c9f2..5957c80 100644 --- a/assets.py +++ b/assets.py @@ -6,5 +6,6 @@ disc_drop_1 = join("sounds", "disc_drop_1.wav") disc_drop_2 = join("sounds", "disc_drop_2.wav") red_coin = load(join("images", "redball90px.png")) +board_pattern = load(join("images", "board1_100px.png")) yellow_coin = load(join("images", "yellowball90px.png")) black_coin = load(join("images", "blackball91px.png")) diff --git a/connect_game.py b/connect_game.py index c5fe4a9..d7e5d65 100644 --- a/connect_game.py +++ b/connect_game.py @@ -50,6 +50,8 @@ def mouse_click(self, event: MouseClickEvent): if self.game_data.game_board.is_valid_location(col): row: int = self.game_data.game_board.get_next_open_row(col) + self.renderer.drop_piece(row, col, self.game_data.turn + 1) + self.game_data.last_move_row.append(row) self.game_data.last_move_col.append(col) self.game_data.game_board.drop_piece(row, col, self.game_data.turn + 1) @@ -69,10 +71,10 @@ def mouse_click(self, event: MouseClickEvent): self.game_data.game_over = True pygame.display.update() - + self.game_data.turn += 1 self.game_data.turn = self.game_data.turn % 2 - + @bus.on("game:undo") def undo(self): """ @@ -89,6 +91,8 @@ def undo(self): self.game_data.turn += 1 self.game_data.turn = self.game_data.turn % 2 + pygame.draw.rect(self.renderer.screen, black, (0, 0, self.game_data.width, self.game_data.sq_size)) + self.renderer.draw_coin(self.renderer.game_data, self.renderer.game_data.posx - self.game_data.radius, self.game_data.margin) def update(self): """ @@ -114,4 +118,4 @@ def print_board(self): """ Prints the state of the board to the console. """ - self.game_data.game_board.print_board() + self.game_data.game_board.print_board() \ No newline at end of file diff --git a/game_board.py b/game_board.py index 73a4cd5..e1f1849 100644 --- a/game_board.py +++ b/game_board.py @@ -26,9 +26,9 @@ def print_board(self): """ Prints the state of the board to the console. """ - print(flip(self.board, 0)) - print(" ---------------------") - print(" " + str([1, 2, 3, 4, 5, 6, 7])) + #print(flip(self.board, 0)) + #print(" " + "---"*self.cols) + #print(" " + str([i+1 for i in range(self.cols)])) def drop_piece(self, row, col, piece): """ @@ -61,7 +61,6 @@ def check_square(self, piece, r, c): """ Checks if a particular square is a certain color. If the space is off of the board it returns False. - :param piece: The piece color to look for. :param r: The row to check. :param c: The column to check. @@ -153,4 +152,4 @@ def tie_move(self): if self.board[r][c] != 0: slots_filled += 1 - return slots_filled == 42 + return slots_filled == self.rows*self.cols \ No newline at end of file diff --git a/game_data.py b/game_data.py index a7ae2fc..0ec5043 100644 --- a/game_data.py +++ b/game_data.py @@ -1,6 +1,7 @@ from typing import Tuple from game_board import GameBoard +import pyautogui #For getting screen size class GameData: @@ -12,8 +13,12 @@ class GameData: height: int width: int sq_size: int + font_size: int + character_width: int + margin: int size: Tuple[int, int] game_over: bool + posx: int turn: int last_move_row: [int] last_move_col: [int] @@ -27,8 +32,15 @@ def __init__(self): self.game_board = GameBoard() self.action = None - self.sq_size: int = 100 - self.width: int = 7 * self.sq_size - self.height: int = 7 * self.sq_size + SCREEN_WIDTH, SCREEN_HEIGHT = pyautogui.size() #Resolution of the screen + + self.height: int = (SCREEN_HEIGHT*7)//10 + self.width: int = (SCREEN_WIDTH*8)//10 + self.sq_size: int = min(self.height//(self.game_board.rows), self.width//(self.game_board.cols), 100) + self.font_size = int(self.sq_size*0.8) + self.character_width = self.font_size*3//5 + self.margin: int = self.sq_size//20 + self.height: int = self.sq_size * (self.game_board.rows) + self.sq_size + self.width: int = self.sq_size * self.game_board.cols self.size: Tuple[int, int] = (self.width, self.height) - self.radius: int = int(self.sq_size / 2 - 5) + self.radius: int = int(self.sq_size / 2 - self.margin) diff --git a/game_renderer.py b/game_renderer.py index 465063b..1fc736d 100644 --- a/game_renderer.py +++ b/game_renderer.py @@ -2,13 +2,21 @@ from typing import Any, Optional, Union import pygame +import time from pygame import mixer from pygame.font import FontType from pygame.ftfont import Font from pygame.gfxdraw import aacircle, filled_circle -from assets import (black_coin, disc_drop_1, disc_drop_2, event_sound, - red_coin, yellow_coin) +from assets import ( + black_coin, + board_pattern, + disc_drop_1, + disc_drop_2, + event_sound, + red_coin, + yellow_coin, +) from config import black, blue, red, white, yellow from events import GameOver, MouseHoverEvent, PieceDropEvent, bus from game_data import GameData @@ -44,11 +52,14 @@ def __init__(self, screen, game_data: GameData): :param screen: The screen. :param game_data: All of the data for the game. """ - self.myfont = pygame.font.SysFont("monospace", 75) - self.label = self.myfont.render("CONNECT FOUR!!", 1, white) - screen.blit(self.label, (40, 10)) - self.screen = screen self.game_data = game_data + self.screen = screen + + self.myfont = pygame.font.SysFont("monospace", self.game_data.font_size) + message = "CONNECT FOUR!" + self.label = self.myfont.render(message, 1, white) + self.screen.blit(self.label, ((self.game_data.width - len(message)*self.game_data.character_width)//2, + self.game_data.margin)) pygame.display.set_caption("Connect Four | Mayank Singh") pygame.display.update() @@ -60,14 +71,14 @@ def on_mouse_move(self, event: MouseHoverEvent): :param event: Information about the hover, namely the x position """ posx = event.posx + self.game_data.posx = event.posx pygame.draw.rect( self.screen, black, (0, 0, self.game_data.width, self.game_data.sq_size) ) self.draw_coin( self.game_data, - posx - (self.game_data.sq_size / 2), - int(self.game_data.sq_size) - self.game_data.sq_size + 5, + posx - self.game_data.radius, self.game_data.margin, ) def draw_red_coin(self, x, y): @@ -76,7 +87,9 @@ def draw_red_coin(self, x, y): :param x: The x position to draw the coin. :param y: The y position to draw the coin. """ - self.screen.blit(red_coin, (x, y)) + radius = self.game_data.radius + sq_size = self.game_data.sq_size + self.screen.blit(pygame.transform.scale(red_coin, (2*radius, 2*radius)), (x, y)) def draw_yellow_coin(self, x, y): """ @@ -84,7 +97,9 @@ def draw_yellow_coin(self, x, y): :param x: The x position to draw the coin. :param y: The y position to draw the coin. """ - self.screen.blit(yellow_coin, (x, y)) + radius = self.game_data.radius + sq_size = self.game_data.sq_size + self.screen.blit(pygame.transform.scale(yellow_coin, (2*radius, 2*radius)), (x, y)) def draw_black_coin(self, x, y): """ @@ -104,46 +119,20 @@ def draw_coin(self, game_data, x, y): :param y: The y position for the coin to be drawn. """ if game_data.turn == 0: - self.screen.blit(red_coin, (x, y)) + radius = game_data.radius + sq_size = game_data.sq_size + self.screen.blit(pygame.transform.scale(red_coin, (2*radius, 2*radius)), (x, y)) else: - self.screen.blit(yellow_coin, (x, y)) + radius = game_data.radius + sq_size = game_data.sq_size + self.screen.blit(pygame.transform.scale(yellow_coin, (2*radius, 2*radius)), (x, y)) def draw(self, game_data: GameData): """ Draws the game state, including the board and the pieces. :param game_data: All of the data associated with the game. """ - if game_data.action == "undo": - filled_circle( - self.screen, - game_data.last_move_row, - game_data.last_move_col, - self.game_data.radius, - black, - ) - - aacircle( - self.screen, - game_data.last_move_row, - game_data.last_move_col, - self.game_data.radius, - black, - ) - - self.draw_black_coin( - game_data.last_move_col * self.game_data.sq_size + 5, - self.game_data.height - - ( - game_data.last_move_row * self.game_data.sq_size - + self.game_data.sq_size - - 5 - ), - ) - - game_data.game_board.print_board() - game_data.action = None - - self.draw_board(game_data.game_board) + self.draw_board(game_data) @bus.on("game:over") def on_game_over(self, event: GameOver): @@ -159,59 +148,99 @@ def on_game_over(self, event: GameOver): color = yellow if not event.was_tie: - self.label = self.myfont.render(f"PLAYER {event.winner} WINS!", 1, color) - self.screen.blit(self.label, (40, 10)) + message = f"PLAYER {event.winner} WINS!" + self.label = self.myfont.render(message, 1, color) + self.screen.blit(self.label, ((self.game_data.width - len(message)*self.game_data.character_width)//2, + self.game_data.margin)) mixer.music.load(event_sound) mixer.music.play(0) else: + message = "GAME DRAW!" + self.label = self.myfont.render(message, 1, white) + self.screen.blit(self.label, ((self.game_data.width - len(message)*self.game_data.character_width)//2, + self.game_data.margin)) + mixer.music.load(os.path.join("sounds", "event.ogg")) mixer.music.play(0) - self.myfont = pygame.font.SysFont("monospace", 75) - self.label = self.myfont.render("GAME DRAW !!!!", 1, white) - self.screen.blit(self.label, (40, 10)) - def draw_board(self, board): + def drop_piece(self, row, col, turn): """ - Draws the game board to the screen. - :param board: The game board. + Animates the falling piece + :param row: The row in which the piece will land + :param col: The column in which the piece will fall + :param turn: Which player's coin fell """ - sq_size = 100 - height = 700 - radius = int(sq_size / 2 - 5) + sq_size = self.game_data.sq_size + radius = self.game_data.radius - for c in range(board.cols): - for r in range(board.rows): + y = 0 + acc = self.game_data.height*8 + y_max = 0 + self.game_data.game_board.rows*sq_size - (row)*sq_size + delta_t = 0.005 + + t = 0 + while(t<10): + t += delta_t + y = 0.5*acc*(t**2) + if(y>=y_max): pygame.draw.rect( self.screen, - blue, - (c * sq_size, (r + 1) * sq_size, sq_size, sq_size), - ) - aacircle( - self.screen, - int(c * sq_size + sq_size / 2), - int((r + 1) * sq_size + sq_size / 2), - radius, black, + (0,0, self.game_data.width, sq_size) ) - filled_circle( - self.screen, - int(c * sq_size + sq_size / 2), - int((r + 1) * sq_size + sq_size / 2), - radius, - black, + break + time.sleep(delta_t) + pygame.draw.rect( + self.screen, + black, + (col*sq_size, 0, sq_size, self.game_data.game_board.rows*sq_size - (row)*sq_size) + ) + if turn==1: + self.draw_red_coin( + int(col * sq_size) + self.game_data.margin, int(y) + ) + + elif turn==2: + self.draw_yellow_coin( + int(col * sq_size) + self.game_data.margin, int(y) ) + + for r in range(self.game_data.game_board.rows - row): + self.screen.blit(pygame.transform.scale(board_pattern, (sq_size, sq_size)), (col*sq_size, (r+1)*sq_size)) + pygame.display.update() + + def draw_board(self, data): + """ + Draws the game board to the screen. + :param data: All data associated with the game. + """ + + board = data.game_board + + sq_size = data.sq_size + height = data.height + radius = data.radius + + pygame.draw.rect( + self.screen, + black, + (0, sq_size, self.game_data.width, self.game_data.height) + ) + for c in range(board.cols): + for r in range(board.rows): + self.screen.blit(pygame.transform.scale(board_pattern, (sq_size, sq_size)), (c*sq_size, (r+1)*sq_size)) for c in range(board.cols): for r in range(board.rows): if board.board[r][c] == 1: self.draw_red_coin( - int(c * sq_size) + 5, height - int(r * sq_size + sq_size - 5) + int(c * sq_size) + self.game_data.margin, height - int(r * sq_size + sq_size - self.game_data.margin) ) elif board.board[r][c] == 2: self.draw_yellow_coin( - int(c * sq_size) + 5, height - int(r * sq_size + sq_size - 5) + int(c * sq_size) + self.game_data.margin, height - int(r * sq_size + sq_size - self.game_data.margin) ) pygame.display.update() diff --git a/images/board1_100px.png b/images/board1_100px.png new file mode 100644 index 0000000..5b8dabd Binary files /dev/null and b/images/board1_100px.png differ diff --git a/requirements.txt b/requirements.txt index 4fbdf5c..06ccdb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pygame numpy -event-bus \ No newline at end of file +event-bus +pyautogui \ No newline at end of file