Build a firewall that measures traffic
The firewall no longer gives you a request rate. You receive raw IP addresses, timestamps, and destination ports. Work out how to track each source and calculate how many requests it has sent in the preceding ten seconds.
The network receives 20 requests per second for 30 seconds, for a total of 600 requests. Some sources send short bursts, some keep flooding, and others send ordinary traffic. One source goes quiet long enough to recover before sending another burst.
Your servers share 100 health points. Every request that violates the policy and reaches a server removes 2 health points. At zero health, the wave ends early. A source's early requests are legitimate if they are within policy; an IP is not permanently classified as an attacker.
Write the request handler
Complete handle_request(request). The game calls your function once for every incoming request, in chronological order. Return the string "allow" or "block".
Each call receives a fresh dictionary with exactly these fields:
{
"ip": "203.0.113.42",
"timestamp": 12.45,
"port": 443,
}
ip: The source IP address, as a string.timestamp: Seconds on the simulated clock. Multiple requests can have the same timestamp.port: The destination port, as an integer.
There is no supplied rate, recent-request count, or rate-calculation helper. Choose your own data structure and implement the timestamp comparisons and counting yourself. The starting history dictionary is optional; you may replace it with another approach.
Your code is initialized once at the start of each wave. Module-level state persists between requests in that wave and is reset for each new attempt or independent validation scenario. Do not call handle_request() yourself or use the real clock.
Enforce this policy
- Rate limit: Allow at most 20 requests per IP within the preceding 10 seconds. A count of 21 or more means block, including on port 443.
- Port rule: When the IP is within the rate limit, allow port 443 and block all other ports.
- Independent sources: Track each IP separately. Traffic from one source must not use another source's allowance.
Include the current request in its IP's count. Count every received request, including those you block for rate or port violations.
For a request arriving at time now, include timestamps in (now - 10, now]. A request exactly ten seconds old is no longer in the window. Do not reset counts at fixed ten-second boundaries: the window moves with every request.
Blocking applies to the current request, not permanently to the IP. Allow compliant traffic again after enough old requests expire. A continuing flood remains blocked because blocked attempts also count.
Run and inspect
Press Run to evaluate your Python and play its recorded decisions through the network. The visible traffic is repeatable so you can compare attempts.
- Correct block: The request breaks apart at the firewall.
- Policy violation allowed: The request hits a server, flashes red, and causes damage.
- Compliant request allowed: The server flashes green and the served counter increases.
- Compliant request blocked: Health is unchanged, but the decision is incorrect.
The bottom inspector samples individual requests and shows the source, the independently checked rolling-window count, and your decision. Those counts are visual feedback only; they are never passed into your Python. After a mistake, the result holds an example explaining the expected decision.
The window count is requests in ten seconds, not requests per second. Each request represents one observed request in this simulated policy, not a raw measurement of real internet traffic.
Traffic generation stops after 30 seconds; the final requests then finish traveling. Edits submitted while the wave is running are queued: the latest queued code runs next, and the superseded wave cannot award completion.
Success
Finish with full health, serve every compliant request, block every policy violation, and pass all additional validation scenarios. These include the 20th and 21st requests, exact timestamp boundaries, interleaved IPs, simultaneous requests, blocked attempts, recovery, and bursts crossing fixed time boundaries.
Blocking everything, allowing everything, permanent bans, lifetime counters, or hard-coded IP lists will not pass. You may use lists, dictionaries, collections.deque, or another correct approach; validation checks behavior rather than requiring one particular implementation.
Syntax errors, invalid return values, and missing functions stop the wave with a readable message. A loop guard stops typical accidental infinite loops. Keep the implementation bounded and use the supplied timestamps. This exercise permits imports from collections, math, and bisect; it does not need files, networking, or other services.
Optional damage sound
The existing short damage cue and mute controls are unchanged. Sound plays only when bad traffic reaches a server and browser audio is available; visual damage feedback works when muted.
This is a simulated automation exercise, not a production firewall or a complete DDoS mitigation system.