您的位置:首页 > 编程语言 > Python开发

python实现的“打砖块”游戏 Brick & Ball in Python

2006-04-19 17:34 886 查看
根据摩托罗拉C289手机的同名游戏写成,使用了Python的curses,因此最好在linux下面运行。最近又用tkinter改写了界面,从而不在受限于curses的支持,还重写的游戏的算法,比以前的运行效率好多了。



1  #!/usr/bin/python
2  #
3  # Brick & Ball in Python
4  # by Jerry Fleming <jerryfleming@etang.com>
5  #
6  # This is a small game adapted from that in Motorola Mobile C289, and my first game in Python :)
7  #
8  # This progrma is best run under linux. Since Windows port of Python has poor curses support,
9  # play it under Windows is not recommended. If you have no linux box available, try Cygwin,
10  # though it too has poor curses support.
11  #
12  # As I am a newbie to Python, please tell me if you have a better implementation or any suggestions.
13  #
14  # TODO:
15  # re-implemente it with wxPython, so one does not have to rely on the ugly curses.
16  # session support.
17  # pausing, especially when confirming
18  # resize terminal at run time
19  #
20  # HISTORY
21  # 2006-04-19: first version
22  #
23  #
24
25  import curses
26  import _curses
27  import thread
28  from time import sleep
29  from string import split
30  from random import randint
31
32  # parameters: adjust them to fit you terminal
33  brick_width = 7
34  brick_gap_x = 1
35  brick_gap_y = 1
36  speed = 0.05 # sleep time to control moving speed of the ball
37  pause = 1 # time to pause
38
39  # terminal initialization
40  stdscr = curses.initscr()
41  curses.noecho()
42  curses.cbreak()
43  curses.curs_set(0)
44  stdscr.keypad(1)
45  screen_height, screen_width = stdscr.getmaxyx()
46  screen_height = screen_height - 1
47  screen_width = screen_width - 1
48  brick_rows = screen_height / 4
49  if brick_rows > 7: brick_rows = 7
50  brick_cols = (screen_width + brick_gap_x) / (brick_width + brick_gap_x)
51  brick_margin = (screen_width - brick_cols * (brick_width + brick_gap_x) + brick_gap_x)/2
52  pad_position = randint(0, screen_width - brick_width)
53  ball_position = [screen_height - 3, randint(0, screen_width - brick_width)]
54  ball_direction = [-1, 1] # abs(tan(a)) must be 1
55  char = ''
56  bricks = []
57  game_score = 0
58  ScreenSizeError = 'ScreenSizeError'
59
60  tStart = '''
61   ______        _       _         ___      ______        _ _     _          ______             _
62  (____  /      (_)     | |       / _ /    (____  /      | | |   (_)        (_____ /        _  | |
63   ____)  ) ____ _  ____| |  _   ( (_) )    ____)  )_____| | |    _ ____     _____) )   _ _| |_| |__   ___  ____
64  |  __  ( / ___) |/ ___) |_/ )   ) _ (    |  __  ((____ | | |   | |  _ /   |  ____/ | | (_   _)  _ / / _ /|  _ /
65  | |__)  ) |   | ( (___|  _ (   ( (/  /   | |__)  ) ___ | | |   | | | | |  | |    | |_| | | |_| | | | |_| | | | |
66  |______/|_|   |_|/____)_| /_)   /__//_)  |______//_____|/_)_)  |_|_| |_|  |_|     /__  |  /__)_| |_|/___/|_| |_|
67                                                                                   (____/
68
69
70                                 by Jerry Fleming <jerryfleming@etang.com>
71
72
73                                            GAME STARTING ...
74
75                         (this assumes that your terminal be larger that 130x40)
76  '''
77
78  tExit = '''
79                                                 88888                         88888
80  88888888888             88       ad88888ba     88 8b        d8      d8          88
81  88                      ""   ,d d8"     "8b    88  Y8,    ,8P     ,8P'          88
82  88                           88 ""      a8P    88   Y8,  ,8P     d8"            88
83  88aaaaa     8b,     ,d8 88 MM88MMM   ,a8P"     88    "8aa8"    ,8P' 8b,dPPYba,  88
84  88"""""      `Y8, ,8P'  88   88     d8"        88     `88'    d8"   88P'   `"8a 88
85  88             )888(    88   88     ""         88      88   ,8P'    88       88 88
86  88           ,d8" "8b,  88   88,    aa         88      88  d8"      88       88 88
87  88888888888 8P'     `Y8 88   "Y888  88         88      88 8P'       88       88 88
88                                                 88888                         88888
89  '''
90
91  tOver = '''
92    ,ad8888ba,                                                                                            88
93   d8"'    `"8b                                                                                           88
94  d8'                                                                                                     88
95  88            ,adPPYYba, 88,dPYba,,adPYba,   ,adPPYba,     ,adPPYba,  8b       d8  ,adPPYba, 8b,dPPYba, 88
96  88      88888 ""     `Y8 88P'   "88"    "8a a8P_____88    a8"     "8a `8b     d8' a8P_____88 88P'   "Y8 88
97  Y8,        88 ,adPPPPP88 88      88      88 8PP"""""""    8b       d8  `8b   d8'  8PP""""""" 88         ""
98   Y8a.    .a88 88,    ,88 88      88      88 "8b,   ,aa    "8a,   ,a8"   `8b,d8'   "8b,   ,aa 88         aa
99    `"Y88888P"  `"8bbdP"Y8 88      88      88  `"Ybbd8"'     `"YbbdP"'      "8"      `"Ybbd8"' 88         88
100  '''
101
102  tGoon = '''
103                                                                  88888                         88888
104    ,ad8888ba,                                      ad88888ba     88 8b        d8      d8          88
105   d8"'    `"8b                                    d8"     "8b    88  Y8,    ,8P     ,8P'          88
106  d8'                                              ""      a8P    88   Y8,  ,8P     d8"            88
107  88             ,adPPYba,      ,adPPYba,  8b,dPPYba,   ,a8P"     88    "8aa8"    ,8P' 8b,dPPYba,  88
108  88      88888 a8"     "8a    a8"     "8a 88P'   `"8a d8"        88     `88'    d8"   88P'   `"8a 88
109  Y8,        88 8b       d8    8b       d8 88       88 ""         88      88   ,8P'    88       88 88
110   Y8a.    .a88 "8a,   ,a8"    "8a,   ,a8" 88       88 aa         88      88  d8"      88       88 88
111    `"Y88888P"   `"YbbdP"'      `"YbbdP"'  88       88 88         88      88 8P'       88       88 88
112                                                                  88888                         88888
113  '''
114
115  def init_game():
116          '''Game initializing.'''
117          global bricks
118          # display the bricks
119          for row in range(brick_rows):
120                  y = row * (1 + brick_gap_y)
121                  for col in range(brick_cols):
122                          x = col * (brick_gap_x + brick_width) + brick_margin
123                          stdscr.addstr(y, x, ' ' * brick_width, curses.A_REVERSE)
124                          bricks.append([y, x])
125          # move the pad to center of bottom at starting up
126          stdscr.addstr(screen_height - 1, pad_position, ' ' * brick_width, curses.A_REVERSE)
127          # move the ball to left bottom side at starting up
128          stdscr.addstr(ball_position[0], ball_position[1], ' ', curses.A_REVERSE)
129          # display score board
130          stdscr.addstr(screen_height, 0, ' SCORE: '+ ('%03d' % game_score) + ' '* 4, curses.A_REVERSE)
131          stdscr.addstr(screen_height, 15, 'USE q to quit' + ' '* (screen_width - 28), curses.A_REVERSE)
132          # final step to init display
133          stdscr.refresh()
134
135  def move_pad(lock):
136          '''Move the pad to catch the ball.'''
137          global char, pad_position
138          char = stdscr.getch()
139          if char == ord('q'): quit_game(lock)
140          if char == curses.KEY_LEFT: pad_position = pad_position - 1
141          if char == curses.KEY_RIGHT: pad_position = pad_position + 1
142          if pad_position < 0: pad_position = 0
143          if pad_position >= screen_width - brick_width: pad_position = screen_width - brick_width
144          stdscr.addstr(screen_height - 1, 0, ' ' * screen_width)
145          stdscr.addstr(screen_height - 1, pad_position, ' ' * brick_width, curses.A_REVERSE)
146          stdscr.refresh()
147
148  def move_ball(lock):
149          '''Move the ball to a direction.'''
150          global ball_position, ball_direction
151          # clear the old position, do not if in pad
152          if ball_position[0] != screen_height - 1:
153                  stdscr.addstr(ball_position[0], ball_position[1], ' ')
154          ball_position[0] = ball_position[0] + ball_direction[0]
155          ball_position[1] = ball_position[1] + ball_direction[1]
156          stdscr.addstr(ball_position[0], ball_position[1], ' ', curses.A_REVERSE)
157          detect_collision(lock)
158          stdscr.refresh()
159          sleep(speed)
160
161  def detect_collision(lock):
162          '''Detect whether the ball has hit something, change direction if yes.'''
163          global bricks, ball_direction, game_score
164          # hit upper wall
165          if ball_position[0] == 0 :
166                  ball_direction = [- ball_direction[0], ball_direction[1]]
167          # hit left and right wall
168          if ball_position[1] == 0 or ball_position[1] == screen_width:
169                  ball_direction = [ball_direction[0], - ball_direction[1]]
170          # hit brick
171          for brick in bricks:
172                  # hit from bottom or upper direction
173                  if brick[1] <= ball_position[1] + ball_direction[1] <= brick[1] + brick_width /
174                  and ball_position[0] + ball_direction[0] == brick[0]:
175                          ball_direction[0] = - ball_direction[0]
176                          stdscr.addstr(brick[0], brick[1], ' '* brick_width)
177                          game_score = game_score + 10
178                          stdscr.addstr(screen_height, 8, '%03d' % game_score, curses.A_REVERSE)
179                          bricks.remove(brick)
180                          # another level to continue the game
181                          if not len(bricks): another_level()
182          # hit body of pad
183          if ball_position[0] == screen_height - 2 and pad_position <= ball_position[1] <= pad_position + brick_width:
184                  ball_direction[0] = - ball_direction[0]
185          # hit bottom
186          elif ball_position[0] == screen_height - 1:
187                  ball_direction[0] = - ball_direction[0]
188          # hit left side of pad (hit bottom already)
189          if ball_position[1] == pad_position and  char == curses.KEY_LEFT and ball_direction[1]:
190                  ball_direction[1] = - ball_direction[1]
191          # hit right side of pad (hit bottom already)
192          if ball_position[1] == pad_position + brick_width and  char == curses.KEY_RIGHT and not ball_direction[1]:
193                  ball_direction[1] = - ball_direction[1]
194          # hit bottom wall, game over
195          if ball_position[0] == screen_height - 1 and not pad_position <= ball_position[1] <= pad_position + brick_width:
196                  line_num = 0
197                  for line in split(tOver, "/n"):
198                          stdscr.addstr(screen_height / 2 + line_num, 10, line)
199                          line_num = line_num + 1
200                  stdscr.refresh()
201                  curses.flash()
202                  sleep(1)
203                  curses.flash()
204                  sleep(1)
205                  lock.release()
206
207  def quit_game(lock):
208          '''Confirm quit the game.'''
209          global char, speed
210          if char != ord('q'): return
211          speed_old = speed # store the value for resume
212          speed = pause # wait for a long time
213          line_num = 0
214          for line in split(tExit, "/n"):
215                  stdscr.addstr(screen_height / 2 + line_num, 10, line)
216                  line_num = line_num + 1
217          stdscr.addstr(screen_height, 15, 'USE n to continue, any other key to quit' + ' '* (screen_width - 40), curses.A_REVERSE)
218          stdscr.refresh()
219          #char = stdscr.getch() ## fix me: why can't we use the global char?
220          if char == ord('n'):
221                  speed = speed_old
222                  line_num = 0
223                  for line in split(tExit, "/n"):
224                          stdscr.addstr(screen_height / 2 + line_num, 10, ' ' * len(line) )
225                          line_num = line_num + 1
226                  stdscr.refresh()
227          else:
228                  lock.release()
229
230  def another_level():
231          '''Confirm another level of the game.'''
232          global speed, char
233          speed_old = speed # store the value for resume
234          speed = pause # wait for a long time
235          line_num = 0
236          for line in split(tGoon, "/n"):
237                  stdscr.addstr(screen_height / 2 + line_num, 10, line)
238                  line_num = line_num + 1
239          stdscr.addstr(screen_height, 15, 'USE n to quit, any other key to continue' + ' '* (screen_width - 40), curses.A_REVERSE)
240          stdscr.refresh()
241          #char = stdscr.getch()
242          if char == ord('n'):
243                  lock.release()
244          else:
245                  stdscr.erase()
246                  speed = speed / 2
247                  init_game()
248
249  def looper(fun, lock):
250          '''Dispatcher to drive th ball and pad.'''
251          try:
252                  while lock.locked(): globals()[fun](lock)
253          except _curses.error, diag:
254                  if lock.locked(): lock.release()
255
256  # main loop starts here
257  if __name__ == "__main__":
258          try:
259                  line_num = 0
260                  for line in split(tStart, "/n"):
261                          stdscr.addstr(screen_height /4 + line_num, 10, line)
262                          line_num = line_num + 1
263                  stdscr.refresh()
264                  sleep(1)
265                  stdscr.erase()
266                  init_game()
267                  locks = []
268                  for i in range(2):
269                          lock = thread.allocate_lock()
270                          lock.acquire()
271                          locks.append(lock)
272                  thread.start_new_thread(looper, ('move_ball', locks[0]))
273                  thread.start_new_thread(looper, ('move_pad', locks[1]))
274                  while locks[0].locked() and locks[1].locked: pass # main loop
275          except _curses.error, diag:
276                  msg = 'Your terminal is too small: ' + str(diag)
277          except 'dd':
278                  msg = 'Game exit abnormally.'
279          else:
280                  msg = 'Game stopped.'
281          # out of loop means stop
282          for i in range(2):
283                  if locks[i].locked(): locks[i].release()
284          height, width = stdscr.getmaxyx()
285          if height - 1 < screen_height or width - 1 < screen_width:
286                  msg = 'Your terminal is shrinked.'
287          # out of loop means stop
288          curses.curs_set(1)
289          stdscr.keypad(0)
290          curses.nocbreak()
291          curses.echo()
292          curses.endwin()
293          print msg
294
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: