> alex_lu

/.../.../

> tSnake

Snake but multiplayer and the apples try to kill you
  • python
  • solo
  • games
  • command line

Posted: Wed Mar 04 2026

Project: Sun Feb 01 2026 till Invalid Date

    Oops. Sometimes with long hours of work, you’re getting carried away in the zone. Other times, it’s not so productive. These 15 hours were a bit of both (just kidding, 100% the latter), while I tried to figure out sockets and networking.

    tSnake is an in-progress multiplayer game inspired by “snake”. It will be playable from the terminal and made with Python (where I try my best to develop custom tools for display, input, etc.).

    During these 15 hours, I learned how to host a server and connect clients to it. This involved youtube tutorials, documentation scouring, and plenty of banging my head against the wall. One of my main goals with projects like this is to maximise my understanding of what I’m using. This sometimes means spending longer than I should have to to solve simple bugs, avoiding direct answers from AI and minimising the use of heavily abstracted libraries. In this session, my focus was on setting up a client-server connection that can simulate the actual game but without the UI yet.

    One thing I found as a noob to networking protocols and asynchronous threads (especially in Python) was that keeping track of execution points is essential to properly cleaning up threads. For just about 8 hours, I was changing single words and rerunning the client and server programs over and over to try to understand why sockets were disconnecting preemptively or programs finishing before receiving data. This led me to a couple findings about the code and the process of fixing code. First is that properly closing, and keeping track of when you close things is extremely important to avoid a hundred errors from popping up with no easy stack trace to follow. Before starting, mocking up even just 2-10 lines of rules about data typing, which thread is in charge of what, and who is going to manage clean up will save one from tons of confusion later on. A specific problem I had was with context managers. By using a ‘with …connection’, statement in each “state” that a thread could be in, I was unintentionally severing connections before I’m actually done with them. To solve this, I had a main execution point, that would handle the states, with one of them being a dedicated “close state” that repeatedly just waits for the client thread to acknowledge that it is getting kicked off before immediately force closing it. This solves the issues of the server trying to send info to a “non-socket” (already closed connection) and the client potentially being behind in the logic, but not necessarily the “calls” it needs to make to the server.

    Secondly, debugging sucks when everything depends on the implementation of everything else. With my client-server setup that used arbitrary values, of which I figured would make testing easier, modifying any code to find the source of a bug quickly becomes a maze with no walls. What I mean by this, is that you’re stuck rebuilding everything just even when you have a clear idea for the change needed. For example, I needed to see what data was being sent from the server, firstly, while two clients are connected, then later for just one. Since my main program needed both connections before starting getting to where I needed, I had to rewrite the path from connection to the game point and rerun the entire program for the server and both clients each time I needed to test a value for just one client in a specific case. While, not exactly a direct solution, I found that looking away from the code and just thinking about the logic without testing can help reduce the overhead if you do get stuck in a hole like this. Rather than looking for flaws in blocks of irrelevant code, you’re only thinking about what you need to find. This also keeps your eyes from burning out, causing more mistakes later on.

    Real-time games process drawing and events independently of input. Let’s say we wanted to create an enemy that can move around even while the player is idle. If the source of input blocked execution, every other entity depends on the player taking an action in order to process the next frame.

    The last segment of 15 hours was attempting to figure out how to use my existing input system (using getch and mscvrt.getwch). Those with experience with getch will know that attempting to thread just a getch call will fail and result in no actual inputs. This is because getch is a thread-safe, blocking call. This ensures that there are never conflicts when reading from the buffer, but also makes our job a bit harder. To solve this, I of course went to good ol Stack Overflow https://stackoverflow.com/questions/24192171/python-using-threading-to-look-for-key-input-with-getch and tried using multiprocessing instead of the threads module. My next idea was to just use a library intended for key inputs, however this was not ideal. I wanted to limit dependencies, and capturing keys wasn’t quite the solution I wanted. Finally, I did what I should have done from the start, I reviewed the documentation for msvcrt. Guess what, kbhit (and selecting from the buffer via termios also works) can be used to check if there’s something in the buffer before acutally getching it… This was the solution I needed. Simple, quick, and probably the intended way to use getch.

    I learned from this just how important taking a break from one problem can be. By looking away from the actual code, I was able to regather my logic and figure out potential solutions. Extending upon this idea, having multiple sources to reference is crucial for unblocking problems. While forums and overviews might be useful for direct information, the documentation might have the detail needed to accomplish a more specific task Overall, taking on unfamiliar tasks can be difficult, result in great frustrations, and a severe lack of sleep, but these experiences are integral to growing, and it feels darn good to overcome a big hurdle. ; 1