baseball win expectancy finder now has balls and strikes!

Here it is!

This wasn’t too much work, and like I mentioned last time it was nice to work on adding a new feature to something. Although I guess I did recreate the app’s shell with create-react-app, I didn’t port it to TypeScript 🙂

One annoying part is that there’s just not that much data so you can pretty easily get into situations where the stats are probably “wrong”. For example, in the top of the 8th inning, no outs or runners, home team up by 2, and a 2-0 count the home team has an 87.08% chance to win. But if the batter gets another ball to make the count 3-0 (good for the visiting team), now the home team has an 88.11% chance to win. I guess I should add a warning when the sample size gets too small or something. (although I don’t know what “too small” is)

I was a little worried that adding balls and strikes would bloat the size of the data files, which did happen. The worst-case is that it would increase the file size by 12x (4 choices for balls * 3 for strikes), but in practice it’s more like 9x. But it turns out that computers are fast so doing the lookups is only barely slower than before.

My original plan was to add the balls and strikes data to the mobile app (and make it an in-app purchase to unlock), but the increase in data size and corresponding memory usage make me less excited about it. Maybe at some point…

Marriage map now in React + TypeScript!

I ported my same-sex marriage map in React + TypeScript! It now uses the usStateMap component I wrote a while ago.

This was interesting because the original map was just vanilla JavaScript and jQuery, so the “port” was really more of a rewrite. It also meant I got to get rid of a ton of logic around state management and also some pretty hacky code around drawing the map itself.

I went ahead and published the code on GitHub (it was public before but in a less convenient way), too, mostly so the usStateMap component now shows that two projects use it 🙂

I ended up leaving out a few features because they didn’t make a ton of sense (flashing map colors, pending court cases), and I also decided to make the cartogram view the default because it’s a more accurate representation of the number of people that were under particular laws at a particular time.

Honestly, porting all of these projects to React + TypeScript is starting to feel tedious, so I’m going to take a break and start working on some new features for some stuff. Should be fun!

(and it still gives me a bit of a thrill to load the page and see an all-blue map!)

clue solver – now in TypeScript!

Here’s a new version of my Clue solver! It works exactly the same as the old version, but now it uses a more standard React build system (so maybe it’s a little faster?) and I ported it to TypeScript.

TypeScript is now my favorite way to write React apps. It’s really nice to have a real type system with compile-time errors, and it gives me much more confidence when I refactor things.

For a comparison, here’s the old code in plain JavaScript, and here’s the new code in TypeScript. One representative sample of how much nicer things are, in the History class’s render method:

Old code:

for (var i = 0; i < this.props.history.length; ++i)
{
    var event = this.props.history[i][0];
    var eventType = event[0];
    var description = '';
    if (eventType === "suggestion") {
        description = this.props.playerInfo[event[1]][0] + " suggested " + CARD_NAMES[0][event[2]].external + ", " + CARD_NAMES[1][event[3]].external + ", " + CARD_NAMES[2][event[4]].external + " ";
        if (event[5] == -1) {
            description += " - no one refuted";
        }
        else {
            description += " - refuted by " + this.props.playerInfo[event[5]][0] + " with card ";
            if (event[6][0] == -1 && event[6][1] == -1) {
                description += "Unknown";
            }
            else {
                description += CARD_NAMES[event[6][0]][event[6][1]].external;
            }
        }
    } else if (eventType === "whoOwns") {
        var player = "Solution (case file)";
        if (event[1] < this.props.playerInfo.length) {
            player = this.props.playerInfo[event[1]][0];
        }
        description = CARD_NAMES[event[2][0]][event[2][1]].external + " owned by " + player;
    }
    entries.push(<li key={i}>{description}</li>);
}

New code:

let entries = [];
for (let i = 0; i < this.props.history.length; ++i)
{
    let event = this.props.history[i].event;
    let description = '';
    switch (event.history_type) {
        case "suggestion":
            description = this.props.playerInfos[event.suggester_index].name + " suggested " +
                cardNameFromCardIndex({ card_type: CardType.Suspects, index: event.suspect_index }).external + ", " +
                cardNameFromCardIndex({ card_type: CardType.Weapons, index: event.weapon_index }).external + ", " +
                cardNameFromCardIndex({ card_type: CardType.Rooms, index: event.room_index }).external + " ";
            if (event.refuter_index == -1) {
                description += " - no one refuted";
            }
            else {
                description += " - refuted by " + this.props.playerInfos[event.refuter_index].name + " with card ";
                if (isNull(event.refuted_card_index) || isNone(event.refuted_card_index)) {
                    description += "Unknown";
                }
                else {
                    description += cardNameFromCardIndex(event.refuted_card_index).external;
                }
            }
            break;
        case "whoOwns":
            let player = "Solution (case file)";
            if (event.player_index < this.props.playerInfos.length) {
                player = this.props.playerInfos[event.player_index].name;
            }
            description = cardNameFromCardIndex(event.card_index).external + " owned by " + player;
            break;
    }
    entries.push(<li key={i}>{description}</li>);
}

The new code is so much more readable! To be fair, some of the refactoring I could have done in JavaScript to begin with, but TypeScript gives me much more confidence to do bigger refactors.

Also, Visual Studio gives me Intellisense and the ability to rename variables and whatnot. I really don’t want to go back to writing JavaScript in a non-IDE!

us-state-map – a React component for a US map of states!

After releasing State Election Map, my first step was to publish a React component with just the map stuff in it. And after messing around for a while with npm and TypeScript and React, I got it working! The us-state-map component is now available on GitHub and npm, and State Election Map uses it. (it includes the map itself and the date slider)

As with whenever I mess around with React I’m pretty sure I’m doing some parts idiomatically “wrong”. (the build step in particular is pretty hacked-together) I like to work on stuff like this to learn how to do things, but it makes me wonder how much I actually learn if I’m just hacking things together and not doing it 100% the correct way. At least it does give me something to copy if I want to do something similar in the future, and I do feel a sense of accomplishment 🙂

State Election Map now available!

My latest project, State Election Map, is now available! It’s a neat way to visualize US presidential election results from 1972 to 2016.

The coolest part is that you can look at each state’s result relative to the national popular vote, so you can see states becoming more Democratic/Republican over time. (check out California and Alabama going on opposite trajectories…)

Other interesting observations: Nixon won by a ton in 1972 (which means that whole Watergate stuff was really unnecessary!), Reagan won by a ton in 1984, and check out the 1976 bizarro-world map! I guess that’s what you get when you have a Southern Democrat running before the Southern Strategy had entirely taken hold…

From a technical perspective, the map is built in React, which I’ve used before, and TypeScript, which was new to me. And I’m a big fan! Using Visual Studio + TypeScript meant I got helpful syntax errors at edit-time, which I’m really not used to in a JavaScript-y language.

I did my best to separate the map/timeline parts into separate components, so I’m hoping to publish them separately and eventutally rewrite my same-sex marriage map to use them too. But that’s a ways down the line!