The day finally came: Eclipse Day at the Googleplex, 2009. This event was hosted by the Google Open Source Program Office, and really, they did a great job. Seriously big thanks to Chris, Leslie, Cat and Ellen, who put together a fantastic program. Except for the forks. (Note to self: Even if Eclipse Day lunch has no forks, I must no longer try stealing forks from a Google cafeteria. You'll get caught and questioned by the cafeteria manager.)
My teammate, Terry Parker, and I gave the keynote presentation, titled "Eclipse in the Enterprise: Lessons from Google" which was a glimpse into what it takes to support all the people at Google who build applications with Eclipse:
Providing Eclipse tools to thousands of engineers at Google is a satisfying job, but it's not always easy. In the last two years Eclipse has gone from being a tool supported by enthusiasts in their spare time to one supported by a team mandated to make engineers' lives better. We will talk about the plug-ins we've written and processes we've established to provide features, enterprise deployment and support, and even share some of the pitfalls discovered along the way.
I wasn't sure how the talk would be received, but it sure seems to have gone over well. Slides are coming shortly, as is, I hope, a YouTube video.
Along those same lines, Joep Rottinghuis from eBay talked about Deploying Successful Enterprise Tools, and it was serendipitous since he managed to touch on all the facets of enterprise tool support that we omitted from our talk: support, documentation and training.
The best part, though, was how people from both eBay and Cisco told us that they were happy to see us talking about the scalability issues we've seen; they have the same problems!
All of this underscores what I have thought since last year's EclipseCon: next year I'd like to see an Enterprise Tools BoF.
Ooh, gotta go: now Miguel is talking about adding special JSNI formatting in editors. Swoon!
In the last year or so I've become addicted to a puzzle game known as Slitherlink. Here's the premise of the game, as described on Wikipedia:
Slitherlink is played on a rectangular lattice of dots. Some of the squares formed by the dots have numbers inside them. The objective is to connect horizontally and vertically adjacent dots so that the lines form a single loop with no loose ends. In addition, the number inside a square represents how many of its four sides are segments in the loop.
Other shapes can be used in lieu of the standard grid, as long as each tile has 4 sides. These include snowflake, penrose, laves and altair. They add complexity by increasing the possible paths from an intersection, but the same rules apply to their solution.
If that didn't make sense, let me show you a fairly simple version of this puzzle. Starting with this unfilled grid,
one of the first things you can spot is the cell with a '0'. A zero means that none of the segments around it are used in the final loop. So one of the first things I can do is remove those four neighboring segments.
Now let's look at the neighboring cell with a '3'. Since one of the four neighboring segments have been removed, it's obvious that the other three lines participate in the loop.
This goes on until the puzzle is solved.
The version It's available as part of Simon Tatham's Portable Puzzle Collection, but there it's called Loopy. One of the nice things about Loopy is that it provides different difficulty levels, and formats other than squares.
After playing for a while you get a good sense of strategies and can play more and more difficult variants. Here's one that I worked on for almost four hours until discovering a problem in the upper right corner.
Variants
With regards to using dictionaries and online tools to solve crosswords, Will Shortz credits his predecessor at the New York Times with this quote: "It's your puzzle. Solve it any way you want." Similarly, you can solve Loopy puzzles any way you want. Here are some of the variants I've come up with while playing:
Speculation: Since Loopy allows you infinite levels of undo, it's easy to use speculation as a puzzle-solving tactic. For instance, if you're not sure whether a particular line segment is part of the loop, you can merely start traversing in a direction to see what follows. If you're taken to an impossible position, you know your guess was wrong, and that the opposite of your guess is correct; a proof-by-contradiction if you will.
There are problems with the speculation variant, though. One is that it only really provides information when your guess turns out to be wrong (unless your guess leads you to completing the loop.) If your guess is correct, or perhaps it just leads you to another position that requires more speculation, you get no concrete information. So using speculation requires making good choices: speculate in small areas where you'll quickly corner yourself.
Another problem with speculation is that you have to remember where you made your guess. While undoing your guesses, if you're not careful, you'll undo too much, or worse, not enough. How I typically address this is by clicking on my guess so many times that when rapidly undoing, it also undoes my many clicks, so visually it looks like a rapid flash.
No speculation: After learning more and more solving techniques, you have less of a need to speculate on the board, and can either solve more complex parts of a puzzle in your head, or better, handle speculating in your head. The no-speculation variant just means just that: no speculating is allowed. If you mark a segment in the puzzle as part of the loop, or not in the loop, it's because you know it to be so. The four-hour mega cairo puzzle above was played with no-speculation.
No undo: A variant of no-speculation, but it requires surprisingly more focus and hand-eye-coordination. When I typically play no-speculation, if I accidentally mark a segment I didn't intend to, I just undo and correct it. No-undo basically means, no undo. It's not quite the same as choosing to solve a crossword puzzle with a pencil or pen, particularly for larger puzzles. Very often you find yourself seeing several obvious steps, and you're bound to make a mistake if you're lazy or not paying attention.
Mark segments only: Although I haven't explicitly said this yet, every segment in the loop can be in one of three states: undecided, on-the-loop or off-the-loop. (These can be seen in these screen captures as yellow, grey and black.) Clicking on an unmarked segment marks it as on-the-loop; right-clicking marks a segment as off-the-loop. On a Mac, there's no right-click -- you have to press the command key in conjunction with a click. So while you can play one-handed on Loopy for Windows, you can't play one-handed on Loopy for Mac. So I discovered this variant where you were only allowed to mark lines as part of the loop. No marking lines as off-the-loop.
This makes the game considerably more difficult. Any segments that you know are off the loop have to be managed by your brain. I tend to not play this variant too often.
Here's a partially completed puzzle. On the left only segments in the loop are marked. If I also marked segments that are not in the loop, the puzzle would look like the one on the right, and there are already three superbly obvious segments in the loop.
Clear only: The opposite of mark segments only. You can only mark those segments that are off the loop, at least, until the puzzle is complete. I've discovered some interesting strategies trying to solve puzzles with this restriction.
Here's another partially completed puzzle. On the left only segments out of the loop are marked. Marking segments that I know are in the loop provides much more information.
This is currently my favorite way to play.
Clear only, one continuous segment: This is basically the same as the clear only variant with one additional rule: you can mark segments in the loop, but all segments in the loop must be connected to each other. This is a slightly easier variant than clear only,
Basically you start the game like you're playing clear only and when you find it most valuable, start a line segment.
In the sample puzzle above it's obvious that everything in the lower right corner of the puzzle is on the loop, but they can't be marked as such. The lower left also has some obvious spots. Maybe in a couple of moves I can connect the lower left to the single segment.
When playing this variant I tend to keep my board free from a single segment for quite a long time. Often if I want to mark a region, the marking doesn't necessarily buy me anything. So I often find thinking about creating a segment as a way to study the puzzle.
On paper: Try printing the puzzles on paper. It feels like an entirely different puzzle.
Conclusion
If you haven't tried Loopy, I suggest you do. If you have limited time during your day, I suggest you play something else.
But if you do play Loopy, by what rules do you play?
Update: Writing this post encouraged me to do try a large puzzle again, but this time playing clear-only, one continuous segment. no speculation. I'm just getting started, as you see.
Update: (Sunday 10AM) I've clarified the way to play clear only, one continuous segment.
My cousin Judy is visiting from London. She had a flght from Heathrow to Newark, but given today's awful weather, she was redirected to Albany, about three hours from Newark by car. Because Albany's airport isn't prepared to handle immigration, she could not disembark.
After over three hours on the ground, the flight finally left for Newark.
when to made several rotations around an arbitrary area
PSE&G only allows you to set up one customer service account per email address. So, if you live on one property in their service area, and then move to another property in their service area, you must register a new account with a brand new email address.
I'm genuinely still a little shocked. No matter how hard I tried, I could not find a way to either add a second account or remove the first one to my email address. I messed with pseg.com for 15 minutes, so I called them for help. After 20 minutes on hold, I asked how to change the account. The customer service representative told me, no, I couldn't do that, I had to create a new account. Being hasty, I thanked the CSR and ended the call, assuming he was just wrong.
Registering a second account with the same address failed. Registering a second account with email+pseg@domain.com also failed, not because it was a duplicate but because PSE&G considers that an invalid email address. Honestly, not much of a surprise, that.
So I called again. Was it really like that? After another 20-minute hold (whoo, these guys are popular) I confirmed with a second CSR that, yes, really, it was like that. She said I should just "create another email at like yahoo mail." The CSR agreed to lodge my complaint and I rung off for the second time.
I have one email address upon which I rely, the destination of all my important personal correspondence. Why should PSE&G email have to be delivered elsewhere? I could create a second account whose purpose is merely to forward all mail to the primary one. I refuse to have to remember a new email account just to pay my electric bill. I'm still hoping that one day all my accounts can be registered through OpenID, so this is somewhat the opposite of that goal.
Perhaps people are so dissatisfied with PSE&G that they vow to move out of their service area before ever using them again?
This is a bug, plain and simple. Most likely a bug in requirements or design. And it got me thinking: what different developement phases might have created such a problem?
Requirements gathering phase: perhaps nobody mentioned this as a possibility. Better still, someone mentioned it as a very rare occurrence.
Design phase: maybe this was an intentional design decision. Maybe they don't particularly care about account history as a person moves between properties.
Database design: assuming a SQL database back-end, perhaps the whole product is wrapped around a database that has a single table called ACCOUNT with columns email_address and account_id. perhaps they can get a lesson in Third Normal Form.
User acceptance testing: perhaps no managers or testers had recently moved within the PSE&G service area, if at all.
Middle-tier design: first thing I'd do is grep for function createaccount($emailaddress, $accountid)
Contract negotiations: The back-end support team has a charging structure based on the number of accounts.
UI design: I ... can't really find a way that UI design gets in the way. Here's the registration form. Maybe you can see something.
In my many years working in the pharma consulting industry, there was a common phrase for difficult non non-negotiable tasks: Phase 2. Could this just have been considered a Phase 2 project, complete with database normalization redesign?
Could be. After all, this consumer site is just over 100 days-old.
Java 5.0 introduced Generics. It also introduced covariant return types. Wikipedia does a fine job describing covariant return types.
Since they were released simultaneously, I consider them to be tightly coupled. For instance, here are simplified versions of an interface and implementation I recently wrote:
Note: I am having difficulty representing greater-than and less-than symbols in Blogger's editor, so you'll have to do with { and }.
Version 1: Java 5, Generics, Covariant return types
public interface Model{T extends Model{T}} { T read(InputStream in); T write(OutputStream out); }
public class MyModel implements Model{MyModel} { public MyModel read(InputStream in) {
...
} public MyModel write(OutputStream out) {
...
} public MyModel setName(String name) {
...
return this;
}
public String getName() { ... } }
Thanks to the covariance, I can write a method chain like this:
new MyModel()
.read(in)
.setName("foo")
.setStopAtMain(false)
...
.write(out);
With Java 1.4, the code would have to look like this
Version 2: Java 1.4
public interface Model { Model read(InputStream in); Model write(OutputStream out); }
class MyModel implements Model { public Model read(InputStream in) { ... } public Model write(OutputStream out) { ... } ...
}
And the method chain would result in a syntax error:
public static void foo() { new MyModel() .read(in) .setName("foo") ^ The method setName(String) is undefined
for the type Model. .write(out); }
Which you could hack around with an ugly cast:
public static void foo() { ((MyModel) new MyModel() .read(in)) .setName("foo") .write(out); }
Back to the Java 5 example: My point is just this: covariant return types don't require generics. All that messy code in version 1 could look much simpler because covariant return types exist on their own without generics:
Version 3: Java 5, Covariant return types
public interface Model{T extends Model{T}} { T Model read(InputStream in); T Model write(OutputStream out); }
public class MyModel implements Model{MyModel} { public MyModel read(InputStream in) {
...
} public MyModel write(OutputStream out) {
...
} public MyModel setName(String name) {
...
return this;
}
public String getName() { ... } }
Lesson learned: I know generics fairly well, but there's a difference between knowing when it's useful and when it isn't. Said another way: when you have a Generic hammer everything looks like a generic nail.