-- | Generates an Android lock pattern.
androidPatternLock :: Schema
androidPatternLock = withDefaultSize 9 $ \ s -> do
    xs <- loop (min s (height * width)) []
    return $ intercalate " - " $ map showPosition xs
  where
    -- Gets `n` points.
    loop :: Int -> [(Int, Int)] -> Builder [(Int, Int)]
    loop n xs | n <= 0 = return $ reverse xs
    loop n xs = do
        x <- oneOf $ possibilities xs
        loop (n - 1) (x : xs)

    -- Grid dimensions.
    height = 3
    width = 3

    -- Text representation for a position.
    showPosition (1, 1) = "center"
    showPosition (i, j) = vshow i ++ hshow j
      where
        vshow 0 = "north"
        vshow 1 = ""
        vshow _ = "south"

        hshow 0 = "west"
        hshow 1 = ""
        hshow _ = "east"

    -- All positions.
    allPositions = [(i, j) | i <- [0 .. height - 1], j <- [0 .. width - 1]]

    {- Possible positions given a list of already used ones.
       The head of the list is the last used position. -}
    possibilities [] = allPositions
    possibilities pps@(p : ps) = filter isPossible candidates
      where
        candidates = allPositions \\ pps

        isPossible q = all (`elem` ps) $ interfere p q

    -- The list of positions that are on the way between two positions.
    interfere (i, j) (k, l) = do
        r <- [1 .. steps - 1]
        return (i + r * vstep, j + r * hstep)
      where
        vdiff = k - i
        hdiff = l - j

        steps = gcd vdiff hdiff

        vstep = vdiff `div` steps
        hstep = hdiff `div` steps

{- | Generates a password with `s` of the original Pokemons and their level.
     Entropy of about 55.5 bits for 4 pokemons. -}
androidPatternLock :: Schema
androidPatternLock = withDefaultSize 9 $ \ s -> do
    xs <- loop (min s (height * width)) []
    return $ intercalate " - " $ map showPosition xs
  where
    -- Gets `n` points.
    loop :: Int -> [(Int, Int)] -> Builder [(Int, Int)]
    loop n xs | n <= 0 = return $ reverse xs
    loop n xs = do
        x <- oneOf $ possibilities xs
        loop (n - 1) (x : xs)

    -- Grid dimensions.
    height = 3
    width = 3

    -- Text representation for a position.
    showPosition (1, 1) = "center"
    showPosition (i, j) = vshow i ++ hshow j
      where
        vshow 0 = "north"
        vshow 1 = ""
        vshow _ = "south"

        hshow 0 = "west"
        hshow 1 = ""
        hshow _ = "east"

    -- All positions.
    allPositions = [(i, j) | i <- [0 .. height - 1], j <- [0 .. width - 1]]

    {- Possible positions given a list of already used ones.
       The head of the list is the last used position. -}
    possibilities [] = allPositions
    possibilities pps@(p : ps) = filter isPossible candidates
      where
        candidates = allPositions \\ pps

        isPossible q = all (`elem` ps) $ interfere p q

    -- The list of positions that are on the way between two positions.
    interfere (i, j) (k, l) = do
        r <- [1 .. steps - 1]
        return (i + r * vstep, j + r * hstep)
      where
        vdiff = k - i
        hdiff = l - j

        steps = gcd vdiff hdiff

        vstep = vdiff `div` steps
        hstep = hdiff `div` steps

{- | Generates a password with `s` of the original Pokemons and their level.
     Entropy of about 55.5 bits for 4 pokemons. -}
androidPatternLock = withDefaultSize 9 $ \ s -> do
    xs <- loop (min s (height * width)) []
    return $ intercalate " - " $ map showPosition xs
  where
    -- Gets `n` points.
    loop :: Int -> [(Int, Int)] -> Builder [(Int, Int)]
    loop n xs | n <= 0 = return $ reverse xs
    loop n xs = do
        x <- oneOf $ possibilities xs
        loop (n - 1) (x : xs)

    -- Grid dimensions.
    height = 3
    width = 3

    -- Text representation for a position.
    showPosition (1, 1) = "center"
    showPosition (i, j) = vshow i ++ hshow j
      where
        vshow 0 = "north"
        vshow 1 = ""
        vshow _ = "south"

        hshow 0 = "west"
        hshow 1 = ""
        hshow _ = "east"

    -- All positions.
    allPositions = [(i, j) | i <- [0 .. height - 1], j <- [0 .. width - 1]]

    {- Possible positions given a list of already used ones.
       The head of the list is the last used position. -}
    possibilities [] = allPositions
    possibilities pps@(p : ps) = filter isPossible candidates
      where
        candidates = allPositions \\ pps

        isPossible q = all (`elem` ps) $ interfere p q

    -- The list of positions that are on the way between two positions.
    interfere (i, j) (k, l) = do
        r <- [1 .. steps - 1]
        return (i + r * vstep, j + r * hstep)
      where
        vdiff = k - i
        hdiff = l - j

        steps = gcd vdiff hdiff

        vstep = vdiff `div` steps
        hstep = hdiff `div` steps

{- | Generates a password with `s` of the original Pokemons and their level.
     Entropy of about 55.5 bits for 4 pokemons. -}