     ; invariant is a disjunction of negated guards
     {:id id :flows flows :edges edges}))
 
-(defn valid-for-inputs [transition inputs]
-  ;todo: some :inputs label in transition's label must be a subset of inputs
-  false)
+(defn intersect-input-intervals [transition inputs]
+  (let [label (get-in transition [:transition :label])
+        interval (:interval transition)
+        ; for each button, intersect its interval with the transition to find the intervals during which each
+        ; button is pressed. The result is an interval during which any of the given buttons is pressed.
+        ; then take the intersection of those per-button intervals to find the intervals during which
+        ; all the indicated buttons are pressed.
+        on (intersect-all (map #(iv/intersection (get inputs %) interval) (some #(= (first %) :on) label)))
+        ; for off, we want no buttons from off to be active during on. so we don't do the final intersection!
+        off (map #(iv/intersection (get inputs %) on) (some #(= (first %) :off) label))]
+    (if (iv/empty-interval? off)
+      (assoc transition :interval on)
+      [Infinity Infinity])))
 
 (defn next-transition [_has ha then inputs]
   (let [reqs (:required-transitions ha)

                       [:gt :y (+ oy oh)]])))
                (concat walls others)))))

(defn goomba [id x y speed state others walls]
  (let [others (disj others id)
        fall-speed 16]

         (zipmap (map :id states) states)))

(defn guard? [g]
  (or (nil? g)
      (and (vector? g)
           (case (first g)
             (:gt :geq :leq :lt) (or (= (count g) 3) (= (count g) 4))
             (:and :or) (every? guard? (rest g))))))

; edge label is a set containing :required | button masks
(defn make-edge [target guard label]
