This project implements a simplified blackjack game using a client–server model over a network.
It follows the specification provided in the Intro to Computer Networks 2025 Hackathon assignment.
This README is written for graders who already know the assignment.
It focuses on how we implemented the system, the absolute execution flow, and non-standard / extra design choices.
common/
constants.py # protocol constants (cookie, ports, sizes)
protocol.py # binary pack/unpack helpers
cards.py # card + deck logic
net_utils.py # recv_exact() and helpers
server/
main_server.py # server entry point
tcp_server.py # tcp accept loop + threading
udp_broadcast.py # udp offer broadcaster
game_engine.py # blackjack logic
client/
main_client.py # client entry point
udp_listener.py # udp discovery
tcp_client.py # game session logic
ui.py # interactive output helpers
test_edge_cases.py # stress & robustness tests
main_server.pystarts.- Server binds a TCP socket to
0.0.0.0with port0(OS assigns a free port). run_tcp_server()is called:- starts an accept loop
- each incoming client is handled in its own thread
- A UDP broadcaster thread is started:
- sends offer packets once per second
- includes magic cookie, message type, TCP port, and server name
- The main server thread blocks on
input():- pressing Enter sets a shared
stop_flag - UDP broadcast thread exits cleanly
- server shuts down gracefully
- pressing Enter sets a shared
main_client.pystarts.- User enters:
- team name
- number of rounds
- Client listens on UDP port
13122. - Upon receiving a valid offer:
- extracts server IP and TCP port
- connects via TCP
- Client plays the requested number of rounds.
- After session ends:
- client returns to listening state
- no restart required
- Server deals:
- 2 cards to player
- 2 cards to dealer (1 hidden)
- Server sends:
- player card
- player card
- dealer up-card
- Client repeatedly sends:
HitttorStand
- Server behavior:
- on hit → send card +
RES_NOT_OVER - on bust → send final loss packet immediately
- on hit → send card +
- Dealer draws until total ≥ 17.
- Server sends final result:
- win / loss / tie
- dummy card if needed
- Server waits for Enter instead of forced termination.
- UDP broadcaster terminates via shared
stop_flag.
- When player busts, server sends:
- bust card
- loss result
- Done in a single payload to avoid deadlock.
Client UI prints:
- card symbols (♠ ♥ ♦ ♣)
- running hand totals
- round banners
- session summary with win-rate
All UI improvements are client-side only and do not alter the protocol.
- Malformed packets raise controlled
ConnectionError. - Only the offending client thread exits.
- Server continues serving other clients.
test_edge_cases.py intentionally tests:
- player busts
- dealer busts and ties
- malformed cookies
- truncated payloads
- zero and high round counts
- random strategies
- concurrent clients
Server survives all tests without process termination.
- Strict separation of protocol logic (
common/) - Thread-per-client model for clarity
- Defensive TCP reads (
recv_exact) - Deterministic and readable server logs
- Grader-friendly structure and flow
Server:
python3 -m server.main_server
Client:
python3 -m client.main_client
Optional debugging:
DEBUG_OFFERS=1 python3 -m client.main_client
This README intentionally avoids restating the assignment text.
All packet formats, sizes, and semantics strictly follow the official specification.
Extra UI, graceful shutdown handling, and stress tests were added for clarity, robustness, and ease of demonstration.