All Euler problems
Project Euler

Touch-screen Password

A touch-screen password consists of a sequence of two or more distinct spots on a rectangular grid, subject to the following rules: 1. The user touches the first spot, then traces straight-line seg...

Source sync Apr 19, 2026
Problem #0879
Level Level 23
Solved By 509
Languages C++, Python
Answer 4350069824940
Length 468 words
searchsequencenumber_theory

Problem Statement

This archive keeps the full statement, math, and original media on the page.

A touch-screen device can be unlocked with a "password" consisting of a sequence of two or more distinct spots that the user selects from a rectangular grid of spots on the screen. The user enters their sequence by touching the first spot, then tracing a straight line segment to the next spot, and so on until the end of the sequence. The user’s finger remains in contact with the screen throughout, and may only move in straight line segments from spot to spot.

If the finger traces a straight line that passes over an intermediate spot, then that is treated as two line segments with the intermediate spot included in the password sequence. For example, on a \(3\times 3\) grid labelled with digits \(1\) to \(9\) (shown below), tracing \(1-9\) is interpreted as \(1-5-9\).

Once a spot has been selected it disappears from the screen. Thereafter, the spot may not be used as an endpoint of future line segments, and it is ignored by any future line segments which happen to pass through it. For example, tracing \(1-9-3-7\) (which crosses the \(5\) spot twice) will give the password \(1-5-9-6-3-7\).

PIC

There are \(389488\) different passwords that can be formed on a \(3 \times 3\) grid.

Find the number of different passwords that can be formed on a \(4 \times 4\) grid.

Problem 879: Touch-screen Password

Mathematical Foundation

Definition (Grid and Collinearity). Place spots at integer coordinates (i,j)(i, j) with 0i,jn10 \leq i, j \leq n-1. Two spots A=(x1,y1)A = (x_1, y_1) and B=(x2,y2)B = (x_2, y_2) have intermediate spots on the segment AB\overline{AB} if and only if g=gcd(x2x1,y2y1)>1g = \gcd(|x_2 - x_1|, |y_2 - y_1|) > 1.

Lemma (Intermediate Spot Enumeration). The lattice points strictly between A=(x1,y1)A = (x_1, y_1) and B=(x2,y2)B = (x_2, y_2) on the segment AB\overline{AB} are exactly

(x1+k(x2x1)g,  y1+k(y2y1)g)for k=1,2,,g1,\left(x_1 + \frac{k \cdot (x_2 - x_1)}{g},\; y_1 + \frac{k \cdot (y_2 - y_1)}{g}\right) \quad \text{for } k = 1, 2, \ldots, g-1,

where g=gcd(x2x1,y2y1)g = \gcd(|x_2 - x_1|, |y_2 - y_1|). There are g1g - 1 such points.

Proof. A lattice point on AB\overline{AB} has the form (x1+t(x2x1),y1+t(y2y1))(x_1 + t(x_2 - x_1), y_1 + t(y_2 - y_1)) for t(0,1)t \in (0, 1). For both coordinates to be integers, t(x2x1)t \cdot (x_2 - x_1) and t(y2y1)t \cdot (y_2 - y_1) must be integers. Writing x2x1=gdxx_2 - x_1 = g \cdot d_x and y2y1=gdyy_2 - y_1 = g \cdot d_y with gcd(dx,dy)=1\gcd(d_x, d_y) = 1, we need tgdxZt \cdot g \cdot d_x \in \mathbb{Z} and tgdyZt \cdot g \cdot d_y \in \mathbb{Z}. Since gcd(dx,dy)=1\gcd(d_x, d_y) = 1, this requires tgZtg \in \mathbb{Z}, i.e., t=k/gt = k/g for integer kk. With 0<t<10 < t < 1: k{1,,g1}k \in \{1, \ldots, g-1\}. \square

Theorem (Password Validity). A sequence of target spots (s1,s2,,sm)(s_1, s_2, \ldots, s_m) determines a valid password as follows: when moving from sis_i to si+1s_{i+1}, all unvisited intermediate spots on sisi+1\overline{s_i s_{i+1}} are automatically inserted into the sequence (in order) before si+1s_{i+1}. The resulting expanded sequence is the password. The move from sis_i to si+1s_{i+1} is valid if and only if si+1s_{i+1} has not been previously visited (either explicitly or via automatic inclusion).

Proof. This follows directly from the problem rules: rule 2 inserts intermediate unvisited spots, and rule 3 states that visited spots are removed from future consideration. \square

Theorem (Symmetry Group). The n×nn \times n grid has the symmetry group of the square, the dihedral group D4D_4 of order 8 (4 rotations and 4 reflections). For n=4n = 4, the 16 spots partition into 3 orbits under D4D_4:

  • 4 corner spots (orbit size 4),
  • 8 edge-center spots (orbit size 8),
  • 4 inner spots (orbit size 4).

Proof. The 4×44 \times 4 grid is invariant under the 8 symmetries of the square. A corner, say (0,0)(0,0), maps to the 4 corners under rotation. An edge-center, say (1,0)(1,0), maps to all 8 non-corner boundary spots. An inner spot, say (1,1)(1,1), maps to the 4 inner spots. \square

Lemma (Bitmask State Space). The state of the DFS enumeration is fully described by the pair (current position,visited bitmask)(\text{current position}, \text{visited bitmask}). For an n×nn \times n grid, there are n22n2n^2 \cdot 2^{n^2} possible states.

Proof. There are n2n^2 choices for the current position and 2n22^{n^2} subsets of visited spots. The pair uniquely determines the set of available moves (and their intermediate spot requirements). \square

Editorial

Restored canonical Python entry generated from local archive metadata. We iterate over each starting spot s. We then iterate over each target spot t not in visited. Finally, check: all intermediate spots on segment current->t.

Pseudocode

for each starting spot s
for each target spot t not in visited
Check: all intermediate spots on segment current->t
must be already visited
if all intermediates are in visited
Simple move: go directly to t
else
Auto-include unvisited intermediates in order

Complexity Analysis

  • Time: The state space has n22n2n^2 \cdot 2^{n^2} states. For n=4n = 4: 16216=1665536=1,048,57616 \cdot 2^{16} = 16 \cdot 65536 = 1{,}048{,}576 states. With memoization (keyed by (position, bitmask)), the enumeration runs in O(n22n2n2)O(n^2 \cdot 2^{n^2} \cdot n^2) time (each state tries O(n2)O(n^2) targets). For n=4n = 4: approximately 1622161.7×10716^2 \cdot 2^{16} \approx 1.7 \times 10^7 operations.
  • Space: O(n22n2)O(n^2 \cdot 2^{n^2}) for memoization. For n=4n = 4: approximately 10610^6 entries.

Answer

4350069824940\boxed{4350069824940}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_879/solution.cpp
#include <bits/stdc++.h>
using namespace std;

// Problem 879: Touch-screen Password
// Count valid passwords on a 4x4 grid

typedef long long ll;

const int N = 4;
const int TOTAL = N * N;  // 16 spots

// Coordinates
int row[16], col[16];

// intermediate[i][j] = bitmask of spots that lie strictly between spot i and spot j
int intermediate[16][16];

void precompute() {
    for (int i = 0; i < TOTAL; i++) {
        row[i] = i / N;
        col[i] = i % N;
    }

    memset(intermediate, 0, sizeof(intermediate));
    for (int i = 0; i < TOTAL; i++) {
        for (int j = 0; j < TOTAL; j++) {
            if (i == j) continue;
            int dr = row[j] - row[i];
            int dc = col[j] - col[i];
            int g = __gcd(abs(dr), abs(dc));
            if (g <= 1) continue;
            int sr = dr / g, sc = dc / g;
            for (int k = 1; k < g; k++) {
                int r = row[i] + k * sr;
                int c = col[i] + k * sc;
                int idx = r * N + c;
                intermediate[i][j] |= (1 << idx);
            }
        }
    }
}

ll total_count = 0;

// DFS: current position, visited bitmask, path length
void dfs(int pos, int visited, int len) {
    if (len >= 2) {
        total_count++;
    }

    for (int next = 0; next < TOTAL; next++) {
        if (visited & (1 << next)) continue;
        if (pos == next) continue;

        int inter = intermediate[pos][next];
        // All intermediate spots must already be visited
        int unvisited_inter = inter & (~visited);

        if (unvisited_inter != 0) {
            // There are unvisited intermediate spots - they get auto-included
            // But this means the actual move goes through them first
            // The rule says: intermediate spots ARE included in the sequence
            // So we can't jump over unvisited spots - they become part of the path
            // Actually, re-reading the problem: the intermediate spot is included
            // in the password. So going from pos to next, if there's an unvisited
            // intermediate spot, it gets added to the path first.
            // This means we need to handle the full chain.

            // For simplicity with the "auto-include" rule:
            // If we try to go from pos to next, any unvisited intermediate
            // spots are automatically visited in order. But the problem says
            // the user traces a straight line - intermediate spots are added.
            // So we should only allow direct moves where either:
            // (a) there are no intermediate spots, or
            // (b) all intermediate spots are already visited
            // Because if an unvisited intermediate spot exists, the finger
            // would stop there first (making it a shorter segment).

            continue;  // Can't skip over unvisited intermediate spots
        }

        int new_visited = visited | (1 << next);
        dfs(next, new_visited, len + 1);
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    precompute();

    total_count = 0;
    for (int start = 0; start < TOTAL; start++) {
        dfs(start, 1 << start, 1);
    }

    cout << total_count << endl;

    return 0;
}