Page 1 of 1

### Tetris variants: post em here!

Posted: Thu Jan 17, 2013 11:35 pm UTC
It's almost a rite of passage for a programmer to recreate tetris, so there's a number of variations floating around. Post up your version (code preferable) and let's see how others have solved the same problems. Here's my version using pygame:

Code: Select all

`#!/usr/bin/pythonimport time, pygame, sys, random, copyfrom random import choiceclass Sprite:   def __init__(self, p, c):      self.pos = p      self.color = c   def __str__(self):      return 'Position: ' + str(self.pos) + '\nColor: ' + str(self.color)   def __eq__(self, other):      return self.__dict__ == other.__dict__   def __ne__(self, other):      return self.__dict__ != other.__dict__class gridGroup:   def __init__(self, a, b, c, d):      self.group = [a,b,c,d]   def moveRelative(self, direction): # checks for collision and moves by one increment      moveAllowed = 1      for block in self.group:         if direction == 'up' and Sprite([block.pos[0],block.pos[1] - movespeed], WHITE) in grid:            moveAllowed = 1         elif direction == 'down' and Sprite([block.pos[0],block.pos[1] + movespeed], WHITE) in grid:            moveAllowed = 1         elif direction == 'left' and Sprite([block.pos[0] - movespeed,block.pos[1]], WHITE) in grid:            moveAllowed = 1         elif direction == 'right' and Sprite([block.pos[0] + movespeed,block.pos[1]], WHITE) in grid:            moveAllowed = 1         else:            moveAllowed = 0            stop() if direction == movevector else 0            break      if moveAllowed:         for block in self.group:            if direction == 'up':               block.pos[1] -= md            elif direction == 'down':               block.pos[1] += md            elif direction == 'left':               block.pos[0] -= md            elif direction == 'right':               block.pos[0] += md   def moveAbsolute(self, coordinates):      # determine shift for keyblock (this is a)      shift = [coordinates[0]-self.group[0].pos[0],coordinates[1]-self.group[0].pos[1]]      for block in self.group:            block.pos[0] += shift[0]            block.pos[1] += shift[1]   def arrange(self, pattern):      # arrange blocks around keyblock (this is a)      # 0 - line      if pattern == 0:         self.group[1].pos = [self.group[0].pos[0],self.group[0].pos[1]-md]         self.group[2].pos = [self.group[0].pos[0],self.group[0].pos[1]+md]         self.group[3].pos = [self.group[0].pos[0],self.group[2].pos[1]+md]      # 1 - L bracket      elif pattern == 1:         self.group[1].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]]         self.group[2].pos = [self.group[1].pos[0],self.group[1].pos[1]+md]         self.group[3].pos = [self.group[1].pos[0],self.group[2].pos[1]+md]      # 2 - reverse L bracket      elif pattern == 2:         self.group[1].pos = [self.group[0].pos[0]-md,self.group[0].pos[1]]         self.group[2].pos = [self.group[1].pos[0],self.group[1].pos[1]+md]         self.group[3].pos = [self.group[1].pos[0],self.group[2].pos[1]+md]      # 3 - T intersection      elif pattern == 3:         self.group[1].pos = [self.group[0].pos[0],self.group[0].pos[1]-md]         self.group[2].pos = [self.group[0].pos[0]-md,self.group[0].pos[1]]         self.group[3].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]]      # 4 - square      elif pattern == 4:         self.group[1].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]]         self.group[2].pos = [self.group[0].pos[0],self.group[0].pos[1]+md]         self.group[3].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]+md]      # 5 - Z block      elif pattern == 5:         self.group[1].pos = [self.group[0].pos[0]-md,self.group[0].pos[1]]         self.group[2].pos = [self.group[0].pos[0],self.group[0].pos[1]+md]         self.group[3].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]+md]      # 6 - reverse Z block      elif pattern == 6:         self.group[1].pos = [self.group[0].pos[0]+md,self.group[0].pos[1]]         self.group[2].pos = [self.group[0].pos[0],self.group[0].pos[1]+md]         self.group[3].pos = [self.group[0].pos[0]-md,self.group[0].pos[1]+md]   def rotate(self):      originSprite = self.group[0].pos      newrotation = copy.deepcopy(self)      moveAllowed = 0      for block in newrotation.group:         block.pos = [block.pos[0]-originSprite[0], block.pos[1]-originSprite[1]]         block.pos = [-block.pos[1], block.pos[0]]         block.pos = [block.pos[0]+originSprite[0], block.pos[1]+originSprite[1]]      for block in newrotation.group:         if Sprite(block.pos, WHITE) in grid:            moveAllowed = 1         else:            moveAllowed = 0            break      print newrotation      print self      if moveAllowed:         self = newrotation   def color(self, color):      for block in self.group:         block.color = color   def __str__(self):      return 'Position: ' + str(self.group[0].pos) + '\nColor: ' + str(self.group[0].color) + '\nPosition: ' + str(self.group[1].pos) + '\nColor: ' + str(self.group[1].color) + '\nPosition: ' + str(self.group[2].pos) + '\nColor: ' + str(self.group[2].color) + '\nPosition: ' + str(self.group[3].pos) + '\nColor: ' + str(self.group[3].color)#global variablesmd = 21 # map density, determines size of play area.  defaults to 30pygame.init()screen = pygame.display.set_mode([md*md, md*md])speed = .07output = 0count = 0movevector = 'down'movespeed = mdinplay = 1#define colorsBLACK = (0,0,0)WHITE = (255,255,255)RED = (255,0,0)BLUE = (0,0,255)PURPLE = (255,0,255)GREEN = (0,255,0)YELLOW = (255,255,0)colors = [RED,BLUE,GREEN,PURPLE,YELLOW]def stop():   global grid   global mainGroup   global inplay   xSpritesFull = []   ySpritesFull = []   # set open blocks to closed   for block in mainGroup.group:      if Sprite(block.pos, WHITE) in grid: #check to see if space is open         grid[grid.index(Sprite(block.pos, WHITE))].color = (75,75,75)   # test if this clears a row or column   xSprites = [Sprite([x*md,md],WHITE) for x in range(1,md-1)]   if not any(block in grid for block in xSprites):      for block in xSprites:         spriteIterator = Sprite(block.pos,(75,75,75))         xSpritesFull.append(grid.index(spriteIterator) if spriteIterator in grid else 0)      for each in xSpritesFull:         grid[each].color = WHITE   ySprites = [Sprite([md,y*md],WHITE) for y in range(1,md-1)]   if not any(block in grid for block in ySprites):      for block in ySprites:         spriteIterator = Sprite(block.pos,(75,75,75))         ySpritesFull.append(grid.index(spriteIterator) if spriteIterator in grid else 0)      for each in ySpritesFull:         grid[each].color = WHITE   print 'Row contains white' if any(block in grid for block in [Sprite([x*md,md],WHITE) for x in range(1,md-1)]) else 'Row full'   print 'Column contains white' if any(block in grid for block in [Sprite([md,y*md],WHITE) for y in range(1,md-1)]) else 'Column full'   #reset control group to center and generate new shape   mainGroup.moveAbsolute([md*(md/2),md*(md/2)])   mainGroup.arrange(random.randrange(0,7))   mainGroup.color(choice(colors))   # check to see if starting space is open   for block in mainGroup.group:      if Sprite(block.pos, WHITE) not in grid: #check to see if starting space is open         inplay = 0         print 'Starting location collision, Game Over.  Press r to reset or q to quit.'         breakdef reset():   global grid   global mainGroup   global inplay   inplay = 1   movespeed = md   #loop through width, appending a sprite object for each column   #loop through the height for each column, appending a sprite for each row   gridwidth = md   gridheight = md   grid = []   for x in range(gridwidth):      for y in range(gridheight):         grid.append(Sprite([x*md,y*md],WHITE))   #generate randomized field.  may not be used for long   # for coordinate in random.sample(grid,md*2):   #    coordinate.color = BLUE   #generate border   for coordinate in grid:      if (0 in coordinate.pos) or (md*md-md in coordinate.pos):         coordinate.color = BLACK    #control block   mainGroup = gridGroup(grid[0], grid[1], grid[2], grid[3])   mainGroup.moveAbsolute([md*(md/2),md*(md/2)])   mainGroup.color(BLUE)   mainGroup.arrange(0)def render():   global grid   tile.fill(GREEN)   tilewhite.fill(WHITE)   for block in grid:      tile.fill(block.color)      background.blit(tile,block.pos)   #print main sprite group   for block in mainGroup.group:      tile.fill(block.color)      background.blit(tile,block.pos)#initialization variablesreset()tile = pygame.Surface((md,md))tilewhite = pygame.Surface((md,md))background = pygame.display.get_surface()background.fill(BLACK)while 1:   render()   pygame.display.update()   #event handler loop   for event in pygame.event.get():      #process keydown events      if event.type == 2:         #control/command keys         if event.key == 113: #exit system, q            sys.exit()         elif event.key == 273: #up - increase rendering speed            movevector = 'up'         elif event.key == 274: #down - decreased rendering speed            movevector = 'down'         elif event.key == 276: #left             movevector = 'left'         elif event.key == 275: #right            movevector = 'right'         elif event.key == 111: #toggle output            output = not output         elif event.key == 45: # minus             movespeed -= md         elif event.key == 61: # plus            movespeed += md         elif event.key == 114: # r - reset game            if inplay:               print 'game in progress, lose first.'            else:               print 'Reset triggered.'               reset()         #flip color on selected grid         elif event.key == 32: #space - flip to black            stop()         #movement keys         if inplay:            if event.key == 119: #w - up               mainGroup.moveRelative('up') if movevector != 'down' else mainGroup.rotate()            elif event.key == 115: #s - down               mainGroup.moveRelative('down') if movevector != 'up' else mainGroup.rotate()            elif event.key == 97: #a - left               mainGroup.moveRelative('left') if movevector != 'right' else mainGroup.rotate()            elif event.key == 100: #d - right               mainGroup.moveRelative('right') if movevector != 'left' else mainGroup.rotate()         else: #unmapped key            print event.key   # automove   mainGroup.moveRelative(movevector) if count%2 == 0 and inplay else 0   count += 1   time.sleep(speed)`

**note on my version: the controls use WASD for movement/flipping and up/down/left/right to change the direction the blocks fall. It's not what i would call 'fun', but was a good exercise nonetheless.

### Re: Tetris variants: post em here!

Posted: Thu Jan 24, 2013 8:50 pm UTC
I'm really glad you posed this. I was home with the flu this week and this prompted me to created my own tetris clone in ruby. I've still got some polishing to do, but this gave me something to get excited about.

Thanks!

### Re: Tetris variants: post em here!

Posted: Sun Feb 03, 2013 8:55 pm UTC
Well this thread has inspired me to try to create my own Tetris clone, in JavaScript (I'm a web developer, so this was my language of choice). There's still a bunch of stuff to fix, but the current version can be seen at http://js-tetris.levacic.net/ (as soon as the subdomain propagates - the IP is 5.153.9.52 if someone feels like testing it before that happens), and the code is hosted at https://github.com/levacic/js-tetris

I have no idea which license to use, there are so many free-ish licenses to choose from, and I've never gotten around to reading about them. I know I don't have to do it, but if anyone has a tip, I'm open to suggestions.

It currently runs smooth only under Chrome, since it was hacked up together quickly, with a primary focus on making it work, but I'm aware of the primary cause of the slowness in other browsers, and will get around to fixing it soon.

### Re: Tetris variants: post em here!

Posted: Wed Feb 06, 2013 10:37 pm UTC