Thursday, September 27, 2012

Adjust pdf quality with Ghostscript and Bash

Sometimes pdf files are too big to attach them to an email. Here is a simple bash script witch uses Ghostscript to adjust a pdf file quality (and size) to three different levels: low, mid and high.
To see the script usage just type ./pdf-quality.sh on the terminal. Don't forget to give it execution permissions: chmod u+x  pdf-quality.sh

 

Code

.pdf-quality.sh.tmp
  1 #/bin/bash
  2 
  3 
  4 # function to print proper usage
  5 function usage() {
  6   echo "Usage: pdf-quality.sh <quality> <input_file>"
  7   echo "Where <quality> is low, mid or high and <input_file> the pdf file to be adjusted."
  8 }
  9 
 10 
 11 # check parameter number
 12 if [[ $# != 2 ]]; then usage; exit 1; fi
 13 
 14 # set variables
 15 QUALITY="$1"
 16 INFILE="$2"
 17 OUTFILE="$(echo "$2" | sed 's/.pdf//')-$QUALITY.pdf"
 18 
 19 # perform action depending on the given parameter
 20 case "$QUALITY" in
 21   "low")
 22     gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -
                                      sOutputFile="$OUTFILE" "$INFILE";;
 23   "mid")
 24     gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -
                                      sOutputFile="$OUTFILE" "$INFILE";;
 25   "high")
 26     gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/printer -
                                      sOutputFile="$OUTFILE" "$INFILE";;
 27   *)  echo "$1 is not a valid option."
 28     usage
 29     exit 1;;
 30 esac
 31 
 32 echo -e "\nall done!"
 33 exit 0

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

Wednesday, August 1, 2012

python: snake game

Here is my second try to write a simple game in python using pygame. I'm not posting here the whole source code since I don't want the post to be too long. The complete source code can be downloaded from this link.

First lets take a look at the game...

Snake Game Screen Cast:


 

Snake Class: 

Besides pygame, im using three self made classes on the game:
  • snake.py
  • food.py 
  • colors.py
The source code has plenty of comments which should make it quite easy to understand what is going on.
snake.py
  1 #==============================================================================================#
  2 # Name        : snake.py                                                                       #
  3 # Description : The snake class definition for the snake game.                                 #
  4 # Author      : Adrian Antonana                                                                #
  5 # Date        : 29.07.2012                                                                     #
  6 #==============================================================================================#
  7 
  8 # imports
  9 import pygame as pg
 10 from colors import *
 11 
 12 # motion direction constants
 13 UP    = 0
 14 DOWN  = 1
 15 LEFT  = 2
 16 RIGHT = 3
 17 
 18 # block sizes
 19 BLOCK_SIZE       = 30
 20 BLOCK_SIZE_INNER = 20
 21 
 22 # snake class definition
 23 class snake:
 24 
 25   # constructor
 26   def __init__(self,surface,headposx=10,headposy=10):
 27     self.surface = surface
 28     self.length  = 10
 29     self.poslist = [(headposx,y) for y in reversed(range(headposy-self.length+1,headposy+1))]
 30     self.motdir  = RIGHT
 31     self.crashed = False
 32 
 33     # for drawing the snake
 34     self.snakeblock = pg.Surface((BLOCK_SIZE,BLOCK_SIZE))
 35     self.snakeblock.set_alpha(255)
 36     self.snakeblock.fill(GREEN)
 37     self.snakeblockdark = pg.Surface((BLOCK_SIZE_INNER,BLOCK_SIZE_INNER))
 38     self.snakeblockdark.set_alpha(255)
 39     self.snakeblockdark.fill(GREEN_DARK)
 40 
 41     # for removing the snake
 42     self.backblock = pg.Surface((BLOCK_SIZE,BLOCK_SIZE))
 43     self.backblock.set_alpha(255)
 44     self.backblock.fill(BLACK)
 45 
 46   # get snake's head position
 47   def getHeadPos(self):
 48     return (self.poslist[0])
 49 
 50   # get the motion direction
 51   def getMotionDir(self):
 52     return self.motdir
 53 
 54   # get the snake positions list
 55   def getPosList(self):
 56     return self.poslist
 57 
 58   # set the motion direction
 59   def setMotionDir(self,motdir):
 60     self.motdir = motdir
 61 
 62   # increase the snake length by one
 63   def incLength(self):
 64     self.length += 1
 65 
 66   # move the snake updates the positions list and checks if the snake has crashed
 67   def move(self):
 68     motdir = self.getMotionDir()
 69     headpos = self.getHeadPos()
 70 
 71     # update positions
 72     if motdir == UP:
 73       poslist = [(headpos[0]-1,headpos[1])]
 74     elif motdir == DOWN:
 75       poslist = [(headpos[0]+1,headpos[1])]
 76     elif motdir == LEFT:
 77       poslist = [(headpos[0],headpos[1]-1)]
 78     elif motdir == RIGHT:
 79       poslist = [(headpos[0],headpos[1]+1)]
 80 
 81     poslist.extend(self.poslist[:-1])
 82     self.poslist = poslist
 83 
 84     # check if crashed
 85     if self.getHeadPos() in self.getPosList()[1:]:
 86       self.crashed = True
 87 
 88   # check if the snake has crashed
 89   def chrashed(self):
 90     return self.crashed
 91 
 92   # grow the snake. add a new position at the end
 93   def grow(self):
 94     lastpos = self.getPosList()[-1]
 95     self.length += 1
 96     self.poslist.append((lastpos[0]-1,lastpos[1]))
 97 
 98   # draw the snake
 99   def draw(self):
100     skb = self.snakeblock
101     skbd = self.snakeblockdark
102     sf = self.surface
103 
104     for blockpos in self.getPosList():
105       sf.blit(skb,(blockpos[1]*BLOCK_SIZE,blockpos[0]*BLOCK_SIZE))
106       sf.blit(skbd,(blockpos[1]*BLOCK_SIZE+5,blockpos[0]*BLOCK_SIZE+5))
107 
108   # delete the snake
109   def remove(self):
110     bkb = self.backblock
111     sf = self.surface
112 
113     # draw block for every snake position
114     for blockpos in self.getPosList():
115       sf.blit(bkb,(blockpos[1]*BLOCK_SIZE,blockpos[0]*BLOCK_SIZE))

Monday, July 23, 2012

mazes: just for fun

Here is a small screencast showing some randomly generated mazes. I slightly modified the code I posted on my previous post to show the solution path step by step as it is created.

Friday, July 20, 2012

python: maze generation and solution

Here I'm posting a maze generation and resolve class written in python. I wanted to learn python and generating and solving mazes is a good exercise to start with.
In addition to the maze class I've written another script using pygame to show the maze and its solution path in a window.
This is my first try with python, there is for sure a best way of doing things. Please feel free to comment and suggest!

The generation algorithm creates a "Perfect" maze. This means that, assuming I wrote it correctly, for every pair of cells chosen there is always exactly one path connecting them. No loops and no isolated regions.
There are many ways for solving mazes, I choose a "Shortest Path Finder" here.

This is a very good website with tons of information about maze-types, generation algorithms, solution algorithms, etc.

Maze Class: Crate, solve and display on terminal

mymazeclass.py
  1 #=============================================================================#
  2 # Name        : mymazeclass.py                                                #
  3 # Autor       : Adrian Antonana                                               #
  4 # Date        : July, 20 2012                                                 #
  5 # Description : Maze class. Constructor generates random perfect maze. Maze   #
  6 #               can also be solved.                                           #
  7 #=============================================================================#
  8 
  9 
 10 # imports
 11 import random
 12 import sys
 13 
 14 
 15 # constants and help for list access
 16 BOTTOMWALL = 0
 17 RIGHTWALL = 1
 18 VISITED = 2
 19 UP = 0
 20 RIGHT = 1
 21 DOWN = 2
 22 LEFT = 3
 23 
 24 
 25 # maze class definition
 26 class Maze:
 27 
 28   #object constructor
 29   def __init__(self,rows,cols):
 30   
 31     # set maze size, walls and visited values
 32     self.rows = rows
 33     self.cols = cols
 34     self.maze = [[[True,True,False] for j in range(cols)] for i in range(rows)]
 35 
 36     # set current position, start and end point
 37     # start and end points are used to solve the maze
 38     # currrow is used to generate the maze
 39     self.startrow = random.randrange(rows)
 40     self.startcol = random.randrange(cols)
 41 
 42     self.endrow = random.randrange(rows)
 43     self.endcol = random.randrange(cols)
 44 
 45     currrow = self.startrow
 46     currcol = self.startcol
 47 
 48     # The searh can be quite deep
 49     if rows*cols > sys.getrecursionlimit():
 50       sys.setrecursionlimit(rows*cols+10)
 51 
 52     # generate the maze with depth-first algorithm
 53     self._gen_maze(currrow,currcol)
 54 
 55     # number matrix for solving
 56     self.numtable = [[-1 for j in range(cols)]for i in range(rows)]
 57 
 58     #solution path
 59     self.solutionpath = []
 60 
 61   #-----------------------------------------------------------------------------
 62 
 63   # returns the maze in ascii characters for printing on terminal
 64   def __str__(self):
 65 
 66     # the upper wall first
 67     outtable = '.'+self.cols*'_.'+'\n'
 68 
 69     for i in range(self.rows):
 70       outtable += '|'
 71 
 72       for j in range(self.cols):
 73         if self.maze[i][j][BOTTOMWALL]:
 74           outtable += '_'
 75         else:
 76           outtable += ' '
 77         if self.maze[i][j][RIGHTWALL]:
 78           outtable += '|'
 79         else:
 80           outtable += '.'
 81 
 82       outtable += '\n'
 83 
 84     outtable += 'Start Point   : ('+str(self.startrow)+','+str(self.startcol)+')\n'
 85     outtable += 'End Point     : ('+str(self.endrow)+','+str(self.endcol)+')\n'
 86 
 87     return outtable
 88 
 89   #------------------------------------------------------------------------------
 90 
 91   # get a list with posible directions from the current position
 92   def _get_dirs(self,r,c):
 93     dirlist = []
 94 
 95     # check limits
 96     if r-1 >= 0           : dirlist.append(UP)
 97     if r+1 <= self.rows-1 : dirlist.append(DOWN)
 98     if c-1 >= 0           : dirlist.append(LEFT)
 99     if c+1 <= self.cols-1 : dirlist.append(RIGHT)
100 
101     return dirlist
102 
103   #------------------------------------------------------------------------------
104 
105   # generates the maze with depth-first algorithm
106   def _gen_maze(self,r,c,d=None):
107     maze = self.maze
108 
109     # knock down the wall between actual and previous position
110     maze[r][c][VISITED] = True
111     if   d == UP    : maze[r]  [c]    [BOTTOMWALL] = False
112     elif d == DOWN  : maze[r-1][c]    [BOTTOMWALL] = False
113     elif d == RIGHT : maze[r]  [c-1]  [RIGHTWALL]  = False
114     elif d == LEFT  : maze[r]  [c]    [RIGHTWALL]  = False
115 
116     # get the next no visited directions to move
117     dirs = self._get_dirs(r,c)
118 
119     # random reorder directions
120     for i in range(len(dirs)):
121       j = random.randrange(len(dirs))
122       dirs[i],dirs[j] = dirs[j],dirs[i]
123 
124     # make recursive call if the target cell is not visited
125     for d in dirs:
126       if d==UP:
127         if not maze[r-1][c][VISITED]:
128           self._gen_maze( r-1,c,UP )
129       elif d==DOWN:
130         if not maze[r+1][c][VISITED]:
131           self._gen_maze( r+1,c,DOWN )
132       elif d==RIGHT:
133         if not maze[r][c+1][VISITED]:
134           self._gen_maze( r,c+1,RIGHT )
135       elif d==LEFT:
136         if not maze[r][c-1][VISITED]:
137           self._gen_maze( r,c-1,LEFT )
138 
139   #------------------------------------------------------------------------------
140 
141   # solve the maze by filling it with numbers(algorithm name?)
142   def _solve_maze_aux(self,r,c,n):
143     maze = self.maze
144     numtable = self.numtable
145     numtable[r][c] = n
146 
147     # check if the end has been reached
148     if (r,c) != (self.endrow,self.endcol):
149       directions = self._get_dirs(r,c)
150 
151       # recursive calls only if there is no wall between cells and
152       # targel cell is not marked (=-1)
153       for d in directions:
154         if   d==UP    and not maze[r-1][c][BOTTOMWALL] and numtable[r-1][c] == -1:
155           self._solve_maze_aux(r-1,c,n+1)
156         elif d==DOWN  and not maze[r][c][BOTTOMWALL]   and numtable[r+1][c] == -1:
157           self._solve_maze_aux(r+1,c,n+1)
158         elif d==RIGHT and not maze[r][c][RIGHTWALL]    and numtable[r][c+1] == -1:
159           self._solve_maze_aux(r,c+1,n+1)
160         elif d==LEFT  and not maze[r][c-1][RIGHTWALL]  and numtable[r][c-1] == -1:
161           self._solve_maze_aux(r,c-1,n+1)
162 
163   #------------------------------------------------------------------------------
164 
165   # get the solution path
166   def _get_solution_path(self):
167     actrow = self.endrow
168     actcol = self.endcol
169     startrow = self.startrow
170     startcol = self.startcol
171     path = []
172     numtable = self.numtable
173     path = self.solutionpath
174 
175     while (actrow,actcol) != (startrow,startcol):
176       path.append((actrow,actcol))
177       directions = self._get_dirs(actrow,actcol)
178       for d in directions:
179         if d== UP:
180           if numtable[actrow][actcol]-1 == numtable[actrow-1][actcol]:
181             actrow -=1
182             break
183         elif d== DOWN:
184           if numtable[actrow][actcol]-1 == numtable[actrow+1][actcol]:
185             actrow += 1
186             break
187         elif d== LEFT:
188           if numtable[actrow][actcol]-1 == numtable[actrow][actcol-1]:
189             actcol -= 1
190             break
191         elif d== RIGHT:
192           if numtable[actrow][actcol]-1 == numtable[actrow][actcol+1]:
193             actcol += 1
194             break
195 
196     path.append((actrow,actcol))
197     path.reverse()
198 
199   #------------------------------------------------------------------------------
200 
201   # solve the maze
202   def solve_maze(self):
203     self._solve_maze_aux(self.startrow,self.startcol,0)
204     self._get_solution_path()

Pygame: Show maze

It is possible to print the maze on the terminal with the above class but it doesn't look very good. So I defined a few functions using pygame to show mazes properly including the solution path.

mazeprogram.py
  1 #!/usr/bin/python2
  2 
  3 
  4 #=============================================================================#
  5 # Name        : mazeprogram.py                                                #
  6 # Autor       : Adrian Antonana                                               #
  7 # Date        : July, 20 2012                                                 #
  8 # Description : Function definitions for drawing a maze in a window using     #
  9 #               pygame and the maze class (mymazeclass.py) I made.            #
 10 #=============================================================================#
 11 
 12 # imports
 13 from mymazeclass import *
 14 from sys import *
 15 import pygame
 16 
 17 
 18 #-------------------------------------------------------------------------------------#
 19 #              function definitions for drawing the maze with pygame                  #
 20 #-------------------------------------------------------------------------------------#
 21 
 22 # generates a window with maze with all cells isolated from each other
 23 def base_window(m):
 24   winwidth = m.cols*CELLSIZE+(m.cols+1)*WALLSIZE
 25   winheight = m.rows*CELLSIZE+(m.rows+1)*WALLSIZE
 26   w = pygame.display.set_mode((winwidth,winheight))
 27   w.fill(BLACK)
 28 
 29   for i in range(m.rows):
 30     for j in range(m.cols):
 31       pygame.draw.rect(w,WHITE,(WALLSIZE+(j*CELLSIZE+j*WALLSIZE),WALLSIZE+(i*CELLSIZE+
 32       i*WALLSIZE),CELLSIZE,CELLSIZE))
 33 
 34   return w
 35 
 36 #--------------------------------------------------------------------------------------
 37 
 38 # knocks down walls from base_window to create the path
 39 def maze_window(m):
 40   w = base_window(m)
 41 
 42   for i in range(m.rows):
 43     for j in range(m.cols):
 44       if not m.maze[i][j][BOTTOMWALL]:
 45         pygame.draw.rect(w,WHITE,(j*CELLSIZE+(j+1)*WALLSIZE,(i+1)*CELLSIZE+(i+1)
 46         *WALLSIZE,CELLSIZE,WALLSIZE))
 47       if not m.maze[i][j][RIGHTWALL]:
 48         pygame.draw.rect(w,WHITE,((j+1)*CELLSIZE+(j+1)*WALLSIZE,i*CELLSIZE+(i+1)
 49         *WALLSIZE,WALLSIZE,CELLSIZE))
 50 
 51   pygame.display.update()
 52   return w
 53   
 54 #--------------------------------------------------------------------------------------
 55 
 56 # paints the solution path in the maze window
 57 def maze_path_window(m,w):
 58   path = m.solutionpath
 59 
 60   # print every cell within the solution path
 61   for index in range(len(path)-1):
 62     actrow = path[index][0]
 63     actcol = path[index][1]
 64     nextrow = path[index+1][0]
 65     nextcol = path[index+1][1]
 66     pygame.draw.rect(w,RED,(actcol*CELLSIZE+(actcol+1)*WALLSIZE,actrow*CELLSIZE+(actrow+
 67     1)*WALLSIZE,CELLSIZE,CELLSIZE))
 68 
 69     # also paint the white spaces between the cells
 70     if actrow == nextrow:
 71       if actcol < nextcol:
 72         pygame.draw.rect(w,RED,((actcol+1)*CELLSIZE+(actcol+1)*WALLSIZE,actrow*CELLSIZE+
 73         (actrow+1)*WALLSIZE,WALLSIZE,CELLSIZE))
 74       else:
 75         pygame.draw.rect(w,RED,(actcol*CELLSIZE+actcol*WALLSIZE,actrow*CELLSIZE+(actrow+
 76         1)*WALLSIZE,WALLSIZE,CELLSIZE))
 77     elif actcol == nextcol:
 78       if actrow < nextrow:
 79         pygame.draw.rect(w,RED,(actcol*CELLSIZE+(actcol+1)*WALLSIZE,(actrow+1)*CELLSIZE+
 80         (actrow+1)*WALLSIZE,CELLSIZE,WALLSIZE))
 81       else:
 82         pygame.draw.rect(w,RED,(actcol*CELLSIZE+(actcol+1)*WALLSIZE,actrow*CELLSIZE+
 83         actrow*WALLSIZE,CELLSIZE,WALLSIZE))
 84 
 85 
 86   # add a different color for start and end cells
 87   startrow = path[0][0]
 88   startcol = path[0][1]
 89   endrow = path[-1][0]
 90   endcol = path[-1][1]
 91 
 92   pygame.draw.rect(w,BLUE,(startcol*CELLSIZE+(startcol+1)*WALLSIZE,startrow*CELLSIZE+(
 93   startrow+1)*WALLSIZE,CELLSIZE,CELLSIZE))
 94   pygame.draw.rect(w,GREEN,(endcol*CELLSIZE+(endcol+1)*WALLSIZE,endrow*CELLSIZE+(endrow+
 95   1)*WALLSIZE,CELLSIZE,CELLSIZE))
 96   pygame.display.update()
 97   
 98 
 99 #================================================================================#
100 #                                Main Program                                    #
101 #================================================================================#
102 
103 # sizes and colors
104 CELLSIZE = 5
105 WALLSIZE = 2
106 
107 WHITE = (255,255,255)
108 BLACK = (0,0,0)
109 RED = (255,0,0)
110 GREEN = (0,255,0)
111 BLUE = (0,0,255)
112 
113 # get the maze size from program arguments
114 rows = int(argv[1])
115 cols = int(argv[2])
116 
117 # generate random maze, solve it
118 maze = Maze(rows,cols)
119 print maze
120 
121 maze.solve_maze()
122 #print maze
123 #print maze.solutionpath
124 
125 # show the maze with the solution path
126 pygame.init()
127 window = maze_window(maze)
128 maze_path_window(maze,window)
129 
130 while True:
131   for event in pygame.event.get():
132     if event.type == pygame.QUIT:
133       sys.exit()

Example Screenshot

Running "mazeprogram.py" an the shell as follows:
    $ ./mazeprogram.py 40 40

Generate + Solve Time

A graphic comparing maze size with generation + resolution time.

Nice Vs Ugly Code

In my first try I wrote some code without really thinking about what data structures I needed and how exactly I wanted to solve the maze generation and solution problems. As a result the "ugly" code is extremely slow when maze size increases. Here is a comparison between both codes execution times.
Good Code Vs Bad Code comparison:
Take care of your code!!!