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
.