Commits

César Bustíos committed 2fadb20

Changing directory structure

  • Participants
  • Parent commits 3beacb6

Comments (0)

Files changed (2)

File geezle.py

-#-*- coding: utf-8 -*-
-
-##############################################################################
-#
-# This file is part of Geezle
-#
-# Copyright (C) 2012  César Bustíos Benites
-#
-# Geezle is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Geezle is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-#
-# Author: César Bustíos Benites <cesarbustios@gmail.com>
-# Email: cesarbustios@gmail.com
-# Google+: https://plus.google.com/u/0/103603507868093099204
-# License: GPL v3
-#
-# Geezle was based on Al Sweigart's game from the book "Making Games with
-# Python and Pygame"
-#
-##############################################################################
-
-import sys
-import os
-import random
-import pygame
-from pygame.locals import *
-
-
-# Paths
-PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
-IMAGES_PATH = os.path.join(PROJECT_PATH, 'data/images')
-FONTS_PATH = os.path.join(PROJECT_PATH, 'data/fonts')
-SOUNDS_PATH = os.path.join(PROJECT_PATH, 'data/sounds')
-UNREVEALED_BOX_IMG_PATH = os.path.join(IMAGES_PATH, 'unrevealed_logo1.png')
-BACKGROUND_IMG_PATH = os.path.join(IMAGES_PATH, 'memory_background.jpg')
-BACKGROUND_MUSIC_PATH = os.path.join(SOUNDS_PATH, 'memory_music.mp3')
-
-# Main screen resolution
-SCREEN_RESOLUTION = (800, 600)
-
-# Game and board settings
-WINDOW_WIDTH = 640
-WINDOW_HEIGHT = 480
-HALF_WIDTH = WINDOW_WIDTH / 2
-HALF_HEIGHT = WINDOW_HEIGHT / 2
-FPS = 30
-BOX_SIZE = 40
-BOX_GAP = 20
-BOARD_COLS = 9
-BOARD_ROWS = 6
-BOARD_WIDTH = (BOARD_COLS * BOX_SIZE) + ((BOARD_COLS - 1) * BOX_GAP)
-BOARD_HEIGHT = (BOARD_ROWS * BOX_SIZE) + ((BOARD_ROWS - 1) * BOX_GAP)
-IMAGES_NEEDED = (BOARD_COLS * BOARD_ROWS) / 2
-HIGHLIGHT_WIDTH = 3
-X_MARGIN = ((WINDOW_WIDTH - BOARD_WIDTH) / 3)
-Y_MARGIN = ((WINDOW_HEIGHT - BOARD_HEIGHT) / 3)
-
-# Colors
-WHITE = (255, 255, 255)
-BLACK = (0, 0, 0)
-BLUE = (25, 25, 255)
-GOLD = (205, 180, 12)
-HIGHLIGHT_COLOR = BLUE
-
-# Images
-UNREVEALED_BOX_IMG = pygame.image.load(UNREVEALED_BOX_IMG_PATH)
-BACKGROUND_IMG = pygame.image.load(BACKGROUND_IMG_PATH)
-
-pygame.init()
-SCREEN = pygame.display.set_mode(SCREEN_RESOLUTION)
-CLOCK = pygame.time.Clock()
-
-# Loading and playing background music:
-pygame.mixer.music.load(BACKGROUND_MUSIC_PATH)
-pygame.mixer.music.play(-1, 0.0)
-
-
-def main():
-    pygame.display.set_caption('Geezle - Memory Puzzle')
-
-    board = generate_board(False)
-    new_game = False
-
-    # Number of moves
-    score = 0
-
-    # Pixel coordinates for mouse events
-    x = 0
-    y = 0
-
-    # First selected logo [row, col]
-    first_choice = None
-
-    while True:
-        # New board?
-        if new_game:
-            board = generate_board(False)
-            new_game = False
-
-        # Background image
-        SCREEN.blit(BACKGROUND_IMG, (0, 0))
-        draw_table(WHITE, BLACK)
-
-        draw_board(board)
-        clicked = False
-
-        # Events handling
-        for event in pygame.event.get():
-            if event.type == QUIT:
-                pygame.quit()
-                sys.exit()
-            elif event.type == MOUSEMOTION:
-                x, y = event.pos
-            elif event.type == MOUSEBUTTONUP:
-                if event.button == 1:
-                    x, y = event.pos
-                    clicked = True
-
-        # Update and show current moves
-        update_score(str(score))
-
-        # New game, Quit, Sound, etc.
-        rect1, rect2 = display_ingame_options('pixel.ttf')
-        if clicked:
-            # New game?
-            if rect1.collidepoint(x, y):
-                new_game = True
-                score = 0
-            # Quit!!!???
-            elif rect2.collidepoint(x, y):
-                pygame.quit()
-                sys.exit()
-
-        row, col = get_box(board, x, y)
-        if row != None and col != None:
-            # Mouse is over a box
-            if not board['revealed'][row][col]:
-                highlight_box(board, row, col)
-
-            # Box is clicked
-            if not board['revealed'][row][col] and clicked:
-                reveal_box(board, row, col)
-                if first_choice == None:
-                    first_choice = (row, col)
-                else:
-                    logo1 = board['logos'][first_choice[0]][first_choice[1]]
-                    logo2 = board['logos'][row][col]
-
-                    # If your memory sucks, mark both as unrevealed
-                    if logo1 != logo2:
-                        pygame.time.delay(500)
-                        board['revealed'][first_choice[0]][first_choice[1]] = False
-                        board['revealed'][row][col] = False
-                    else:
-                        # TODO: Animate matched logos or something?
-                        pass
-
-                    score += 1
-                    first_choice = None
-
-        # Check if game is over
-        if all_matched(board['revealed']):
-            display_game_over_message('mk.ttf')
-
-        pygame.display.update()
-        CLOCK.tick(FPS)
-
-
-def animate_unrevealed(rect):
-    """
-    Scales the unrevealed image when clicked
-    """
-    img = UNREVEALED_BOX_IMG
-    height = img.get_height()
-    width = img.get_width()
-    x = rect.x
-    y = rect.y
-
-    while height >= 0 or width >= 0:
-        # Temporary surface for replacing previous unrevealed box image
-        tmp = pygame.surface.Surface((BOX_SIZE, BOX_SIZE))
-        tmp.fill(WHITE)
-        SCREEN.blit(tmp, (rect.x, rect.y))
-
-        # Scale and blit the image
-        img = pygame.transform.scale(img, (width, height))
-        SCREEN.blit(img, (x, y))
-        pygame.time.delay(25)
-        pygame.display.update()
-        height -= 5
-        width -= 5
-
-        # TODO: Calculate
-        x += 3.5
-        y += 3.5
-
-
-def highlight_box(board, row, col):
-    """
-    Highlights a box when mouse is over
-    """
-    rect = board['rects'][row][col]
-    r = pygame.rect.Rect(rect.left-5, rect.top-5, BOX_SIZE+10, BOX_SIZE+10)
-    pygame.draw.rect(SCREEN, HIGHLIGHT_COLOR, r, HIGHLIGHT_WIDTH)
-
-
-def get_box(board, x, y):
-    """
-    Return [row, col] position of box at pixel position (x, y) if found.
-    """
-    for row in range(BOARD_ROWS):
-        for col in range(BOARD_COLS):
-            rect = board['rects'][row][col]
-            if rect.collidepoint(x, y):
-                return row, col
-    return None, None
-
-
-def display_game_over_message(win_style):
-    """
-    Message for logo masters.
-    """
-    # Font
-    win_font = pygame.font.Font(os.path.join(FONTS_PATH, win_style), 96)
-
-    # Surface
-    win_surface = win_font.render('You win!', True, GOLD)
-
-    # Rect
-    win_rect = win_surface.get_rect()
-
-    # Position
-    win_rect.center = (HALF_WIDTH, HALF_HEIGHT)
-
-    SCREEN.blit(win_surface, win_rect)
-
-
-def display_ingame_options(font_style):
-    """
-    Returns options rects for colliding process
-    """
-    x_margin = 75
-    y_margin = 30
-
-    # Font
-    option_font = pygame.font.Font(os.path.join(FONTS_PATH, font_style), 20)
-
-    # Surfaces
-    option1_surface = option_font.render('New Game', True, GOLD)
-    option2_surface = option_font.render('Quit', True, GOLD)
-
-    # Rects
-    option1_rect = option1_surface.get_rect()
-    option2_rect = option2_surface.get_rect()
-
-    # Positions
-    option1_rect.center = (HALF_WIDTH - x_margin, WINDOW_HEIGHT - y_margin)
-    option2_rect.center = (HALF_WIDTH + x_margin, WINDOW_HEIGHT - y_margin)
-
-    # Show the options
-    SCREEN.blit(option1_surface, option1_rect)
-    SCREEN.blit(option2_surface, option2_rect)
-
-    return option1_rect, option2_rect
-
-
-def reveal_box(board, row, col):
-    """
-    Revealed a box when it is clicked
-    """
-    rect = board['rects'][row][col]
-
-    # Is it already revealed?
-    if board['revealed'][row][col] == True:
-        return
-
-    # Play awesome sound
-    sound_file = os.path.join(SOUNDS_PATH, 'reveal_box.wav')
-    sound = pygame.mixer.Sound(sound_file)
-    sound.play()
-
-    animate_unrevealed(rect)
-
-    # Mark box as revealed
-    board['revealed'][row][col] = True
-    SCREEN.blit(board['logos'][row][col], (rect.x, rect.y))
-    pygame.display.update()
-
-
-def all_matched(revealed_data):
-    """
-    Returns true if there are no signs of memory damage in your brain
-    """
-    for row in range(BOARD_ROWS):
-        if False in revealed_data[row]:
-            return False # Not yet buddy
-    # No signs of Alzheimer
-    return True
-
-
-def get_logos_images():
-    """
-    Returns a shuffled list of logos images
-    """
-    logos = []
-    logos_path = os.path.join(IMAGES_PATH, 'logos')
-
-    for img in os.listdir(logos_path):
-        try:
-            # Go Archie!
-            logos.append(pygame.image.load(os.path.join(logos_path, img)).convert_alpha())
-        except pygame.error, e:
-            continue
-    random.shuffle(logos)
-
-    return logos
-
-
-def generate_board(reveal=False):
-    """
-    Generate all the necessary data: the image logos, the rect that
-    represents each image on the surface and the revealed data.
-    Returns a dictionary containing all the information.
-    """
-    logos = [list() for r in range(BOARD_ROWS)]
-    rects = [list() for r in range(BOARD_ROWS)]
-    revealed = []
-
-    # Get the logos images
-    images = get_logos_images()[:IMAGES_NEEDED]
-    assert len(images) >= IMAGES_NEEDED, 'Not enough logos'
-    images *= 2
-    random.shuffle(images)
-
-    x = X_MARGIN
-    y = Y_MARGIN
-    for row in range(BOARD_ROWS):
-        for col in range(BOARD_COLS):
-            rect = images[0].get_rect()
-            rect.x = x
-            rect.y = y
-            rects[row].append(rect)
-            logos[row].append(images[0])
-            x += (BOX_SIZE + BOX_GAP)
-            del images[0]
-        x = X_MARGIN
-        y += (BOX_SIZE + BOX_GAP)
-
-    for i in range(BOARD_ROWS):
-        revealed.append([reveal] * BOARD_COLS)
-
-    # Everyday I'm shuffling
-    random.shuffle(images)
-
-    return {'logos': logos, 'rects': rects, 'revealed': revealed}
-
-
-def draw_board(board):
-    """
-    Draw the board according to the current revealed state
-    """
-    for row in range(BOARD_ROWS):
-        for col in range(BOARD_COLS):
-            rect = board['rects'][row][col]
-            x = rect.x
-            y = rect.y
-            if not board['revealed'][row][col]:
-                SCREEN.blit(UNREVEALED_BOX_IMG, (x, y))
-            else:
-                SCREEN.blit(board['logos'][row][col], (x, y))
-
-
-def draw_table(bg_color, border_color):
-    margin = 20
-
-    # Position and dimensions
-    x = X_MARGIN - margin
-    y = Y_MARGIN - margin
-    width = BOARD_WIDTH + margin * 2
-    height = BOARD_HEIGHT + margin * 2
-    pygame.draw.rect(SCREEN, bg_color, (x, y, width, height))
-
-    # Table border
-    width = 5
-    point_list = (
-        (x, y),
-        (x + BOARD_WIDTH + margin * 2, y),
-        (x + BOARD_WIDTH + margin * 2, y + BOARD_HEIGHT + margin * 2),
-        (x, y + BOARD_HEIGHT + margin * 2)
-    )
-    pygame.draw.polygon(SCREEN, BLACK, point_list, width)
-
-
-def update_score(score):
-    score_font = pygame.font.Font(os.path.join(FONTS_PATH, 'pixel.ttf'), 25)
-    score_text = 'Moves: %s' % str(score.zfill(3))
-    score_surface = score_font.render(score_text, True, GOLD)
-    score_rect = score_surface.get_rect()
-    score_rect.center = (HALF_WIDTH, 25)
-    SCREEN.blit(score_surface, score_rect)
-
-
-if __name__ == '__main__':
-    main()

File src/memory.py

+#-*- coding: utf-8 -*-
+
+##############################################################################
+#
+# This file is part of Geezle
+#
+# Copyright (C) 2012  César Bustíos Benites
+#
+# Geezle is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Geezle is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+#
+# Author: César Bustíos Benites <cesarbustios@gmail.com>
+# Email: cesarbustios@gmail.com
+# Google+: https://plus.google.com/u/0/103603507868093099204
+# License: GPL v3
+#
+# Geezle was based on Al Sweigart's game from the book "Making Games with
+# Python and Pygame"
+#
+##############################################################################
+
+import sys
+import random
+import os
+from os.path import abspath, dirname, join
+
+import pygame
+from pygame.locals import *
+
+
+# Paths
+PROJECT_PATH = abspath(dirname(__file__))
+IMAGES_PATH = join(PROJECT_PATH, 'data/images')
+FONTS_PATH = join(PROJECT_PATH, 'data/fonts')
+SOUNDS_PATH = join(PROJECT_PATH, 'data/sounds')
+UNREVEALED_BOX_IMG_PATH = join(IMAGES_PATH, 'unrevealed_logo1.png')
+BACKGROUND_IMG_PATH = join(IMAGES_PATH, 'memory_background.jpg')
+BACKGROUND_MUSIC_PATH = join(SOUNDS_PATH, 'memory_music.mp3')
+
+# Main screen resolution
+SCREEN_RESOLUTION = (800, 600)
+
+# Game and board settings
+WINDOW_WIDTH = 640
+WINDOW_HEIGHT = 480
+HALF_WIDTH = WINDOW_WIDTH / 2
+HALF_HEIGHT = WINDOW_HEIGHT / 2
+FPS = 30
+BOX_SIZE = 40
+BOX_GAP = 20
+BOARD_COLS = 9
+BOARD_ROWS = 6
+BOARD_WIDTH = (BOARD_COLS * BOX_SIZE) + ((BOARD_COLS - 1) * BOX_GAP)
+BOARD_HEIGHT = (BOARD_ROWS * BOX_SIZE) + ((BOARD_ROWS - 1) * BOX_GAP)
+IMAGES_NEEDED = (BOARD_COLS * BOARD_ROWS) / 2
+HIGHLIGHT_WIDTH = 3
+X_MARGIN = ((WINDOW_WIDTH - BOARD_WIDTH) / 3)
+Y_MARGIN = ((WINDOW_HEIGHT - BOARD_HEIGHT) / 3)
+
+# Colors
+WHITE = (255, 255, 255)
+BLACK = (0, 0, 0)
+BLUE = (25, 25, 255)
+GOLD = (205, 180, 12)
+HIGHLIGHT_COLOR = BLUE
+
+# Images
+UNREVEALED_BOX_IMG = pygame.image.load(UNREVEALED_BOX_IMG_PATH)
+BACKGROUND_IMG = pygame.image.load(BACKGROUND_IMG_PATH)
+
+pygame.init()
+SCREEN = pygame.display.set_mode(SCREEN_RESOLUTION)
+CLOCK = pygame.time.Clock()
+
+# Loading and playing background music:
+pygame.mixer.music.load(BACKGROUND_MUSIC_PATH)
+pygame.mixer.music.play(-1, 0.0)
+
+
+def main():
+    pygame.display.set_caption('Geezle - Memory Puzzle')
+
+    board = generate_board(False)
+    new_game = False
+
+    # Number of moves
+    score = 0
+
+    # Pixel coordinates for mouse events
+    x = 0
+    y = 0
+
+    # First selected logo [row, col]
+    first_choice = None
+
+    while True:
+        # New board?
+        if new_game:
+            board = generate_board(False)
+            new_game = False
+
+        # Background image
+        SCREEN.blit(BACKGROUND_IMG, (0, 0))
+        draw_table(WHITE, BLACK)
+
+        draw_board(board)
+        clicked = False
+
+        # Events handling
+        for event in pygame.event.get():
+            if event.type == QUIT:
+                pygame.quit()
+                sys.exit()
+            elif event.type == MOUSEMOTION:
+                x, y = event.pos
+            elif event.type == MOUSEBUTTONUP:
+                if event.button == 1:
+                    x, y = event.pos
+                    clicked = True
+
+        # Update and show current moves
+        update_score(str(score))
+
+        # New game, Quit, Sound, etc.
+        rect1, rect2 = display_ingame_options('pixel.ttf')
+        if clicked:
+            # New game?
+            if rect1.collidepoint(x, y):
+                new_game = True
+                score = 0
+            # Quit!!!???
+            elif rect2.collidepoint(x, y):
+                pygame.quit()
+                sys.exit()
+
+        row, col = get_box(board, x, y)
+        if row != None and col != None:
+            # Mouse is over a box
+            if not board['revealed'][row][col]:
+                highlight_box(board, row, col)
+
+            # Box is clicked
+            if not board['revealed'][row][col] and clicked:
+                reveal_box(board, row, col)
+                if first_choice == None:
+                    first_choice = (row, col)
+                else:
+                    logo1 = board['logos'][first_choice[0]][first_choice[1]]
+                    logo2 = board['logos'][row][col]
+
+                    # If your memory sucks, mark both as unrevealed
+                    if logo1 != logo2:
+                        pygame.time.delay(500)
+                        board['revealed'][first_choice[0]][first_choice[1]] = False
+                        board['revealed'][row][col] = False
+                    else:
+                        # TODO: Animate matched logos or something?
+                        pass
+
+                    score += 1
+                    first_choice = None
+
+        # Check if game is over
+        if all_matched(board['revealed']):
+            display_game_over_message('mk.ttf')
+
+        pygame.display.update()
+        CLOCK.tick(FPS)
+
+
+def animate_unrevealed(rect):
+    """
+    Scales the unrevealed image when clicked
+    """
+    img = UNREVEALED_BOX_IMG
+    height = img.get_height()
+    width = img.get_width()
+    x = rect.x
+    y = rect.y
+
+    while height >= 0 or width >= 0:
+        # Temporary surface for replacing previous unrevealed box image
+        tmp = pygame.surface.Surface((BOX_SIZE, BOX_SIZE))
+        tmp.fill(WHITE)
+        SCREEN.blit(tmp, (rect.x, rect.y))
+
+        # Scale and blit the image
+        img = pygame.transform.scale(img, (width, height))
+        SCREEN.blit(img, (x, y))
+        pygame.time.delay(25)
+        pygame.display.update()
+        height -= 5
+        width -= 5
+
+        # TODO: Calculate
+        x += 3.5
+        y += 3.5
+
+
+def highlight_box(board, row, col):
+    """
+    Highlights a box when mouse is over
+    """
+    rect = board['rects'][row][col]
+    r = pygame.rect.Rect(rect.left-5, rect.top-5, BOX_SIZE+10, BOX_SIZE+10)
+    pygame.draw.rect(SCREEN, HIGHLIGHT_COLOR, r, HIGHLIGHT_WIDTH)
+
+
+def get_box(board, x, y):
+    """
+    Return [row, col] position of box at pixel position (x, y) if found.
+    """
+    for row in range(BOARD_ROWS):
+        for col in range(BOARD_COLS):
+            rect = board['rects'][row][col]
+            if rect.collidepoint(x, y):
+                return row, col
+    return None, None
+
+
+def display_game_over_message(win_style):
+    """
+    Message for logo masters.
+    """
+    # Font
+    win_font = pygame.font.Font(join(FONTS_PATH, win_style), 96)
+
+    # Surface
+    win_surface = win_font.render('You win!', True, GOLD)
+
+    # Rect
+    win_rect = win_surface.get_rect()
+
+    # Position
+    win_rect.center = (HALF_WIDTH, HALF_HEIGHT)
+
+    SCREEN.blit(win_surface, win_rect)
+
+
+def display_ingame_options(font_style):
+    """
+    Returns options rects for colliding process
+    """
+    x_margin = 75
+    y_margin = 30
+
+    # Font
+    option_font = pygame.font.Font(join(FONTS_PATH, font_style), 20)
+
+    # Surfaces
+    option1_surface = option_font.render('New Game', True, GOLD)
+    option2_surface = option_font.render('Quit', True, GOLD)
+
+    # Rects
+    option1_rect = option1_surface.get_rect()
+    option2_rect = option2_surface.get_rect()
+
+    # Positions
+    option1_rect.center = (HALF_WIDTH - x_margin, WINDOW_HEIGHT - y_margin)
+    option2_rect.center = (HALF_WIDTH + x_margin, WINDOW_HEIGHT - y_margin)
+
+    # Show the options
+    SCREEN.blit(option1_surface, option1_rect)
+    SCREEN.blit(option2_surface, option2_rect)
+
+    return option1_rect, option2_rect
+
+
+def reveal_box(board, row, col):
+    """
+    Revealed a box when it is clicked
+    """
+    rect = board['rects'][row][col]
+
+    # Is it already revealed?
+    if board['revealed'][row][col] == True:
+        return
+
+    # Play awesome sound
+    sound_file = join(SOUNDS_PATH, 'reveal_box.wav')
+    sound = pygame.mixer.Sound(sound_file)
+    sound.play()
+
+    animate_unrevealed(rect)
+
+    # Mark box as revealed
+    board['revealed'][row][col] = True
+    SCREEN.blit(board['logos'][row][col], (rect.x, rect.y))
+    pygame.display.update()
+
+
+def all_matched(revealed_data):
+    """
+    Returns true if there are no signs of memory damage in your brain
+    """
+    for row in range(BOARD_ROWS):
+        if False in revealed_data[row]:
+            return False # Not yet buddy
+    # No signs of Alzheimer
+    return True
+
+
+def get_logos_images():
+    """
+    Returns a shuffled list of logos images
+    """
+    logos = []
+    logos_path = join(IMAGES_PATH, 'logos')
+
+    for img in os.listdir(logos_path):
+        try:
+            # Go Archie!
+            logos.append(pygame.image.load(join(logos_path, img)).convert_alpha())
+        except pygame.error, e:
+            continue
+    random.shuffle(logos)
+
+    return logos
+
+
+def generate_board(reveal=False):
+    """
+    Generate all the necessary data: the image logos, the rect that
+    represents each image on the surface and the revealed data.
+    Returns a dictionary containing all the information.
+    """
+    logos = [list() for r in range(BOARD_ROWS)]
+    rects = [list() for r in range(BOARD_ROWS)]
+    revealed = []
+
+    # Get the logos images
+    images = get_logos_images()[:IMAGES_NEEDED]
+    assert len(images) >= IMAGES_NEEDED, 'Not enough logos'
+    images *= 2
+    random.shuffle(images)
+
+    x = X_MARGIN
+    y = Y_MARGIN
+    for row in range(BOARD_ROWS):
+        for col in range(BOARD_COLS):
+            rect = images[0].get_rect()
+            rect.x = x
+            rect.y = y
+            rects[row].append(rect)
+            logos[row].append(images[0])
+            x += (BOX_SIZE + BOX_GAP)
+            del images[0]
+        x = X_MARGIN
+        y += (BOX_SIZE + BOX_GAP)
+
+    for i in range(BOARD_ROWS):
+        revealed.append([reveal] * BOARD_COLS)
+
+    # Everyday I'm shuffling
+    random.shuffle(images)
+
+    return {'logos': logos, 'rects': rects, 'revealed': revealed}
+
+
+def draw_board(board):
+    """
+    Draw the board according to the current revealed state
+    """
+    for row in range(BOARD_ROWS):
+        for col in range(BOARD_COLS):
+            rect = board['rects'][row][col]
+            x = rect.x
+            y = rect.y
+            if not board['revealed'][row][col]:
+                SCREEN.blit(UNREVEALED_BOX_IMG, (x, y))
+            else:
+                SCREEN.blit(board['logos'][row][col], (x, y))
+
+
+def draw_table(bg_color, border_color):
+    margin = 20
+
+    # Position and dimensions
+    x = X_MARGIN - margin
+    y = Y_MARGIN - margin
+    width = BOARD_WIDTH + margin * 2
+    height = BOARD_HEIGHT + margin * 2
+    pygame.draw.rect(SCREEN, bg_color, (x, y, width, height))
+
+    # Table border
+    width = 5
+    point_list = (
+        (x, y),
+        (x + BOARD_WIDTH + margin * 2, y),
+        (x + BOARD_WIDTH + margin * 2, y + BOARD_HEIGHT + margin * 2),
+        (x, y + BOARD_HEIGHT + margin * 2)
+    )
+    pygame.draw.polygon(SCREEN, BLACK, point_list, width)
+
+
+def update_score(score):
+    score_font = pygame.font.Font(join(FONTS_PATH, 'pixel.ttf'), 25)
+    score_text = 'Moves: %s' % str(score.zfill(3))
+    score_surface = score_font.render(score_text, True, GOLD)
+    score_rect = score_surface.get_rect()
+    score_rect.center = (HALF_WIDTH, 25)
+    SCREEN.blit(score_surface, score_rect)
+
+
+if __name__ == '__main__':
+    main()