Wednesday, August 22, 2012

python: yat (yet another tetris)


Who doesn't want to write its own version of one of the most popular games in the video game history, huh?

Here is YAT, my tetris version written  in python using the popular game developing module pygame.

Source code is available here.

Screenshots


About surfaces:

I think, besides the game logic, the most interesting thing here is the use of two different surfaces to show the blocks and the game information on the screen (including the next block).
This is managed in the main game file following. Basically I'm updating the game information surface with the "updateInfo" function and the blocks screen with the table class method "show". Both functions update the game surface "screen" with their own surfaces by themselves.

.tempfile
  1 #======================================================================#
  2 # This file is part of YAT (Yet Another Tetris).                       #
  3 #                                                                      #
  4 # YAT is free software: you can redistribute it and/or modify          #
  5 # it under the terms of the GNU General Public License as published by #
  6 # the Free Software Foundation, either version 3 of the License, or    #
  7 # (at your option) any later version.                                  #
  8 #                                                                      #
  9 # YAT is distributed in the hope that it will be useful,               #
 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of       #
 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        #
 12 # GNU General Public License for more details.                         #
 13 #                                                                      #
 14 # You should have received a copy of the GNU General Public License    #
 15 # along with YAT.     If not, see <http://www.gnu.org/licenses/>.      #
 16 #======================================================================#
 17 
 18 #==========================================================================#
 19 # Name       : yat.py (Yet Another Tetris)                                 #
 20 # Description: Main game file for the tetris like game "yat"               #
 21 # Author     : Adrian Antonana                                             #
 22 # Date       : 17.08.2012                                                  #
 23 # Copyright  : Adrian Antonana 2012                                        #
 24 #==========================================================================#
 25 import pygame as pg
 26 import table  as tb
 27 import blocks as bk
 28 import colors as col
 29 
 30 #==========================================================================#
 31 #                     Global Variables and Constants                       #
 32 #==========================================================================#
 33 GAME_SPEED     = 500
 34 SPEED_INC_TICK = 50
 35 LINES_INC_TICK = 10
 36 LEVEL          = 1
 37 REMOVED_LINES  = 0
 38 MAX_LEVEL      = 10
 39 FPS            = 100
 40 
 41 #==========================================================================#
 42 #                           Function Definitions                           #
 43 #==========================================================================#
 44 
 45 #-------------------- Checks when blocks have to move down ----------------#
 46 def delay(ticks):
 47   return (ticks % GAME_SPEED) >= GAME_SPEED-10
 48 
 49 #------------------ Increases the Game Level and Game Speed ---------------#
 50 def incSpeed(remlines):
 51   global GAME_SPEED
 52   global LEVEL
 53 
 54   if LEVEL < MAX_LEVEL:
 55     if remlines / (LEVEL*LINES_INC_TICK) == 1:
 56       LEVEL += 1
 57       GAME_SPEED -= SPEED_INC_TICK
 58       return True
 59   return False
 60 
 61 #---------------------- Updates the information surface -------------------#
 62 def updateInfo(nb):
 63   global LEVEL_NUM_TEXT
 64   global LINES_NUM_TEXT
 65   global infosurface
 66 
 67   LEVEL_NUM_TEXT = font.render(str(LEVEL),True,col.WHITE)
 68   LINES_NUM_TEXT = font.render(str(REMOVED_LINES),True,col.WHITE)
 69   infosurface.fill(col.GREY_DARK)
 70   infosurface.blit(LEVEL_TEXT,LEVEL_TEXT_OFFSET)
 71   infosurface.blit(LEVEL_NUM_TEXT,LEVEL_NUM_TEXT_OFFSET)
 72   infosurface.blit(LINES_TEXT,LINES_TEXT_OFFSET)
 73   infosurface.blit(LINES_NUM_TEXT,LINES_NUM_TEXT_OFFSET)
 74   nb.show(infosurface,NEXT_BLOCK_OFFSET,20,INF_BLOCK_SIZE)
 75 
 76 
 77 #==========================================================================#
 78 #              Initialize pygame (display,mixer and clock)                 #
 79 #==========================================================================#
 80 pg.init()
 81 pg.mixer.init()
 82 sndblockplaced = pg.mixer.Sound("sounds/block_placed.wav")
 83 sndblockrotate = pg.mixer.Sound("sounds/block_rotate.wav")
 84 sndremovelines = pg.mixer.Sound("sounds/remove_lines.wav")
 85 sndlevelup     = pg.mixer.Sound("sounds/level_up.wav")
 86 sndgameover    = pg.mixer.Sound("sounds/game_over.wav")
 87 clock          = pg.time.Clock()
 88 pg.display.set_caption("yat - yet another tetris")
 89 pg.key.set_repeat(10,50)
 90 
 91 #==========================================================================#
 92 #                         Information surface                              #
 93 #==========================================================================#
 94 INFO_SURFACE_HEIGHT   = 105
 95 FONT_SIZE             = 30
 96 FONT_SIZE_GAME_OVER   = 60
 97 UPPER_OFFSET          = 20
 98 LEFT_OFFSET           = 10
 99 INF_BLOCK_SIZE        = 20
100 font                  = pg.font.SysFont(pg.font.get_default_font(),FONT_SIZE)
101 font_game_over        = pg.font.SysFont(pg.font.get_default_font(),
                            FONT_SIZE_GAME_OVER)
102 LEVEL_TEXT            = font.render("Level : ",True,col.WHITE)
103 LINES_TEXT            = font.render("Lines : ",True,col.WHITE)
104 LEVEL_TEXT_OFFSET     = (LEFT_OFFSET,UPPER_OFFSET)
105 LEVEL_NUM_TEXT_OFFSET = (70+LEFT_OFFSET,UPPER_OFFSET)
106 LINES_TEXT_OFFSET     = (LEFT_OFFSET,INFO_SURFACE_HEIGHT-40)
107 LINES_NUM_TEXT_OFFSET = (70+LEFT_OFFSET,INFO_SURFACE_HEIGHT-40)
108 NEXT_BLOCK_OFFSET     = tb.BLOCK_SIZE*tb.WIDTH - INF_BLOCK_SIZE * 5
109 
110 #==========================================================================#
111 #                            Game over text                                #
112 #==========================================================================#
113 GAME_OVER_TEXT        = font_game_over.render("GAME OVER",True,col.WHITE)
114 GAME_OVER_TEXT_OFFSET = ((tb.BLOCK_SIZE*tb.WIDTH/2)-120,(tb.BLOCK_SIZE*tb.
                            HEIGHT/2)-50)
115 
116 #==========================================================================#
117 #                         Initialize surfaces                              #
118 #==========================================================================#
119 screen         = pg.display.set_mode((tb.BLOCK_SIZE*tb.WIDTH,tb.
                     BLOCK_SIZE*tb.HEIGHT+INFO_SURFACE_HEIGHT))
120 tablesurface   = screen.subsurface((0,INFO_SURFACE_HEIGHT,tb.BLOCK_SIZE*tb.
                     WIDTH,tb.BLOCK_SIZE*tb.HEIGHT))
121 infosurface    = screen.subsurface((0,0,tb.BLOCK_SIZE*tb.WIDTH,
                     INFO_SURFACE_HEIGHT))
122 
123 #==========================================================================#
124 #                        Block spawn position                              #
125 #==========================================================================#
126 BLOCK_SPAWN_POS = (0,(tb.WIDTH/2)-1)
127 
128 #==========================================================================#
129 #               Create the table and an initial block                      #
130 #==========================================================================#
131 t     = tb.table(tablesurface)
132 b     = bk.block(BLOCK_SPAWN_POS)
133 nextb = bk.block(BLOCK_SPAWN_POS)
134 
135 #==========================================================================#
136 #                 Draw initial information surface                         #
137 #==========================================================================#
138 updateInfo(nextb)
139 
140 #==========================================================================#
141 #                            Main loop                                     #
142 #==========================================================================#
143 running = True
144 
145 while running:
146   clock.tick_busy_loop(FPS)
147   t.adBlock(b.getPosList(),b.getType())
148   t.show()
149 
150   # check if the fall delay has been reached. If yes, move block down.
151   if delay(pg.time.get_ticks()):
152     if b.canMovDown(t.getHeight(),t.getOcupPosList(b.getPosList())):
153       t.adBlock(b.getPosList(),bk.E)
154       b.movDown()
155     else:
156       # if the block can't move down, spawn a new block
157       t.adBlock(b.getPosList(),b.getType())
158       sndblockplaced.play()
159       retval = t.delFullLines()
160       if retval != 0:
161         sndremovelines.play()
162         REMOVED_LINES += retval
163         if incSpeed(REMOVED_LINES):
164           sndlevelup.play()
165       b.__init__(BLOCK_SPAWN_POS,nextb.getType())
166       nextb = bk.block(BLOCK_SPAWN_POS)
167       updateInfo(nextb)
168 
169       # check if the game is over
170       if t.gameOver(b.getPosList()):
171         t.adBlock(b.getPosList(),b.getType())
172         t.show()
173         running = False
174 
175   # get one event from the queue and perform action
176   event = pg.event.poll()
177   if event.type == pg.KEYDOWN:
178     key = event.key
179     if key == pg.K_ESCAPE:
180       running = False
181     elif key == pg.K_DOWN:
182       if b.canMovDown(t.getHeight(),t.getOcupPosList(b.getPosList())):
183         t.adBlock(b.getPosList(),bk.E)
184         b.movDown()
185       else:
186         # if the block can't move down, spawn a new block
187         t.adBlock(b.getPosList(),b.getType())
188         sndblockplaced.play()
189         retval = t.delFullLines()
190         if retval != 0:
191           sndremovelines.play()
192           REMOVED_LINES += retval
193           if incSpeed(REMOVED_LINES):
194             sndlevelup.play()
195         b.__init__(BLOCK_SPAWN_POS,nextb.getType())
196         nextb = bk.block(BLOCK_SPAWN_POS)
197         updateInfo(nextb)
198 
199         # check if the game is over
200         if t.gameOver(b.getPosList()):
201           t.adBlock(b.getPosList(),b.getType())
202           t.show()
203           running = False
204 
205     # move/rotate block left/right
206     elif key == pg.K_LEFT:
207       if b.canMovLeft(t.getWidth(),t.getOcupPosList(b.getPosList())):
208         t.adBlock(b.getPosList(),bk.E)
209         b.movLeft()
210     elif key == pg.K_RIGHT:
211       if b.canMovRight(t.getWidth(),t.getOcupPosList(b.getPosList())):
212         t.adBlock(b.getPosList(),bk.E)
213         b.movRight()
214     elif key == pg.K_LCTRL:
215       t.adBlock(b.getPosList(),bk.E)
216       if b.rotLeft(t.getHeight(),t.getWidth(),t.getOcupPosList(b.getPosList()
                       )):
217         sndblockrotate.play()
218     elif key == pg.K_LALT:
219       t.adBlock(b.getPosList(),bk.E)
220       if b.rotRight(t.getHeight(),t.getWidth(),t.getOcupPosList(b.getPosList(
                        ))):
221         sndblockrotate.play()
222 
223 #==========================================================================#
224 #                           The game is over                               #
225 #==========================================================================#
226 tablesurface.fill(col.BLACK)
227 t.setSurfAlpha(60)
228 t.show()
229 tablesurface.blit(GAME_OVER_TEXT,GAME_OVER_TEXT_OFFSET)
230 pg.display.flip()
231 sndgameover.play()
232 
233 quit = False
234 while not quit:
235   event = pg.event.wait()
236   if  event.type == pg.QUIT:
237     quit = True
238   if event.type == pg.KEYDOWN:
239     if event.key == pg.K_ESCAPE:
240       quit = True

3 comments:

  1. It's not work!!! Could you send me a new code and video tutorial for me at Yeantourssophea@gmail.com .

    Thanks!

    ReplyDelete
  2. OK i have to confess i came to this post familiar with the title of it. But whatever coding or programming is done I have no clue about it. It looks so technical, WOW.

    ReplyDelete
  3. Wow !!! absolutely fantastic blog. I am very glad to have such useful information.

    หนังออนไลน์

    ReplyDelete