February 25, 2008

The Glide

I'm starting to get the hang of skiing. As I watched the snow fall all day Friday at work my excitement grew. I had been hoping for one more good snowfall this season so I could get just a little more skiing in.

Friday night we had a dinner party with neighbors, then after that (around 12:30 am) I headed over to the Arboretum for about 20 minutes of skiing. The sledding hill was packed with (fairly uncooperative) people, most of which appeared to be college students. The snow was a little bit too powdery, but it was a good time nonetheless.

Saturday was supposed to be the big day. I ended up heading out to Allerton Schroth trail at about 2 pm. Unfortunately, much of the snow had melted (though it was still below freezing), and it was very spotty. That is, there was lots of bare ground that I had to try to avoid. So it was good, but not great. I thought the chances for good snow on Sunday were pretty slim.

Fortunately, I was proven wrong. The snow was actually fantastic. I again went over to the Arboretum. The snow was just a little icy, but not too much, and it was less spotty than it had been at Allerton on Saturday. In fact, I was skiing so well that I deviated from my original plan of doing a couple of loops at the Arboretum and just kept heading farther and farther out. There is some wooded area just across Windsor road. When I got to Windsor I took the skis off, ran across the road, put the skis back on, and kept right on going. The snow in this area was packed down and frozen just enough that I was able to glide nearly effortlessly along. After I skied in that wooded area for a while I crossed Race street and skied around at Meadowbrook park. I was now a few miles from home.

It felt so hot Sunday that I took my hat off. Then I took my gloves off (yes, skiing without gloves... it was weird). Then I had to roll up my sleeves. I had the foresight to bring a water bottle with me. Just when I reached to the back of my waist pack to get it the lid popped off and all my water spilled onto the snow. Crap. I guess it was time to head home soon anyway.

After lunch, I went back out for a 40 mile bike ride, which was somewhat enjoyable, though fairly boring. Everything looked like this:

You couldn't really tell where the ground ended and the sky began.

February 20, 2008

The Lunar Eclipse

As you probably know, there was a lunar eclipse tonight. I wanted to take pictures, but it was 12˚. Fortunately, I already took pictures of a lunar eclipse on 10/27/2004. Here is a sequence of the photos.

February 17, 2008

The Tour de Groundhog

Last night after dinner my dad told me I was crazy for planning to do a bike race today. Of course, at the time it was 34˚ and raining. Luckily the weather was a little bit better for the Tour de Groundhog today. In fact, the weather started out a little too good, as I had to shed several layers once I started warming up.

I noticed on the drive over to Springfield that there was a lot of standing water/ice on the ground from some recent flooding. It wasn't until I started warming up that I realized what this would mean for the race: mud, and lots of it.

I did the masters 30+ race (which is interesting given that I am 29, but apparently they use your age at the end of the calendar year...). There were only 7 people in the race, so it wasn't too crowed. After about 10 meters the course immediately turned into the woods and from there it was a battle of wills more than an athletic competition. A good 30-40% of the course was unridable. So there was lots of dismounting and running/walking with the bike. About 2 times each lap my tires would literally stop spinning, so I would have to stop and pull a handful of mud out of both my front and rear brakes in order to be able to ride again.

The course map

It certainly was an adventure. I did get lapped by the winner, but that wasn't totally unexpected. I ended up 5th of the 7 people in the masters race, which was good enough to win some socks and water bottles (two things of which I own copious quantities, but still never enough). Just to give you an idea how bad the conditions were, I travelled less than 3 miles in 33 minutes of racing. This is a slower pace than I averaged running for 28.4 miles at the Riddle Run ultramarathon 3 weeks ago. Perhaps my dad knew what he was talking about.

The Birthday Dinner

Yesterday was MomScho's birthday so we took her and my dad out to Manzella's, an Italian restaurant in downtown Champaign.

My brother Travis and his wife Blake who live in Dallas ordered her a fruit basket that we picked up from Edible Arrangements in Champaign.

February 14, 2008

The Heart Shaped Window

On our first Valentine's Day Melissa was mad at me for some reason. So for our second Valentine's Day I decided to make her a really special gift. I stayed up until about 2-3 a.m. (which was quite rare, as I like to get to bed early) the night before writing a computer program that made a heart shaped window with a customized message in it. With the state of software in 1998 this was actually a pretty challenging task, but I worked hard and made something I thought we could both be proud of.

When I showed it to Melissa the next day (2/14/1998) I didn't quite get the reaction I was hoping for. Her attitude was basically that if I hadn't spent so much time working on the program I would have had more time to spend with her. Sheesh.

Anyway, here's what it looked like:

And here's the source code (wow I used to write some really ugly code):

//
// HeartWDEF.c
//

#define HASGOAWAY 0


pascal long main(short, CWindowPtr, short, long);
long  DoDraw(CWindowPtr, long);
long  DoHit(CWindowPtr, long);
long  DoCalcRgns(CWindowPtr, long);
void GetStrucRegion(CWindowPtr window, RgnHandle rgn);
void  GetGoAwayBox(CWindowPtr w, Rect *r);

OSErr PictToRgn(PicHandle pic, RgnHandle rgn){
 int   width = 0, height = 0;
 GrafPtr  mask, savePort;
 Rect   r = (*pic)->picFrame;
 OSErr  err;
 
 width = r.right - r.left;
 height = r.bottom - r.top;
 
 SetRect(&r, 0, 0, width, height);
 //OffsetRect(&r, r.left, r.top);

 mask = (GrafPtr) NewPtr(sizeof(GrafPort));
 OpenPort(mask);
 
 mask->portBits.bounds = r;
 mask->portBits.rowBytes = ((width + 15) / 8);
 mask->portBits.baseAddr = NewPtr(mask->portBits.rowBytes * height);
 
 GetPort(&savePort);
 SetPort(mask);
 
 EraseRect(&r);
 DrawPicture(pic, &r);
 
 SetPort(savePort);
 
 err = BitMapToRegion(rgn, &(mask->portBits));
 
 DisposePtr((Ptr) mask->portBits.baseAddr);
 DisposePtr((Ptr) mask);
 
 return err;
}

pascal long main(short varCode, CWindowPtr w, short message, long param){
 long  val = 0;
 
 switch(message){
  case wDraw:
   val = DoDraw(w, param);
   break;
  
  case wHit:
   val = DoHit(w, param);
   break;
   
  case wCalcRgns:
   val = DoCalcRgns(w, param);
   break;
   
  case wNew:
  case wDispose:
   break;
 }
 return val;
}

long DoDraw(CWindowPtr w, long param){
 long  val = 0;
 WindowPeek wp;
 Rect  r;
 RGBColor save;
 PicHandle pict;
 Point  p;
 WindowPtr window;
 GWorldPtr saveWorld;
 GDHandle device;
 PenState savePen;
 
 GetForeColor(&save);
 GetPenState(&savePen);
 
 PenNormal();
 wp = (WindowPeek) w;
 
 if(param == 0){
  // copy the regions so we can offset them
  RgnHandle diff = NewRgn();
  
  DiffRgn(wp->strucRgn, wp->contRgn, diff);
  
  ForeColor(redColor);
  PaintRgn(diff);
  
  PenNormal();
  /*if(wp->hilited){
   ForeColor(blackColor);
   FrameRgn(diff);
  }*/
  
  DisposeRgn(diff);

#if HASGOAWAY  
  if(wp->hilited){
   GetGoAwayBox(w, &r);
   
   ForeColor(whiteColor);
   PaintRect(&r);
  }
#endif

 }
 
#if HASGOAWAY 
 else if(param == wInGoAway){
  GetGoAwayBox(w, &r);
  InvertRect(&r);
 }
#endif

 SetPenState(&savePen);
 RGBForeColor(&save);
 
 return val;
}

long DoHit(CWindowPtr w, long param){
 long  val = 0;
 WindowPeek wp;
 Point  p;
 Rect  r;
 
 p.h = LoWord(param);
 p.v = HiWord(param);
 
 wp = (WindowPeek) w;
 
 if(PtInRgn(p, wp->contRgn)) val = wInContent;
 
 else if(PtInRgn(p, wp->strucRgn)){
  val = wInDrag;
  
#if HASGOAWAY  
  GetGoAwayBox(w, &r);
  if(PtInRect(p, &r)) val = wInGoAway;
#endif

 }
 else val = wNoHit;
 
 return val;
}

long DoCalcRgns(CWindowPtr w, long param){
 SInt32  val = 0;
 WindowPeek wp;
 PicHandle pic;
 Rect  r;
 RgnHandle rgn;
 
 wp = (WindowPeek) w;
 
 r = w->portRect;
 OffsetRect(&r, - ((WindowPtr)w)->portBits.bounds.left, - ((WindowPtr)w)->portBits.bounds.top);
 
 rgn = NewRgn();
 pic = GetPicture(4096);
 PictToRgn(pic, rgn);
 ReleaseResource((Handle) pic);
 OffsetRgn(rgn, r.left, r.top);
 CopyRgn(rgn, wp->strucRgn);
 DisposeRgn(rgn);
 
 rgn = NewRgn();
 pic = GetPicture(4097);
 PictToRgn(pic, rgn);
 ReleaseResource((Handle) pic);
 OffsetRgn(rgn, r.left, r.top);
 CopyRgn(rgn, wp->contRgn);
 DisposeRgn(rgn);
 
 return val;
}

void GetGoAwayBox(CWindowPtr w, Rect *r){
 WindowPeek  wp = (WindowPeek) w;
 
 *r = (*(wp->contRgn))->rgnBBox;
 
 r->top -= 0;
 r->bottom = r->top + 10;
 r->left += 12;
 r->right = r->left + 10;
}


//
// HeartWindow.c
//
#define kNumFields 7

typedef struct{
	Str255	str[kNumFields];
} Message;
typedef Message* MessagePtr;
typedef MessagePtr* MessageHandle;

void DoUpdate(void);

Boolean 		quitting;
WindowPtr		window;
Message			message;

void LiveDragger(WindowPtr w, Point p, Rect *r){
	Point				mouse, newmouse, original, origin;
	EventRecord			e;
	GrafPtr				save;
	
	EventAvail(mouseDown, &e);
	
	if(!(e.modifiers & optionKey)){
		DragWindow(w, p, r);
	}
	else{
		GetPort(&save);
		SetPort(w);
		
		GetMouse(&mouse);
		LocalToGlobal(&mouse);
		SetPt(&origin, 0, 0);
		LocalToGlobal(&origin);
		original = origin;
		
		SetPort(save);
		
		while(StillDown()){
			if(WaitNextEvent(updateMask, &e, 0, NULL)){
				DoUpdate();
			}
			else{
				GetPort(&save);
				SetPort(w);
				
				GetMouse(&newmouse);
				LocalToGlobal(&newmouse);
				
				if(newmouse.h != mouse.h || newmouse.v != mouse.v){
					if(PtInRect(newmouse, r)){
						MoveWindow(w, origin.h + newmouse.h - mouse.h, origin.v + newmouse.v - mouse.v, false);
						SetPt(&origin,0,0);
						LocalToGlobal(&origin);
						mouse = newmouse;
					}
					else{
						MoveWindow(w, original.h, original.v, false);
					}
				}
				SetPort(save);
			}
		}
		
		GetPort(&save);
		while(w != nil){
			SetPort(w);
			InvalRect(&w->portRect);
			w = (WindowPtr) ((WindowPeek) w)->nextWindow;
		}
		SetPort(save);	
	}		
}

void GetItemHandle(DialogPtr d, short index, Handle *h){
	short	type;
	Rect	r;
	
	GetDialogItem(d, index, &type, h, &r);
}

void EraseWindow(){
	Rect		r;
	GrafPtr		savePort;
	
	GetPort(&savePort);
	SetPort(window);
			
	r = (*(((WindowPeek) window)->contRgn))->rgnBBox;
	OffsetRect(&r, -r.left, -r.top);
	EraseRect(&r);
	InvalRect(&r);
	
	SetPort(savePort);
}

void PStringCopy( ConstStr255Param srcString, Str255 destString ){
	SInt16 index = StrLength( srcString );

	do {
		*destString++ = *srcString++;
	} while ( --index >= 0 );
}

void CopyMessage(MessagePtr src, MessagePtr dst){
	int		i;
	
	for(i = 0; i < kNumFields; i++) PStringCopy(src->str[i], dst->str[i]);
}

void LoadMessage(){
	int				i;
	MessageHandle 	mh = (MessageHandle) GetResource('hwms', 128);
	
	if(mh == nil){	
		for(i = 0; i < kNumFields; i++){
			GetIndString(message.str[i], 128, i + 1);
		}
	}
	else{
		CopyMessage(*mh, &message);
		ReleaseResource((Handle) mh);
	}
}

void EditMessage(){
	short	item = 0, i;
	Str255	str;
	Handle	h;
	
	DialogPtr d = GetNewDialog(129, nil, (WindowPtr) -1);
	
	SetDialogDefaultItem(d, 1);
	SetDialogCancelItem(d, 2);
	SetDialogTracksCursor(d, true);
	
	for(i = 0; i < kNumFields; i++){
		GetItemHandle(d, i + 3, &h);
		
		SetDialogItemText(h, message.str[i]);
	}
	
	ShowWindow(d);
	while(item != 1 && item != 2){
		ModalDialog(nil, &item);
	}
	HideWindow(d);
	
	// if the user hit ok
	if(item == 1){
		MessageHandle	old;
		
		// save the info to the app's resource fork
		for(i = 0; i < kNumFields; i++){
			GetItemHandle(d, i + 3, &h);
			
			GetDialogItemText(h, message.str[i]);
		}
		
		old = (MessageHandle) GetResource('hwms', 128);
		if(old == nil){
			old = (MessageHandle) NewHandle(sizeof(Message));
		
			if(old == nil){
				StopAlert(130, nil);
			}
            else{	
				CopyMessage(&message, *old);
		
				AddResource((Handle) old, 'hwms', 128, "\p");
				ChangedResource((Handle) old);
				
				ReleaseResource((Handle) old);
			}
		}
		else{
			CopyMessage(&message, *old);
		
			WriteResource((Handle) old);
			ChangedResource((Handle) old);
			
			ReleaseResource((Handle) old);
		}
		
		
		EraseWindow();
	}
	
	DisposeDialog(d);
	InitCursor();
}

void DoAbout(){
	Alert(128, nil);
}

void DoAppleChoice(short item){
	if(item == 1) DoAbout();
	else{
		Str255	daName;

		GetMenuItemText(GetMenuHandle(128), item, daName);
		OpenDeskAcc(daName);
	}
}

void DoFileChoice(short item){
	switch(item){
		case 1:
			// edit the text
			EditMessage();
			break;
			
		default:
			quitting = true;
			break;
	}
}	

void DoEditChoice(short item){

}

void DoMenuChoice(long choice, EventModifiers modifiers){
	short	id, item;

	id = HiWord(choice);
	item = LoWord(choice);

	switch(id){
		case 128:
			DoAppleChoice(item);
			break;

		case 129:
			DoFileChoice(item);
			break;

		case 130:
			DoEditChoice(item);
			break;
	}

	HiliteMenu(0);
}

void DoDiskEvent(const EventRecord *e){
	Point 	dialogCorner;

	if((e->message >> 16 ) != noErr){
		SetPt(&dialogCorner, 112, 80);
		DIBadMount(dialogCorner, e->message);
	}
}

void DoOSEvent(const EventRecord *e){
	SInt16		osMessage;
	WindowPtr	w;

	osMessage = (e->message & osEvtMessageMask) >> 24;

	switch(osMessage){
		case suspendResumeMessage:
			break;

		case mouseMovedMessage:
			break;
	}
}

void DoNullEvent(const EventRecord *e){
	WindowPtr 	w;	
}

void DrawCenteredString(Str255 str, short y){
	MoveTo((328 - StringWidth(str))/2, y);
	DrawString(str);
}

void DoUpdate(){
	GrafPtr		savePort;
	RGBColor	saveColor;
	Rect		r;
	int			size = 18, i, y = 116;
	Str255		str;
	
	BeginUpdate(window);
	GetForeColor(&saveColor);
	GetPort(&savePort);
	SetPort(window);
	
	ForeColor(redColor);
	TextFont(applFont);
	TextSize(size);
	
	for(i = 0; i < kNumFields; i++, y += (size + 6)){
		DrawCenteredString(message.str[i], y);
	}
	
	SetPort(savePort);
	RGBForeColor(&saveColor);
	EndUpdate(window);
}

void DoMouseDown(EventRecord *e){
	WindowPtr	w;
	SInt16 		part;

	part = FindWindow(e->where, &w);

	switch(part){
		case inMenuBar:
			DoMenuChoice(MenuSelect(e->where), e->modifiers);
			break;
			
		case inSysWindow:
			SystemClick(e, w);
			break;

		case inContent:
			//quitting = true;
			break;

		case inDrag:
			//DragWindow(w, e->where, &qd.screenBits.bounds);
			LiveDragger(w, e->where, &qd.screenBits.bounds);
			break;

		case inGoAway:
			if(TrackGoAway(w, e->where)){
				quitting = true;
			}
			break;
	}
}

void DoKeyDown(const EventRecord *e){
	SInt16 	key;

	key = (e->message & charCodeMask);

	if(e->modifiers & cmdKey){
		DoMenuChoice(MenuKey(key), e->modifiers);
	}
}

void ProcessEvents(void){
	EventRecord 	e;

	if(WaitNextEvent(everyEvent, &e, 0, nil)){
		
		SetCursor(&qd.arrow);
		
		switch(e.what){
			case nullEvent:
				DoNullEvent(&e);
				break;
	
			case mouseDown:
				DoMouseDown(&e);
				break;
				
			case keyDown:
			case autoKey:
				DoKeyDown(&e);
				break;
			
			case updateEvt:
				DoUpdate();
			
			case diskEvt:
				DoDiskEvent(&e);
				break;
			
			case kHighLevelEvent:
				AEProcessAppleEvent(&e);
				break;
		}
	}
	
	DoNullEvent(&e);
}

pascal OSErr	HandleOpenDocument(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	return noErr;
}

pascal OSErr	HandleOpenApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	
	return noErr;
}

pascal OSErr	HandleQuitApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){
	
	quitting = true;
	
	return noErr;
}

OSErr InitEvents(void){
	OSErr	err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc(HandleOpenApplication), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr)
		return err;

	if((err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc(HandleQuitApplication), 0, false)) != noErr)
		return err;

	return noErr;
}

void CheckMachine(){
	SysEnvRec	thisMac;

	SysEnvirons(7, &thisMac);
  	
  	if(!thisMac.hasColorQD){
  		StopAlert(131, nil);
  		ExitToShell();	
  	}
	
	if(thisMac.systemVersion < 0x0700){
		StopAlert(131, nil);
		ExitToShell();
	}
}

OSErr Initialize(void){
	OSErr		err;
	short		w1, w2, h1, h2;
	Handle		code;
	MenuHandle	menu;
	
	InitGraf(&qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();	
	InitDialogs(nil);
	InitCursor();
	FlushEvents(everyEvent, 0);

	MaxApplZone();
	MoreMasters();
	MoreMasters();
	MoreMasters();

	CheckMachine();

	err = InitEvents();
	if(err != noErr) return err;
	
	LoadMessage(&message);
	
	SetMenuBar(GetNewMBar(128));
	menu = GetMenu(128);
	AppendResMenu(menu, 'DRVR');
	DrawMenuBar();
	
	quitting = false;
	
	window = GetNewCWindow(128, nil, (WindowPtr) -1);
	
	code = GetResource('WDEF',4096);						
	if(code != nil) ((WindowPeek) window)->windowDefProc = code;
	
	ShowWindow(window);
	
	return noErr;
}

void Finalize(void){
	HideWindow(window);
	DisposeWindow(window);
}

void main(void){
	if(Initialize() == noErr){
		
		while(!quitting){
			ProcessEvents();
		}
	}
	
	Finalize();
}

February 13, 2008

The Candy Heart

"I've never been able to put into words how I feel about you. But somewhere among these trillions of hearts, those words must already exist. And I'm gonna find them." --Philip J. Fry

I just found a candy heart that looks like it might say "MELI MY ♡". Or maybe it says "MELT MY ♡", I don't know.

The Snow Person

Melissa made an adorable snow person after the big snow last week. She knitted the hat for me several years ago. The scarf came from Goodwill (yet it matches surprisingly well).

February 10, 2008

The Bald Tire

Yesterday I did a 50 mile group ride from Urbana to Monticello and back. It was the first ride of this distance & intensity I have done in months, and it gave me a pretty good idea exactly how out of shape I am.

As we were just coming back into town I got a flat tire. Some other riders stopped to help me change it and we quickly realized that my rear tire had outlasted its usefulness by a number of miles. In many places the tread had completely worn through and the purple liner was visible.

I suppose that's what happens with cheap tires. I got this tire in a pinch at a bike store in Peoria after discovering my rear Brontrager Race Lite had been sliced up somehow in the trunk of my old car. I still had a Race Lite tire in front and it has lasted over 5000 miles.

Needless to say it was time for new tires. Fortunately, Champaign Cycle was not too far away, so I decided to save myself a trip later in the day and just go straight to the bike shop on my way home. I picked up a pair of Bontrager Race Lite Hardcase tires, which have been highly recommended by multiple people.

February 5, 2008

The Escape

This is the unlikely story of my favorite race of all time.

Most every summer I attend Apple's annual Worldwide Developers Conference in California. The first few years I attended the conference it took place in San Jose, then it later switched to San Francisco. Both fine choices.

In 2003 I was at the peak of my triathlon prowess. That summer I did a triathlon or ran a road race almost every weekend. Earlier in the spring I was planning which races I wanted to do, and on a whim I decided to search for races in San Francisco on the weekends before and after the conference. I lucked out. The Alcatraz "Escape from the Rock" Triathlon was taking place in San Francisco the day before WWDC started.

There was only one problem. Triathlons have become so popular that big races nearly always fill up months ahead of time. With just a few weeks to go until the race I was sure it would be full. I emailed the race organizers and I was delighted to hear back from them that there were 2 spots available. I promptly snapped one up. The race entry fee was rather large, but I figured that if this actually worked out it would be a once in a lifetime opportunity.

Pre-race

I didn't really want to deal with taking my own bike on the plane so I rented a bike from Bay City Bike. The organizers required all equipment to be set up the day before the race, so I arranged to fly out to San Francisco a day early. My flight arrived on time. I took a taxi to my hotel, unpacked, then took a bus to Bay City Bike. They had my reservation and I picked up the bike. It was a low end Giant road bike, which is not super great, but it was more than adequate for my needs.

I rode the bike with my backpack full of race gear to the transition area at an old warehouse near Crissy Field. I picked up my registration packet for the race and I set up the bike and all my other gear. Now it was time to return to my hotel. Except I no longer had the bike. And I didn't see any taxis or buses. So I walked about 3 miles back to the bike shop (the only place I knew for sure where I could find a bus) then took the bus back downtown to my hotel. Given that this was the worse thing that had happened so far, and everything else had gone off without a hitch, I wasn't too upset about it. I mean, think of all the things that could have gone wrong (delayed flight, missing hotel reservations, missing bike, missing registration, bad weather, etc.).

Race Day

I woke up very early the next morning. I gathered my running shoes, my wetsuit & goggles, and headed to the race start. I had no problems finding a taxi, as the city was dead at this time of the morning. Perhaps I was a little too early. I saw very few people near the race staging area, but I didn't want to leave anything to chance. I had worked so hard just to get to this point I wasn't going to let it slip away. I set up my gear in the first transition area. It was time.

The course map

The Swim

All the race participants gathered onto a boat and headed to Alcatraz island. The organizers went over the race instructions on the way out there. The boat pulled up close to the east side of the island, they opened up the doors, and everybody started jumping into the water. I was one of the last people remaining on the boat and I was nervous as hell when it was my turn.

Then I jumped.

The mid-June 58˚ water of the San Francisco bay hit me like a blast of winter wind. I was wearing a wetsuit, but it still took a minute or so for the water in the wetsuit to heat up to a bearable temperature. My face on the other hand didn't have the luxury of a neoprene covering. It was cold, and it would remain cold for the next 35 minutes.

As I treaded water, trying to catch my breath, I noticed that several people had started swimming already, even though the race hadn't started yet. I was quite some distance back from the imaginary starting line anyway, so I started swimming as well. The boat horn blew to signal the start of the race just as I reached the line and I kept right on going.

My swim cap

Now, swimming in the San Francisco bay is notoriously difficult--it's part of the mystique of Alcatraz. I was beginning to understand why. In addition to the frigid water, the waves were pretty rough. No amount of swimming laps in a pool, nor even short open water swims in muddy midwestern lakes, prepared me for the constant ups and downs of the ocean-like bay water. I had never swam in saltwater before and this also caused me some difficulties. In the normal course of swimming I sometimes get water in my mount and I inadvertently swallow some of it. Well, the salt water was causing me to gag and I had to stop for a few seconds a handful of times to prevent myself from puking.

Finally, the current is incredibly strong. We couldn't just swim straight towards our destination or we would end up out in the Pacific ocean. We had to aim significantly to the east of our destination and the current would correct our course. When I was just a few hundred meters out I realized I had overshot the small entrance to municipal harbor and I started swimming vigorously against the current. By the time I reached the entrance I was still 20 meters or so down current of where I needed to be. That's when the current slammed me (and everyone around me) into the pier. The force of hitting the pier wasn't so bad, but it was covered with barnacles. As I pushed off of the pier to get back where I needed to be these tiny sharp crustacea sliced my bare fingers and toes in several places, leaving me bleeding as I exited the water and I ran up onto the shore.

All things considered it was actually a pretty decent swim. I did the 1.5-ish miles in 35 minutes, which put me roughly in the middle of the pack. I don't think I could have expected any better.

The Run

Normally triathlons are arranged in swim-bike-run order, but this race was a little different. In order for the locations to work out correctly there was a 2.5 mile run between the swim and the bike, then another 7.5 mile run after the bike. So it was swim-run-bike-run.

After exiting the water I ran to the transition area put on my shoes and tried to wipe off as much blood as possible from my fingers. I didn't want it on my triathlon suit, so I wiped it on my race number. Fortunately, the cuts were tiny and the bleeding stopped within a few minutes.

My somewhat bloody race bib

I did the first 2.5 mile run at a pretty easy pace and maintained my position in the middle of the pack. Normally in triathlons the run is where I excel, but there was a lot of racing left to do.

The Bike

I reached the transition area at the warehouse, grabbed my bike, and I was off. Immediately there was a large steep hill (this is San Francisco). The road went uphill for a mile, then downhill for a mile, then there was a turnaround, then uphill for a mile, then downhill for a mile, turnaround, repeat, repeat.

The bike ride was hilly. I had never trained on hills. There are no hills in central Illinois. But a funny thing happened during the race--a rather unexpected thing. I started passing people. And not just a few people, but I started passing a lot of people. Every uphill section I passed dozens of people. Every downhill section I passed a few more. After the hilly 12 mile bike ride there really weren't that many people left ahead of me. Apparently I can bike well on hills.

Rob on the bike. Thanks to my mom for buying the official race photograph.

The Run

The second run was an out-and-back. It was longer (7.5 miles) and harder (hills, trails, sand, etc). It started up a trail that went right under the Golden Gate bridge. I mean right under. I could jumped up and touched the bottom of the bridge. The trail wound up, down, and around, and ended at Baker beach. It was at the end of the beach that I saw the race leader headed back the opposite direction. Hmm. How far is it to the turn around? How many people are ahead of me?

After a stroll through the sand the course wound through a neighborhood then up a hill to the Legion of Honor. Going up this hill another runner passed me. Again, the run is where I usually excelled, so it was rare that other people passed me while running in a triathlon. I wasn't going let this slide. I reached the turnaround top of the hill after counting 24 people ahead of me.

On the downhill I caught back up with the runner who had passed me earlier and did likewise to him. Then after running back across Baker beach I reached the notorious sand ladder. The sand ladder is basically a set of stairs made out of wood and sand. It climbs the steep hill from Baker beach up to the trail that goes right under the Golden Gate bridge. There was no chance of running up this thing, so I just did the best I could to keep walking at a brisk pace until I got to the top. From there on it was all down hill.

The windy trail made its way back under the bridge then back down to sea level. I caught one other runner at the bottom of this hill as we both made a mad dash for the finish line.

Epilog

I ended up finishing in 2:38:19, good enough for 23nd place overall (out of 440 finishers) and 2nd place in my age group. Not only was the race a whole lot of fun, it was my best triathlon performance ever, and one of my best performances ever in any kind of race.

I am still amazed to this day that all the necessary pieces came together to allow me to take part in this amazing event. It was truly a once in a lifetime opportunity. Until the next once in a lifetime opportunity comes along, I'm satisfied to have escaped from Alcatraz.

February 4, 2008

The Local News II

You have got to be kidding me. Is this The Onion?

February 3, 2008

The Seinfeld Hair

My hair has been doing an early 90's Jerry Seinfeld thing for the past couple of weeks (poofy on top and a bit long and scragly in the back), so I finally bit the bullet and cut it off.

The Mac Pro RAM

Our new Mac Pro came with 2 GB of memory, which I promptly upgraded to 6 GB. I was shocked that:

  • The FBDIMMs have heat sinks
  • The heatsinks are enormous

The Schroth Trail II

Brett & I skied 8.1 miles this morning on the Schroth trail at Allerton park. We did an outer loop and an inner loop. The entire trail had ski tracks already laid, so we didn't have to do any of the really hard work. The warm temperatures yesterday made for some icy (i.e. fast) skiing today with plenty of glide.

This was the first time I have cross country skied where I wasn't just in an open field. This trail actually had winding turns and rolling hills. It was much more technically challenging and it took some getting used to... but it was also much more enjoyable. The freezing fog (a phrase I just learned on this morning's weather forecast) created a layer of hoar frost (another new phrase, thanks Brett) on the trees and prairie grass, so the views were spectacular (when I was in front, that is, otherwise all I could see was Brett's butt). Much like yesterday, I overdressed, and paid the price in sweat.

I'm hoping to get at least one more ski session in before the current snow melts, but given that it's raining right now and it's only going to get warmer tomorrow, things don't look promising.

February 2, 2008

The Illini Chill

This morning was the Illini Chill bike ride in St. Joe, IL. It's a 22 mile ride in the middle of winter that may or may not benefit the Boy Scouts (there were lots of Boy Scouts hanging around at the start and the half way point).

It started with a "lumberjack breakfast" at a church in St. Joe. For some reason I assumed this meant pancakes but I was way off. It was greasy biscuits, greasy gravy, greasy eggs, greasy sausage, greasy bacon, greasy hash browns. Fortunately I found a small table with bagels, toast, and an apple (all of which I at least consider to be edible). I had been expecting to meet several of my cycling friends from Champaign-Urbana, but I did not recognize a sole at the breakfast.

Next was the small matter of the 22 miles in sub-freezing temperatures with a considerable amount of wind. There was a much smaller crowd at the start than anyone anticipated. Eventually, the ride organizers realized that many people were waiting at the location where the ride started last year, rather than the new location where it started this year. Both groups started at roughly the same time and we all met up once we got out of town.

I was now riding with some of my friends (Karl, Luke, Larry, Tom, Mark), but it was very slow going. The country roads were in terrible shape. They were very slick. There were huge drifts. In some places we couldn't even tell we were on a road. After the first turn when we got a cross wind, 3 people hit the deck right away. Fortunately the considerable snow padded their falls and nobody was injured.

It was actually pretty fun. All I could do was grin and think to myself, this is some crazy shit.

I was wearing 3 layers of socks (including my magic Gore-Tex socks), 3 layers of pants, and 3 layers of shirts/jackets. I was overdressed. With the wind at our backs I was sweating buckets. I had been cold standing around the parking lot before the ride so I thought it better to err on the side of warmth.

We made our way to the half way point at a church in Royal, IL. More Boy Scouts were waiting with some hot chocolate and granola bars. After a short rest we decided to deviate from the set course in order to travel on roads which were more likely to be cleared of snow. We started quickly on the way back, only to turn into a strong headwind for several miles. This was less fun... but we made it back without incident.

After the ride was a spaghetti lunch at yet another church. It was a good time with some exciting (if a tad scary at times) riding.

Our route for the 2008 Illini Chill ride

February 1, 2008

The Snowfall

The snow began while I was eating lunch at El Toro yesterday. By the time I left work a few inches of snow had accumulated on the ground and it made for some difficult bicycling on the way home. I bided my time until about 10:30 pm or so, at which point I could wait any longer. I had to go skiing.

I changed clothes, grabbed my skis, and walked 2 blocks to the Arboretum. I was pleasantly surprised to find a fresh set of ski tracks in the snow--I was not the only person in Urbana with this idea. About half way around the 1.5 mile loop I saw the other skier. He was heading the opposite direction at a pretty good clip. I found out today the mystery skier was none other than Gary Cziko, who I know from Champaign County Bikes. Apparently, he's quite the cross country skier.

3/4 of the way around the loop I went up the (artificial, of course) hill. This hill is short, steep, and one of only two places in Urbana to sled. So, of course, at 11 pm it was packed with people. I worked my way up to the top (it seemed to be mostly kids sledding, but I did see several fresh, empty beer bottles), then back down the other side to complete my loop. The snow was still falling at this point, so by the time I reached my initial tracks they were pretty well covered up by fresh snowfall.

This morning we awoke to a fresh 6-8" of snow which needed to be shoveled off the driveway and sidewalk. Melissa & I made short, sweaty work of it.

The bike ride to work went slowly, as the roads had a lot of mushy snow (which is hard to navigate through on a bike). Fortunately, with the local schools cancelled and many people staying home from work, the roads were not at all busy. On the way home the road conditions were considerably better, but still not great.

Tonight more skiing is on the agenda, then tomorrow is the Illini Chill bike ride in St. Joe. That should make for some good outdoor fun.