Random Linux Fact #7

13 07 2009

Ensuring Two Directories Contain the Same Files

This is a quick and dirty method

I was in the process of copying a rather large folder from my laptop to an external harddrive when the external got unplugged, halting the copying process. I was lucky enough that the bulk of the files were located in five folders in the top level of the directory I was copying. Quickly scanning through the contents of each of those five folders, I decided that there were only two that looked sparse. After re-copying those two folders into the top directory, I wanted to ensure that the original directory and the copied one contained everything exactly. This is what I did (If dir1 was copied to dir2):

ls -R dir1 > list1; ls -R dir2 > list2; md5sum list1 list2; rm list1; rm list2

In case it’s not obvious, this lists the files/folders recursively in dir1, storing the result in list1. The same is done for dir2. The results are then compared — If the result from the md5sum is the same, then the directories are exactly the same (or more technically, they have the same structure in terms of file/folder names). The “rm” commands remove the unnecessary lists.

This is a superficial way to compare directory equivalence.

Brief Review of Ubuntu 9.04

28 04 2009

Jaunty Jackalope is out! Here is a brief (i.e. neurotic and in list form) review of Ubuntu 9.04.

Remember that I’m comparing 9.04 with Linux Mint 5, which is compatible with Ubuntu 8.04. I’m only listing features that I deem important. Also realize that I’m writing this from the LiveCD; I have not actually installed the system. If you don’t like curse words, go away. I don’t want your kind here. I’m on coffee overdose, don’t mess with me. ;)

Upstream improvements from GNOME:
- Totem can download Subtitles for movies. That’s totally legal, right?
- PulseAudio probably still sucks (go back to OSS! It’s GPL again!). But now it has per-application volume control. That’s kinda cool in a “why would I use that?” way.
- Monitors/projectors are easier to set up, apparently.
- Yeah, that’s about it.

Ubuntu’s new features:
- includes OpenOffice 3.0. Thank GOD.
- includes Firefox 3.0, GIMP 2.6.6
- support for ext4 file system
- And again, that’s about it.

Some notes…

The Good:
- TONS faster. TONS. Props to everyone involved in making this possible.
- Really good themes included with the distro, my favorite being “Dust” (minus the poo-poo-brown selection color).
- Subpixel smoothing is default on my laptop! Hooray! No ugly font!
- Great-looking login screen.
- BEAUTIFUL notification system. When connecting to a wireless network or ejecting a USB, instead of an ugly text bubble, you see a sexy, semi-transparent notification which is on-par with OS X.
- Shutdown/Logout moved to upper-right. I’m not sure how much I like this move. What I do like is that upon clicking “Shut Down”, the machine will automatically power off after 60 seconds.

The Bad:
- Does not come with a graphical FTP client.
- The space on the laptop’s touchpad dedicated to scrolling (in Synaptics driver) is way too small. I literally have to smash my finger against the edge to scroll. Maybe my touchpad is too small?
- Scrolling on the Desktop switches workspaces. I believe this was also in Hardy Heron. I would rather not have that functionality.
- I thought that GIMP had fixed the “OMG THREE APP WINDOWS!” problem… or was that just wishful thinking?
- Firefox does not know the words “movie” or “Firefox”

The Ugly:
- The default Ubuntu theme. Including all TWO wallpapers included with the distribution. They were on track to matching Fedora’s art with Hardy Heron’s wallpaper, but NOOOO, they had to go for ugly. Can’t you get rid of the trivial games and put some decent artwork in the distro?
- Totem. Why THE FUCK are we still using that abomination?

I was really excited for this release and was looking to replace Linux Mint 5 on my computer with the latest-greatest software, but Ubuntu failed to meet my expectations. There was nothing ground-breaking or mind-boggling about it. I like the snappiness that this edition brings, but the better speed and subtle interface improvements can wait until I try Linux Mint 7, which by its roadmap, looks like it’s going to be a great release. Though I’ll probably still stick to my LTS release if it doesn’t turn out to be all that great.

Thoughts on Bad Web Design

19 04 2009

In my mind, there’s not much difference between a bad web design and a bad company. I usually associate the former with the latter; if your website is shit, I’m not going to bother. I make these judgments universally — be it with a store where I am a potential customer or a company for which I am a potential employee. I will not bother.

There are four big tip-offs that I shouldn’t mess with a company. Three of these have to do with web design. The last has to do with professionalism. I usually don’t take into account how pretty a website looks. The prettier the better, but I’ll take a plain website over a garbage one anyday. Qualifications for garbage websites (and therefore garbage companies) are:

If I can’t read your website because of the contrast of colors (or lack thereof), I’m not going to try. If your website is completely in Flash, I’m not going to try. Goodbye. (You heard me right, I’ll never work for Dior.)

If your website is cluttered, I will be annoyed. I might trudge through it if I don’t have to think too hard about where to look.

If pixelated graphics are laden throughout, I will be peeved. It’s not that hard to make sure a picture fits. I would like to point out at this time that you should not stretch your graphic non-proportionally; IT WILL LOOK LIKE SHIT. And don’t use *.gif files. You really don’t want God to kill all those precious kittens.

And finally, if your website has grammatical errors, I will not think twice about hitting that great big “X” in the sky to eternally banish you from my web browser. Even if I forgive you for all the other times I wanted to gouge my eyes out, I can’t forgive unprofessionalism. Even if you don’t know how to build a website, you should know how to write. Goodbye.

I would like to point out a website that passes the readability test — unfortunately — but does not pass any other test. I won’t even forward you to the home page; how’s ’bout the About page: http://www.mensusa.com/page1.aspx … Just read the first three sentences…

You can tell something’s wrong when you see /page1.aspx instead of /about for an About page.

Just browsing around the site, you can tell that it belongs in some sort of hall of fame.

I know this site got ragged on by Photoshop Disasters, but I can’t resist.

So ends my spiel. Learn from it.

Well, Well, Well…

3 04 2009

You learn something new everyday. To quote one of my new favorite links

GIF files have their length defined at the start of the file; any bytes after are ignored. ZIP files have a table at the end; anything at the start of the file is ignored. The result is that a file can be both a GIF and a ZIP, just change the extension.

So .gif files are useful after all! … Just not as gif’s.

This picture can be renamed as a .zip, and it will contain the source for DeCss:


Happy April Fool’s!

1 04 2009

Just wanted to quckly blog about Google’s April Fool’s Day joke… They really outdid themselves on this one. They claim to have built an AI program that has evolved to think pandas are adorable; she has almost super-human abilities. The best parts are that her blogspot locks up IE so you can’t close it, and the Google Maps with her on it have a Rick Roll on Redmond :P

LaTeX Math Equation to PNG

24 03 2009

I went hunting a few days ago to find a program that would turn a LaTex typestted math equation into a png. I found Hyperlatex a few days ago, and it works pretty well. I made a bash file to use it, taking a LaTeX math equation as an argument (without the “$” or “$$” surrounding the eqn.). I put these contents into a file called mathtex:

echo “\documentclass{article}
\[" > $file.tex
echo $math >> $file.tex
echo " \]
\end{document}” >> $file.tex
hyperlatex -png $file
convert $pic.png -resize 30% $pic.png
rm $file.makeimage $file.log $file.dvi $file.aux $file.tex $pic.ps

An example would be: ./mathtex “\frac{(r_1+r_2)^2}{r_1^2+2r_1r_2+r_2^2}=1\to(r_1+r_2)^2=r_1^2+2r_1r_2+r_2^2″
Produces eqn1.png:


Imagemagick is required for the resizing. I use a large resolution at first so that it doesn’t look ugly like the hyperlatex examples.

If you sudo gedit /usr/bin/ps2image, you can add “-background white” to the pnmtopng command on line 43 so that the background isn’t transparent.

My Design of a Sliding Block Puzzle

9 03 2009

I spent a ton of time on this, and made a perfect grade on the design! (That usually NEVER happens with my teacher). So here it is on display :P

The most popular sliding block puzzle contains fifteen tiles in a four by four grid, with one slot. The game is solved when all tiles are placed in numerical order, with the blank space in the lower right corner. To achieve this state, the player attempts to slide each tile to its correct position. A common strategy is to solve the first two rows (as these are trivial to place), and then concentrate on the final two rows. Upon solving the puzzle, the player might then randomize the tiles so she can play again.

I will design a sliding puzzle allowing a grid of up to row=10 by col=10. As I first attempt to design the puzzle, I think to have a Tile class with a position (row, col) and a label of row + col + 1. I then realize that a button on a grid will have these properties; there will be no need for such a class, and I will use the term “button” and “tile” interchangeably in my design. I then find that a Board class will not be necessary either, as the board is merely a double array of buttons. Indeed, I realize this puzzle is purely graphical, so I decide my implementation will not need a business layer and subsequently will not
need a Model class. I further realize that there are only two operations of the game: randomize the board and move a tile. I decide that the simplicity of this does not call for an external Controller class. Therefore, I find it is only necessary for me to have two classes: a SlidingBlock class which will display the sliding block puzzle, and a Main class which will input the number of rows and columns and create the SlidingBlock object with those values.

Class Main will take either two integer arguments in the form of Strings or no arguments. No arguments will create a default SlidingBlock object; two arguments will create a SlidingBlock with that many rows and columns. For example, “java Main 7 9″ will create a SlidingBlock with 7 rows and 9 columns.

Class SlidingBlock will have attributes for a default board size, for keeping up with the blank tile’s position, and for a grid of buttons which represent the board. Its constructor will either create a default board or one with the given parameters, and then initialize the board with initBoard().

We initialize the board by first creating and labeling each button in order (making sure the last tile is blankly labeled), and then randomizing the board with randomize().

Randomizing the board will switch the tiles randomly so that the client can play a new game.

Clicking a button will only be of consequence if that button is to the right, left, top, or bottom of the blank tile. When the client clicks one of the tiles adjacent to the blank tile, the labels of those buttons switch and the values which maintain the blank tile’s position update accordingly. If it happens that the blank tile ends up in the bottom right corner, we check the board for accuracy. The board is solved if all tiles are in numerical order. If the board is solved, then we display a congratulatory message and randomize the board for another game. We check if the board is solved with checkBoard().

Class SlidingBlock will implement JFrame and have a GridLayout with a dimension of “row” by “col”. It will have attributes of integers DEFAULT_ROW = DEFAULT_COL = 4, OFFSET = 1, row, col, blankRow, blankCol, where blankRow and blankCol are the row and col position respectively of the blank tile. It will have a String WHITESPACE used for parsing
actionCommands. It will also have a double dimensioned array of JButtons called board. Its default constructor will assign row to DEFAULT_ROW and col to DEFAULT_COL, make a call to super() with a new GridLayout, and then initialize the board with initBoard(). Another constructor will take two integer arguments and set row and col to those values as well as call
initBoard() after creating a new GridLayout. The pseudo-code for the non-default constructor:

row = Integer.parseInt( r )
col = Integer.parseInt( c )
super( new GridLayout( row, col ) )

The method intiBoard() initializes the board. First it initializes the board attribute as a JButton[row][col]. Then for each row r and each col c it creates a new JButton with an action command of “$r $c” and a label of r * c + OFFSET. It puts that button in board in the correct location [r][c] and adds the actionListener for the button. The actionPerformed() will call the method respond() with the actionCommand. It then makes the last JButton the blank tile by calling setText() on board[row-1][col-1]
with a null String and setting blankRow to row-1 and blankCol to col-1. Finally, initBoard() calls randomize() to shuffle the tiles. The pseudo-code for initBoard() follows:

board = new JButton[row][col]
for r from 0 to row:
. . for c from 0 to col:
. . . . set button's actionCommand to "$r $c"
. . . . set button's text to r * col + c + OFFSET
. . . . board[r][c] = button
. . . . add actionListener to button where actionPerformed calls respond() with the actionCommand
. . . . add(button)
. . set the text of the button at board[row-1][col-1] to ""

To randomize the board, I decide to swap the blank tile with an adjacent tile for row * col * 10 times. Choosing this random tile means choosing if the row or column will remain the same as the tiles, and then choosing whether to pick the tile before or after the blank tile on that row or column. At first glance, intuition tells me that swapping two places will mean switching two JButtons in board. Upon further examination, I realize I will be able to switch just the label and the actionCommand. However, switching the actionCommands would mean that the JButton would have an incorrect position describing it, as its
position and actionCommand are one and the same. Indeed, I realize that it does not matter what the position of the adjacent tile is with which I switch. The only tile whose position interests us is the blank tile — the relative position of the clicked tile to the blank tile is how we determine a move. So we switch the labels of the two tiles in a board for row * col * 10 times, and to deal with the blank tile, we set blankRow to the other tile’s row number, and blankCol to the other tile’s col number. The tile is blank if its label is a null String. I omit the minute detail of actually choosing random numbers as this is
language-specific, and I do not know the technical details of Java’s Random object. It strikes me that switching two tiles will be necessary when the client moves a tile, so I decide to have another method swap() which deals with this process. Swap will take two JButton objects blank and other as parameters. I decide that the methods which utilize swap() *must* send the blank tile as the first parameter. The pseudo-code for randomize():

for random number of times between 10 and 100:
. . assign blankTile as the JButton at (blankRow, blankCol)
. . pick random tile randTile adjacent to blankTile
. . swap( blankTile, randTile )

The pseudo-code for swap():

set blankRow and blankCol to the row and col of JButton other.
other.setText( "" )

The method respond() will respond every time a button is clicked. We parse the clicked tile’s actionCommand (cmd) into two integers clickedRow and clickedCol. The only time we act is if clickedRow and the blankRow are the same and their col values are off by one, or if their col values are the same and their row values are off by one. When this condition is true, we would switch the clicked tile and the blank tile with swap( board[blankRow][blankCol], board[clickedRow][clickedCol] ). Then if the blank tile is the last tile in the last position (board[row-1][col-1]), we call checkBoard() which returns a boolean value true if the game is solved or false if not. If it returns true, we tell the player that she won with a JOPtionPane. We then randomize() the board so that the client can play again if she so chooses. The pseudo-code for respond():

String [] tokens = cmd.split( WHITESPACE )
clickedRow = tokens[0]
clickedCol = tokens[1]
if ((clickedRow==blankRow && Math.abs(clickedCol-blankCol)==1)
. ||(clickedCol==blankCol && Math.abs(clickedRow-blankRow)==1)):
. . swap(board[blankRow][blankCol], board[clickedRow][clickedCol])
. . if (blankRow == row-1 && blankCol == col-1 && checkBoard()):
. . . . JOptionPane.showMessageDialog(null, "You won!")
. . . . randomize()

checkBoard() loops through board up to but not including the last tile, comparing each button’s label to the current row added to the current col plus the OFFSET (i.e. the current position). In other words, we make sure that the tile labeled “1″ is in the first position, “2″ in the second, etc. Return false if any of the compares is false. Return true when all are proven true. The pseudo-code for this method:

correct = true
for r from 0 to row (and correct):
. . for c from 0 to col (and correct):
. . . . button = board[r][c]
. . . . correct = button.getText().equals("" + (r*col+c+OFFSET))
return correct


Get every new post delivered to your Inbox.