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.
Monday, July 23, 2012
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.
mymazeclass.py
mazeprogram.py
$ ./mazeprogram.py 40 40
Good Code Vs Bad Code comparison:
Take care of your code!!!
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
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.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!!!
Saturday, June 2, 2012
Sed Back References: Defining Regions
Sed has the ability to define regions in the regular expression definition area. This regions can be then back referenced using the special characters "\1" to "\9". To define a region it has to be surrounded with parentheses.
As an example, lets say you have a nine characters long line that has to be splitted into three three-character regions:
Note I'm passing the "-r" argument to sed, otherwise I would have to escape the parenthesis characters to prevent sed to take them as literals.
Following is a "real life" example. I have a plain text file with email addresses and contact names with this format:
name_1:last_name_1:mail_1@domain.com
name_2:last_name_2:mail_2@domain.com
name_3:last_name_3:mail_3@domain.com
name_4:last_name_4:mail_4@domain.com
This could be difficult to read as the file grows. With just one line using sed defining regions, the output of a cat command can be formatted in a "human readable" format:
Thunar Custom Actions: Resize and Rotate Images with Convert
Here I'm posting two simple bash scripts I use along with Thunar Custom Actions to resize and rotate images. Both of them work for on or more selected files.
Rotated images will be changed in place, resizing images will keep the original file and prepend "resized_to_<percent>" to the resized image, where <percent> indicates the selected reduction percent applied.
resize script:
1 #!/bin/bash 2 3 PERCENT="$1" 4 shift 5 while (( "$#" )); do 6 convert "$1" -resize "$PERCENT%" -quality 100 "resized_to_${PERCENT}%_$1" 7 shift 8 done 9 10 exit 0
rotate script:
1 #!/bin/bash 2 3 4 DIRECTION="$1" 5 shift 6 7 case "$DIRECTION" in 8 "r") 9 while (( "$#" )); do 10 convert -rotate 90 -quality 100 "$1" "$1" 11 shift 12 done 13 ;; 14 "l") 15 while (( "$#" )); do 16 convert -rotate -90 -quality 100 "$1" "$1" 17 shift 18 done 19 ;; 20 esac 21 22 exit 0
Adding Custom Actions
Take a look at this thunar wiki article it describes how to add custom actions to Thunar.The resize script will need the desired resizing percent as an argument.
The rotate script needs the desired rotate direction (l or r), it always rotates in 90° steps.
Openbox Window Manager: Oblogout
I'm using Oblogout to shutdown, restart, etc. from Openbox. It looks very nice and it's easy to configure and customize.
To configure it you will have to edit the "oblogout.conf" file. In Archlinux this file is placed in the "/etc" directory.
To shutdown and restart the system i'm using the good old "shutdown" command, "xlock" to lock the screen and "openbox --exit" to log out.
out.sh
To be able to restart/shutdown without root privileges I added following lines at the end of the "/etc/sudoers" file.
Remember to edit the "sudoers" file using "visudo"
out.sh
oblogout screenshot:
To configure it you will have to edit the "oblogout.conf" file. In Archlinux this file is placed in the "/etc" directory.
To shutdown and restart the system i'm using the good old "shutdown" command, "xlock" to lock the screen and "openbox --exit" to log out.
1 [settings] 2 usehal = false 3 4 [looks] 5 opacity = 70 6 bgcolor = black 7 buttontheme = oxygen 8 buttons = cancel, logout, restart, shutdown, lock 9 10 [shortcuts] 11 cancel = Escape 12 shutdown = S 13 restart = R 14 logout = L 15 lock = K 16 17 [commands] 18 shutdown = sudo shutdown -h now 19 restart = sudo shutdown -r now 20 lock = xlock & 21 logout = openbox --exit
To be able to restart/shutdown without root privileges I added following lines at the end of the "/etc/sudoers" file.
Remember to edit the "sudoers" file using "visudo"
92 %power ALL= (ALL) NOPASSWD: /sbin/shutdown 93 %power ALL= (ALL) NOPASSWD: /sbin/reboot 94 %power ALL= (ALL) NOPASSWD: /sbin/halt
oblogout screenshot:
Monday, May 21, 2012
Sub Directories Tree Script: Sed
When working on the shell sooner or later one realizes that "sed" is a basic tool that although it seems to be a bit confusing at first glance, if you learn how to use it, on the long run will help you a lot.
The best way to learn how to use sed for me is by examples. So here is a script that shows all sub directories in a path with a tree-like look.
It's based on a script by Dem Pilafian I found somewhere on the Internet.
The script shows the sub directories tree on the current path if no argument is provided or the sub directories tree of the provided path.
Script code:Usage: ./lstree.sh [<path>]
1 #!/bin/bash 2 3 # if path is provided then list directories in path 4 if [ ! -z "$1" ] && [ ! -d "$1" ]; then 5 echo "$1: directory not found" 6 exit 1 7 elif [ ! -z "$1" ]; then 8 cd "$1" 9 fi 10 11 # show actions 12 pwd 13 echo ' |' 14 ls -R | grep ':$' | sed -r 's/:$//' | sed -r 's/[^/]*\//---/g' | sed -r 's/(-*)-/ |\1>/' | sed '/\./d' 15 16 # check if no folders 17 if [ `ls -F -1 | grep "/" | wc -l` = 0 ] 18 then echo "-> no sub-directories" 19 fi 20 21 exit 0Sample output:
Line 14 is the line that does all the "magic":
- sed #1: removes the trailing colons
- sed #2: replaces sub directories with "---"
- sed #3: replaces the first and the last "-" with "|" and ">" respectively
- sed #4: removes the "." line (current directory)
Sometimes it is hard for me to figure out what is the regular expression defined in a sed command actually matching.
Lets take the 2nd sed and pipe a fake path to it:
All regular expression matches have been replaced because of the "g" at the end of the sed command. Now, replacing the "g" with numbers starting from 1 to N, one can see what are the single substitutions that have been made:
That's a simple way to check what the regular expression defined in the sed command is actually matching.
As I said at the beginning of this post, spend some time learning sed!
Wednesday, May 16, 2012
Painless Backup with Bash Script
This is a bash script I made to make my files backup process a little bit easier. The script has four basic options:
- Show usage
- Create backup
- Save the backup on my backup drive on remote machine
- Retrieve backup from remote machine
The script is written to backup the files and directories I want in my computer but it should be pretty easy to configure it to suit your needs.
I don't want this post to be too long so I'm not posting here the whole text. If you want to download the whole script, here is the link.
Run the script with the "b" option to see a brief description of usage, options and actions.
Here are just a few comments about how it works and how to modify it to make you own backups.
Here are just a few comments about how it works and how to modify it to make you own backups.
Configuration
Lines 23-52 determine what files and directories to backup, what programs to use, etc. Modify these lines to suit your needs.
23 DATE=$(date --rfc-3339=date) 24 BASE="$HOME" 25 BASE_SYS="/etc" 26 HOME_CONF_DIR=".config" 27 BACKUP_DIR="backup" 28 SYNC_DIR="sync" 29 DOCS_DIR="documents" 30 DOWN_DIR="downloads" 31 PACK_DIR="packages" 32 PICS_DIR="pictures" 33 SCRIPTS_DIR="scripts" 34 VIDS_DIR="videos" 35 LOCAL_HOST="$(uname -n)" 36 BACKUP_HOST="linuxmachine" 37 BACKUP_HOST_BASE_DIR="/mnt/backup" 38 TAR_OPTS="cf" 39 SYNC_OPTS="-avz --human-readable --progress" 40 BZIP_OPTS="-9 -v -f" 41 MIN_PARM_NUM=1 42 MAX_PARM_NUM=2 43 PROGS_LIST="bzip2 rsync" 44 45 # options 46 VALID_MODES=(b s r h) 47 VALID_ACTIONS=(home documents downloads packages pictures scripts videos conffileshome conffilessys conffilesall full) 48 49 # What will be backed up. Files and directories 50 CONF_DIRS_HOME=$(echo "$HOME_CONF_DIR/"{openbox,tint2,conky}) 51 CONF_FILES_HOME=".Xresources .xinitrc" 52 CONF_FILES_SYS=$(echo "$BASE_SYS/"{mkinitcpio.conf,inittab,fstab,vimrc,rc.conf,bash.bashrc, modprobe.d/modprobe.conf})
Backing up
Making a "full" backup which includes home directories, user's configuration files and system wide configuration files:Sync with remote host
Retrieving backup from remote host
Main part description
Basically the script works as follows:- parameter check
- options check
- check programs
- perform requested action
254 ##################################################################### 255 ########################## parameter checks ######################### 256 ##################################################################### 257 258 # check parameters and programs 259 check_parm_num $# $MIN_PARM_NUM $MAX_PARM_NUM 260 [[ $? != 0 ]] && usage 261 check_valid_parm $1 $2 262 [[ $? != 0 ]] && usage 263 check_programs "$PROGS_LIST" 264 265 ##################################################################### 266 ############################# main part ############################# 267 ##################################################################### 268 269 270 # builds the list of actions to be done (only needed when backing up) 271 [[ "$1" == "b" ]] && build_action_list "$2" 272 273 # perform requested actions 274 case "$1" in 275 "b") 276 # create backup directory for current date and localhost 277 [[ -d "$BASE/$BACKUP_DIR/$LOCAL_HOST/$DATE" ]] || mkdir -p "$BASE/$BACKUP_DIR/$LOCAL_HOST/$DATE" 278 cd "$BASE/$BACKUP_DIR/$LOCAL_HOST/$DATE" 279 for action in $ACTION_LIST; do backup "$action"; done 280 ;; 281 "s") 282 sync "$BACKUP_HOST" "$BACKUP_HOST_BASE_DIR" "$LOCAL_HOST" 283 ;; 284 "r") 285 retrieve "$BACKUP_HOST" "$BACKUP_HOST_BASE_DIR" "$BACKUP_DIR" "$LOCAL_HOST" 286 ;; 287 "h") 288 usage 289 ;; 290 esac
I tried to keep the main part as small and clear as possible by using functions. This functions are commented including parameters that they need and what they do.
For instance here is the "build_action_list":
build_action_list.sh
Any comments or questions?
Hope this will make your backing up a little bit easier.
For instance here is the "build_action_list":
59 #builds a list of "actions" (what is going to be backed up) 60 # $1: action 61 function build_action_list () { 62 case "$1" in 63 "full") 64 ACTION_LIST="documents downloads packages pictures scripts videos conffileshome conffilessys" 65 ;; 66 "home") 67 ACTION_LIST="documents downloads packages pictures scripts videos" 68 ;; 69 "conffilesall") 70 ACTION_LIST="conffileshome conffilessys" 71 ;; 72 *) 73 ACTION_LIST="$1" 74 ;; 75 esac 76 77 return 0 78 }
Any comments or questions?
Hope this will make your backing up a little bit easier.
Subscribe to:
Posts (Atom)












