Brute-force the switchboard The other day, our team went to an escape room. One of the puzzles involved a board of six mechanical switches where you had to find the correct combination of on and off in order to unlock a box, somewhat like this:

-v-v-v-
-v-v-v-


Being developers, we decided it would be more efficient to try every single one of 2^6=64 combinations than actually solve the puzzle. So we assigned some poor guy to do some binary counting:

-v-v-v-
-v-v-v-

-v-v-v-
-v-v-^-

-v-v-v-
-v-^-v-

-v-v-v-
-v-^-^-


and so on.

The challenge
Write a program that, given the switches all in off position as a string formatted as above, generates all combinations of on and off in any order.

You can write either a full program or a function. Thus, your program can either take in input through stdin, a file, or as a single string argument, and either return or print the output. If returned, the output may be in a list/array/etc. rather than a single string. If the output is a single string, the boards should be separated by newlines (trailing newlines are allowed.)

The input strings will match the regex r'((-v)+-)(\n(-v)+-)*' and represent one board with all switches off. This means no zero case, and switches are left-aligned. Each row might not have the same number of switches. 

Each output board should be of the exact same format as the input, except that the v's may be replaced by ^'s as required. The output boards can be separated by any number of newlines.

Since runtime is naturally O(2^n) in the number of switches, your code will not be tested on any more than 10 switches in any arrangement.

This is code-golf, so shortest code in number of bytes wins.

Sample inputs and outputs

Input:

-v-


Possible output:

-v-
-^-


Input:

-v-
-v-


Possible output:

-^-
-^-
-^-
-v-

-v-
-^-


-v-
-v-


Since it's extremely tedious to check your answer for bigger numbers of switches, here's a Python script as a sanity check tool. (I've included a currently commented-out snippet to generate expected output from a given input file in case you want more test cases.) It's quite a bit less flexible in terms of input and output than the spec, unfortunately; put the input string in a file named 'input' and the newline-separated output (sorry, no list formatting) in a file named 'output' in the same directory and run python3 sanitycheck.py.
Haskell, 25 24 23 17 bytes



mapM$min"^v".pure


Try it online!

-1 byte thanks to @H.PWiz

-1 byte thanks to @nimi

Returns a list of strings. The TIO has 2 extra bytes for the function declaration - I've seen other people leave it off when they write the function pointfree so I'm doing the same unless told otherwise.

Previous Answer (25 bytes)

g 'v'="v^"
g x=[x]
mapM g


The explanations are all for the previous answer, which works pretty much the same way, except I inlined the definition of g. The way g works now is by using lexical comparison to substitute ^v for v and keep everything else the same.

Interestingly, this works for arbitrary switchboards:

&gt;&gt;&gt; mapM g "-----^-----"
  ["-----v-----", "-----^-----"]


Explanation (Short)

g 'v'="v^" -- for 'v', choose between 'v' or '^'
g x=[x]    -- for any other character, choose just that character
mapM g     -- find all ways to choose characters using g on the given input


Explanation (Long)

mapM is a pretty scary function for those not familiar with Haskell. But it's not hard to understand in this context. By making it act on Strings (which in Haskell are lists of characters), I've specialized it to its definition for lists. So in this context, its type signature is

mapM :: (a -&gt; [b]) -&gt; [a] -&gt; [[b]]
--      ^^^^^^^^^^                  arg 1: a function from any a to a list of b
--                    ^^^           arg 2: a list of a
--                           ^^^^^ return: a list of list of b


It is actually even more specialized in my usage of it - a and b are both Char - so we can see the type signature as

mapM :: (Char -&gt; String) -&gt; String -&gt; [String]


Let's quickly look at what g does before explaining how mapM works.

g :: Char -&gt; String
g 'v' = "v^"
g  x  = [x]


g uses pattern matching to convert the Char 'v' into the string "v^"; everything else gets converted to a singleton string (remember, strings are just lists of Chars, so we can put x in a singleton list). Testing on the REPL, we find this is the case

&gt;&gt;&gt; g 'a'
  "a"
&gt;&gt;&gt; g 'b'
  "b"
&gt;&gt;&gt; g 'v'
  "v^"


Note that g has the right type to be an argument of mapM (unsurprisingly!).

We'll explore how mapM works by giving it g and the argument

"-v-\n-v-"


as input.

mapM first maps g over the String, and because g converts Chars to Strings, this gives us a list of Strings

["-", "v^", "-", "\n", "-", "v^", "-"]


While this is the correct output type, mapM does slightly more. You can think of it as forming all Strings that you could create from this list if you had to pick a single character from each String in it (in order).

So for the first element, you have no choice other than to pick the Char '-'. For the second element, you can choose between 'v' and '^', so on and so forth.

It's roughly equivalent to this python code:

result = []
for x1 in "-":
  for x2 in "v^":
    for x3 in "-":
      ...
        result.append(''.join([x1, x2, x3, x4, x5, x6, x7]))


Except that since Haskell separates between Chars and Strings, when it puts the Chars into a list, it doesn't need to join them.

So the final output is

["-v-\n-v-", "-v-\n-^", "-^-\n-v-", "-^-\n-^-"]


as desired.