Project 2: Keyboard Character Controller
Build responsive player movement with one-shot and hold actions.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 1: Beginner |
| Time Estimate | Weekend |
| Main Programming Language | C# |
| Alternative Programming Languages | F#, VB.NET |
| Coolness Level | Level 2 |
| Business Potential | 2. Micro-SaaS / Pro Tool |
| Prerequisites | MonoGame basics, C# fundamentals, guide concept chapters |
| Key Topics | input polling, intent mapping, movement normalization |
1. Learning Objectives
By completing this project, you will:
- Implement a complete vertical slice focused on input polling, intent mapping, movement normalization.
- Practice deterministic verification instead of subjective “it feels right” debugging.
- Build instrumentation that explains behavior in-frame.
- Connect implementation choices to architecture and interview-level reasoning.
2. All Theory Needed (Per-Concept Breakdown)
Input Polling and Edge Detection
Fundamentals
Input Polling and Edge Detection is a core competency for this project because it defines the contract between intent, simulation, and output. At a minimum, you need to know what this concept controls, what data it reads, what data it mutates, and which invariants it must preserve under frame spikes and state transitions. A reliable implementation starts by defining those invariants explicitly before writing any logic. In this project, the concept is not treated as optional optimization. It is the mechanism that keeps behavior consistent, testable, and debuggable when complexity rises.
Deep Dive into the concept
The operational value of Input Polling and Edge Detection is that it gives you a stable place to reason when bugs appear. Most beginner implementations fail by mixing responsibilities or by letting hidden side effects mutate state from multiple directions. You should avoid that by writing a clear phase model: input acquisition, intent interpretation, state mutation, and presentation. When this phase model is explicit, you can instrument each stage and identify where divergence begins. This project assumes you build that instrumentation from day one.
For deterministic validation, define at least one golden scenario with fixed starting values and predictable event sequence. The scenario should include one stress condition, such as a frame spike, rapid input, or high-entity burst, depending on the project. Then compare expected and observed state checkpoints at fixed tick counts. If outputs diverge, inspect phase by phase instead of patching constants. This workflow is the difference between robust engineering and accidental success.
Failure modes often hide in transition edges: entering/exiting menus, resetting rounds, reloading maps, reconnecting network peers, or reclaiming pooled objects. The safest pattern is to treat transitions as first-class operations with preconditions and postconditions. Preconditions ensure the system can transition; postconditions verify invariants after transition. Keep those checks in debug builds, because they catch regressions early.
Where you’ll apply it in this file: see 3.2 Functional Requirements, 5.3 The Core Question, 6.2 Critical Test Cases. Also used in adjacent projects: Previous Project and Next Project.
How this fit on projects
- Primary usage: this project implementation and testing phases.
- Secondary usage: migration and extension in adjacent projects.
Definitions & key terms
- Invariant: a condition that must stay true after every update.
- Golden scenario: deterministic test run used as reference behavior.
- Phase model: explicit order of operations in each frame/tick.
Mental model diagram
Input/Events -> Interpretation -> State Mutation -> Verification -> Presentation
| | | | |
+--------------+----------------+----------------+---------------+
deterministic checkpoints + debug logs
How it works
- Capture incoming signals.
- Transform to project-level intents.
- Apply mutation in controlled order.
- Check invariants immediately.
- Render/log current state.
Invariants: deterministic checkpoints match under same inputs. Failure modes: hidden side effects, unordered transitions, stale references.
Minimal concrete example
Pseudo sequence:
- apply_intent("action")
- mutate_state()
- assert(invariant_a)
- assert(invariant_b)
- emit_debug_metrics()
Common misconceptions
- “This concept is polish only.” It is correctness-critical.
- “If it works once, architecture is fine.” Without deterministic checks, regressions stay invisible.
Check-your-understanding questions
- Which invariant is most likely to fail first in this project?
- What is your deterministic golden path?
- Which transition requires explicit guard checks?
Check-your-understanding answers
- The one linking state mutation order and visible output.
- A fixed input/event sequence with known checkpoint hashes.
- Any transition that swaps major modes or ownership.
Real-world applications
- Stable gameplay systems under real frame variance.
- Team debugging workflows with reproducible evidence.
- Safer feature growth without architectural collapse.
Where you’ll apply it
- Sections 3, 5, and 6 of this file.
- Adjacent projects: Previous Project, Next Project.
References
- MonoGame documentation and API references.
- “Clean Architecture” by Robert C. Martin.
- “Game Programming Patterns” by Robert Nystrom.
Key insights Architecture value appears when the system is under stress, not only on happy-path runs.
Summary Input Polling and Edge Detection provides the control surface that keeps this project explainable and testable.
Homework/Exercises to practice the concept
- Write three invariants before implementing any new feature.
- Define one deterministic stress scenario and expected checkpoints.
- List two transition guards and their failure actions.
Solutions to the homework/exercises
- Invariants should cover state validity, timing, and ownership boundaries.
- Use fixed seed, fixed inputs, and state hash at known ticks.
- Guard invalid transitions with explicit rejection and log reason.
Intent Mapping
Fundamentals
Intent Mapping complements the first concept by addressing spatial, logical, or synchronization correctness depending on project scope. The goal is not raw feature breadth; it is dependable behavior that can be validated quickly. You should model this concept with explicit data shapes and deterministic update rules. When behavior differs from expectation, you need one location where truth lives, one set of checks that prove correctness, and one debug visualization path that explains failures.
Deep Dive into the concept
A strong implementation begins with data contracts. Define the smallest state representation that supports your feature, then codify transitions with clear pre/post conditions. Avoid deriving critical values in multiple places. Duplicate derivation is a common source of subtle divergence, especially when one path updates during pause, reconnect, or map reload while another path does not.
Performance should be framed as budgeted correctness. First make behavior accurate and deterministic, then profile and optimize hot paths with evidence. In projects with many entities or graph queries, optimization opportunities include caching, batching, and staged updates. In projects with network or persistence boundaries, optimization often means reducing redundant serialization, controlling message frequency, or minimizing schema churn.
Testing this concept requires scenario variety: a golden success run, one failure-input run, and one edge-case run. Keep all three deterministic where possible. Your edge-case run should mirror real user pain: alt-tab interruption, missing asset, stale save version, high-latency burst, or crowded entity wave. Measure not only whether app survives, but whether state remains coherent.
Where you’ll apply it in this file: 3.6 Edge Cases, 4.4 Algorithm Overview, 7.1 Frequent Mistakes. Also used in adjacent projects: Previous Project and Next Project.
How this fit on projects
- Primary usage: this project’s core mechanic validation.
- Secondary usage: production hardening and refactor safety.
Definitions & key terms
- Single source of truth: one canonical representation for critical state.
- Edge-case run: deterministic scenario designed to break assumptions.
- Budget: explicit performance or correctness threshold.
Mental model diagram
Canonical Data -> Transition Rules -> Observable Output
| | |
v v v
validation checks profiling hooks user-visible behavior
How it works
- Define canonical state structures.
- Apply transitions via one controlled pipeline.
- Validate expected invariants at checkpoints.
- Profile and optimize only proven bottlenecks.
- Re-run deterministic scenarios after each change.
Invariants: one source of truth remains coherent across transitions. Failure modes: duplicate derivations, inconsistent edge-case handling.
Minimal concrete example
Pseudo validation:
- before_transition_hash = hash(core_state)
- apply_transition(event)
- assert(valid_state(core_state))
- log(hash(core_state), delta_time_ms)
Common misconceptions
- “Optimization first prevents rework.” Premature optimization hides correctness bugs.
- “Edge cases can wait.” They define production readiness.
Check-your-understanding questions
- What is the canonical state for this mechanic?
- Which edge-case run is mandatory before sign-off?
- How will you prove optimization did not alter behavior?
Check-your-understanding answers
- The minimal data structure that all transitions depend on.
- The run that stresses known fragile boundaries.
- By replaying deterministic scenarios and comparing checkpoints.
Real-world applications
- Reliable feature growth in indie or small-team game projects.
- Production QA with deterministic repro paths.
- Interview discussions about correctness under constraints.
Where you’ll apply it
- Sections 3.6, 4.4, 6.2, and 7.1 in this file.
- Related project links: Previous Project, Next Project.
References
- “Algorithms, Fourth Edition” for data/algorithm rigor.
- “Code Complete” for defensive design and validation.
- MonoGame docs for runtime behavior expectations.
Key insights Correctness and maintainability scale only when state ownership stays explicit.
Summary Intent Mapping turns feature behavior into something measurable, explainable, and production-safe.
Homework/Exercises to practice the concept
- Define one canonical state schema for this project.
- Write one success and one failure deterministic scenario.
- Identify one optimization that is safe only after profiling.
Solutions to the homework/exercises
- Keep schema minimal and version-aware if persisted/transmitted.
- Include fixed setup, action sequence, expected checkpoint outputs.
- Apply optimization to measured hotspot only after behavior lock.
3. Project Specification
3.1 What You Will Build
A full implementation slice for Keyboard Character Controller with deterministic validation tooling. Included: core gameplay/system behavior, instrumentation overlay, and reliability handling for failure conditions. Excluded: non-essential polish effects that do not improve learning signal.
3.2 Functional Requirements
- Core feature loop behaves consistently under normal use.
- Deterministic golden scenario produces expected checkpoints.
- At least one controlled failure path is handled safely with user-visible feedback.
- Debug output surfaces key runtime metrics relevant to this project.
3.3 Non-Functional Requirements
- Performance: maintain stable frame pacing under defined stress scenario.
- Reliability: no unhandled exceptions on known failure inputs.
- Usability: user can understand current mode/state from visual cues.
3.4 Example Usage / Output
Run app -> enter main interaction loop -> trigger core action -> verify metrics overlay.
Expected: behavior is stable, transitions are explicit, and failure paths are recoverable.
3.5 Data Formats / Schemas / Protocols
- Runtime state snapshot shape: mode, entities_or_board, timers, flags, metrics.
- Optional persistence/network payload includes schema/protocol version field.
3.6 Edge Cases
- Input burst or rapid repeated action.
- Frame spike or temporary slowdown.
- Missing/invalid external data (asset, save, or network payload).
- Mode transition during active operation.
3.7 Real World Outcome
A learner can run this project and visibly confirm correctness without reading code internals.
3.7.1 How to Run (Copy/Paste)
cd project-root
# build with your chosen .NET/IDE workflow
# run the MonoGame app
# verify debug overlay appears and updates every frame
3.7.2 Golden Path Demo (Deterministic)
- Start from default state with fixed seed.
- Perform a predefined action sequence.
- Verify expected checkpoints (state hash, score/state/value transitions, timing metrics).
3.7.3 Failure Demo
- Trigger a controlled invalid action or invalid external input.
- Verify graceful error state and safe recovery options.
3.7.4 GUI/Desktop Behavior
- Main interaction surface shows current mode and key stats.
- Debug panel displays deterministic checkpoint values.
- Error state is visible, actionable, and non-crashing.
ASCII wireframe:
+-------------------------------------------------------------+
| Title / Mode FPS:60 Frame:001245 |
|-------------------------------------------------------------|
| |
| [Primary interactive area for Keyboard Character Controller] |
| |
| |
|-------------------------------------------------------------|
| Debug: state_hash=... warnings=0 last_transition=... |
+-------------------------------------------------------------+
4. Solution Architecture
4.1 High-Level Design
Input/Event Layer -> Domain Update Layer -> Render/Presentation Layer
| | |
+------ diagnostics--+------ checkpoint logs---+
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Input/Event Adapter | Collect and normalize incoming actions | Keep device/network specifics outside domain logic |
| Domain Core | Apply project rules and transitions | Single source of truth for mutable state |
| Presentation Layer | Draw state and user feedback | Separate rendering from mutation |
| Diagnostics | Log checkpoints and stress metrics | Deterministic replay and regression checks |
4.3 Data Structures (No Full Code)
State {
mode,
entities_or_board,
timers,
metrics,
flags
}
4.4 Algorithm Overview
- Capture events/input.
- Convert to high-level intents.
- Apply deterministic update pipeline.
- Validate invariants.
- Render and log metrics.
Complexity Analysis (typical)
- Time: O(n) to O(n log n) depending on entity/path/protocol workload.
- Space: O(n) for active objects and diagnostics buffers.
5. Implementation Guide
5.1 Development Environment Setup
# install dependencies
# prepare content/tools
# run baseline smoke test
5.2 Project Structure
project-root/
├── src/
│ ├── app-loop
│ ├── domain
│ ├── presentation
│ └── diagnostics
├── content/
├── tests/
└── docs/
5.3 The Core Question You Are Answering
“How do I implement Keyboard Character Controller so behavior stays deterministic, observable, and resilient under stress and failure paths?”
5.4 Concepts You Must Understand First
- Input Polling and Edge Detection
- Which invariants define correctness?
- Which transition edges are fragile?
- Intent Mapping
- What is canonical state ownership?
- Which scenarios invalidate assumptions?
5.5 Questions to Guide Your Design
- Which module owns the final truth for critical state?
- What deterministic checkpoints prove behavior is correct?
- How does the system respond when input/data is invalid?
5.6 Thinking Exercise
Map one full interaction sequence from start state to completion, including one induced fault.
Questions:
- Where is recovery handled?
- Which metrics confirm recovery succeeded?
5.7 The Interview Questions They’ll Ask
- Why is deterministic verification valuable in game/system development?
- What invariants matter most in this project?
- How do you isolate root cause when output diverges?
- How did architecture choices reduce future maintenance cost?
- What failure mode did you intentionally design for?
5.8 Hints in Layers
Hint 1: Define invariants before implementation.
Hint 2: Build diagnostics early, not after bugs appear.
Hint 3: Use pseudocode checkpoints:
update(); assert(invariants); render(); log(metrics)
Hint 4: Compare golden scenario outputs before/after each refactor.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Architecture boundaries | “Clean Architecture” | Dependency Rule |
| Deterministic loops/patterns | “Game Programming Patterns” | Game Loop, State, Object Pool |
| Algorithms/validation | “Algorithms, Fourth Edition” | Relevant graph/data chapters |
| Practical engineering rigor | “Code Complete” | Defensive programming |
5.10 Implementation Phases
Phase 1: Foundation
- Define state schema and invariants.
- Build baseline loop with diagnostics.
Checkpoint: deterministic baseline scenario passes.
Phase 2: Core Functionality
- Implement primary mechanic for Keyboard Character Controller.
- Add transition guards and error handling.
Checkpoint: main feature loop stable under normal and stress inputs.
Phase 3: Polish and Edge Cases
- Add failure-path UX handling and final debug toggles.
- Run regression matrix and document outcomes.
Checkpoint: success and failure demos both pass.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| State ownership | distributed vs canonical | canonical | easier validation and debugging |
| Error handling | crash-fast vs graceful recovery | graceful recovery | better production realism |
| Metrics strategy | ad-hoc logs vs structured checkpoints | structured checkpoints | deterministic regressions |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit-style checks | Validate core transitions | invariant checks, guard checks |
| Integration checks | Validate module interaction | update->render->log consistency |
| Edge-case checks | Validate resilience | invalid input/data, stress run |
6.2 Critical Test Cases
- Golden deterministic scenario.
- Stress scenario (frame/input/entity/network depending on project).
- Failure scenario with safe recovery.
6.3 Test Data
fixtures/
- deterministic-seed
- expected-checkpoints
- invalid-input-case
7. Common Pitfalls and Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Hidden side effects | inconsistent behavior under stress | enforce single mutation pipeline |
| Missing transition guards | invalid mode/state combos | explicit preconditions and postconditions |
| Late diagnostics | hard-to-reproduce bugs | add structured logs from first milestone |
7.2 Debugging Strategies
- Reproduce with deterministic fixture before modifying logic.
- Compare checkpoint logs at each phase boundary.
- Visualize active state and transition source in overlay.
7.3 Performance Traps
- Doing expensive recomputation every frame without event gating.
- Excess allocations inside hot update paths.
- Logging too much in release mode.
8. Extensions and Challenges
8.1 Beginner Extensions
- Add one extra deterministic scenario fixture.
- Add one new debug visualization toggle.
8.2 Intermediate Extensions
- Add replay export/import for bug reports.
- Add profile mode that records timing histogram.
8.3 Advanced Extensions
- Build automated regression script for golden scenarios.
- Add module interface tests for future refactors.
9. Real-World Connections
9.1 Industry Applications
- Building reliable vertical slices for indie production milestones.
- Establishing deterministic debugging workflows for small teams.
9.2 Related Open Source Projects
- MonoGame framework: https://github.com/MonoGame/MonoGame
- LiteNetLib (network-focused projects): https://github.com/RevenantX/LiteNetLib
9.3 Interview Relevance
This project gives concrete material for discussing deterministic design, architecture boundaries, and debugging under constraints.
10. Resources
10.1 Essential Reading
- MonoGame docs: https://docs.monogame.net/
- “Game Programming Patterns” by Robert Nystrom.
- “Clean Architecture” by Robert C. Martin.
10.2 Video Resources
- MonoGame walkthrough playlists (community) for render/input workflows.
- Architecture/code review sessions focused on game loops.
10.3 Tools and Documentation
- MGCB tooling docs and templates.
- .NET diagnostics tools (dotnet-trace, profiler integrations).
10.4 Related Projects in This Series
- Previous: Previous Project
- Next: Next Project
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the two core concepts (Input Polling and Edge Detection and Intent Mapping) without notes.
- I can describe my deterministic validation strategy.
- I can explain why my state ownership model is safe.
11.2 Implementation
- Functional requirements are complete.
- Success and failure demos both work.
- Edge cases are documented and tested.
11.3 Growth
- I documented one thing I would redesign in v2.
- I can present tradeoffs from this project in an interview.
12. Submission / Completion Criteria
Minimum Viable Completion
- Core feature loop works.
- One deterministic golden scenario passes.
- One failure path is handled safely.
Full Completion
- All critical tests pass.
- Debug instrumentation is usable and documented.
- Architecture boundaries are explicit and clean.
Excellence (Going Above and Beyond)
- Automated regression checks added.
- Cross-project reusable module extracted and documented.