The Tetris Project

So Glenn,

Where do we get this graphics.py file? Did I miss something?

@zinkeldonk there is a link in the mooc email…

Here is the link:

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-189-a-gentle-introduction-to-programming-using-python-january-iap-2011/assignments/graphics.py

I know I’m running a bit ahead, but I’m trying to figure out how to add colour to my tetris project. MOOC-E suggests to use this code:

rectangle.setFill(‘deep pink’)

But it doesn’t work for me. Am I missing an import of colour scheme or what?

Thank you so much!

Here’s an example that might help. It draws three shapes with various fill colors:

from graphics import *

def draw_i_shape(x, y, position):
    BLOCKSIZE = 50
    if position == "horizontal":
        for i in range(4):
            rect = Rectangle(Point(x + BLOCKSIZE * i, y), Point(x + BLOCKSIZE * i + BLOCKSIZE, y + BLOCKSIZE))
            rect.setFill("red")
            rect.draw(win)
    elif position == "vertical":
        for i in range(4):
            rect = Rectangle(Point(x, y + BLOCKSIZE * i), Point(x + BLOCKSIZE, y + BLOCKSIZE * i + BLOCKSIZE))
            rect.setFill("blue")
            rect.draw(win)
win = GraphWin('Rect', 800, 500)
squareBox = Rectangle(Point(200, 200), Point(400, 400))
squareBox.setFill("yellow")
squareBox.draw(win)
draw_i_shape(100, 50, "horizontal")
draw_i_shape(500, 140, "vertical")
win.mainloop()

I am inclined, at this early stage, to build the Tetris game WITHOUT so many fancy libraries, so we aren’t ahead of ourselves. So I’ll pick it up where you started in your first post and try to build this without extra modules.

There’s a unicode character box, \u2588, that works better than #, I think.

Printing characters in different colors is a key piece. But it is surprisingly very difficult, using Window 7 as I am, to use colors. per http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python, unless you import a library for that.

So already I am stuck with importing a module. Stackoverflow recommends Colorama to solve the problem (http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python/3332860#3332860) and here’s the module page: https://pypi.python.org/pypi/colorama

I was able to install it, and then, following the instructions, I created a new file with:

from colorama import init
init()
from colorama import Fore
print(Fore.RED + '\u2588')

But my output is still blue. Any idea why?

-Bram

Hi @bmoreinis, I posted about why this may not work earlier this week - http://discourse.p2pu.org/t/3rd-course-email-a-little-more-functions-please/609?u=bkglass

@bmoreinis,

Ultimately, the console-based version of Tetris will get bogged down, because each time the user presses an arrow key, or a shape drops down on its own accord, the entire board will need to be re-printed, row-by-row. It will get really slow.

Aside from random, the only imported code needed for the graphical version of the Tetris game is the graphics.py file that we are given. If you really would like the game to be self-contained, without its needing to import graphics, it is possible to scavenge the necessary code from graphics.py, and then to remove the import graphics statement. The scavenging task is a bit of work, but it is doable, though I’m not sure what the terms of use are for the graphics.py file. Just keep it a secret. :wink:

That makes sense, Glenn.

I guess maybe we should not be leaping into Tetris before we’ve mastered concepts like two dimensional arrays … I’ll let Tetris go for now. But initial thwacking at it got me into this Colorama thing that seems like a useful issue to be able to solve.

Here’s the issue I just created on Github: https://github.com/tartley/colorama/issues/60

I have also just put the issue up on the Python IRC channel.

AH - yes, I am a noob. IDLE force-colors text. I need to use PyCharm (an IDLE that respects text coloring)
Thanks to Python IRC! Resolved issue on Github.

Now I’m seeing how important it is to understand the differences between console and command line…

@bmoreinis you can run your script in Windows command prompt or Windows PowerShell and the colored text will work… I generally use Notepad++ and PowerShell for most things anyway.

Whether or not having different colored text will be useful for me remains to be seen… But it was fun seeing something besides ‘white’ for a change. :open_mouth:

So I managed to get the colours working, but now I’m trying to do this week’s (week 4) assignment. I know how to draw squares in graph and colour them. But if I want to make an “F” shaped form, how do I append the final square to the i-shape?

Here:

Doing it square-by-square doesn’t really make sense to me, but I’m not sure how to make this with iterations.

My silly code so far:

rect = Rectangle(Point(50, 50), Point(250, 100))
rect.setFill(“blue”)
rect.draw(win)

rect = Rectangle(Point(250, 50), Point(400, 100))
rect.setFill(“orange”)
rect.draw(win)

rect = Rectangle(Point(500, 50), Point(650, 100))
rect.setFill(“cyan”)
rect.draw(win)

Also this makes for one big rectangle instead of having several separate small squares… Should I make a small 50x50 square and multiply by the number of squares required?

Thanks a lot and sorry for being such a noob. :smile:

… You are absolutely right - and this concept is the motivation behind object oriented programming (OOP).

Note that an object of type Rectangle can be created with the following …

rect = Rectangle(Point(250, 50), Point(400, 100))

… then can be colored by …

rect.setFill("orange")

… and drawn by …

rect.draw(win)

… and so, we need to be able to do something like this in order to deal with the various Tetris shapes …

s = S_shape(4, 7) # create a new S shape at position 4, 7 on the Board
s.draw() #draw the S shape
s.move(3, 2) # move the S shape 3 positions to the right and 2 positions down
s.rotate() # rotate the S shape

To enable ourselves to do that, we first need to write code that, when we need to create the S shape as a unit, we can invoke to create the 4 rectangles that will comprise it, and fill them all with the correct color for that shape. We also need to write code that can draw, move, and rotate the entire shape as a unit.

But before we can do any of that, we will need to do the following work on Codecademy, which is about object oriented programming …

@Glenn I agree using Classes will probably be a good way to go as things progress. I have been struggling
the last couple of weeks trying to wrap my head around them… Classes can seem like a riddle wrapped
in a mystery inside an enigma.

I found this link to be a good beginning Introduction to Classes.

I have worked through most of ‘Learn Python The Hard Way’ up to Exercise 43… But I needed to find more
explanation on Classes. I found a post that uses Exercise 43 as an example to explain in depth about how
Classes work… This was one of the best things I have read concerning Classes. - https://gist.github.com/anonymous/91865cb266659c2c3f24

Hopefully, this is helpful for others learning about Classes.

@vijolica9 I used the code @Glenn had posted as a starting point for the “F” shape in four rotation positions. I am not sure if this on the right track or if what I am doing will be a mess later… But I will be working on the other shapes anyway.

Here is @Glenn’s code that I modified:

# tetris.py mod from @Glenn code
# May 18, 2015
from graphics import *

### function that draws an F shape on the board
### x, y is board location, in pixels, of upper left corner of the shape
### x is distance in pixels from left margin
### y is distance in pixels from top margin
### position is whether it is up, down, right, or left
def draw_F_shape(x, y, position):
    BLOCKSIZE = 50
    if position == "down":
        for i in range(3):
            rect = Rectangle(Point(x + BLOCKSIZE * i, y), Point(x + BLOCKSIZE * i + BLOCKSIZE, y + BLOCKSIZE))
            rect.setFill("red")
            rect.draw(win)
        rect2 = Rectangle(Point(x, y + BLOCKSIZE), Point(x + BLOCKSIZE, y + BLOCKSIZE + BLOCKSIZE))
        rect2.setFill("red")
        rect2.draw(win)
    elif position == "left":
        for i in range(3):
            rect = Rectangle(Point(x, y + BLOCKSIZE * i), Point(x + BLOCKSIZE, y + BLOCKSIZE * i + BLOCKSIZE))
            rect.setFill("blue")
            rect.draw(win)
        rect2 = Rectangle(Point(x - BLOCKSIZE, y), Point(x, y + BLOCKSIZE))
        rect2.setFill("blue")
        rect2.draw(win)
    elif position == "up":
        for i in range(3):
            rect = Rectangle(Point(x + BLOCKSIZE * i, y), Point(x + BLOCKSIZE * i + BLOCKSIZE, y + BLOCKSIZE))
            rect.setFill("green")
            rect.draw(win)
        rect2 = Rectangle(Point(x + BLOCKSIZE * i, y - BLOCKSIZE), Point(x + BLOCKSIZE * i + BLOCKSIZE, y))
        rect2.setFill("green")
        rect2.draw(win)
    elif position == "right":
        for i in range(3):
            rect = Rectangle(Point(x, y + BLOCKSIZE * i), Point(x + BLOCKSIZE, y + BLOCKSIZE * i + BLOCKSIZE))
            rect.setFill("yellow")
            rect.draw(win)
        rect2 = Rectangle(Point(x + BLOCKSIZE, y + BLOCKSIZE * 2), Point(x + BLOCKSIZE * 2, y + BLOCKSIZE * 3))
        rect2.setFill("yellow")
        rect2.draw(win)
            
win = GraphWin('Rect', 800, 500)

draw_F_shape(50, 50, "down")
draw_F_shape(300, 50, "left")
draw_F_shape(400, 100, "up")
draw_F_shape(600, 50, "right")

win.mainloop()

The code we are currently discussing for drawing Tetris shapes is helpful for our learning Python and graphics, but if we are going to switch to an object-oriented approach later, which I have reason to believe will happen, we are going to need to make major revisions to any code we write now. So, we should keep @bkglass’s concern in mind, when he writes “… if what I am doing will be a mess later …”. Well - his code is actually well-written, and not at all a mess, but since we will likely be taking a different approach later on, there will, in fact, be lots of rewriting to do.

What we are doing now regarding Tetris is good practice, and, in my opinion, we should proceed in the direction that the MMOOC has set for us, but should do so with the expectation that whatever we do now will need to be changed later, as we learn new programming techniques. After all, we began by drawing shapes with the console, then advanced toward drawing shapes with graphics, and will advance again toward using classes to represent each type of shape.

We have already been given a taste of object oriented programming. The Rectangle type is defined as a class in the graphics.py file that we were given. In any case, the Tetris project , as it was originally designed in MIT 6.189, provided students with a template that offered lots of guidance for how to complete the project by using an object oriented approach, including detailed instructions for developing each class. So, it will work out well.

To gain a perspective on how Tetris might be implemented, we could look at how MIT 6.189 presented it, but since we have not been assigned the task of doing this yet, we do so without a guarantee that this will be the final form of our project.

Here are some links to the past project:

To put the above material in proper perspective, though, we need a good grounding in object oriented programming. We may be getting a little ahead of the MMOOC sequence, though.

1 Like

@Glenn I agree… But for me, I need to resist looking too far ahead. I need to work through the ‘frustration’ of figuring things out in order to really learn this stuff.

When I was working through Codecademy, there was one person in the forums I found the most helpful. He would never flat out give me the solution for why my code was not working… But instead, he would say things like, “Take a look at how line 14 is actually being evaluated.”

This approach, as frustrating as it was sometimes, really forced me to learn what I was doing. Like the old proverb says - give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime. So no ‘peeking’ for me. :see_no_evil:

However, I do have a question about how this code from the email works:

shapes = [(Point(Point(center.x - 1, center.y),
           Point(center.x, center.y),
           Point(center.x + 1, center.y),
           Point(center.x + 1, center.y + 1)),

I did a search in the graphics.py for ‘center’.

Search "center" (10 hits in 1 file)
  C:\Users\Bernard\Desktop\pymooc\graphics.py (10 hits)
	Line 21: 10 centered in a 100x100 window:
	Line 318:           "justify":"center",
	Line 457:     def getCenter(self):
	Line 498:     def __init__(self, center, radius):
	Line 499:         p1 = Point(center.x-radius, center.y-radius)
	Line 499:         p1 = Point(center.x-radius, center.y-radius)
	Line 500:         p2 = Point(center.x+radius, center.y+radius)
	Line 500:         p2 = Point(center.x+radius, center.y+radius)
	Line 505:         other = Circle(self.getCenter(), self.radius)
	Line 853:     t = Text(Point(5,5), "Centered Text")

I narrowed down to this part:

Line 438:  class _BBox(GraphicsObject):
    		# Internal base class for objects represented by bounding box
    		# (opposite corners) Line segment is a degenerate case.

I understand how the ‘center’ point applies to a circle, or even a square… But how is this working for something like the “J” shape?

This code …

shapes = [(Point(Point(center.x - 1, center.y),
           Point(center.x, center.y),
           Point(center.x + 1, center.y),
           Point(center.x + 1, center.y + 1)),
           	           .
           	           .
           	           .
]

… appears to assume that we have set up a game board in which each Point represents a position on the board in x, y coordinates. In the case of Tetris, each position would be a square. The shapes variable represents a list of positions of squares that make up a single Tetris shape.

Here, the variable center represents a Point and has an x and y coordinate. This center is an arbitrarily chosen Point among the four that make up the shape. Note that the locations of all the other Points are defined relative to that one by adding or subtracting offsets. So, the second Point in the above list would be at coordinates center.x and center.y. The adjacent first, third, and fourth Points also form parts of the shape, which appears to be a J.

This code seems to be converging in the direction of the code for the MIT version of Tetris.

@Glenn I had not looked at the tetris_template.py until now… Apparently that code can came from a child Class of the parent base - class Shape()… Which seems to be getting the block size, outline, and color from the class Block(Rectangle)… Which seems to be a child Class of the class Rectangle() from the graphics.py

It looks like I’m going to need to chew on this template for awhile… a long while.

Yes, that code is from the J_shape class, which is a child of the Shape class. The J_shape and its siblings primarily describe the Tetris shapes by specifying the relative coordinates of their component Blocks, but they inherit lots of functionality (or should we say methodology :wink: ) from Shape, for example, get_blocks, draw, move, and other methods that implement behavior.

Check out the __init__ method of the J_shape class. It calls the __init__ method of its parent Shape class, passing it the coords of its component Blocks and the string, 'orange'. In the __init__ method of Shape, a loop at the end initializes a list of Blocks, each of the specified color, which is 'orange' in the case of the J_shape, and each with its specified coordinates.

I have found that it helps to have a printed copy of the template file, as a reference.

Note how the Instruction handout, 6.189 Final Project – Tetris!, specifies, in detail, the order in which we should add functionality to the various components, so that right from the start, and at every step along the way, the program is functional, until finally we having a working Tetris game.

1 Like

In 1. Tetris Class Overview, within the instructions, we are asked to examine the tetris_template.py file, presumably in IDLE, run it, and make sure an empty board appears on the screen. The board is a gray rectangle, and might get rendered behind the console window, so if you don’t see it, drag the console window to another part of the screen.

Then, in 2. Creating a random shape, we need to add some code to the file to create and draw a shape.

Notice that the class Tetris() code contains a list of shapes:

SHAPES = [I_shape, J_shape, L_shape, O_shape, S_shape, T_shape, Z_shape]

In the create_new_shape method of the Tetris class, we need to replace pass with code that randomly selects a shape from that list, and returns it:

#YOUR CODE HERE
shape_index = random.randint(0, len(self.SHAPES) - 1)
return self.SHAPES[shape_index](Point(int(self.BOARD_WIDTH/2), 0))

Note that the __init()__ method of the Tetris class already contains the line:

self.current_shape = self.create_new_shape()

So, the returned instance has been assigned to self.current_shape. Now, we need to draw that shape on the board. The Board class contains a draw_shape method that we can call:

# draw_shape method in the Board class)
####  YOUR CODE HERE ####
self.board.draw_shape(self.current_shape)

Then, when we run the program, we should see a shape at the top of the board:

If anyone has trouble getting this to happen, we can discuss and debug here.