2025 · Solo developer
Super Mario Sluggers Baseball Stats
Batting statistics from a hand-built linked list
- Java
- Data Structures
- File I/O
- lines across 4 classes
- 850
- ranked stat categories
- 6
- scoring codes in the sequel
- 44
Highlights
- Wrote a generic Node and LinkList from scratch — insert, delete by name, search, recursive toString, and an in-place sort — instead of reaching for a built-in collection.
- Sorted by relinking nodes rather than swapping their payloads, which is the version that actually exercises pointer handling.
- Modelled each batter as a Comparable Player and derived batting average and on-base percentage under real scoring rules, where a walk, sacrifice, or hit by pitch is a plate appearance but not an at-bat.
- Ranked the top three players across six categories, including strikeouts, where the leader is the lowest value rather than the highest.
What it does
The program reads a file of batting records — one player per line, their results as a compact string of outcome characters:
Peach OOOHHHWWWSSSKWOOOS
DK HHHOHHHHHHH
Mario HWKPOSHWKPOSHWKPOS
Each character is one plate appearance: an out, hit, walk, strikeout, sacrifice, or hit by
pitch. Every player is parsed into a Player, inserted into the list, and the program prints
each batter's line followed by a leaderboard ranking the top three in six categories.
The list is the point
This was the assignment where the collection stops being free, so the roster lives in a
LinkList<TypeData> and Node<TypeData> written by hand — insert, delete by name, search,
a recursive toString, and a sort.
The sort is the part worth pointing at. It is a bubble sort, but it swaps nodes, not the
data inside them — tracking the previous node, reassigning next on both sides of the pair,
and updating head when the first node moves. Swapping payloads instead would have been a
few lines shorter and would have taught me nothing; relinking is where you find out whether
you actually understand what a reference is. Drop one and the list does not throw, it just
quietly gets shorter.
The rules are the hard part
Counting characters is easy. Baseball scoring is not, because a plate appearance and an at-bat are different things, and that difference is exactly what separates batting average from on-base percentage. A walk, sacrifice, or hit by pitch is a plate appearance but not an at-bat, so a player who walks every time has a batting average of .000 and an on-base percentage of 1.000.
Strikeouts add a twist to the leaderboard: the leader is the player with the fewest, so ranking cannot assume bigger is better. That is a flag through the ranking routine rather than a special case bolted on beside it.
Get any of this wrong and the program still runs and still prints a tidy table. There is no exception to catch — only a batting average that anyone who follows the sport would spot as wrong immediately.
Where it went next
A later project in the same course took the same domain considerably further. Instead of one
outcome character it reads real scorekeeping notation — 1B, 6-3, F8, SF7, E7 — with
the 44 codes it understands defined in an external key file grouped under seven categories, so
new notation can be added without recompiling.
Its input is a play-by-play log naming a team on every line, so batters are now keyed by name
in a map, tagged home or away, split into two rosters, and sorted independently. Player
gained errors and a team alongside the original counters. The output is a full two-team box
score with nine statistics per batter, followed by leaders with ties grouped onto one line.
That version is the better program, and it is the one that gets to use a library collection. This one is where the data structure had to be earned first.
What I would change next
I would separate parsing, statistics, and formatting, and put the scoring rules behind tests driven by known box scores so a rule change is verified rather than eyeballed. The sort should be a merge sort — bubble sort on a linked list is quadratic for no good reason — and malformed input should be reported by line number instead of skipped.