''' Purpose: simulate a digital version of a 15 puzzle ''' from PIL import Image import random def inbounds( spot ) : ''' Determines whether spot is a valid grid location for the 15 puzzle ''' pass def slide_black_tile_left() : ''' If legal, simulates sliding rightward the tile on BOARD that is to the left of the black tile spot: that is; the tile is overlaid on the black tile spot; the black tile is overlaid on the now available spot; the location of the black tile is updated ''' global BLACK_TILE_SPOT, BLACK_TILE pass def slide_black_tile_right() : ''' If legal, simulates sliding leftward the tile on BOARD that is to the right of the black tile spot. ''' global BLACK_TILE_SPOT, BLACK_TILE pass def slide_black_tile_down() : ''' If legal, simulates sliding upward the tile on BOARD that is below the black tile spot ''' global BLACK_TILE_SPOT, BLACK_TILE pass def slide_black_tile_up() : ''' If legal, simulates sliding downward the tile on BOARD that is above the black tile spot ''' global BLACK_TILE_SPOT, BLACK_TILE pass # globals def initialize_globals( image ) : ''' Initializes the global variables to appropriate values based on image image ''' global LINE_THICKNESS, GRID_COLOR, TILE_DIMENSIONS, BOARD, BLACK_TILE, BLACK_TILE_SPOT LINE_THICKNESS = 4 GRID_COLOR = 'SteelBlue' tw = image.width // 4 th = image.height // 4 TILE_DIMENSIONS = ( tw, th ) bw = 4 * tw + 5 * LINE_THICKNESS bh = 4 * th + 5 * LINE_THICKNESS BOARD = Image.new( 'RGB', ( bw, bh ), GRID_COLOR ) BLACK_TILE = Image.new( 'RGB', ( tw, th ), 'BLACK' ) BLACK_TILE_SPOT = ( 3, 3 ) def cleanup( image ) : ''' Returns a version of image image where the bottom right corner is replaced by a black tile ''' global TILE_DIMENSIONS, BLACK_TILE tw, th = TILE_DIMENSIONS nw, nh = 4 * tw, 4 * th box = ( 0, 0, nw, nh ) new_image = image.crop( box ) ( x, y ) = convert_spot_into_image_coordinate( ( 3, 3 ) ) new_image.paste( BLACK_TILE, (x, y ) ) new_image = new_image.convert( "RGB" ) return new_image def initialize_board( image ): ''' Initializes the board using image an already cleaned-up image image ''' for r in range( 0, 4 ) : for c in range( 0, 4 ) : g_spot = ( r, c ) tile = get_image_tile(image, g_spot ) overlay(tile, g_spot) overlay( BLACK_TILE, (3, 3) ) def scramble_board( image ) : ''' Assigns the non-BLACK tiles of image to random grid locations on the board ''' initialize_board( image ) # set up board standard_ordering = [ # define standard ordering (0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1), (0, 2), (1, 2), (2, 2), (3, 2), (0, 3), (1, 3), (2, 3), ] random_ordering = standard_ordering[ : ] # make a copy of that ordering random.shuffle( random_ordering ) # shuffle it for i in range( 0, 15 ) : # need to fill in all the board tiles g_spot = standard_ordering[ i ] # get spot on board to be tiled r_spot = random_ordering[ i ] # get spot on image to be the tile tile = get_image_tile( image, g_spot ) # get tile from image overlay(tile, r_spot) # paste tile onto board def get_image_tile( image, g_spot ) : ''' Returns a copy of the image tile at grid location g_spt ''' global TILE_DIMENSIONS # need to know tile size tw, th = TILE_DIMENSIONS # get tile width and height tile = Image.new( "RGB", ( tw, th ) ) # create a blank tile ulc = convert_spot_into_image_coordinate( g_spot ) # map spot to image location x1, y1 = ulc # get x- and y- values of the ulc spot x2, y2 = x1 + tw, y1 + th # set the lrc of tile box = ( x1, y1, x2, y2 ) # set box with ulc and lrc tile = image.crop( box ) # get the subimage that is our tile return tile # hand back the tile def get_board_tile( g_spot ) : ''' Returns a copy of the board tile at grid location g_spt ''' global TILE_DIMENSIONS # need to know tile size tw, th = TILE_DIMENSIONS # get tile width and height tile = Image.new( "RGB", ( tw, th ) ) # create a blank tile ulc = convert_spot_into_board_coordinate( g_spot ) # map spot to board location x1, y1 = ulc # get x- and y- values of the ulc spot x2, y2 = x1 + tw, y1 + th # set the lrc of tile box = ( x1, y1, x2, y2 ) # set box with ulc and lrc tile = BOARD.crop( box ) # get the subimage that is our tile return tile # hand back the tile def overlay( tile, g_spot ) : ''' Copies tile onto board starting at grid location g_spot ''' global BOARD ( x, y ) = convert_spot_into_board_coordinate( g_spot ) # get location of spot on board # to be copy target BOARD.paste( tile, ( x, y ) ) # paste tile at that spot def convert_spot_into_image_coordinate( g_spot ) : ''' Returns the image coordinate of the upper-left-hand corner of the tile at grid location g_spot ''' global TILE_DIMENSIONS # need to know tile size tw, th = TILE_DIMENSIONS # get tile width and height r, c = g_spot # get the row and column value of the spot x = r * tw # scale by tile size to the get y = c * th # the corresponding coordinate return ( x, y ) # return the coordinate def convert_spot_into_board_coordinate( g_spot ) : ''' Returns the board coordinate of the upper-left-hand corner of the tile at grid location g_spot ''' global LINE_THICKNESS # need to know thickness of lines # separating tiles global TILE_DIMENSIONS # need to know tile size tw, th = TILE_DIMENSIONS # get tile width and height r, c = g_spot # get the row and column value of the spot x = r * tw + ( r + 1 ) * LINE_THICKNESS # need to scale by tile size and number of y = c * th + ( c + 1 ) * LINE_THICKNESS # lines to left of tile return ( x, y ) # return the coordinate def setup( image ) : ''' Sets up the global variables. Gets a cleaned-up version of image (that is, an image whose lower-right-hand tile area is black. Initializes the board to a standard ordering. And, returns the cleaned-up image ''' initialize_globals( image ) image = cleanup( image ) initialize_board( image ) return image if ( __name__ == "__main__" ) : global BOARD import url random.seed( 1112 ) mandrill = 'http://www.cs.virginia.edu/~cs1112/images/mandrill/full.png' tj = 'http://www.cs.virginia.edu/~cs1112/images/tj.png' image = url.get_image( mandrill ) image = url.get_image( tj ) image = setup( image ) BOARD.show() scramble_board( image ) BOARD.show()