Tetris variants: post em here!

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

myrcutio
Posts: 44
Joined: Wed Dec 30, 2009 7:28 pm UTC

Tetris variants: post em here!

Postby myrcutio » 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/python

import time, pygame, sys, random, copy
from random import choice

class 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 variables
md = 21 # map density, determines size of play area.  defaults to 30
pygame.init()
screen = pygame.display.set_mode([md*md, md*md])
speed = .07
output = 0
count = 0
movevector = 'down'
movespeed = md
inplay = 1

#define colors
BLACK = (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.'
         break

def 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 variables
reset()
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.
"Lightning must have hit it, and now it won't work in anything but Windows 95."

Faustus runs afoul of Microsoft.

Falling
Posts: 175
Joined: Mon Nov 24, 2008 4:30 pm UTC

Re: Tetris variants: post em here!

Postby Falling » 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!

bittyx
Posts: 194
Joined: Tue Sep 25, 2007 9:10 pm UTC
Location: Belgrade, Serbia

Re: Tetris variants: post em here!

Postby bittyx » 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.

User avatar
Entropy
Posts: 233
Joined: Sun Apr 20, 2008 9:40 pm UTC

Re: Tetris variants: post em here!

Postby Entropy » Wed Feb 06, 2013 10:37 pm UTC



Return to “Coding”

Who is online

Users browsing this forum: No registered users and 12 guests