Skip to main content

How Programming Constraints Changed the Way I Designed Whisker 2

· 5 min read

I used to think programming was just about implementing designs. Then I learned that programming constraints actually make me a better designer.

The Original Design​

For Whisker 2, I wanted a cooking system where you could combine any ingredients in any order to make custom dishes. Players would experiment and discover recipes.

The design doc in my head:

  • 20+ ingredients
  • Infinite possible combinations
  • Players create their own recipes
  • Customers request specific dishes

This sounded amazing and creative.

The Programming Reality​

Then I tried to implement it.

// My first attempt
public class CookingSystem : MonoBehaviour {
public List<Ingredient> availableIngredients;

public Dish CreateDish(List<Ingredient> ingredients) {
// How do I check if this is a valid dish?
// How do I know what the customer wanted?
// How do I give feedback if it's wrong?

// ...this is going to be way more complicated than I thought
}
}

The problems became obvious fast:

Problem 1: How do I validate "correct" dishes?

If players can make anything, how does the game know what's a sandwich vs what's garbage? Do I hardcode every possible combination?

if (ingredients.Contains(bread) && ingredients.Contains(cheese)) {
return "Sandwich";
} else if (ingredients.Contains(flour) && ingredients.Contains(sugar)) {
return "Cake";
}
// ...repeat for 100+ combinations?

That would be nightmare to maintain.

Problem 2: How do customers request custom dishes?

If dishes are freeform, I can't show a picture of what they want. I can't say "I want a sandwich with cheese and lettuce" because that assumes sandwiches exist as a concept.

Problem 3: How do players learn recipes?

With no recipe book, players would just throw random ingredients together hoping something works. That's not fun—that's frustrating.

The Constraint-Driven Redesign​

I stepped back and asked: What's the smallest system that still feels creative?

The answer: Predefined recipes, but with customization options.

New design:

  • 6 base recipes (sandwich, cake, soup, salad, pizza, pasta)
  • Each recipe has 2-3 required ingredients
  • Each recipe has 2-3 optional ingredients for customization
  • Customers request a base dish, sometimes with specific extras

This was way simpler to program:

[System.Serializable]
public class Recipe {
public string dishName;
public List<Ingredient> requiredIngredients;
public List<Ingredient> optionalIngredients;
}

public Dish CreateDish(List<Ingredient> playerIngredients) {
foreach (Recipe recipe in availableRecipes) {
if (HasAllRequired(playerIngredients, recipe.requiredIngredients)) {
Dish dish = new Dish(recipe.dishName);
dish.AddExtras(GetOptionalIngredients(playerIngredients, recipe.optionalIngredients));
return dish;
}
}
return null; // Invalid dish
}

private bool HasAllRequired(List<Ingredient> playerIngredients, List<Ingredient> required) {
foreach (Ingredient req in required) {
if (!playerIngredients.Contains(req)) return false;
}
return true;
}

Why the Constrained Version is Actually Better​

At first, I was disappointed. The original infinite-possibility system felt more "creative" and "ambitious."

But the constrained version had huge advantages:

1. Players could learn the system

With 6 base recipes, players learned them fast. After 2-3 levels, they didn't need to think about "what makes a sandwich." They just grabbed bread, cheese, and lettuce automatically.

2. Customization still felt meaningful

"Make a sandwich" → You choose what goes in it
"Make a sandwich with tomatoes" → You have to remember to add tomatoes

This gave players agency without overwhelming them.

3. The code was maintainable

Adding a new recipe meant creating one Recipe object. Not rewriting the entire validation system.

4. I could add tutorial hints

if (customer.wantsExtraIngredient && !dish.HasExtra(customer.wantedExtra)) {
ShowHint("The customer wanted extra " + customer.wantedExtra.name + "!");
}

With freeform dishes, I couldn't give feedback like this.

5. The game felt more focused

Instead of "figure out what's possible," the game became "master these recipes and serve them fast." That's a clearer, more satisfying challenge.

What This Taught Me About Design​

Programming constraints aren't limitations—they're design tools.

When I hit a programming wall, it's usually because my design is too vague or too complicated.

The constraint forced me to ask:

  • What's the core experience I want?
  • What's the simplest version that delivers that experience?
  • What can I cut without losing what matters?

Now when I design a system, I prototype it in code early. Not to "lock in" the design, but to find out if it actually works.

The Process I Use Now​

  1. Design the ideal version
    (What would this look like with infinite time and perfect code?)

  2. Write pseudocode or a rough implementation
    (What are the actual technical challenges?)

  3. Identify the constraints
    (What's hard? What's repetitive? What's fragile?)

  4. Redesign around the constraints
    (What's a simpler version that solves the same problem?)

  5. Test if the simpler version still feels good
    (Did I lose something important?)

Usually, the simpler version is better anyway.

Examples from Other Systems​

Oven Timer System

Original idea: Players manually control temperature and time
Programming constraint: That's a lot of UI and state management
Better design: Click to start cooking, done automatically after fixed time
Result: Still satisfying, way easier to implement

Customer Patience System

Original idea: Patience decreases based on real-time clock
Programming constraint: What happens if player pauses? What if frame rate drops?
Better design: Patience decreases based on game ticks, not wall time
Result: More predictable, works the same on any device

Ingredient Placement

Original idea: Drag and drop ingredients anywhere on counters
Programming constraint: Collision detection and state management nightmare
Better design: Click ingredient → Click counter → Automatically placed in grid
Result: Faster to play, easier to code

The Lesson​

Good code makes good games. Good games make good code.

I used to see programming as the "technical part" after design. Now I see them as the same thing.

The best designs are the ones that are simple to implement, easy to maintain, and clear to players. Programming constraints help me find those designs.

When something is hard to code, it's often a sign that the design needs another pass.