Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

For something like collision detection, you're always going to have n² cases to handle, even with something like pattern matching. The question then becomes whether you have n² methods or n² branches. There's a "combinatorial explosion" regardless; you just can't avoid it for some things. Then it's just a question of how you structure the code.

Consider the multimethod approach:

    (defgeneric collide? (a b))
    (defmethod collide? ((a bus) (b person)) ...)
    (defmethod collide? ((a person) (b bus)) ...)
    (defmethod collide? ((a bus) (b bus)) ...)
    (defmethod collide? ((a person) (b person)) ...)
Versus the pattern-matching approach (abusing CL's case expression for clarity):

    (defun collide? (a b)
      (case (list (type-of a) (type-of b))
        ((bus person) ...)
        ((person bus) ...)
        ((bus bus) ...)
        ((person person) ...)))
Beside potential performance implications of implementing one way or the other, you also have an open-closed conundrum. Multimethods leave the set of behaviors open, while the pattern-matching leaves it closed. That is, if a library wanted to define a new kind of collidable entity, it's trivial to add with a new library method. Not so easy with the pattern-matching approach. For some applications, this doesn't matter; for some it does.


You can avoid the n² problem if every object uses the same type of collision mesh (or bounding box, or bitmap, or whatever). Now the problem is to get from the object to the collision mesh, and that's O(n).

The bigger question is, is it practical at all for everyone to use the same type of collision object?


The answer to the bigger question is: not always. But let's assume that in our case, the answer is yes, and we can use a bounding box for everything. Let's also assume that when a collision is detected between two objects, the results of that collision vary depending on what those objects are. How do we handle that? Well, we're back at the n² problem again.

I once wrote a version of Asteroids in C++ in which every object had a particular bounding shape that was used for the detection. For the logic, I still had n² cases to determine the result of that collision. Looking back, I contend that it would have been cleaner to use the visitor pattern, since all the logic in my game was contained in various game objects except for the collision handling logic. [1]

It's good to be skeptical of design patterns. I've worked at several companies where liberal application of patterns really mucked up the code base. But in some cases, they really can be the best solution -- when used conservatively and carefully.

[1]: Looking back I also would have used an entity system rather than "classic" OOP, but that would not have solved the n² case dilemma.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: