Friday, June 20, 2008

Memory Mapped Files

I've only recently learned about memory-mapped files in Linux. Until recently, to perform file I/O, I followed the textbook examples of using read and write calls on file descriptors or streams. It wasn't until I browsed the git source (to help with my git clone) that I came across a mysterious mmap() call.

Not only is it more efficient for the reasons given in the Wikipedia article, but it is easier to work with file contents as if they were already in memory. There's no need to learn the syntax and semantics of various read and write calls.

The only tough part is remembering the mmap syntax. But the call can be readily encapsulated. For example, to read a file, I define a function like the following:
void *map_file_to_memory(size_t *len, const char *path) {
int fd = open_or_die(path, O_RDONLY);
*len = get_size_of(fd);
if (!*len) {
close(fd);
return NULL;
}
void *map = mmap(NULL, *len, PROT_READ, MAP_PRIVATE, fd, 0);
if (MAP_FAILED == map) die("mmap failed");
close(fd);
return map;
}
There are concerns with large files, but hopefully madvise() is well-written and can prevent excessive page faults.

Friday, May 16, 2008

Thai Rummy

I've already written about my fascination with playing cards and card games. At last I've filled in a conspicuous gap in my repertoire: a two-player card game I can play for hours.

I've somehow overlooked a family of two-player card games that require only a standard deck and are playable in many situations: Gin Rummy and friends. I had tried playing before but I never appreciated them until recently, when a veteran taught me an interesting variant, and repeatedly beat me easily. At first, that is!

Apparently, this variant is popular in Thailand, but I have yet to see these precise rules described elsewhere, though 500 Rum comes close.

Deal: I believe the first dealer is determined randomly. In following hands, the loser of the previous hand is dealer.

The dealer deals 13 cards to each player, and places the remainder, the stock, face-down. The top card of the stock is placed face-up in between the players to start the discard pile. The non-dealer goes first.

One aims to meld all cards in hand, either in sets: 3 or 4 cards of the same rank, or in runs: 3 or more cards of the same suit in sequence. A card can only be melded once, so it is impossible for it to count towards a set and a sequence. Aces are always high, and 2s are always low, so in particular A-2-3 is an invalid run.

Play: Each turn consists of three phases:

1. Draw: a player can take a card within the discard pile, if they can use it to form a set or run with at least one card in their hand and optionally using discards further up in the pile, and additionally if they take every card lying on top. They must display the meld, by laying it on the table in front of them.

In other words, they take some number of cards from the top of the discard pile, and use the bottommost card taken in a meld, which must then be exposed immediately.

If a player is unwilling or unable to take any cards from the discard pile, they must draw a card from the stock, unless the stock is exhausted in which case the player must pass. If both players pass, the hand ends.

2. Meld: the player may display on the table as many melds as desired from their hand, so long as they have at least one card left in hand. They may add cards to existing melds belonging to either player, but in both cases the cards are placed in front of the player extending the meld.

3. Discard: the player must discard one card. If it is their last card because all their other cards have been displayed, then it is placed face-down on the discard pile and the hand ends. Otherwise it is placed face-up on top of the discard pile and off to one side, so that every card in the discard pile can be seen at all times.

Scoring: A player receives 50 points for placing a card face-down on the discard pile, that is, when they have melded all their cards save one which becomes the discard. They are considered the winner of the hand, even if they ultimately have a lower score. If the game ends but both players still have cards, the winner is the player with the higher score.

A player adds the values of the cards displayed in front of them and subtracts the values of the cards remaining in their hand: cards of rank 2 to 9 are worth 5 points each, 10, J, Q, K are worth 10 points each, and Aces are 15 points each, with two exceptions. Both the Queen of Spades and Two of Clubs are worth 50 points.

Typically a hand ends because one player has melded their entire hand, and only one player subtracts the values of cards from their total. Both players subtract values of cards only when a hand ends because both players have passed.

There are two "stupidity" penalties. Firstly, if a player wins by melding their whole hand after taking just the top card of the discard pile, that is, the opponent's last discard, the opponent subtracts 50 points from their score.

Secondly, during the game, when a player discards a card that could have been used to extend an exposed meld, that player loses 50 points. The penalty is cumulative; each offence costs a player 50 points.

Lastly, there is a doubling rule: if a player does not display any melds except possibly in the final turn, their score for the hand is doubled. So if a player wins by melding their entire hand, and had not previously displayed any melds, their score is doubled. This is almost always beneficial because they usually have a positive score. And if a player wins by melding their entire hand and their opponent has not yet displayed a single meld, the opponent's score is doubled. This is always harmful to the opponent because they must have a negative score, seeing as no melds were made and cards remain in hand.

Winning: The final score is added to a running total. There are two victory conditions. If a player has -500 or fewer points, their opponent wins the game instantly. Otherwise the first player to have 500 points or more after going out wins the game.

More players: if there are a few more players, deal three fewer cards per extra player.

Distinguishing Characteristics

Summary for those familiar with rummy games:
  • Aces always high, 2s always low
  • Can take variable number of cards from top of discard pile; must use the bottommost card taken
  • Must make a new run or set with this card; cannot just extend existing meld
  • Must meld with at least one card in hand with this card
  • Boathouse rule: must discard when going out
  • Hand ends immediately after a player goes out
  • 50 pts for going gin
  • -50 pts for giving opponent single discard used to go out
  • -50 pts for discard card that could augment a displayed meld
  • Score doubled for never displaying melds
  • Values of cards: 2-9: 5 pts, 10-K: 10 pts, A: 15, except QS, 2C: 50 pts.
  • -500 pts loses the game
  • 500 pts insufficient for win; must also go out
Update 20090504: Ricky emailed me about his site, rummy.com, where you can find rules and background information for other rummy variants from around the world. You can also play them online.

Wednesday, March 26, 2008

Thinkpad X61 Wireless and Ubuntu

At last I have resolved a long-standing gripe with Ubuntu on my ThinkPad X61.

Months ago, soon after installing Gutsy Gibbon, I found that my wireless card initially worked well. But a few minutes later, my connection was dead, and I could only restore it by unloading the iwl4965 driver, reloading it, and reconnecting to my access point. Unfortunately, after an indeterminite amount of time the problem would reoccur.

I experimented a little and discovered that by instructing my wireless router to accept anything only 802.11g, the problem vanished. When away from home and using other wireless networks, occasionally my connection would spuriously disconnect. Fortunately, the wireless networks I usually frequent somehow never cause this bug to manifest.

Yesterday my wireless connection was unbearably flaky, and several times, even reloading the driver proved fruitless. I finally decided to look for a proper solution.

Typing dmesg revealed that every time the connection dropped, a message similar to the following would be logged:
[  154.652000] wlan0: No ProbeResp from current AP 04:11:46:fe:24:b0 - assume out of range
Googling for "iwl4965 no proberesp" shows that many others have had the same trouble. Some pages were unanswered bug reports, and others contained discussion where the consensus was to reload the kernel module as a workaround. Happily, before long I stumbled across a FAQ for the ThinkPad T61 that describes a permanent fix, which I'll repeat:
  1. Download the latest iwlwifi microcode
  2. Replace /lib/firmware/2.6.22-14-generic/iwlwifi-4965-1.ucode.
  3. Reload iwl4965 module
The new microcode banished my wireless woes, and the dreaded "No ProbeResp" no longer appears in the logs.

Now I'm left with one remaining significant complaint: the graphics driver. My i965 mostly behaves but there are minor annoyances involving compiz, and major ones involving Wine. I'll leave this for another time.

Thursday, March 20, 2008

Cross-compiling GMP

I build Windows binaries from Ubuntu, using the MinGW port (search for packages starting with mingw).

For the PBC library I need a Windows version of the GMP library, which I build using:
$ ./configure --prefix=~/cross/gmp --host=i586-mingw32msvc --build=local
$ make install
I don't fully understand how it works, but when it's over, I have libraries I can link against to produce Windows executables that can do multi-precision arithmetic.

I sometimes test the binaries with wine. Thus Windows can be completely avoided in the development cycle, from coding to building to testing. However, for an important project, I'd test on at least one real Windows box.

Monday, March 17, 2008

xrandr

In the old days, to get the external VGA output working on my laptop in X, or even switch resolutions, I'd have to edit an overly complex configuration file and restart X. Nowadays, I use the X Resize and Rotate Extension via xrandr, which is considerably more convenient. I like to control displays through the command-line interface. I find typing the full command cumbersome, so I use this simple script instead:
#!/bin/bash
case "$1" in
left)
xrandr --output VGA --auto --left-of LVDS
;;
right)
xrandr --output VGA --auto --right-of LVDS
;;
up)
xrandr --output VGA --auto --above LVDS
;;
down)
xrandr --output VGA --auto --below LVDS
;;
big)
xrandr --output VGA --mode 1600x1200 --above LVDS
;;
off)
xrandr --output VGA --off
;;
*)
xrandr --output VGA --auto --same-as LVDS
;;
esac

Saturday, March 15, 2008

Thinkpad X61 Volume Buttons

I tried in vain to get the brightness adjustment keys to work, but at least the volume up and down buttons were easy to handle. They trigger bona fide X key events, so I added a couple of lines to .xbindkeys:
"amixer -c 0 set PCM 1dB+"
XF86AudioRaiseVolume

"amixer -c 0 set PCM 1dB-"
XF86AudioLowerVolume

Tuesday, March 4, 2008

GP2X Revisited


Soon after my last post, I tried out some simple ideas for smoothing out the spurious stylus events on my GP2X. The results were promising, but unfortunately before I could experiment anymore my device began malfunctioning. The left and right edges of the screen did not respond properly, and the problem quickly worsened. Soon I could only draw on about a third of the display, in a central vertical band.

Luckily it was still under warranty. I sent it back to www.GP2Xstore.com, who promptly passed it on to GamePark Holdings for repair. A few weeks later a replacement arrived. I still can't draw on the extreme left, right and bottom edges, but it works well enough for my primitive drawing program. The improved behaviour due to jitter correction is self-evident.