Skip to content

Missouri S&T

Four years at an engineering school, and the projects that turned lecture-hall theory into something that switches on.

The Thunderfury Incident

"Did someone say Thunderfury, Blessed Blade of the Windseeker?" β€” The IT Department, probably

I have a confession: early in college, I casually played World of Warcraft (WoW). With that off my chest, let me tell you about the time I almost got into trouble over a shell script. And how WoW was involved.

To set the scene: I was in campus housing, just getting started in Linux land, trying to master the shell. I mostly tinkered on my MacBook, but we had access to shared, virtual Linux workstations. Think of it as a free timeshare for college students. I realized something: I could broadcast messages to everyone else logged into the same machine via wall. I also had access to about 40 shared workstations across the network.

So I wrote a script and tested it for a couple of minutes in a computer lab. I got my confirmation from the confused looks on people's faces. Each person on a workstation would be spammed randomly with the simple message: β€œDid someone say Thunderfury, Blessed Blade of the Windseeker?”.

A day or so later, I was hanging out in the computer science lounge, talking to a friend who happened to grade for the introductory programming class. He was eager to tell me my script had hit a student’s assignment submission and muddled it. The student went to the teacher and accused me of hacking their computer. Amused, the teacher just said β€œoh, that’s just Illya” and graded over it.

But, how did they know it was me?

See, my script had a bit of a flaw. The -n flag in wall suppresses the banner that shows who sent the message; omitting it broadcasts your message with your username attached. So there was no hiding from it, which is why I only gave it a β€œlight test run.”

I gave it a couple of weeks and didn’t hear anything about it. So I published a blog post, shell script attached, and called it a day. Coast was clear.

Until a few months later, when I got a text from a friend that worked in the IT department. They were upset about my shell script. And they weren’t upset that I ran it, they were upset I published it on my blog.

Now, I was worried. They were meeting later in the day to decide what action to take. I held my breath and took down the page. I got a follow-up that everything would be okay. I asked my friend to apologize on my behalf, and I wouldn’t do it again. And that’s the last I heard of it.

So, here’s that blog post.


If your school is anything like mine (engineering and science, mostly), you probably have some kind of virtual Linux machines you can SSH into. If you’ve done any digging, you might have realized that commands such as wall or write are not disabled. If you are anything like me, you probably thought about writing a shell script that will automatically log you in, spam something (i.e. the famous Thunderfury, Blessed Blade of the Windseeker) on a random machine, and leave. Well you’re in luck.

#!/bin/bash

PASSWORD="your-password-here"
USERNAME="your-username"
HOST_PREFIX="linux"
MIN_HOST=1
MAX_HOST=39
MESSAGE="Did someone say [Thunderfury, Blessed Blade of the Windseeker]?"

spam() {
    local n=$(( RANDOM % (MAX_HOST - MIN_HOST + 1) + MIN_HOST ))
    local host
    printf -v host "%s%02d" "$HOST_PREFIX" "$n"

    sshpass -p "$PASSWORD" ssh -t -l "$USERNAME" "$host" \
        "printf '%s\n' '$MESSAGE' | wall"
}

while :; do
    spam
    sleep $(( RANDOM % 60 + 1 ))
done

Set PASSWORD, USERNAME, and HOST_PREFIX up top. MIN_HOST and MAX_HOST bound the random suffix, zero-padded to two digits (so linux01 through linux39). The loop fires on a random 1-60 second interval. Neat!

Academia Portfolio

4 years, 10 projects, 91k lines of code. Pain is temporary, GPA is forever.

Between 2014 and 2018 I wrote a lot of code for school. Below are my favorite projects from my academic years.

This repository isn't just source files; it's a collection of memories. Ten years later, I can (and will) tell you where I was when I wrote each of them. I remember how late into the night I stayed at it. I remember how I wrote them, from the elegant one-liners to the nastiest regexes and bit manipulation you will ever see. I remember why I wrote them, because pain is temporary, but GPA is forever.

If you'd rather read the notes and problem sets themselves, they're collected in Academia Notes. The full source is on GitHub.


Notes

Document Pages Description
Curated 284 Curated selection of best work
Assignments 498 Homework with solutions
Notes 473 Lecture notes and study materials
Complete 1,113 Everything
Loading document...
1 / 1 (preview)

Projects

Senior Year:

  1. Chess AI
  2. Puzzle Solvers
  3. Shape Packer
  4. Linear Algebra Library
  5. CFG Tracer
  6. Splatoonio

Junior Year:

  1. Space Invaders
  2. Camelot

Sophomore Year:

  1. CLC Tally
  2. Grading Suite

1. Chess AI

A chess AI engine built from scratch using bitboards. Each position fits in a set of 64-bit integers, one bit per square, so move generation is pure bitwise operations: shifts for sliding pieces, masks to prevent wraparound at the edges of the board. On top of that, alpha-beta pruning, iterative deepening, and a custom evaluation function that considers piece position, king safety, and pawn structure.

Chess AI Demo

This was my most memorable software project throughout all of college; with good reason, it was the most notorious within our computer science department. Part one of the project was generating a complete chess move engine with a simple search algorithm to explore the game space. This was one of the handful of assignments I ever turned in late. And I started it a week before it was assigned.

Bitboards were nasty; I never anticipated how complex they would make the engine. But it was a proper learning experience, and I came out much stronger at bit manipulation. While well structured, it was a large surface area for bugs to fester. I tested my move generator against a standard Python one to see if it was correct; after running through >50 test cases, I called it good. Except there's still one test case, on the desktop of a computer I no longer have, that I could never get to pass. And I never even knew why.

This and a few other engine bugs made me skip the class-wide chess AI competition, one of my big regrets of college. I contemplated submitting a build that inverted the fitness function, producing an agent that optimizes for losing, but chickened out, afraid it would error on an illegal move. The decision wasn't pride; with 18 credit hours and a part-time internship, my straight-As were hanging on for dear life.

Code

The moving function shifts bits to simulate piece movement, masking edge files to prevent wraparound. Each piece type builds on this primitive.

Bitboard MoveEngine::moving(const Bitboard& board, const Direction& direction) {
    const static Bitboard aFileInverse = 0xfefefefefefefefe;
    const static Bitboard hFileInverse = 0x7f7f7f7f7f7f7f7f;

    switch (direction) {
        case north:     return board << 8;
        case south:     return board >> 8;
        case east:      return (board << 1) & aFileInverse;
        case west:      return (board >> 1) & hFileInverse;
        case northeast: return (board << 9) & aFileInverse;
        case northwest: return (board << 7) & hFileInverse;
        case southeast: return (board >> 7) & aFileInverse;
        case southwest: return (board >> 9) & hFileInverse;
        default:        return Bitboard();
    }
}

Pawns are the worst: different moves per color, double-moves from the starting rank, diagonal captures only when an enemy is present.

Bitboard MoveEngine::pawnMoves(const Bitboard& pawn, Bitboard self,
                               Bitboard enemy, const Color& selfColor) {
    const Bitboard enemyOriginal = enemy;
    self = ~self;
    enemy = ~enemy;

    static Bitboard secondRank = 0xff00;
    static Bitboard seventhRank = 0xff000000000000;

    if (selfColor == white) {
        return (pawnNorthMovesWithBlockers(pawn, self & enemy)
              | pawnNorthNorthMovesWithBlockers(pawn & secondRank,
                                                self & enemy)
              | (moving(pawn, northeast) & enemyOriginal)
              | (moving(pawn, northwest) & enemyOriginal))
            ^ pawn;
    } else {
        return (pawnSouthMovesWithBlockers(pawn, self & enemy)
              | pawnSouthSouthMovesWithBlockers(pawn & seventhRank,
                                                self & enemy)
              | (moving(pawn, southeast) & enemyOriginal)
              | (moving(pawn, southwest) & enemyOriginal))
            ^ pawn;
    }
}

And FEN (Forsyth-Edwards Notation), the standard for serializing a chess position as a string. Parsing it requires a regex that looks like someone smashed their keyboard. The apology is warranted.

std::string FenParser::getToken(const FenToken& token) {
    // lol sorry
    const char* regexString =
        R"((([pPnNbBrRqQkK0-8]{1,8}/?){8})\s*(w|b)\s*)"
        R"(([KQkq-]{0,4})\s*([a-hA-H0-8\-]{1,2})\s*)"
        R"((\d+)\s*(\d+)*)";
    std::regex regexExpression(regexString);
    std::smatch match;

    if (std::regex_search(fenString, match, regexExpression)) {
        switch (token) {
            case board:       return match[1];
            // off by one, regex error; don't ask
            case colorAtPlay: return match[3];
            case castling:    return match[4];
            case enPassant:   return match[5];
            case halfTurns:   return match[6];
            case fullTurns:   return match[7];
            default:
                throw std::logic_error("Fen String is fucking broke");
        }
    } else {
        throw std::logic_error("Fen String is fucking broke");
    }
}

2. Puzzle Solvers

Four puzzle solvers built around different search algorithms. The standout is an A* implementation with custom heuristics that chews through state-space problems in milliseconds. Each puzzle forced careful thought about state representation and admissible heuristics. Watching the solver walk thousands of states to find an optimal path, faster than you can blink, was deeply satisfying.

Mechanical Match Demo

The AI class had two programming projects: part two was the chess AI above. Part one, much simpler, was an AI engine that played a match-three clone. Reasonable project with sizeable scope, it was one of the projects that gave me real confidence as a programmer then. I remember comparing solutions with my friend Mark: his had a smaller code footprint, and he said mine was bloated. I remarked "your code is concise, but mine is poetic", then showed everyone my one-line move generator, like poetry.

Here's my desk setup at the time, editing this very code. I'm particularly proud of the new MacBook Pro; I bought it with my own internship money.

My desk setup at Missouri S&T, circa senior year

Code

Python generators let you build lazy sequences that compute on demand. Instead of materializing all moves upfront, the generator yields valid ones one at a time. Memory stays flat regardless of how many possible moves exist, because only the ones we actually touch get computed.

@staticmethod
def actions(state):
    # This is ugly, but by abusing list comprehension, I get lazy evaluation.
    # In turn, I actually do a linear search of the entire space, but only store
    # the states that are legal. Thank you, generators.

    row_max, column_max = MechanicalMatch.grid_size(state.grid)

    return [] if state.swaps >= state.max_swaps else (
        Action((row, column), direction)
        for row in range(0, row_max)
        for column in range(0, column_max)
        for direction in [Direction.UP, Direction.LEFT]
        if MechanicalMatch.swap_is_valid(state.grid, (row, column), direction)
    )

3. Shape Packer

An evolutionary algorithm for 2D shape packing. Given irregular shapes and a rectangular board, find the placement that maximizes coverage. The genome encodes position and orientation per piece; mutation perturbs placements, recombination swaps configurations between parents. Fitness proportional selection, k-tournament (with and without replacement), truncation for survival.

Shape Packer Demo

This was my first real test of writing performant Python, and boy was it full of lessons. In short, my code was slooowww. Not hours but days slow. Every submission felt like a grueling experience (this is when I fell in love with tmux). But there's a certain fun to watching the convergence in such tight packings. Optimization problems are fun.


4. Linear Algebra Library

A templated C++ linear algebra library. Matrices, vectors, and decompositions (LU, QR, Cholesky). Heavy use of operator overloading so matrix math reads naturally. The final project ties it all together to solve linear systems with different numerical methods.

This assignment taught me that our library was sometimes open until 3am; I found a spot in the basement next to the vending machines. It wasn't a mental test, it was an endurance one. The course wasn't just about numerical modeling in code, but about writing good numerical modeling code: fully templated, high test coverage, with proper documentation. Two weeks to deliver 2k lines of code and 57 test cases, and a self-imposed single night to ship 1.6k lines of comments.

This problem called for solving steepest descent, using the various matrix and vector types we'd built:

  • vector
  • banded matrix
  • diagonal matrix
  • rectangular matrix
  • symmetric matrix

Code

An iterative linear system solver that follows the gradient downhill until it converges. The initial guess is just the b vector because "why not."

template <typename T>
Vector<T> SteepestDescentSolver<T>::operator()(const SymmetricMatrix<T>& A,
                                               const Vector<T> b) {
    Vector<T> x = b; // initial guess is the b vector, cause why not
    T alpha{};

    unsigned i = 0;
    Vector<T> residual = b - (A * x);

    if (!isDiagonallyDominant(A)) {
        throw NonDiagonallyDominantMatrixError();
    }

    while (norm(residual) > EPSILON && i++ < MAX_ITERATIONS) {
        residual = b - (A * x);
        alpha = (residual * residual) / ((A * residual) * residual);
        x += alpha * residual;
    }

    return x;
}

5. CFG Tracer

Undergraduate research project that instruments C++ code to trace control flow at runtime. A control flow graph represents all possible paths through a program: nodes are basic blocks, edges are jumps. This tool parses source, identifies basic blocks, and generates execution traces. Boost handled the regex. The goal was to understand how programs actually execute versus how we think they execute.

Even by my senior year, most of the bigger projects were codebases I developed or co-developed; this was my first notable exception. With a fellow researcher, our job was to pick up an existing codebase from a graduate student and get it running. I thought it would be a walk in the park, but it needed some massaging. I particularly liked this assignment because it was a semester-long, tag-team effort to push someone else's work forward.


6. Splatoonio

Capstone project, a multiplayer mobile game in Flutter/Dart. Went from concept to deployed app with a team. Real-time synchronization, touch controls, cross-platform deployment. The kind of project where you learn that 80% of software engineering is communication.

I hope Nintendo doesn't read this. We had a team vote on the project name, and Splatoonio won. We can change it.

This was my most "complete" software project in college: server, client, docs, pitch, you name it. And it was hardly my doing; it was a team project, and our team was the dream team. No, literally: our team name was "Dream Team", after we realized we averaged two internships per person and all of us were in the same AI and numerical modeling classes (the most demanding combination at our college).

Our last presentation of the year was naturally a live demo, and we couldn't disappoint. We wanted to showcase the rendering across campus because our classroom definitely wasn't big enough, and we only had a production build with no demo wiring. So I showed up on game day in running gear, introduced our team, and proceeded to run across most of campus with the game running. I even timed my return to the last minute of the demo to make a statement. We were unanimously the top project of the class, affirmed by one of the most memorable rounds of applause I got as a student.


7. Space Invaders

Space Invaders running on an 8051 microcontroller. Assembly and C, pressed against tight memory constraints. Every byte mattered. Implementing smooth sprite movement and collision detection on hardware this limited teaches you what efficiency really means.

Space Invaders Demo

Cold November nights, coding with How I Met Your Mother playing in the background (see Code, below). This one assignment made me appreciate video game logic: writing a screen rendering engine with nothing but ncurses is a tall order. Keeping track of not just bounding boxes but changing state, animations, player input, drawing, all of it.

Despite the complexity and having never done anything like it, I got something working. It had several bugs centered around the aliens: they never progressed down the screen, they never shot, you could never hit the one in the last row. But it was satisfying nonetheless. One snag: this was supposed to run on hardware with 4k of memory, and my first compile for the target platform came in at 15k. Yikes. I stripped essentially every library and wrote my own. 8k.

This is the part where I'd love to say I found a clever hack to squeeze under the limit, but there's no perfect ending. I hit my wits' end, talked to the professor, and made up for the failure by implementing another feature.

I did get to present my game to the whole class. And my adventures made for some great memes, which I attached to my homework and presented to the class too.

Code

The game loop is a switch inside a do { } while (true), with the render living in the default: branch. Instead of the usual tick β†’ input β†’ update β†’ draw, this loop reads a key and only redraws when the player didn't press anything. Hold a key and the screen stops updating. Space Invaders with a frame rate inversely proportional to how panicked you are.

do {
    switch (getch()) {
        case KEY_LEFT:  /* ... move ... */  break;
        case KEY_RIGHT: /* ... move ... */  break;
        case ' ':       /* ... shoot ... */ break;
        case 'q':       endwin(); exit(0);  break;
        default:
            createHeader(&game, &header);
            createShooter(game.gunner.center, &game, &footer);
            createGameboard(&game, &gameboard,
                            stateOfAliens, stateOfShot);
            draw(&game, &header, &gameboard, &footer);
            break;
    }
    i++;
    if (i % STATE_CHANGE_ALIENS == 0) {
        stateOfAliens = stateOfAliens ? false : true;
    }
    stateOfShot = (i % 25 == 0);
} while (true);

The alien-selection logic is three branches of nested ternaries, picking which invader sprite to draw based on the row and animation frame.

if ((i / heightOfAverageAlien + 2) % 3 == 2) {
    (*aliens)[i][j] = stateOne
        ? smallInvaderOne[i % heightOfAverageAlien][j % smallWidth]
        : smallInvaderTwo[i % heightOfAverageAlien][j % smallWidth];
} else if ((i / heightOfAverageAlien + 2) % 3 == 0) {
    (*aliens)[i][j] = stateOne ? mediumInvaderOne[...]
                               : mediumInvaderTwo[...];
} else {
    (*aliens)[i][j] = stateOne ? largeInvaderOne[...]
                               : largeInvaderTwo[...];
}

The comment two lines above this block is the most honest sentence I ever wrote in a CS assignment:

// Then we mod by 3 because that's the number of aliens, and we
// compare to a number I put there because the returned numbers
// baffle me.

I had found an empirically-correct offset, and rather than figure out why, I shipped a comment saying so. Ten-years-later me is proud.

And then there's the HIMYM tax, paid in a split declaration so the comments land the punchline:

// It's gonna be legend..
void waitForIt(unsigned char seconds);
// ..ary! Legendary.

void waitForIt(unsigned char seconds) {
    unsigned int retTime = (unsigned int)time(0) + (unsigned int)seconds;
    while (time(0) < retTime);
}

8. Camelot

A team software engineering project with full documentation, UML diagrams, and Doxygen-generated API docs. Agile methodology, code reviews, collaborative development. The code itself is less interesting than the practice of building software with other people. An optional iOS chat client hooks into the server for real-time messaging. Swift, JSQMessagesViewController for the UI, SwiftSocket for TCP.

Socket Chat Client

This class was pure joy. Not too difficult, not too easy. It was mostly just building useful software: an end-to-end chat interface. I got to make use of my iOS skills while the team built a fully-functioning message server. We put it together with flashy presentations.

Code

The server is exactly what you'd expect from a sophomore who just learned sockets: threaded TCP, a module-global SOCKET_LIST, and a broadcast loop that fans every message out to every connected client.

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        my_socket = self.request
        SOCKET_LIST.append(my_socket)

        while True:
            data = str(my_socket.recv(1024), 'ascii')
            if not data:
                if my_socket in SOCKET_LIST:
                    SOCKET_LIST.remove(my_socket)
                return
            try:
                response = bytes(data, 'ascii')
            except Exception:
                response = bytes(json.dumps({
                    "error": "Something went wrong"
                }), 'ascii')
            for s in SOCKET_LIST:
                s.sendall(response)

The iOS side is sophomore-level in a different way: no push, no WebSockets, no long polling. Just a Timer that reads the TCP socket every second, decodes the bytes into a JSQMessage, and hops back to the main queue to render. Real-time by brute force.

switch client.connect(timeout: 1) {
case .success:
    self.timer = Timer.scheduledTimer(
        withTimeInterval: 1.0,
        repeats: true
    ) { _ in self.getNewMessage() }
    // ...
}

func getNewMessage() {
    DispatchQueue.global(qos: .background).async {
        let data = self.client.read(1024 * 10)
        guard data != nil else { return }

        if let string = String(bytes: data!, encoding: .utf8) {
            let message = JSQMessage(
                senderId: User.reciever.rawValue,
                displayName: getName(User.reciever),
                text: string)
            self.chatView.newMessage(message)
        } else {
            print("not a valid UTF-8 sequence")
        }

        DispatchQueue.main.async {
            self.chatView.finishReceivingMessage()
        }
    }
}

9. CLC Tally

iOS app for tracking student headcounts at Missouri S&T's Computer Learning Center (CLC). Built to solve a real problem: tutors needed a quick way to log how many students they helped. Simple interface, local storage, export.

CLC Tally Screenshot

The irony is that I mostly wrote this in the CLC. I'd never found a tally app with this particular data format, and it was much easier to have my phone always-on taking count than periodically marking a notebook. I ended up being the only user, because I didn't have an App Store account. But this was an "enjoy the journey, not the destination" project: the beauty of developing and improving something weekly that you actually use.

Code

The entire data model is 20 lines. Each tap appends a Date to an array in UserDefaults. The counter is userLog.count. "Users this hour" is a filter. No database, no Core Data, no schema migrations. Sometimes the best software is the software that just works.

class Counter: CustomStringConvertible {
    public var count: Int { return userLog.count }

    private var userLog: [Date] {
        get {
            UserDefaults.standard
                .object(forKey: "log") as? [Date] ?? []
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "log")
            UserDefaults.standard.synchronize()
        }
    }

    public func increment() { userLog.append(Date()) }
    public func decrement() {
        if !userLog.isEmpty { userLog.removeLast() }
    }

    public func usersThisHour() -> Int {
        let hourOf = { (d: Date) in
            Calendar.current.component(.hour, from: d)
        }
        let now = hourOf(Date())
        return userLog.filter { hourOf($0) == now }.count
    }

    var description: String { return "\(count)" }
}

10. Grading Suite

Automated grading tools for CS 1570, the intro programming course. A style checker that enforces coding standards, a roster checker that validates submissions, a grader script that runs test cases, and a plagiarism checker.

Built out of necessity: grading hundreds of submissions by hand became unsustainable. I automated as much as possible so I could focus on the core concepts: algorithms, data structures, and programming paradigms.

The plagiarism checker never flagged anyone until assignment 8 out of 10; unironically, the hardest assignment of the year. Assignment 7 was a pair-programming project, assignment 8 was strict solo; and the same pair from assignment 7 decided to tackle assignment 8 together. I brought it to the instructor, and they asked my opinion on what we should do. Wanting to be fair, I proposed:

They split the work evenly, they should split the grade evenly: take each score and divide by two.

Output

Output is emitted as markdown so it drops straight into whatever report format the course coordinator wanted:

## student_submission.cpp

**80 Column Rule**

- Line 42: `    if (studentName == "John" && assignment.isComplete()`

**Tabs**

- Line 17: `    int counter = 0;`

**Non-Uppercase Constants**

- Line 8: `const int maxStudents = 60;`
- Line 9: `const double passingGrade = 70.0;`

**Header Guards Don't Match**

- Line 3: `#ifndef STUDENT_H`

**Missing Documentation (12 Functions, 4 Lines of Comments)**

Code

Every rule is a regex; most of them look like someone leaned on the keyboard. A sampler:

# 80-column rule: match anything, then demand a non-space in column 81.
# Trailing whitespace counts as a violation, which was the point.
column = r".{80}\S"

# Tabs: anchor to start of line, look for one tab character.
tabs = r"\A\t"

# Non-uppercase constants: "const <type> <name>;" where <name>
# has any lowercase letter. The nested char classes and optional
# assignment tail are what make it ugly.
constants = r"const\s+([a-zA-Z]|_)([a-zA-Z]|[0-9]|_)*\s+" \
            r"(([a-zA-Z]|_)([a-zA-Z]|[0-9]|_)*|\s*,\s*)*" \
            r"([a-zA-Z]|_)([A-Z]|[0-9]|_)*[a-z]+([A-Z]|[0-9]|_)*(\s*=\s*.+)*;"

# Switch without default: grab a whole switch block, then
# re-search inside for `default:`.
switch_block = r"switch\s*\(.*\)\s*\{[^\{;]+\}"

# Header-guard matcher: capture the #ifndef name and #define name, compare them.
header_guard = r"#ifndef\s*(.*)\n#define\s*(.*)"

# Header-comment detector: // or * or whitespace, then
# "File" / ".cpp" / ".hpp" / ".h". Paired with a separate
# /(Author|author)/ check for the author line.
header = r"(\/\/|\*|\s)+.*(File|file|.hpp|.cpp|.h)"

By The Numbers

Metric Value
Courses 25
Total Commits 545
Total Files 1,826
Lines of Code 91,512
Languages 9
  1. TeX: 31,043 lines
  2. C/C++ Header: 28,235 lines
  3. C++: 17,246 lines
  4. SQL: 11,184 lines
  5. Python: 7,758 lines
  6. C: 2,667 lines
  7. Shell: 1,513 lines
  8. Assembly: 656 lines
  9. MATLAB: 337 lines

Commit Activity by Hour

+-------------------------------------------------------+
| Commit Activity by Hour                               |
+-------------------------------------------------------+
| Hour   | Commits | Distribution                       |
+-------------------------------------------------------+
| 00:00  |      14 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                            |
| 01:00  |       4 | β–ˆβ–ˆ                                 |
| 02:00  |      11 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                             |
| 03:00  |       5 | β–ˆβ–ˆ                                 |
| 04:00  |       0 |                                    |
| 05:00  |       0 |                                    |
| 06:00  |       0 |                                    |
| 07:00  |       4 | β–ˆβ–ˆ                                 |
| 08:00  |       7 | β–ˆβ–ˆβ–ˆ                                |
| 09:00  |      32 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                  |
| 10:00  |      34 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                 |
| 11:00  |      30 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                   |
| 12:00  |      28 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                    |
| 13:00  |      21 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                        |
| 14:00  |      31 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                   |
| 15:00  |      24 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                      |
| 16:00  |      36 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                |
| 17:00  |      20 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                         |
| 18:00  |      25 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                      |
| 19:00  |      40 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ              |
| 20:00  |      41 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ             |
| 21:00  |      64 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  |
| 22:00  |      44 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ           |
| 23:00  |      16 | β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                           |
+-------------------------------------------------------+
| Total commits: 545                                    |
+-------------------------------------------------------+

Peak activity: 9 PM with 64 commits.

Activity Heatmap

                          ACTIVITY HEATMAP
──────────────────────────────────────────────────────────────────────

          Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec
        β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”
   2014 β”‚  β–‘ β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚
        β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€
   2015 β”‚    β”‚    β”‚  β–‘ β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚  β–‘ β”‚  β–‘ β”‚
        β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€
   2016 β”‚  β–‘ β”‚  β–‘ β”‚ β–ˆβ–ˆ β”‚ β–ˆβ–ˆ β”‚ β–ˆβ–ˆ β”‚    β”‚  β–‘ β”‚    β”‚    β”‚  β–‘ β”‚  β–’ β”‚  β–‘ β”‚
        β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€
   2017 β”‚  β–‘ β”‚ β–’β–’ β”‚ β–’β–’ β”‚ β–ˆβ–ˆ β”‚  β–’ β”‚    β”‚    β”‚  β–’ β”‚ β–’β–’ β”‚  β–’ β”‚  β–‘ β”‚  β–‘ β”‚
        β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€
   2018 β”‚ β–ˆβ–ˆ β”‚ β–ˆβ–ˆ β”‚ β–ˆβ–ˆ β”‚ β–’β–’ β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚    β”‚
        β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”˜

──────────────────────────────────────────────────────────────────────
             β–‘ 1-10     β–’ 11-30     β–ˆ 31-50     β–ˆβ–ˆ 51+

                   Spring semesters: Jan-May
                     Fall semesters: Aug-Dec

Lines of Code by Year

                     LINES OF CODE BY YEAR
──────────────────────────────────────────────────────────

      2014       2015       2016       2017       2018
        β”‚          β”‚          β”‚          β”‚          β”‚
        β”‚          β”‚          β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚          β”‚          β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚          β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚          β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚          β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
        β”‚      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
  ────────────────────────────────────────────────────────
      ~2k       ~10k       ~20k       ~25k       ~35k

For the notes and problem sets themselves, see Academia Notes.

To my peers and professors at Missouri S&T: thank you. You made those four years special, and I remember them fondly. The late nights coding, the early morning classes where I'd take my first sips of coffee, the many fruitful discussions in between. All of it.

Academia Notes

4 years, 29 courses, 1,113 pages. Because who doesn't love compiler errors from their notes?

From 2014 to 2018 I kept nearly every homework, lecture note, and problem set in LaTeX. Mathematical notation, diagrams, code listings, all rendered properly. Compiled, they come out to 1,113 pages.

The source is on GitHub. Four compilations below, covering different subsets of the material.

Document Pages Description
Curated 284 Curated selection of best work
Assignments 498 Homework with solutions
Notes 473 Lecture notes and study materials
Complete 1,113 Everything
Loading document...
1 / 1 (preview)

Courses Covered

Computer Science

Computer Engineering

Mathematics

Physics

Statistics, Philosophy, Psychology

University of Missouri At Rolla featured in The Simpsons

It’s not often my college β€” Missouri University of Science and Technology, aka Missouri S&T β€” makes an appearance in pop culture. When it does, it’s kind of a big deal; at least me.

So I was incredibly happy to discover that Missouri S&T was featured in the Simpsons. This Reddit thread is where I found this, which lays out the premise:

Lisa was increasing her college tuition budget by gambling online. Every time she won more money, she changed her sweater to a "better" college. We [Rolla] are better than Springfield Community College and worse than Harvard, Yale, Oxford. β€” u/bullhonke
Lisa Simpsons featuring a retro Missouri S&T sweater (S24 E4, 11:40)

It’s fitting, at least for those of us who had the great honor of studying under the legendary Clayton Price. Price taught computer science and was well-known for making his (difficult) assignments based on his favorite show: the Simpsons. I assume many former-students have nightmares in the middle of the night, trying to articulate Bart and Homer doing something comical into code.

The irony is this episode aired 2012, and I never saw this during my tenure. Additionally this was almost a decade after Mr. Price famously got rid of his television, so clearly he never saw it either. But it’s heartwarming to us Missouri S&T alumni to know we existed in a timeline where this episode, Price, and our experiences at Rolla co-existed.

Missouri S&T Satellite Team: Mr & Mrs Satellite

Missouri made. Space grade.

In February 2015, Missouri University of Science and Technology won the Air Force's Nanosat-8 competition, beating MIT, Georgia Tech, and seven other universities. Their winning design: two satellites that would perform proximity operations in space, demonstrating inspection capabilities for non-responsive spacecraft. Nearly a decade later, these satellites are finally ready for launch.

Background

Missouri S&T hired Dr. Hank Pernicka in 2001 to establish their aerospace engineering program. By 2005, the university was competing in national satellite competitions. Dr. Pernicka set clear expectations for his students: "dealing with spacecraft, close to 100% is required for success."

The Missouri S&T Satellite Team (M-SAT) grew from a single course to a 50-person multidisciplinary team by 2015. Their competition history included third place in Nanosat-4 and second place in Nanosat-7, where they notably outperformed MIT.

The Nanosat-8 Competition

The University Nanosat Program, funded by the Air Force Office of Scientific Research, challenges universities to develop flight-ready satellites. Nanosat-8, launched in 2012, focused on proximity operations and space situational awareness – capabilities with direct military applications.

Ten universities competed over two years. The final review occurred January 18-19, 2015, at Kirtland Air Force Base. Winners would receive Air Force launch services for their satellites.

The Mr & Mrs Sat Design

Missouri S&T proposed a two-satellite system:

  • MR SAT (Missouri-Rolla Satellite): The inspector satellite
  • MRS SAT (Missouri-Rolla Second Satellite): Simulates an uncooperative space object

The mission addresses a practical problem. When satellites malfunction or stop responding, operators need to assess damage without risking astronaut spacewalks or expensive replacement missions. Mr & Mrs Sat demonstrates autonomous inspection at a 10-meter distance.

Key Technical Innovations

Stereoscopic Imaging: Custom dual-camera system creates real-time 3D images, allowing precise position and velocity measurements in space.

R-134a Propulsion: Instead of expensive spacecraft propellants, the team used R-134a refrigerant – the same compound found in automotive air conditioning. The cold gas system, stored in a container roughly the size of a 2-liter bottle, provides six degrees of freedom for formation flying. Cost: about $20 per bottle versus thousands for traditional propellants.

Autonomous Control: The Self-sufficient, Accelerated Spacecraft Integration Flight Control System (SASI FCS) maintains formation without ground intervention, critical for practical operations.

Development Challenges

Dr. Pernicka warned students the project would be "underfunded and due in two years – a ridiculous deadline for normal satellite design." Air Force safety requirements added complexity.

Anna Schroeter, Program Manager, coordinated 9 subsystems and 25+ personnel. Her responsibilities included budget proposals, Air Force coordination, and systems integration across multiple engineering disciplines.

The psychological toll was significant. As Dr. Pernicka noted, "Sometimes they fail spectacularly. It's part of the business." Students invested years in projects that could fail during launch or in orbit.

From Competition to Flight Hardware

Winning in 2015 guaranteed launch services but required transforming academic designs into flight-qualified hardware. This meant:

  • Space qualification for every component
  • Redundancy for all critical systems
  • Testing for launch vibrations, thermal cycling (-100Β°F to +200Β°F), and radiation exposure

The process took nearly a decade. By Summer 2024, flight-ready satellites were delivered to the Air Force Research Laboratory. Multiple student generations contributed to the project through graduation cycles and the COVID-19 pandemic.

Mission Profile

The satellites will likely launch on a SpaceX Falcon 9, possibly via the International Space Station. Once deployed, they'll demonstrate:

  • Automated proximity operations
  • 3D imaging and reconstruction
  • Formation flying using low-cost propulsion

Primary mission duration: 4 months
Expected operational life: 2-4 years

Applications include military satellite inspection, commercial satellite diagnostics, and development of future servicing missions.

Educational Impact

The program has produced over 11,000 alumni across 25 years. Many work at NASA, SpaceX, Boeing, and other aerospace companies. Students gain end-to-end spacecraft development experience unavailable in traditional academic programs.

Joseph Nguyen exemplifies the program's reach. A first-generation college student who initially found undergraduate research "a little too challenging," he became Student Director. His scholarship letter stated: "As the first male in my family to attend college, your scholarship will financially assist my family in the expenses of my attendance. I hope to make my community proud and hope to make you proud as a student who dared to change his future."

Technical Significance

The R-134a propulsion system demonstrates how cost constraints drive innovation. If university students can achieve precision formation flying with hardware store refrigerant, the approach could benefit budget-conscious small satellite operators.

The stereoscopic imaging system addresses growing needs for space situational awareness as orbit becomes more congested. Autonomous inspection capabilities have clear national security applications.

Looking Forward

Mr & Mrs Sat awaits launch within 1-2 years. The approaching milestone represents a decade of student effort, technical problem-solving, and persistence through setbacks.

When these satellites finally reach orbit, they'll prove that the greatest space achievements don't always come from billion-dollar budgets or prestigious institutions – sometimes they come from Missouri students with the audacity to make hardware store refrigerant fly. In the cold vacuum above, Mr & Mrs Sat will dance on air conditioning dreams, showing the world that innovation thrives on constraint, that ridiculous deadlines can forge real spacecraft, and that true engineering genius isn't about having the most resources but about having the nerve to reach for the stars with whatever's in your toolbox. They'll orbit as monuments to every student who dared to believe that a hardware store trip could be the first step to space – and that sometimes, the most extraordinary achievements begin with the most ordinary tools.