A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://inventwithpython.com/bigbookpython/project28.html below:

Flooder

#28
Flooder

Flooder is a colorful game where a player tries to fill the board with a single color by changing the color of the tile in the upper-left corner. This new color spreads to all neighboring tiles that matched the original color. It’s similar to the Flood It mobile game. This program also has a colorblind mode, which uses shapes instead of flat colored tiles. It relies on the recursive flood fill algorithm to paint the board and works similarly to the “paint bucket” or “fill” tool in many painting applications.

The Program in Action

Figure 28-1 shows what the output will look like when you run flooder.py.

Figure 28-1: The output of the Flooder game in colorblind mode, showing distinct shapes instead of rectangles

How It Works

Accessibility is a large issue in video games, and addressing it can take many forms. For example, deuteranopia, or red-green colorblindness, causes shades of red and green to appear the same, making it hard to distinguish between red objects and green objects on the screen. We can make Flooder more accessible with a mode that uses distinct shapes instead of distinct colors. Note that even the colorblind mode still uses color. This means you can eliminate the “standard” mode, if you wish, and have even color-sighted users play in the colorblind mode. The best accessibility designs are those that incorporate accessibility considerations from the start rather than add them as a separate mode. This reduces the amount of code we have to write and makes any future bug fixes easier.

Other accessibility issues include making sure that text is large enough to be read without perfect vision, that sound effects have visual cues and spoken language has subtitles for those hard of hearing, and that controls can be remapped to other keyboard keys so people can play the game with one hand. The YouTube channel Game Maker’s Toolkit has a video series called “Designing for Disability” that covers many aspects of designing your games with accessibility in mind.

  1. """Flooder, by Al Sweigart [email protected]
  2. A colorful game where you try to fill the board with a single color. Has
  3. a mode for colorblind players.
  4. Inspired by the "Flood It!" game.
  5. View this code at https://nostarch.com/big-book-small-python-projects
  6. Tags: large, bext, game"""
  7.
  8. import random, sys
  9.
 10. try:
 11.     import bext
 12. except ImportError:
 13.     print('This program requires the bext module, which you')
 14.     print('can install by following the instructions at')
 15.     print('https://pypi.org/project/Bext/')
 16.     sys.exit()
 17.
 18. # Set up the constants:
 19. BOARD_WIDTH = 16  # (!) Try changing this to 4 or 40.
 20. BOARD_HEIGHT = 14  # (!) Try changing this to 4 or 20.
 21. MOVES_PER_GAME = 20  # (!) Try changing this to 3 or 300.
 22.
 23. # Constants for the different shapes used in colorblind mode:
 24. HEART     = chr(9829)  # Character 9829 is '♥'.
 25. DIAMOND   = chr(9830)  # Character 9830 is '♦'.
 26. SPADE     = chr(9824)  # Character 9824 is '♠'.
 27. CLUB      = chr(9827)  # Character 9827 is '♣'.
 28. BALL      = chr(9679)  # Character 9679 is '•'.
 29. TRIANGLE  = chr(9650)  # Character 9650 is '▲'.
 30.
 31. BLOCK     = chr(9608)  # Character 9608 is '█'
 32. LEFTRIGHT = chr(9472)  # Character 9472 is '─'
 33. UPDOWN    = chr(9474)  # Character 9474 is '│'
 34. DOWNRIGHT = chr(9484)  # Character 9484 is '┌'
 35. DOWNLEFT  = chr(9488)  # Character 9488 is '┐'
 36. UPRIGHT   = chr(9492)  # Character 9492 is '└'
 37. UPLEFT    = chr(9496)  # Character 9496 is '┘'
 38. # A list of chr() codes is at https://inventwithpython.com/chr
 39.
 40. # All the color/shape tiles used on the board:
 41. TILE_TYPES = (0, 1, 2, 3, 4, 5)
 42. COLORS_MAP = {0: 'red', 1: 'green', 2:'blue',
 43.               3:'yellow', 4:'cyan', 5:'purple'}
 44. COLOR_MODE = 'color mode'
 45. SHAPES_MAP = {0: HEART, 1: TRIANGLE, 2: DIAMOND,
 46.               3: BALL, 4: CLUB, 5: SPADE}
 47. SHAPE_MODE = 'shape mode'
 48.
 49.
 50. def main():
 51.     bext.bg('black')
 52.     bext.fg('white')
 53.     bext.clear()
 54.     print('''Flooder, by Al Sweigart [email protected]
 55.
 56. Set the upper left color/shape, which fills in all the
 57. adjacent squares of that color/shape. Try to make the
 58. entire board the same color/shape.''')
 59.
 60.     print('Do you want to play in colorblind mode? Y/N')
 61.     response = input('> ')
 62.     if response.upper().startswith('Y'):
 63.         displayMode = SHAPE_MODE
 64.     else:
 65.         displayMode = COLOR_MODE
 66.
 67.     gameBoard = getNewBoard()
 68.     movesLeft = MOVES_PER_GAME
 69.
 70.     while True:  # Main game loop.
 71.         displayBoard(gameBoard, displayMode)
 72.
 73.         print('Moves left:', movesLeft)
 74.         playerMove = askForPlayerMove(displayMode)
 75.         changeTile(playerMove, gameBoard, 0, 0)
 76.         movesLeft -= 1
 77.
 78.         if hasWon(gameBoard):
 79.             displayBoard(gameBoard, displayMode)
 80.             print('You have won!')
 81.             break
 82.         elif movesLeft == 0:
 83.             displayBoard(gameBoard, displayMode)
 84.             print('You have run out of moves!')
 85.             break
 86.
 87.
 88. def getNewBoard():
 89.     """Return a dictionary of a new Flood It board."""
 90.
 91.     # Keys are (x, y) tuples, values are the tile at that position.
 92.     board = {}
 93.
 94.     # Create random colors for the board.
 95.     for x in range(BOARD_WIDTH):
 96.         for y in range(BOARD_HEIGHT):
 97.             board[(x, y)] = random.choice(TILE_TYPES)
 98.
 99.     # Make several tiles the same as their neighbor. This creates groups
100.     # of the same color/shape.
101.     for i in range(BOARD_WIDTH * BOARD_HEIGHT):
102.         x = random.randint(0, BOARD_WIDTH - 2)
103.         y = random.randint(0, BOARD_HEIGHT - 1)
104.         board[(x + 1, y)] = board[(x, y)]
105.     return board
106.
107.
108. def displayBoard(board, displayMode):
109.     """Display the board on the screen."""
110.     bext.fg('white')
111.     # Display the top edge of the board:
112.     print(DOWNRIGHT + (LEFTRIGHT * BOARD_WIDTH) + DOWNLEFT)
113.
114.     # Display each row:
115.     for y in range(BOARD_HEIGHT):
116.         bext.fg('white')
117.         if y == 0:  # The first row begins with '>'.
118.             print('>', end='')
119.         else:  # Later rows begin with a white vertical line.
120.             print(UPDOWN, end='')
121.
122.         # Display each tile in this row:
123.         for x in range(BOARD_WIDTH):
124.             bext.fg(COLORS_MAP[board[(x, y)]])
125.             if displayMode == COLOR_MODE:
126.                 print(BLOCK, end='')
127.             elif displayMode == SHAPE_MODE:
128.                 print(SHAPES_MAP[board[(x, y)]], end='')
129.
130.         bext.fg('white')
131.         print(UPDOWN)  # Rows end with a white vertical line.
132.     # Display the bottom edge of the board:
133.     print(UPRIGHT + (LEFTRIGHT * BOARD_WIDTH) + UPLEFT)
134.
135.
136. def askForPlayerMove(displayMode):
137.     """Let the player select a color to paint the upper left tile."""
138.     while True:
139.         bext.fg('white')
140.         print('Choose one of ', end='')
141.
142.         if displayMode == COLOR_MODE:
143.             bext.fg('red')
144.             print('(R)ed ', end='')
145.             bext.fg('green')
146.             print('(G)reen ', end='')
147.             bext.fg('blue')
148.             print('(B)lue ', end='')
149.             bext.fg('yellow')
150.             print('(Y)ellow ', end='')
151.             bext.fg('cyan')
152.             print('(C)yan ', end='')
153.             bext.fg('purple')
154.             print('(P)urple ', end='')
155.         elif displayMode == SHAPE_MODE:
156.             bext.fg('red')
157.             print('(H)eart, ', end='')
158.             bext.fg('green')
159.             print('(T)riangle, ', end='')
160.             bext.fg('blue')
161.             print('(D)iamond, ', end='')
162.             bext.fg('yellow')
163.             print('(B)all, ', end='')
164.             bext.fg('cyan')
165.             print('(C)lub, ', end='')
166.             bext.fg('purple')
167.             print('(S)pade, ', end='')
168.         bext.fg('white')
169.         print('or QUIT:')
170.         response = input('> ').upper()
171.         if response == 'QUIT':
172.             print('Thanks for playing!')
173.             sys.exit()
174.         if displayMode == COLOR_MODE and response in tuple('RGBYCP'):
175.             # Return a tile type number based on the response:
176.             return {'R': 0, 'G': 1, 'B': 2,
177.                 'Y': 3, 'C': 4, 'P': 5}[response]
178.         if displayMode == SHAPE_MODE and response in tuple('HTDBCS'):
179.             # Return a tile type number based on the response:
180.             return {'H': 0, 'T': 1, 'D':2,
181.                 'B': 3, 'C': 4, 'S': 5}[response]
182.
183.
184. def changeTile(tileType, board, x, y, charToChange=None):
185.     """Change the color/shape of a tile using the recursive flood fill
186.     algorithm."""
187.     if x == 0 and y == 0:
188.         charToChange = board[(x, y)]
189.         if tileType == charToChange:
190.             return  # Base Case: Already is the same tile.
191.
192.     board[(x, y)] = tileType
193.
194.     if x > 0 and board[(x - 1, y)] == charToChange:
195.         # Recursive Case: Change the left neighbor's tile:
196.         changeTile(tileType, board, x - 1, y, charToChange)
197.     if y > 0 and board[(x, y - 1)] == charToChange:
198.         # Recursive Case: Change the top neighbor's tile:
199.         changeTile(tileType, board, x, y - 1, charToChange)
200.     if x < BOARD_WIDTH - 1 and board[(x + 1, y)] == charToChange:
201.         # Recursive Case: Change the right neighbor's tile:
202.         changeTile(tileType, board, x + 1, y, charToChange)
203.     if y < BOARD_HEIGHT - 1 and board[(x, y + 1)] == charToChange:
204.         # Recursive Case: Change the bottom neighbor's tile:
205.         changeTile(tileType, board, x, y + 1, charToChange)
206.
207.
208. def hasWon(board):
209.     """Return True if the entire board is one color/shape."""
210.     tile = board[(0, 0)]
211.
212.     for x in range(BOARD_WIDTH):
213.         for y in range(BOARD_HEIGHT):
214.             if board[(x, y)] != tile:
215.                 return False
216.     return True
217.
218.
219. # If this program was run (instead of imported), run the game:
220. if __name__ == '__main__':
221.     main()

After entering the source code and running it a few times, try making experimental changes to it. The comments marked with (!) have suggestions for small changes you can make. On your own, you can also try to figure out how to do the following:

Exploring the Program

Try to find the answers to the following questions. Experiment with some modifications to the code and rerun the program to see what effect the changes have.

  1. What error message do you get if you change board = {} on line 92 to board = []?
  2. What error message do you get if you change return board on line 105 to return None?
  3. What happens if you change movesLeft -= 1 on line 76 to movesLeft -= 0?

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4