Inspiration
While Tic-Tac-Toe is a conceptually simple game, I wanted to use it as a sandbox to explore low-level multiplayer networking and engine architecture in Python. By keeping the game logic minimal, I could focus entirely on building a robust Local Area Network (LAN) infrastructure and experimenting with different design patterns without the overhead of a massive physics engine.
What it does
It is a fully functional, zero-latency LAN multiplayer Tic-Tac-Toe game. One player acts as the Host, spinning up a local server, while the second player connects via IP address. The game features a custom lobby system with "Ready" states, real-time board synchronization, and an interactive UI with hover states, text-editing, and dynamic turn indicators.
How I built it
The project was built purely in Python using Pygame-CE for rendering and the built-in socket library for networking. The codebase is heavily structured around two distinct architectural patterns:
- Entity-Component-System (ECS) for UI: Instead of relying on standard object-oriented GUI classes, I built a custom ECS. Entities are defined by discrete data payloads (
SpatialComponent,HoverComponent,TextComponent), which are processed independently by decoupled systems (RenderingSystem,ClickingSystem,HoverSystem). - Client-Server Model for Networking: The network layer (
ServerNetworkSystemandClientNetworkSystem) utilizes raw TCP sockets to transmit JSON-encoded byte packets, ensuring reliable state synchronization.
Challenges I ran into
The most significant challenge was architectural: understanding when to implement a strict ECS design and when to opt for simplicity.
Initially, I debated forcing the entire game—including the networking—into the ECS framework. However, I realized that while ECS is incredible for decoupling rendering and UI logic, using it for network state management would introduce unnecessary complexity. I ultimately decided to split the architecture. I used ECS exclusively for the front-end UI and board rendering, but reverted to a straightforward, event-driven Client-Server design for the networking layer. Bridging these two paradigms so they communicated cleanly without bottlenecking the main Pygame loop was a massive, but rewarding, hurdle.
Accomplishments that I'm proud of
I am incredibly proud of how clean and scalable the codebase turned out. By strictly separating the UI components from the rendering systems, adding new interactive elements (like the IP text-entry box or lobby buttons) became as simple as appending a dictionary of components. I also successfully implemented a non-blocking network architecture that allows the custom UI to render smoothly at 60 FPS while concurrently listening for incoming TCP packets.
What I learned
This project was a masterclass in architectural trade-offs. I learned that there is no "silver bullet" design pattern. ECS is incredibly powerful for visual processing and state-independent logic, but strict Client-Server models are far superior for linear, state-dependent network synchronization. Learning how to identify the right tool for the specific layer of the engine was the biggest takeaway from this build.
Built With
- pygame-ce
- python
Log in or sign up for Devpost to join the conversation.