TPPmark 2025

Author: Makoto Kanazawa, Hosei University

Last updated: 2025/10/30

This is an Agda formalization of TPPmark 2025. Tested with Agda 2.8.0 and the Agda standard library v2.3.

{-# OPTIONS --rewriting #-}

open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite
open import Data.Bool using (Bool; true; false; not; _∧_; _xor_; if_then_else_)
open import Data.Bool.Properties 
  using (not-involutive; not-distribˡ-xor; not-distribʳ-xor)
open import Data.Empty using (⊥-elim)
open import Data.Fin using (Fin) renaming (suc to 1+)
open import Data.Fin.Patterns
open import Data.Fin.Properties using (_≟_)
open import Data.List 
  using (List; []; _∷_; [_]; _++_; concat; concatMap; map; foldr; cartesianProduct)
open import Data.List.Membership.Propositional using (_∈_; _∉_)
open import Data.List.Membership.Propositional.Properties 
  using (∈-++⁻; ∈-map⁻; ∈-cartesianProductWith⁻)
open import Data.List.Properties 
  using (++-assoc; cartesianProductWith-distribʳ-++; map-++; foldr-++ ; concat-++)
open import Data.List.Relation.Unary.Any using (here; there)
open import Data.Nat using (; zero; suc)
open import Data.Product using (_×_; _,_; proj₁; proj₂; ; ∃₂)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function using (id; _∘_; flip)
open import Relation.Binary.PropositionalEquality 
  using (_≡_; _≢_; _≗_; cong; cong₂; cong-app; sym; trans; module ≡-Reasoning)
open import Relation.Nullary using (yes; no; does)

Here’s a verbatim quote of the problem:

Let n ≥ 1 be an integer. Arrange n3 identical cube-shaped lamps tightly, forming a large n × n × n cube. On each outer face, an n × n grid of small squares (the exposed faces of the lamps on that face) is visible, for a total of 6n2 squares. Each lamp is either on or off. When one of the small squares on the outer surface is pressed, the n lamps on the straight line joining the pressed square and the square directly opposite it on the far face (*1) toggle simultaneously (on↔︎off).

Given the on/off configuration of all lamps, describe as concisely as possible a necessary and sufficient condition on the configuration to be turned off by repeated applications of the above operation, and prove the correctness of the condition.

(*1) Clarification (Sep 16, 2025): “on the straight line joining the pressed square and the square directly opposite it on the far face” means “on a straight line which vertically penetrates the pressed square”.

Credit: Keisuke Nakano

module TPPmark (n₁ : ) where

n : 
n = suc n₁

_≡ᵇ_ : Fin n  Fin n  Bool
i ≡ᵇ j = does (i  j)

≟-refl : (i : Fin n)  (i  i)  yes refl
≟-refl i with i  i
... | no ¬p = ⊥-elim (¬p refl)
... | yes refl = refl

xor-not-rewriteˡ : (x y : Bool)  not x xor y  not (x xor y)
xor-not-rewriteˡ x y = sym (not-distribˡ-xor x y)

xor-not-rewriteʳ : (x y : Bool)  x xor (not y)  not (x xor y)
xor-not-rewriteʳ x y = sym (not-distribʳ-xor x y)

not-not-rewrite : (x : Bool)  not (not x)  x
not-not-rewrite = not-involutive

{-# REWRITE ≟-refl xor-not-rewriteˡ xor-not-rewriteʳ not-not-rewrite #-}

In Agda, Fin n is the type of natural numbers less than n. The elements of Fin n are written 0F, 1F, 2F, etc. We represent each of the n3 cube-shaped lamps by a triple (i , j , k), where i j k : Fin n. A configuration is a function from Fin n × Fin n × Fin n to Bool, where false means off and true means on. The desired target configuration is one in which all lamps are off.

Lamp : Set
Lamp = Fin n × Fin n × Fin n

Configuration : Set
Configuration = Lamp  Bool

AllOff : Configuration  Set
AllOff c = (i j k : Fin n)  c (i , j , k)  false

Next we need to decide how to represent the small squares on the outer faces of the large n × n × n cube. Let us assume that one of the vertices of the lamp (0F , 0F , 0F) lies at the origin of the xyz coordinate system on the three-dimensional space, and the vertex of the lamp (i , j , k) that is closest to the origin is the point (i, j, k). We refer to the outer face of the large cube that is on the plane x = 0 as X, and denote each square on that face by a triple of the form (X , j , k). This triple is (one of) the exposed face(s) of the lamp (0F , j , k). (When either j or k is 0F, this lamp has more than one exposed face.) Likewise, the face of the large cube that is on the plane y = 0 is Y, and the exposed face of the lamp (i , 0F , k) on the face Y is denoted by (Y , i , k), and similarly for Z and (Z , i , j).

We always adopt the vantage point of the above picture and call a lamp visible just in case at least one of its coordinates is 0F. The light gray color of each visible lamp in the picture is meant to indicate that they are either on or off. We do not bother to name the squares on the three faces that are opposite to X, Y, and Z (which are hidden from view in the above picture), since pressing those squares has the same effect as pressing the squares opposite to them on the faces X, Y, Z.

data Face : Set where
  X Y Z : Face

Square : Set
Square = Face × Fin n × Fin n

face : Square  Face
face (f , _ , _) = f

coord : Square  Fin n × Fin n
coord (_ , i , j) = i , j

The effect of pressing each square can be described succinctly using the Bool-valued equality test on Fin n and Boolean operations. Pressing a square toggles a lamp just in case the projection of the lamp onto the face of the square coincides with the coordinate coord of the square.

lampOf : Square  Lamp
lampOf (X , i , j) = 0F , i , j
lampOf (Y , i , j) = i , 0F , j
lampOf (Z , i , j) = i , j , 0F

project : Face  Lamp  (Fin n × Fin n)
project X (i , j , k) = (j , k)
project Y (i , j , k) = (i , k)
project Z (i , j , k) = (i , j)

project-face : (s : Square)  project (face s) (lampOf s)  coord s
project-face (X , p) = refl
project-face (Y , p) = refl
project-face (Z , p) = refl

press : Square  Configuration  Configuration
press (f , i , j) c l with (i₁ , j₁)project f l
  = (i ≡ᵇ i₁  j ≡ᵇ j₁) xor c l

pressList : List Square  Configuration  Configuration
pressList xs c = foldr press c xs

pressList-++ : (xs ys : List Square)  
  pressList (xs ++ ys)  pressList xs  pressList ys
pressList-++ xs ys c = foldr-++ press c xs ys

The question is when pressing some sequence of squares can bring a given configuration c to one in which all lamps are off. In other words, we are asked to find a necessary and sufficient condition for the following to hold:

∃ λ xs → AllOff (pressList xs c)

The first thing to notice is that no matter which configuration to start with, we can always turn off all lamps that are visible (i.e., all lamps one of whose coordinate is 0F). This is done by visiting each of the three faces in turn, pressing all and only the squares that are lit.

turnOffSquare : Configuration  Square  List Square
turnOffSquare c s = if c (lampOf s) then [ s ] else []

turnOffSquare-property₁ : (c d : Configuration) (s : Square) 
  c (lampOf s)  d (lampOf s) 
  pressList (turnOffSquare c s) d (lampOf s)  false
turnOffSquare-property₁ c d (X , p) h with c (lampOf (X , p)) 
... | false = sym h
... | true = sym (cong not h)
turnOffSquare-property₁ c d (Y , p) h with c (lampOf (Y , p))
... | false = sym h
... | true = sym (cong not h)
turnOffSquare-property₁ c d (Z , p) h with c (lampOf (Z , p))
... | false = sym h
... | true = sym (cong not h)

turnOffSquare-property₂ : (c d : Configuration) (s : Square) (l : Lamp) 
  coord s  project (face s) l  
  pressList (turnOffSquare c s) d l  d l
turnOffSquare-property₂ c d (f , i , j) l h with c (lampOf (f , i , j))
... | false = refl
... | true with i  proj₁ (project f l) | j  proj₂ (project f l)
... | no ¬a | _ = refl
... | yes a | no ¬b = refl
... | yes a | yes b = ⊥-elim (h (cong₂ _,_ a b))

turnOffSquare-property₃ : 
  (f : Face) (c d : Configuration) (xs : List (Fin n × Fin n)) (l : Lamp) 
  project f l  xs  
  pressList (concatMap  p  turnOffSquare c (f , p)) xs) d l  d l
turnOffSquare-property₃ f c d [] l h = refl
turnOffSquare-property₃ f c d ((i₁ , j₁)  xs) l h = begin
  pressList (concatMap g ((i₁ , j₁)  xs)) d l 
    ≡⟨⟩
  pressList (g (i₁ , j₁) ++ concatMap g xs) d l
    ≡⟨ cong-app (pressList-++ (g (i₁ , j₁)) (concatMap g xs) d) l 
  pressList (g (i₁ , j₁)) d′ l
    ≡⟨ turnOffSquare-property₂ c d′ (f , i₁ , j₁) l [2] 
  d′ l
    ≡⟨ turnOffSquare-property₃ f c d xs l [1]  
  d l
    
  where
  open ≡-Reasoning

  g : Fin n × Fin n  List Square
  g p = turnOffSquare c (f , p)

  i j : Fin n
  i = proj₁ (project f l)
  j = proj₂ (project f l)

  d′ : Configuration
  d′ = pressList (concatMap g xs) d

  [1] : (i , j)  xs
  [1] h₁ = h (there h₁)

  [2] : (i₁ , j₁)  (i , j)
  [2] refl = h (here refl)

turnOffSquare c s is either [ s ] or [], depending on whether s is lit in the configuration c. We concatenate these lists for each square s on f to get turnOffFace c f.

enumerateFin : (m : )  List (Fin m)
enumerateFin zero = []
enumerateFin (suc m) = 0F  map 1+ (enumerateFin m)

enumerateFin×Fin : (m : )  List (Fin m × Fin m)
enumerateFin×Fin m = cartesianProduct (enumerateFin m) (enumerateFin m)

turnOffFace : Configuration  Face  List Square
turnOffFace c f = concatMap  p  turnOffSquare c (f , p)) (enumerateFin×Fin n)

turnOffFace c f consists of all squares on the face f that are lit in the configuration c, listed in the lexicographic order.

Starting from the configuration c depicted in the earlier picture, pressing the squares in turnOffFace c Z (from right to left) brings us to a configuration c₁ that looks like the following picture:

All lamps that are on the face Z are now off, which is indicated by the darker shade of gray. Other lamps may have changed states; some of them may be on and others off.

Pressing the squares in turnOffFace c₁ Y then brings us to the following configuration c₂:

Since the Y squares on the top row were already dark in c₁, they were not pressed, so all the lamps that are on the face Z remain off in c₂.

We can then press the squares in turnOffFace c₂ X, which brings us to a configuration c₃ in which all lamps that are visible from our vantage point are off. (Of course, each of the invisible lamps may be on or off at this point.)

This is all intuitively trivial, but proving that a given configuration can always be transformed into one like the above picture in a proof assistant requires some work. There seems to be no obvious way to proceed that immediately suggests itself.

I choose to reason about each individual lamp independently of what happens to the other lamps. Note that for each (i , j), pressing the squares in turnOffFace c f can be broken down into pressing some squares on f different from (f , i , j), followed by possibly pressing (f , i , j), followed by pressing some more squares on f different from (f , i , j).

enumerateFin-split : (m : ) (i : Fin m)  
  ∃₂ λ xs ys  (enumerateFin m  xs ++ i  ys) × 
    i  xs × i  ys
enumerateFin-split (suc m) 0F = 
  [] , map 1+ (enumerateFin m) , refl ,  ()) , [1] (enumerateFin m)
  where
  [1] : (xs : List (Fin m))  0F  map 1+ xs
  [1] (x  xs) (there h) = [1] xs h
enumerateFin-split (suc m) (1+ i)
  with xs , ys , eq , h₁ , h₂enumerateFin-split m i = 
    0F  map 1+ xs , map 1+ ys , cong (0F ∷_) [1] , [2] , [3]
  where
  open ≡-Reasoning

  [1] : map 1+ (enumerateFin m)  map 1+ xs ++ 1+ i  map 1+ ys
  [1] = begin 
    map 1+ (enumerateFin m)       ≡⟨ cong (map 1+) eq 
    map 1+ (xs ++ i  ys)         ≡⟨ map-++ 1+ xs (i  ys) 
    map 1+ xs ++ 1+ i  map 1+ ys 

  [2] : 1+ i  0F  map 1+ xs
  [2] (there h) with y , y∈xs , refl∈-map⁻ 1+ h = h₁ y∈xs

  [3] : 1+ i  map 1+ ys
  [3] h with y , y∈ys , refl∈-map⁻ 1+ h = h₂ y∈ys

enumerateFin×Fin-split : (m : ) (p : Fin m × Fin m)  
  ∃₂ λ xs ys  (enumerateFin×Fin m  xs ++ p  ys) ×
    p  xs × p  ys
enumerateFin×Fin-split m (i , j)
  with xs₁ , ys₁ , eq₁ , h₁₁ , h₁₂enumerateFin-split m i
  with xs₂ , ys₂ , eq₂ , h₂₁ , h₂₂enumerateFin-split m j = 
  xs , ys , [2] , [3] , [4]
  where
  open ≡-Reasoning

  xs = cartesianProduct xs₁ (enumerateFin m) ++ map (i ,_) xs₂
  ys = map (i ,_) ys₂ ++ cartesianProduct ys₁ (enumerateFin m)

  [1] : map (i ,_) (enumerateFin m)  map (i ,_) xs₂ ++ (i , j)  map (i ,_) ys₂
  [1] = begin
    (map (i ,_) (enumerateFin m)               ≡⟨ cong (map (i ,_)) eq₂ 
    map (i ,_) (xs₂ ++ j  ys₂)                ≡⟨ map-++ (i ,_) xs₂ (j  ys₂)  
    map (i ,_) xs₂ ++ map (i ,_) (j  ys₂)     ≡⟨⟩
    map (i ,_) xs₂ ++ (i , j)  map (i ,_) ys₂ )

  [2] : enumerateFin×Fin m  xs ++ (i , j)  ys
  [2] = begin
    enumerateFin×Fin m ≡⟨⟩ 
    cartesianProduct (enumerateFin m) (enumerateFin m) 
      ≡⟨ cong    cartesianProduct  (enumerateFin m)) eq₁ 
    cartesianProduct (xs₁ ++ i  ys₁) (enumerateFin m) 
      ≡⟨ cartesianProductWith-distribʳ-++ _,_ xs₁ (i  ys₁) (enumerateFin m) 
    cartesianProduct xs₁ (enumerateFin m) ++ 
      cartesianProduct (i  ys₁) (enumerateFin m) 
      ≡⟨⟩
    cartesianProduct xs₁ (enumerateFin m) ++ map (i ,_) (enumerateFin m) ++ 
      cartesianProduct ys₁ (enumerateFin m)
      ≡⟨ cong    cartesianProduct xs₁ (enumerateFin m) ++  ++ 
                cartesianProduct ys₁ (enumerateFin m)) [1] 
    cartesianProduct xs₁ (enumerateFin m) ++ (map (i ,_) xs₂ ++ 
      (i , j)  map (i ,_) ys₂) ++ cartesianProduct ys₁ (enumerateFin m)
      ≡⟨ cong (cartesianProduct xs₁ (enumerateFin m) ++_) 
          (++-assoc (map (i ,_) xs₂) ((i , j)  map (i ,_) ys₂) 
            (cartesianProduct ys₁ (enumerateFin m))) 
    cartesianProduct xs₁ (enumerateFin m) ++ map (i ,_) xs₂ ++ 
      (i , j)  map (i ,_) ys₂ ++ cartesianProduct ys₁ (enumerateFin m)
      ≡⟨ sym (++-assoc (cartesianProduct xs₁ (enumerateFin m)) (map (i ,_) xs₂) 
                ((i , j)  map (i ,_) ys₂ ++ cartesianProduct ys₁ (enumerateFin m))) 
    xs ++ (i , j)  ys
      

  [3] : (i , j)  xs
  [3] h with ∈-++⁻ (cartesianProduct xs₁ (enumerateFin m)) h
  ... | inj₁ x 
    with a , _ , a∈xs₁ , _ , refl∈-cartesianProductWith⁻ _,_ xs₁ (enumerateFin m) x 
    = h₁₁ a∈xs₁
  ... | inj₂ y with b , b∈xs₂ , refl∈-map⁻ (i ,_) y = h₂₁ b∈xs₂

  [4] : (i , j)  ys
  [4] h with ∈-++⁻ (map (i ,_) ys₂) h
  ... | inj₁ x with b , b∈ys₂ , refl∈-map⁻ (i ,_) x = h₂₂ b∈ys₂
  ... | inj₂ y 
    with a , _ , a∈ys₁ , _ , refl∈-cartesianProductWith⁻ _,_ ys₁ (enumerateFin m) y 
    = h₁₂ a∈ys₁

turnOffFace-split : (c : Configuration) (f : Face) (p : Fin n × Fin n) 
  ∃₂ λ xs ys 
    pressList (turnOffFace c f)  
      pressList (concatMap  q  turnOffSquare c (f , q)) xs)  
        (pressList (turnOffSquare c (f , p)))  
        (pressList (concatMap  q  turnOffSquare c (f , q)) ys)) ×
    p  xs × p  ys
turnOffFace-split c f p
  with xs , ys , eq , h₁ , h₂enumerateFin×Fin-split n p
  = xs , ys , [2] , h₁ , h₂
  where
  open ≡-Reasoning

  g : (Fin n × Fin n)  List Square
  g q = turnOffSquare c (f , q)

  [1] : concatMap g (enumerateFin×Fin n)  concatMap g xs ++ g p ++ concatMap g ys
  [1] = begin
    concatMap g (enumerateFin×Fin n)        
      ≡⟨ cong (concatMap g) eq 
    concatMap g (xs ++ p  ys)
      ≡⟨ cong concat (map-++ g xs (p  ys)) 
    concat (map g xs ++ g p  map g ys)
      ≡⟨ sym (concat-++ (map g xs) (g p  map g ys)) 
    concatMap g xs ++ g p ++ concatMap g ys
      

  [2] : pressList (concatMap g (enumerateFin×Fin n))  
        pressList (concatMap g xs)  (pressList (g p))  (pressList (concatMap g ys))
  [2] c = begin
    pressList (concatMap g (enumerateFin×Fin n)) c
      ≡⟨ cong    pressList  c) [1] 
    pressList (concatMap g xs ++ g p ++ concatMap g ys) c
      ≡⟨ pressList-++ (concatMap g xs) (g p ++ concatMap g ys) c 
    pressList (concatMap g xs) (pressList (g p ++ concatMap g ys) c)
      ≡⟨ cong (pressList (concatMap g xs)) (pressList-++ (g p) (concatMap g ys) c) 
    pressList (concatMap g xs) (pressList (g p) (pressList (concatMap g ys) c))
      

This allows us to prove two important properties of turnOffFace. First, starting from the configuration c, pressing the squares in turnOffFace c f indeed darkens all squares on the face f.

turnOffFace-property₁ : (c : Configuration) (f : Face) (p : Fin n × Fin n)  
  pressList (turnOffFace c f) c (lampOf (f , p))  false
turnOffFace-property₁ c f p with xs , ys , eq , h₁ , h₂turnOffFace-split c f p
  = trans (cong-app (eq c) (lampOf (f , p))) [6]
  where
  g : (Fin n × Fin n)  List Square
  g q = turnOffSquare c (f , q)

  c₁ c₂ c₃ : Configuration
  c₁ = pressList (concatMap g ys) c
  c₂ = pressList (g p) c₁
  c₃ = pressList (concatMap g xs) c₂

  [1] : project f (lampOf (f , p))  p
  [1] = project-face (f , p)

  [2] : project f (lampOf (f , p))  xs
  [2] rewrite [1] = h₁

  [3] : project f (lampOf (f , p))  ys
  [3] rewrite [1] = h₂

  [4] : c₁ (lampOf (f , p))  c (lampOf (f , p))
  [4] = turnOffSquare-property₃ f c c ys (lampOf (f , p)) [3]

  [5] : c₂ (lampOf (f , p))  false
  [5] = turnOffSquare-property₁ c c₁ (f , p) (sym [4])

  [6] : c₃ (lampOf (f , p))  false
  [6] = trans (turnOffSquare-property₃ f c c₂ xs (lampOf (f , p)) [2]) [5]

Second, if the projection of a lamp l onto the face f is a square that is already dark in the configuration c, then pressing the squares in turnOffFace c f does not affect the state of the lamp l.

turnOffFace-property₂ : (c : Configuration) (f : Face) (l : Lamp) 
  c (lampOf (f , project f l))  false 
  pressList (turnOffFace c f) c l  c l
turnOffFace-property₂ c f l h 
  with xs , ys , eq , h₁ , h₂turnOffFace-split c f (project f l)
  = trans (cong-app (eq c) l) [4]
  where
  p : Fin n × Fin n
  p = project f l

  g : (Fin n × Fin n)  List Square
  g q = turnOffSquare c (f , q)

  c₁ c₂ c₃ : Configuration
  c₁ = pressList (concatMap g ys) c
  c₂ = pressList (g p) c₁
  c₃ = pressList (concatMap g xs) c₂

  [1] : c₁ l  c l
  [1] = turnOffSquare-property₃ f c c ys l h₂

  [2] : g p  []
  [2] rewrite h = refl

  [3] : c₂  c₁
  [3] rewrite [2] = refl

  [4] : c₃ l  c l
  [4] rewrite [3] = trans (turnOffSquare-property₃ f c c₁ xs l h₁) [1]

We are now ready to prove what we called “intuitively trivial” above.

turnOffAllFaces : Configuration  List Square
turnOffAllFaces c = turnOffFace c₂ X ++ turnOffFace c₁ Y ++ turnOffFace c Z
  where 
  c₁ c₂ : Configuration
  c₁ = pressList (turnOffFace c Z) c
  c₂ = pressList (turnOffFace c₁ Y) c₁

AllOffX : Configuration  Set
AllOffX c = (j k : Fin n)  c (0F , j , k)  false

AllOffY : Configuration  Set
AllOffY c = (i k : Fin n)  c (i , 0F , k)  false

AllOffZ : Configuration  Set
AllOffZ c = (i j : Fin n)  c (i , j , 0F)  false

AllOffXYZ : Configuration  Set
AllOffXYZ c = AllOffX c × AllOffY c × AllOffZ c

turnOffAllFaces-property : (c : Configuration)  
  AllOffXYZ (pressList (turnOffAllFaces c) c)
turnOffAllFaces-property c = [8]
  where
  open ≡-Reasoning

  c₁ c₂ c₃ : Configuration
  c₁ = pressList (turnOffFace c Z) c
  c₂ = pressList (turnOffFace c₁ Y) c₁
  c₃ = pressList (turnOffFace c₂ X) c₂

  [1] : pressList (turnOffAllFaces c) c  c₃
  [1] = begin
    pressList (turnOffAllFaces c) c
      ≡⟨⟩ 
    pressList (turnOffFace c₂ X ++ turnOffFace c₁ Y ++ turnOffFace c Z) c
      ≡⟨ pressList-++ (turnOffFace c₂ X) (turnOffFace c₁ Y ++ turnOffFace c Z) c 
    pressList (turnOffFace c₂ X) (pressList (turnOffFace c₁ Y ++ turnOffFace c Z) c) 
      ≡⟨ cong (pressList (turnOffFace c₂ X)) 
          (pressList-++ (turnOffFace c₁ Y) (turnOffFace c Z) c) 
    c₃
      

  [2] : AllOffZ c₁
  [2] i j = turnOffFace-property₁ c Z (i , j)

  [3] : AllOffY c₂
  [3] i k = turnOffFace-property₁ c₁ Y (i , k)

  [4] : AllOffZ c₂
  [4] i j = trans (turnOffFace-property₂ c₁ Y (i , j , 0F) ([2] i 0F)) ([2] i j)

  [5] : AllOffX c₃
  [5] j k = turnOffFace-property₁ c₂ X (j , k)

  [6] : AllOffY c₃
  [6] i k = trans (turnOffFace-property₂ c₂ X (i , 0F , k) ([3] 0F k)) ([3] i k)

  [7] : AllOffZ c₃
  [7] i j = trans (turnOffFace-property₂ c₂ X (i , j , 0F) ([4] 0F j)) ([4] i j)

  [8] : AllOffXYZ (pressList (turnOffAllFaces c) c)
  [8] rewrite [1] = [5] , [6] , [7]

So, starting from any configuration, we can turn off all lamps that are visible from our vantage point. In the resulting configuration (which we called c₃), some lamps that are invisible from our vantage point may be on. If so, is there any additional sequence of squares that we can press to turn all lamps off? This seems difficult because in order to toggle an invisible lamp, we must press a square on one of the faces X, Y, and Z, which necessarily toggles a visible lamp. We can prove that it is indeed impossible. Given an initial configuration, the states of the visible lamps in any reachable configuration completely determines the states of all other lamps.

For each invisible lamp (1+ i , 1+ j , 1+ k), we associate seven visible lamps with it. These are the lamps we get by changing some or all of the coordinates of (1+ i , 1+ j , 1+ k) to 0F. The following picture highlights the seven visible lamps associated with the lamp (4F , 5F , 4F).

We note that the XOR of the state of the invisible lamp (1+ i , 1+ j , 1+ k) and the states of the seven visible lamps associated with it is an invariant, since pressing any square toggles either zero or exactly two of these eight lamps. This means that, given an initial configuration, the state of an invisible lamp in any reachable configuration is completely determined by the states of the seven visible lamps associated with it.

Given a configuration c, we refer to the function that associates with each invisible lamp the XOR of the states in c of the eight associated lamps as the signature of c. The signature of a configuration is invariant under any square-pressing. The signature of an all-off configuration is clearly the constant false function. Therefore, for a configuration c to be turned into an all-off configuration by pressing some sequence of squares, it is necessary and sufficient that c has the constant false function as its signature.

Signature : Set
Signature = (Fin n₁ × Fin n₁ × Fin n₁)  Bool

signature : Configuration  Signature
signature c (i , j , k) = 
  c (0F , 0F , 0F) xor c (0F , 0F , 1+ k) xor c (0F , 1+ j , 0F) xor 
  c (0F , 1+ j , 1+ k) xor c (1+ i , 0F , 0F) xor c (1+ i , 0F , 1+ k) xor 
  c (1+ i , 1+ j , 0F) xor c (1+ i , 1+ j , 1+ k)

signature-AllOff : (c : Configuration)  AllOff c  signature c  λ _  false
signature-AllOff c h (i , j , k) 
  rewrite h 0F 0F 0F
        | h 0F 0F (1+ k)
        | h 0F (1+ j) 0F
        | h 0F (1+ j) (1+ k)
        | h (1+ i) 0F 0F
        | h (1+ i) 0F (1+ k)
        | h (1+ i) (1+ j) 0F
        | h (1+ i) (1+ j) (1+ k) = refl

signature-press : (s : Square) (c : Configuration)  
  signature (press s c)  signature c
signature-press (X , j , k) c (i₁ , j₁ , k₁) 
  with j ≡ᵇ 1+ j₁ | k ≡ᵇ 1+ k₁ | j ≡ᵇ 0F | k ≡ᵇ 0F
... | false | false | false | false = refl
... | false | false | false | true = refl
... | false | false | true | false = refl
... | false | false | true | true = refl
... | false | true | false | false = refl
... | false | true | false | true = refl
... | false | true | true | false = refl
... | false | true | true | true = refl
... | true | false | false | false = refl
... | true | false | false | true = refl
... | true | false | true | false = refl
... | true | false | true | true = refl
... | true | true | false | false = refl
... | true | true | false | true = refl
... | true | true | true | false = refl
... | true | true | true | true = refl
signature-press (Y , i , k) c (i₁ , j₁ , k₁) 
  with i ≡ᵇ 1+ i₁ | k ≡ᵇ 1+ k₁ | i ≡ᵇ 0F | k ≡ᵇ 0F
... | false | false | false | false = refl
... | false | false | false | true = refl
... | false | false | true | false = refl
... | false | false | true | true = refl
... | false | true | false | false = refl
... | false | true | false | true = refl
... | false | true | true | false = refl
... | false | true | true | true = refl
... | true | false | false | false = refl
... | true | false | false | true = refl
... | true | false | true | false = refl
... | true | false | true | true = refl
... | true | true | false | false = refl
... | true | true | false | true = refl
... | true | true | true | false = refl
... | true | true | true | true = refl
signature-press (Z , i , j) c (i₁ , j₁ , k₁) 
  with i ≡ᵇ 1+ i₁ | j ≡ᵇ 1+ j₁ | i ≡ᵇ 0F | j ≡ᵇ 0F
... | false | false | false | false = refl
... | false | false | false | true = refl
... | false | false | true | false = refl
... | false | false | true | true = refl
... | false | true | false | false = refl
... | false | true | false | true = refl
... | false | true | true | false = refl
... | false | true | true | true = refl
... | true | false | false | false = refl
... | true | false | false | true = refl
... | true | false | true | false = refl
... | true | false | true | true = refl
... | true | true | false | false = refl
... | true | true | false | true = refl
... | true | true | true | false = refl
... | true | true | true | true = refl

signature-pressList : (xs : List Square) (c : Configuration)  
  signature (pressList xs c)  signature c
signature-pressList [] c _ = refl
signature-pressList (x  xs) c z = begin
  signature (pressList (x  xs) c) z     ≡⟨⟩
  signature (press x (pressList xs c)) z ≡⟨ signature-press x (pressList xs c) z 
  signature (pressList xs c) z           ≡⟨ signature-pressList xs c z 
  signature c z                          
  where open ≡-Reasoning

sufficiency-lemma₁ : 
  (c : Configuration)  AllOffXYZ c  signature c   _  false)  AllOff c
sufficiency-lemma₁ c (hx , hy , hz) h 0F j k = hx j k
sufficiency-lemma₁ c (hx , hy , hz) h (1+ i) 0F k = hy (1+ i) k
sufficiency-lemma₁ c (hx , hy , hz) h (1+ i) (1+ j) 0F = hz (1+ i) (1+ j)
sufficiency-lemma₁ c (hx , hy , hz) h (1+ i) (1+ j) (1+ k) 
  = trans [1] (h (i , j , k))
  where
  [1] : c (1+ i , 1+ j , 1+ k)  signature c (i , j , k) 
  [1] rewrite hx 0F 0F | hx 0F (1+ k) | hx (1+ j) 0F | hx (1+ j) (1+ k) 
            | hy (1+ i) 0F | hy (1+ i) (1+ k) | hz (1+ i) (1+ j)
    = refl

sufficiency : (c : Configuration)  
  signature c   _  false)  
   λ xs  AllOff (pressList xs c)
sufficiency c h = 
  turnOffAllFaces c , sufficiency-lemma₁ c₁ (turnOffAllFaces-property c) [1]
  where
  c₁ : Configuration
  c₁ = pressList (turnOffAllFaces c) c

  [1] : signature c₁  λ _  false
  [1] z = trans (signature-pressList (turnOffAllFaces c) c z) (h z)

necessity : (c : Configuration)  
  ( λ xs  AllOff (pressList xs c))  signature c   _  false)
necessity c (xs , h) z = 
  trans (sym (signature-pressList xs c z)) (signature-AllOff c₁ h z)
  where
  c₁ : Configuration
  c₁ = pressList xs c

Is this necessary and sufficient condition “as concise as possible”? The condition can be expressed as a Boolean formula which is a conjunction of the negations of (n − 1)3 XORs, each of which contains eight Boolean variables, so the total number of occurrences of Boolean variables in the formula is 8(n − 1)3. The formula involves n3 variables altogether, and of the 2n3 rows of its truth table, exactly 23n2 − 3n + 1 of them makes the formula true. The question is whether there is an equivalent formula which contains significantly fewer than 8(n − 1)3 occurrences of Boolean variables. I see no easy way of finding one. We really need all n3 variables, so as a function of n, the length of our formula is at most a constant multiple of the length of the shortest equivalent formula.