module WhereMyRomba where open import Function open import Data.Integer open import Data.List open import Data.Product open import Data.List.Membership.Propositional open import Data.List.Membership.DecPropositional using (_∈?_) open import Data.List.Relation.Unary.Any using (here; there) open import Relation.Nullary.Decidable open import Relation.Binary.Definitions using (Decidable) open import Relation.Binary.PropositionalEquality hiding ([_]) -- This lemma simply says that list membership for tuples of integers is decidable. -- I.e. given a tuple of integers x, and a list of tuples of integers xs, there is an -- algorithmn that determines wheter or not x is in xs... lemm : ∀ (x : ℤ × ℤ) xs → Dec (x ∈ xs) lemm = _∈?_ lemm₂ where lemm₂ : ∀ (x y : ℤ × ℤ) → Dec (x ≡ y) lemm₂ (x₁ , x₂) (y₁ , y₂) with x₁ ≟ y₁ | x₂ ≟ y₂ ... | no h | _ = no λ{ refl → h refl} ... | yes _ | no t = no λ{ refl → t refl} ... | yes refl | yes refl = yes refl data Direction : Set where N : Direction E : Direction S : Direction W : Direction data Action : Set where F : Action L : Action R : Action -- This define a datatype for the romba state. -- It simply consists of a direction and a position, you can use the constructor -- `@` like this: -- You can also use 𝒹 and 𝓅 to extract the direction and position. record RombaState : Set where constructor _@_ field 𝒹 : Direction 𝓅 : ℤ × ℤ open RombaState move : Direction → ℤ × ℤ → ℤ × ℤ move N (x , y) = x , y - + 1 move E (x , y) = x + + 1 , y move S (x , y) = x , y + + 1 move W (x , y) = x - + 1 , y turnLeft : Direction → Direction turnLeft N = W turnLeft E = N turnLeft S = E turnLeft W = S -- Turning right is the same as turning left 3 times :-) turnRight = turnLeft ∘ turnLeft ∘ turnLeft {- This function is where the important stuff happens, it describes a single movement of the romba, reacting to possible obstacles. In this function we encode a basic invariant: The romba is assumed to not be inside an obstacle! Given this assumption, we also prove that the romba won't end up inside a wall. -} step : Action -- Given an action, → (obstacles : List (ℤ × ℤ)) -- a set of obstacles → (s₁ : RombaState) -- a romba state, → 𝓅 s₁ ∉ obstacles -- and a proof that the romba isn't inside an obstacle. → Σ RombaState (λ s₂ → 𝓅 s₂ ∉ obstacles) -- Returns a new romba state, and a proof that it still isn't -- on an obstacle. {- With is like a `case .. of`. Here we use the with to check wheter or not moving the romba in its current direction would place it inside an obstacle, i.e. if it's new position would be an element in the list of obstacles. This is what we need the lemma for. -} step F obstacles (𝒹 @ 𝓅) h with lemm (move 𝒹 𝓅) obstacles -- If moving it would not place it inside an obstacle, then we know... well, that -- moving it would not place it inside an obstacle of course! This the proof that -- the variable `t` contains. ... | no t = 𝒹 @ move 𝒹 𝓅 , t -- Othewise, we simply don't move, and use the fact the we are not currently inside -- and obstecla to prove that we will not end up inside and obstale (`h`) ... | yes _ = 𝒹 @ 𝓅 , h step L obstacles (𝒹 @ 𝓅) h = turnLeft 𝒹 @ 𝓅 , h step R obstacles (𝒹 @ 𝓅) h = turnRight 𝒹 @ 𝓅 , h -- In romba' and romba we just chain a bunch of `step` calls together. romba' : (s₁ : RombaState) → (obstacles : List (ℤ × ℤ)) → (𝓅 s₁ ∉ obstacles) → List Action → Σ RombaState (λ s₂ → 𝓅 s₂ ∉ obstacles) romba' s₁ obstacles h [] = s₁ , h romba' s₁ obstacles h (action ∷ actions) = let s₂ , t = step action obstacles s₁ h in romba' s₂ obstacles t actions romba : (obstacles : List (ℤ × ℤ)) → (+ 0 , + 0) ∉ obstacles → List Action → RombaState romba obstacles h actions = proj₁ (romba' (N @ (+ 0 , + 0)) obstacles h actions) -- Examples -- For example, if the robot starts at (0, 0) and faces north, moving forward would -- place it at (0, -1) ex₀ : move N (+ 0 , + 0) ≡ (- + 0 , - + 1) ex₀ = refl -- For example, if the robot starts at (0, 0) and faces north, moving forward -- would place it at (0, -1). If it turned right and moved forward again, it -- would be at (1, -1) ex₁ : move (turnRight N) (move N (+ 0 , + 0)) ≡ (+ 1 , - + 1) ex₁ = refl -- Actions: "FRFFRFFRFFRF" -- Obstacles: No obstacles -- Expected: (0, 0) ex₂ : 𝓅 (romba [] (λ()) (F ∷ R ∷ F ∷ F ∷ R ∷ F ∷ F ∷ R ∷ F ∷ F ∷ R ∷ F ∷ [])) ≡ (+ 0 , + 0) ex₂ = refl -- Actions: "FRFFRFF" -- Obstacles: (0, -1) -- Expected: (2, 2) ex₃ : 𝓅 (romba [ + 0 , - + 1 ] (λ{ (here ()); (there ())}) (F ∷ R ∷ F ∷ F ∷ R ∷ F ∷ F ∷ [])) ≡ (+ 2 , + 2) ex₃ = refl -- Actions: "FRFRRFLF" -- Obstacles: (0, -1), (1, 0) -- Expected: (-1, 1) ex₄ : 𝓅 (romba ((+ 0 , - + 1) ∷ (+ 1 , + 0) ∷ []) (λ{ (there (here ()))}) (F ∷ R ∷ F ∷ R ∷ R ∷ F ∷ L ∷ F ∷ [])) ≡ (- + 1 , + 1) ex₄ = refl