The objective is to get the whole grid to be one color. You start at the top-left corner, red/orange here, and progress your way across by clicking the palette on the right. My next move was to click green, which earned me two more pieces. For an added challenge the player must do this in 25 (or fewer) turns.
It's difficult but it can be done:
I keep track of tiles the user gains in a list called "blob." The tiles adjacent to ones in blob are called "neighbors." When the user chooses a color, I iterate over each tile in blob and find all its neighbors (if valid: corners, for example, only have two neighbors). Each neighboring tile gets added to the blob if it's the same color; otherwise it is added to the neighbor list.
I toyed with Pygame subsurfaces to make the panel. (Compare this to my 15 puzzle, where the score and restart button were added "on the same layer" as the game.) The code is a little messy here, but it's a nifty enough method of placing buttons that I'll work on cleaning it up in future projects.
Here's the code:
'''Flood It''' import pygame as pyg, random pyg.font.init() YELLOW = (243,246,29) PURPLE = (96,92,168) GREEN = (126,157,30) PINK = (237,112,161) BLUE = (70,177,226) RED = (220,74,32) colors = [YELLOW, PURPLE, GREEN, PINK, BLUE, RED] m,n = 14,14 pad = 15 size = 25 most = 25 area = (size,size) mid = round(len(colors)/2) p_wid = mid*(pad+size)+pad # panel (non-game area) width height,width = m*size,n*size+p_wid window = pyg.display.set_mode( (width,height) ) pyg.display.set_caption("Flood-It!") class Tile: board = window.subsurface(pyg.Rect(0,0,n*size,m*size)) def __init__(self,i): self.i = i self.color = random.choice(colors) self.rect = pyg.Rect( ((i%m)*size,(i//n)*size), area) self.draw() def __repr__(self): return str(self.i) def draw(self): pyg.draw.rect(Tile.board,self.color,self.rect) class Button: def __init__(self,i,color): gap = lambda x: x * (size+pad) self.color = color self.drect = pyg.Rect( (gap(i%mid),gap(i//mid)), area) self.rect = self.drect.move(*Pane.bpane.get_abs_offset()) self.draw() def draw(self): pyg.draw.rect(Pane.bpane,self.color,self.drect) class Board: def __init__(self): ''' A Board object has three lists: 1) Board - a collection of ordered tiles 2) Blob - tiles the player accrues 3) Neighbors - tiles that may be added to blob ''' self.board = [Tile(i) for i in range(m*n)] self.blob = [self.board[0]] self.neighbors = [] self.make_blob(self.blob[0].color) def __getitem__(self,i): '''Board[i] gets ith item from Board.board''' return self.board[i] def get_neighbors(self,tile): '''For a given tile: fetches the ones beside, above, and below it''' i = tile.i dirs = [-n,1,n,-1] # north,east,south,west return [self[i+d] for d in dirs if self.is_valid(i+d,d)] def is_valid(self,i,j): '''Checks if index belongs to a valid neighbor''' if 0 <= i < m*n: # without these conditions, blob wraps around corners if j == 1: return i%m >= (i-j)%m elif j == -1: return i%m <= (i-j)%m return True def make_blob(self,color): '''Adds more tiles to blob''' for cell in self.blob: cell.color = color for neighbor in self.get_neighbors(cell): if neighbor.color == cell.color: if neighbor not in self.blob: self.blob.append(self[neighbor.i]) elif neighbor not in self.neighbors: self.neighbors.append(self[neighbor.i]) class Text: '''A text block, optionally clickable''' def __init__(self,text,surf,font_size=25): surf.fill( (0,0,0) ) font = pyg.font.SysFont('Arial',font_size) font_surf = font.render(text,font_size,(255,255,255)) self.rect = font_surf.get_rect().move(*surf.get_abs_offset()) surf.blit(font_surf, (0,0)) class Pane: a = size+pad b = p_wid-pad pane = window.subsurface(pyg.Rect(n*size,0,p_wid,height)) bpane = pane.subsurface(pyg.Rect(pad,pad,b,2*a)) click = pane.subsurface(pyg.Rect(pad,3*(size+pad),b,2*a)) replay = pane.subsurface(pyg.Rect(pad,5*(size+pad),b,a)) end = pane.subsurface(pyg.Rect(pad,7*(size+pad),b,a)) def __init__(self): self.buttons = [Button(i,c) for i,c in enumerate(colors)] self.restart = Text('Replay',Pane.replay) self.end = Text('Quit',Pane.end) self.update() def update(self,count=0): self.click = Text('Clicks: %d/%d' % (count,most),Pane.click) def flood_it(): board = Board() pane = Pane() clicks = 0 play = True while True: for event in pyg.event.get(): if event.type == pyg.QUIT: pyg.quit() quit() if event.type == pyg.MOUSEBUTTONDOWN: for button in pane.buttons: if button.rect.collidepoint(event.pos): if play: board.make_blob(button.color) [tile.draw() for tile in board.board] clicks += 1 pane.update(clicks) if len(board.blob) == m*n or clicks >= most: play = False elif pane.restart.rect.collidepoint(event.pos): flood_it() elif pane.end.rect.collidepoint(event.pos): pyg.quit() quit() pyg.display.update() flood_it()
No comments:
Post a Comment