Can my 4-note music box play that song?

CJam, 34 31 bytes

8,q{~2*f{_@-_zF8b&,@@+8,=*}0-}/

Did this on my phone, so I'll have to put up an explanation later. Output is nonempty iff truthy.

Try it online | Test suite

Explanation

The new code shifts the layout a little:

2    3    4

1    .    5

0/8  7    6

Even numbers correspond to string positions and odd numbers correspond to crank positions.

Here's what happens:

8,           Create the range [0 1 .. 7] of possible starting positions
             We can leave the string positions [0 2 4 6] in since it doesn't matter
q{ ... }/    For each character in the input...
  ~2*          Evaluate as integer and double, mapping "1234" -> [2 4 6 8]
  f{ ... }     Map over our possible current crank positions with the string
               position as an extra parameter
    _@-          Calculate (string - crank), giving some number in [-7 ... 7]
    _z           Duplicate and absolute value
    F8b          Push 15 base 8, or [1 7]
    &,           Setwise intersection and get length. If (string - crank) was in
                 [-7 -1 1 7] then the move was valid and this evaluates to 1, otherwise 0
    @@+          Calculate ((string - crank) + string)
    8,=          Take modulo 8, giving the new crank position. x%y in Java matches the
                 sign of x, so we need to do ,= (range + index) to get a number in [0 .. 7]
    *            Multiply the new crank position by our previous 0 or 1
  0-           Remove all 0s, which correspond to invalid positions

The stack is then automatically printed at the end. Any possible ending positions will be in the output, e.g. for the input 1 the output is 31, which means the crank can end facing either left or up.

If only CJam had filter with extra parameter...


Edit: Temporarily rolling back while I convince myself that this 29-byte works:

8,q{~2*f{_@-_7b1#@@+8,=|}W-}/

Pyth, 30 27 bytes

f!-aVJ.u%-ysYN8zTtJ,1 7cR2T

Here's the idea:

 1.5  1  0.5
              
  2       0
 
 2.5  3  3.5

The crank is always at a half-integer position c. At each step, we reflect it over an integer position note n by setting c = 2*n-c. If n is valid, c changes by ±1 mod 8. If n is invalid, c changes by ±3 mod 8. We cumulatively reduce over the input to collect all values of c, and then see if all notes were valid. We do this for every starting value of c, because it's shorter than checking just the ones adjacent to the first note.

Formatted:

f
  ! -
      aV J .u
              % -
                  y s Y
                  N
                8
              z
              T
         t J
      ,
        1 
        7
  cR2 T

Test suite.


Haskell, 93 88 87 bytes

any(all(\(a,b:c)->1>mod(a!!1-b)4).(zip=<<tail)).mapM((\a->[[a,a+1],[a+1,a]]).read.pure)

This evaluates to an anonymous function that takes a string and returns a boolean. Test suite here.

Explanation

The idea is that the lambda on the right maps a number a to [[a,a+1],[a+1,a]], the two possible "moves" that take the crank over that number, according to the following diagram:

  1 (2) 2

(1/5)  (3)

  4 (4) 3

In the main anonymous function, we first do mapM((...).read.pure), which converts each character to an integer, applies the above lambda to it, and chooses one of the two moves, returning the list of all resulting move sequences. Then, we check if any of these sequences has the property that the second number of each move equals the first number of the next modulo 4, which means that it's a physically possible sequence. To do this, we zip each move sequence with its tail, check the condition for all the pairs, and see if any sequence evaluates to True.