Shifty Eyes Shifting I's

Shifty-eyes ASCII guys like to shift ASCII Ii's:

>_> <_< >_< <_>


Given a string of shifty-guys, spaced apart or separate lines, shift the Ii's side to side, left the wall and right the skies:

Ii


The shortest shifter wins the prize.

Say What?

Write a program or function that takes in a string of an arbitrary list of these four ASCII emoticons, either space or newline separated (with an optional trailing newline):

>_>
<_<
>_<
<_>


For example, the input might be

>_> >_> <_>


or

>_>
>_>
<_>


(The method you support is up to you.)

Each emoticon performs a different action on the I and i characters, which always start like this:

Ii

• >_> shifts I to the right by one, if possible, and then shifts i to the right by one.
• <_< shifts I to the left by one, if possible, and then shifts i to the left by one, if possible.
• >_< shifts I to the right by one, if possible, and then shifts i to the left by one, if possible.
• <_> shifts I to the left by one, if possible, and then shifts i to the right by one.

I cannot be shifted left if it is at the left edge of the line (as it is initially), and cannot be shifted right if i is directly to its right (as it is initially).

i cannot be shifted left if I is directly to its left (as it is initially), but can always be shifted right.

Note that with these rules, I will always remain to the left of i, and I is attempted to be shifted before i for all emoticons.

Your program or function needs to print or return a string of the final Ii line after applying all the shifts in the order given, using spaces ( ) or periods (.) for empty space. Trailing spaces or periods and a single trailing newline are optionally allowed in the output. Don't mix spaces and periods.

For example, the input

>_>
>_>
<_>


has output

I...i


because the shifts apply like

start  |Ii
>_>    |I.i
>_>    |.I.i
<_>    |I...i


The shortest code in bytes wins. Tiebreaker is higher voted answer.

Test Cases

#[id number]
[space separated input]
[output]


Using . for clarity.

#0
[empty string]
Ii

#1
>_>
I.i

#2
<_<
Ii

#3
>_<
Ii

#4
<_>
I.i

#5
>_> >_>
.I.i

#6
>_> <_<
Ii

#7
>_> >_<
.Ii

#8
>_> <_>
I..i

#9
<_< >_>
I.i

#10
<_< <_<
Ii

#11
<_< >_<
Ii

#12
<_< <_>
I.i

#13
>_< >_>
I.i

#14
>_< <_<
Ii

#15
>_< >_<
Ii

#16
>_< <_>
I.i

#17
<_> >_>
.I.i

#18
<_> <_<
Ii

#19
<_> >_<
.Ii

#20
<_> <_>
I..i

#21
>_> >_> <_>
I...i

#22
<_> >_> >_> >_> <_> <_<
.I...i

#23
<_> >_> >_> >_> <_> <_< >_< <_< >_<
..Ii

#24
>_> >_< >_> >_> >_> >_> >_> >_> <_> <_> <_<
...I.....i

• So the dots are optional, spaces can be there instead? – Rɪᴋᴇʀ Mar 3 '16 at 22:17
• Are trailing spaces allowed in the output? – mbomb007 Mar 3 '16 at 22:32
• Can the input be a 2D char array, with each shifty guy on a line? – Luis Mendo Mar 3 '16 at 22:34
• @RikerW - Yes. mbomb - Yes, that's mentioned. Don - No. – Calvin's Hobbies Mar 3 '16 at 22:46
• Watch Shifty Eyes become an esoteric language... – cat Mar 4 '16 at 0:58

CJam, 33 bytes

0Xq2%{'=-_3$+[2$W]-,*@+}/S*'i+'It


Uses the same algorithm as my Python answer, except with 0-indexing. Essentially:

• Only look at the arrows in the input, converting < to -1 and > to 1
• Only apply an update if it doesn't move us to position -1 and doesn't move us to the position of the other char
• Since the arrows alternate between applying to I and applying to i, we alternate which position we update after each arrow

Thanks to @MartinBüttner for golfing the output step, taking off 5 bytes.

0X                 Initialise the two positions a = 0, b = 1
q2%                Read input and take every second char, leaving just the arrows

{ ... }/           For each arrow in the input...
'=-              Subtract '=, leaving -1 for '< and 1 for '>
_3$+ Duplicate and add a. Stack looks like [a b diff a+diff] [2$W]-           Perform setwise subtraction, [a+diff] - [b -1]
The result is empty list if a+diff is either b or -1, else [a+diff]
,                Length, yielding 0 or 1 respectively
0 means we don't want to update the char, 1 means we do
*                Multiply diff by this result
@+               Add to a. Stack now looks like [b a'] where a' is a updated

After the above loop, the stack ends with [(I position) (i position)]

S*                 Create (i position) many spaces
'i+                Stick an 'i at the end - this is the right place due to 0-indexing
'It                Set (I position) to 'I


Perl, 5956 54 bytes

Includes +1 for -p

Run with the input on STDIN, e.g. perl -p shifty.pl <<< ">_> <_< >_< <_>"

shifty.pl:

s%^|<|(>)%y/iI/Ii/or$_=Ii;$1?s/i |i$/ i/:s/ i/i /%reg  Explanation The control string alternates instructions for i and I and the rule is the same for both of them if you formulate them as: • < Move left if there is a space to the left • > Move right if there is a space or end of string to the right So I'm going to swap i and I in the target string at each step so I only need to apply the rule to one letter ever. This is the y/iI/Ii/ I will walk the control string looking for < and > using a substitution which is usually the shortest way in perl to process something character by character. To avoid having to write $var =~ I want the control string in the perl default variable $_. And I also want an easy way to distinguish < from >. All this can be accomplished using s%<|(>)% code using$1 to distinguish < from > %eg


The target string I also want to manipulate using substitutions and for the same reason I want that in $_ too. $_ being two things at once seems impossible.

However I can have my cake and eat it too because the $_ inside the body of a substitution does not have to remain the same as the $_ being substituded. Once perl started substituting a string this string will not change even if you change the variable the string originally came from. So you can do something like:

s%<|(>)% change $_ here without disturbing the running substitution %eg  I want to replace the original $_ by the initial "Ii" only the very first time the substitution body gets executed (otherwise I keep resetting the target string). This replacement however also has to happen for an empty control string, so even for the empty control string the body needs to be executed at least once. To make sure the substition runs an extra time at the start of the control string (even for empty control strings) I change the substitution to:

s%^|<|(>)% change $_ here without disturbing the running substitution %eg  I will run the y/iI/Ii/ as the first thing inside the substitution code. While $_ is still the control string this won't yet contain any Ii, so if the transliteration indicates nothing was changed that is my trigger initialize $_: y/iI/Ii/or$_=Ii


Now I can implement the actual moving of the letters. Since I start with a swap all moves should be done on i, not I. If $1 is set move i to the right: s/i |i$/ i/


If $1 is not set move i to the left s/ i/i /  Notice that at the start of the control string when I match ^ $1 will not be set, so it tries to move i to the left on the initial string Ii. This won't work because there is no space there, so the intial string remains undisturbed (this is why I put the () around > instead of <)

Only one problem remains: at the end of the outer substitution $_ is set to result of the outer substitution regardless of what you did to $_ inside the substitution body. So the target string with the proper placement of i and I gets lost. In older perls this would be a fatal flaw. More recent perls however have the r modifier which means "make a copy of the original string, do your substitution on that and return the resulting string (instead of number of matches)". When I use that here the result is that the modified command string gets discarded while the original $_ is not disturbed by perl and left after the substitution. However the disturbing I do on $_ is still done after perl left $_ alone. So at the end $_ will be the proper target string.

The -p option makes sure the original string is in $_ and also prints the final $_.

• The initial string is Ii, not iI. – user48538 Mar 4 '16 at 16:07
• @zyabin101 The extra ^ match means I have to swap them. So the reverse initialization is correct. – Ton Hospel Mar 4 '16 at 18:27

LittleLua - 178 Bytes

r()l=sw(I)o=1 D='.'f q=1,#l do i l[q]:s(1,1)=='>'t i z+1~=o t z=z+1 e else i z-1>0 t z=z-1 e e i l[q]:s(3)=='>'t o=o+1 else i o-1~=z t o=o-1 e e e p(D:r(z).."I"..D:r(o-z-1)..'i')


Straight forward implementation.

Ungolfed:

r()                             --call for input
l=sw(I)                         --Split input by spaces
o=1                             --Hold i position (z holds I position)
D='.'                           --Redundant character
f q=1,#l do                     --Foreach table entry
i l[q]:s(1,1)=='>' t        --If the first eye points right
i z+1~=o t z=z+1 e      --Verify no collision and move the I
else
i z-1>0 t z=z-1 e       --If it points left.. .same...
e                           --yatta yatta...
i l[q]:s(3)=='>' t
o=o+1
else
i o-1~=z t o=o-1 e
e
e
p(D:r(z).."I"..D:r(o-z-1)..'i')--String repeats to print correct characters.


What is LittleLua?

LittleLua is a work in progress to try to level the playing fields between my language of choice for these challenges and esoteric languages that often have extremely powerful built-ins.

LittleLua is a Lua 5.3.6 interpreter with an additional module (LittleLua.Lua), as well as function and module names shrunk. These changes will expand over the next day or two, until I'm happy, but as it stands several of the largest changes between LittleLua and a the standard Lua interpreter are:

Functions and modules are shrunk:

io.read() -> r() (Value stored in built in variable "I")
string -> s
string.sub -> s.s or stringvalue:s
etc.


Built in variables

LittleLua has several built in variables to shrink some tasks:

z=0
o=10
h1="Hello, World!"
h2="Hello, World"
h3="hello, world"
h4=hello, world!"
etc.


Built in Functions

Currently a depressingly small list, but here it is:

d(N) -> Prints NxN identity matrix
sw(str) -> Splits string at spaces and returns table of results
sc(str) -> Splits string at commas and returns table of results
sd(str) -> Removes white space from a string (not including tabs)
ss(str,n) -> Swap N characters from beginning and end of string
sr(str,n) -> Swap N characters from beginning and end of string retaining order
sd(str) -> Split string into array of characters
co(ta) -> Concatenate table with no delimiter
co(ta, delim) -> Concatenate table with delimiter: delim

• So is this a Lua golfing Lang? – Downgoat Mar 3 '16 at 22:55
• Yes! Obviously (I hope) a work in progress. I felt I was at a little bit of a disadvantage with other languages being able to take input, sort it, trim it, split it and implicitly return it with a few characters, so I got the source for lua and I've been hacking away for a little bit. This specific version was finished before this challenge started, which is unfortunate. You know what they say, you get the experience right after you need it. – Skyl3r Mar 3 '16 at 22:59
• Stupid question -- take, say $, and use that in place of end or e -- non-A-Za-z-word chars don't need spaces around them, right? That'd shave a byte per end / e – cat Mar 4 '16 at 1:01 • Yeah, I was trying to make that work. By simply replacing the token with a non alphanumeric character it throws an error. I haven't dug deep enough to find out why yet though – Skyl3r Mar 4 '16 at 1:31 • You golfed if to i, saving one byte per use, and end to e, saving two, but you left else alone? Even in this simple program (5 ifs and 2 elses), you're wasting more bytes on else than you save on if. (I'm assuming that's a planned improvement?) – Darrel Hoffman Mar 4 '16 at 21:42 Retina, 101 86 $
¶Ii
(^¶

s^>(.*)I( )?
$1$2I
s^<(.*?)( )?I
$1I$2
s^_>(.*)i
$1 i s^_<(.*?) ?i$1i


Try it online

Saved 15 bytes thanks to daavko!

Takes input separated by newlines and outputs with the eyes separated by spaces.

Explanation:

I will explain stage by stage as usual. All of these stages are in Retina's Replace mode. That means the first line is a regular expression and the second line is a replacement string.

$¶Ii  Add the initial Ii to the end of the input. (^¶  The backtick separates the stage from the options. The option character ( indicates that this stage is the start of a loop of stages to be executed repeatedly in order until a full cycle is completed without changing the input. Since this open parenthesis is never closed, all of the remaining stages are a part of this loop. The actual stage is very simple, if the first character of the string is a newline then delete it. This is just to help make handling the empty input easier, otherwise it'd be golfier to add it on to the two last stages. s^>(.*)I( )?$1$2I  Here, the option s causes the Regex metacharacter . to match newlines. This stage causes a leading > to match the I followed by an optional space. Then it replaces that match with the stuff after the >, followed by the optional space (so the empty string if the space couldn't be matched), and then the I. s^<(.*?)( )?I$1I$2  This stage is very similar to the previous one, only the optional space is before the I, and the order and eye are reversed. s^_>(.*)i$1 i


The handling of i is actually often simpler, because we don't have to worry about optionally adding or removing as i can always move right. For the i cases we match away the underscore as well as the greater/less than sign, but otherwise do similar logic. This one adds a space before the i.

s^_<(.*?) ?i
$1i  Again similar to the above, but it deletes the character before the i if that character is a space, otherwise it only removes the emoticon. • You can get it down to 86 with: s^_>(.*)i( |$)? => s^_>(.*)i and its replacement $1$#2$* i => $1 i, and s^_<(.*?)( )?i => s^_<(.*?) ?i and its replacement $1i$2 => $1i. – daavko Mar 3 '16 at 22:54 • @mbomb007 Yes, I've tested it for all 24 inputs. No errors found. – daavko Mar 3 '16 at 23:03 • @daavko Thanks! I knew I had some stuff lying around from when I copied between the two cases, but I had to leave my computer soon after posting. Edited :) – FryAmTheEggman Mar 3 '16 at 23:09 Python, 142141134122 121 bytes Saved 19 bytes thanks to xnor. def f(l,I=0,i=1): for a,_,b in l.split():I-=[I>0,-(i!=I+1)][a>'='];i-=[i!=I+1,-1][b>'='] return'.'*I+'I'+'.'*(i+~I)+'i'  Example: >>> assert f('<_> >_> >_> >_> <_> <_<') == '.I...i' >>>  Explanation: def f(l, I=0, i=1): for a, _, b in l.split(): I-= -(i!=I+1) if a == '>' else I > 0 i-= -1 if b == '>' else i!=I+1 return '.'*I + 'I' + '.'*(i-I-1) + 'i'  • Your byte count from the paste is 148 - looks like you pasted the code with the extra spaces into the answer. – Celeo Mar 4 '16 at 0:47 • @Celeo: each line in the function body is indented with 1 tab character. You can verify that by clicking "edit". However, SE renders code with tabs replaced by 4 spaces. It's possible to indent function body with 1 space, instead of 1 tab, though. – vaultah Mar 4 '16 at 5:34 • Won't i always stay greater than I? – xnor Mar 4 '16 at 6:01 • @xnor: can't believe I missed that :( Thanks. – vaultah Mar 4 '16 at 6:08 • @vaultah I think this lets you simplify the line to a string concatenation of dots, I, dots, i, without any need for lists and joining. – xnor Mar 4 '16 at 6:09 GNU sed, 81 bytes (including +1 for -r flag) #!/bin/sed -rf s!<!s/ I/I /;!g s!>!s/I / I/;!g s!_(.{9})!\L\1!g s!i !i!g s/.*/echo Ii|sed '&'/e  This creates a new sed program from the input (which you can see by removing the last line), and applies it to the start state Ii. Explanation • The first two lines convert < and > to 'substitute' commands that shift I left and right respectively. • Then we change the one following _ to work on i rather than I • i is not bounded by any right-hand edge, so don't add or consume space following it • Finally, apply the created command to the input Ii. s///e always uses /bin/sh as its shell, so I couldn't shorten this to sed '&'<<<Ii as I wanted (that's a Bash redirection syntax). Test results $ for i in '' '>_>' '<_<' '>_<' '<_>' '>_> >_>' '>_> <_<' '>_> >_<' '>_> <_>' '<_< >_>' '<_< <_<' '<_< >_<' '<_< <_>' '>_< >_>' '>_< <_<' '>_< >_<' '>_< <_>' '<_> >_>' '<_> <_<' '<_> >_<' '<_> <_>' '>_> >_> <_>' '<_> >_> >_> >_> <_> <_<' '<_> >_> >_> >_> <_> <_< >_< <_< >_<' '>_> >_< >_> >_> >_> >_> >_> >_> <_> <_> <_<'
> do printf '%s => ' "$i"; ./74719.sed <<<"$i" | tr \  .; done | cat -n
1   => Ii
2  >_> => I.i
3  <_< => Ii
4  >_< => Ii
5  <_> => I.i
6  >_> >_> => .I.i
7  >_> <_< => Ii
8  >_> >_< => .Ii
9  >_> <_> => I..i
10  <_< >_> => I.i
11  <_< <_< => Ii
12  <_< >_< => Ii
13  <_< <_> => I.i
14  >_< >_> => I.i
15  >_< <_< => Ii
16  >_< >_< => Ii
17  >_< <_> => I.i
18  <_> >_> => .I.i
19  <_> <_< => Ii
20  <_> >_< => .Ii
21  <_> <_> => I..i
22  >_> >_> <_> => I...i
23  <_> >_> >_> >_> <_> <_< => .I...i
24  <_> >_> >_> >_> <_> <_< >_< <_< >_< => ..Ii
25  >_> >_< >_> >_> >_> >_> >_> >_> <_> <_> <_< => ...I.....i


Javascript (ES6) 176 171 168 155 148 147 142 141 bytes

//v8 - I was sure saving off math was saving a byte, thanks ETF
_=>_.split .map(m=>(I=m<'='?I-1||0:Math.min(i-1,I+1))+(i=m[2]=='<'?Math.max(I+1,i-1):i+1),i=1,I=0)&&'.'.repeat(I)+'I'+'.'.repeat(--i-I)+'i'

//v7 - not checking first char when I can check whole string - changed how I create end string (stolen from edc65)
_=>_.split .map(m=>(I=m<'='?I-1||0:M.min(i-1,I+1))+(i=m[2]=='<'?M.max(I+1,i-1):i+1),i=1,I=0,M=Math)&&'.'.repeat(I)+'I'+'.'.repeat(--i-I)+'i'

//v6 - one more byte
_=>_.split .map(m=>(I=m[0]=='<'?M(0,I-1):Math.min(i-1,I+1))+(i=m[2]=='<'?M(I+1,i-1):i+1),i=1,I=0,M=Math.max)+((a=Array(i))[I]='I')&&a.join.+'i'

//v5 - not filling array with '.', just joining with . later
_=>_.split .map(m=>(I=m[0]=='<'?M(0,I-1):Math.min(i-1,I+1))+(i=m[2]=='<'?M(I+1,i-1):i+1),i=1,I=0,M=Math.max)&&((a=Array(i))[I]='I')&&a.join.+'i'

//v4 - realized I didn't need to split >_> on _, just use the 0th and 2nd index
_=>_.split .map(m=>(I=m[0]=='<'?M(0,I-1):Math.min(i-1,I+1))+(i=m[2]=='<'?M(I+1,i-1):i+1),i=1,I=0,M=Math.max)&&((a=Array(i).fill.)[I]='I')&&a.join+'i'

//v3 - saving Math to a var (thanks @Verzio)
_=>_.split .map(m=>(I=(m=m.split_)[0]=='<'?M(0,I-1):Math.min(i-1,I+1))+(i=m[1]=='<'?M(I+1,i-1):i+1),i=1,I=0,M=Math.max)&&((a=Array(i).fill.)[I]='I')&&a.join+'i'

//v2 - as a single expression! (thanks @Downgoat)
_=>_.split .map(m=>(I=(m=m.split_)[0]=='<'?Math.max(0,I-1):Math.min(i-1,I+1))+(i=m[1]=='<'?Math.max(I+1,i-1):i+1),i=1,I=0)&&((a=Array(i).fill.)[I]='I')&&a.join+'i'

//version 1
_=>{I=0;i=1;_.split .map(m=>(I=(m=m.split_)[0]=='<'?Math.max(0,I-1):Math.min(i-1,I+1))+(i=m[1]=='<'?Math.max(I+1,i-1):i+1));(a=Array(i).fill.)[I]='I';return a.join+'i'}


Usage

f=_=>_.split .map(m=>(I=m<'='?I-1||0:Math.min(i-1,I+1))+(i=m[2]=='<'?Math.max(I+1,i-1):i+1),i=1,I=0)&&'.'.repeat(I)+'I'+'.'.repeat(--i-I)+'i'

f(">_> >_< >_> >_> >_> >_> >_> >_> <_> <_> <_<")
//"...I.....i"


Degolfed (v6, v7 isn't much different)

//my solution has changed slightly, but not significantly enough to redo the below

_=>                   //take an input
_.split           //split to each command <_<
.map(              //do something for each command (each command being called m)
m=>
(I=            //set I to.... 'I will be the index of the letter I'
m[0]=='<'?   //is the first char of the command '<'?
Math.max(0,I-1)   //yes => move I to the left (but don't move past 0)
:
Math.min(i-1,I+1)  //no => move I to the right one, but keep it one less than i
)

+              //also we need to mess with i
(i=
m[2]=='<'?    //is the 3rd char of the command '<'?
Math.max(I+1,i-1)  //yes => move i to the left, but keep it one right of I
:
i+1         //move i to the right (no bounds on how far right i can go)
)

,i=1,I=0       //set I to 0 and i to 1 initially
)
+                  //this just lets us chain commands into one expression, not really adding
(
(a=Array(i))[I]='I') //create an array of length i (will be one shorter than we really need)
//set element I to 'I'

&&                    //last chained command, we know the array creation will be true
//so javascript will just output the next thing as the return for the function
a.join.+'i'         //join the array with '.' (into a string) and append i
//i will always be the last element

• Instead of using =>{ ... } you could make is a expression and save quite a few bytes – Downgoat Mar 3 '16 at 23:03
• I was creeping up on quitting time at work and wanted to wrap things up :) I tried to get rid of it, but couldn't quite get it before 4 :P I'll take another look – Charlie Wynn Mar 4 '16 at 14:42
• Hint: save yourself a byte and just write out Math twice. – ETHproductions Mar 6 '16 at 16:18

MATL, 56555049 47 bytes

1Hj3\q4ZC"w@1)+lhX>yqhX<w@3)+yQhX>]tZ"105b(73b(


Try it online!

1           % push 1: initial position of 'I'
H           % push 2: initial position of 'i'
j           % take input as a string
4\q         % modulo 3 and subtract 1: this gives -1 for '<', 1 for '>'
4Zc         % arrange blocks of length 4 as columns of 2D array. This pads last
% block with a zero (because the separating space won't be there).
% Only first and third and last rows will be used
"           % for each column
w         %   swap: move position of 'I' to top of stack
@1)       %   first number (+/-1) of current column: tells where the 'I' moves
lhX>      %   max with 1: 'I' cannot move left past position 1
y         %   duplicate position of 'i'
qhX<      %   max with that number minus 1: 'I' cannot collide with 'i'
w         %   swap: move position of 'i' to top of stack
@3)       %   last number (+/-1) of current column: tells where the 'i' moves
y         %   duplicate position of 'I'
QhX>      %   max with that number plus 1: 'i' cannot collide with 'I'
]           % end for each
t           % duplicate position of 'I'. This is the output string length
Z"          % string with that many spaces
105b(       % set 'i' at its computed position in that string
73b(        % set 'I' at its computed position in that string

• the lack of matching parens + braces-- it burNS MY EYES – cat Mar 4 '16 at 1:03
• @tac Haha. At least the quotes "match" – Luis Mendo Mar 4 '16 at 1:04

Retina, 91 86 bytes

I probably didn't take the best approach, so it can probably be golfed more. And no, I did not copy FryAmTheEggman (I know they're really similar in our approaches). I didn't even see his answer until after I posted mine.

$¶Ii (^¶ sr^<_(.*)( |)I$1I$2 s^>_(.*)I( ?)$1$2I sr^<(.*) ?i$1i
s^>(.*)i
$1 i  Try it online • You don't need the ( |) at the end of last match line, as there will never be any space after i. Also, again on last match line, you don't need closing bracket for the loop. Unclosed loop is auto-closed at end of file in Retina. – daavko Mar 3 '16 at 23:12 • Thanks. I used to have spaces after i and something after that replace. Forgot to change those. – mbomb007 Mar 4 '16 at 14:39 Javascript (ES6) 166 bytes Using Charlie Wynn's answer, I managed to save 10 bytes by defining Math.max as M and calling M each time his script uses _=>{I=0;i=1;_.split .map(m=>(I=(m=m.split_)[0]=='<'?M=Math.max;M(0,I-1):M(i-1,I+1))+(i=m[1]=='<'?M(I+1,i-1):i+1));(a=Array(i).fill.)[I]='I';return a.join+'i'}  (I didn't write this golf, Charlie Wynn did here. I merely modified it to make it shorter) • Welcome to PPCG! Here, we make comments on posts that can be improved. Unless you have a (radically) different solution, you would comment a golfing suggestion on the original post. – Conor O'Brien Mar 3 '16 at 23:49 • I would've, but I don't have enough reputation to do that. – Verzlo Mar 4 '16 at 0:41 • It can stay, but people may end up downvoting. Sorry. I think it can stay, but others may not. – Rɪᴋᴇʀ Mar 4 '16 at 0:56 • I was going to comment on the other answer the changes you did, before seeing your answer. +1 on that! But your code throws SyntaxError: missing : in conditional expression on Firefox. You could fix it with _=>{I=0,i=1,M=Math.max;_.split .map(m=>(I=(m=m.split_)[0]=='<'?M(0,I-1):M(i-1,I+1))+(i=m[1]=='<'?M(I+1,i-1):i+1));(a=Array(i).fill.)[I]='I';return a.join+'i'}, which has the same exact size. – Ismael Miguel Mar 4 '16 at 9:32 • I do get a syntax error in chrome though – Charlie Wynn Mar 4 '16 at 15:07 JavaScript (ES6), 115 118 Edit: 3 bytes saved thx CharlieWynn a=>a.split .map(x=>x[x>'='?q&&(--q,++p):p&&(--p,++q),2]>'='?++q:q&&--q,p=q=0)&&'.'.repeat(p)+I${'.'.repeat(q)}i


p is the number of spaces before I; q is the number of spaces between I and i. Neither can be negative.

Less golfed

a=>(
a.split(' ').map( x=> (
x>'='?q&&(--q,++p):p&&(--p,++q), // try to move I based on 1st char of x
x[2]>'='?++q:q&&--q // try to move i based on 3rd char of x
)
, p=q=0), // starting values of p and q
'.'.repeat(p)+'I' + '.'.repeat(q) +'i' // return value
)


Test

f=a=>a.split .map(x=>x[x>'='?q&&(--q,++p):p&&(--p,++q),2]>'='?++q:q&&--q,p=q=0)&&'.'.repeat(p)+I${'.'.repeat(q)}i console.log=x=>O.textContent+=x+'\n' ;[['','Ii'], ['>_>','I.i'], ['<_<','Ii'], ['>_<','Ii'], ['<_>','I.i'], ['>_> >_>','.I.i'], ['>_> <_<','Ii'], ['>_> >_<','.Ii'], ['>_> <_>','I..i'], ['<_< >_>','I.i'], ['<_< <_<','Ii'], ['<_< >_<','Ii'], ['<_< <_>','I.i'], ['>_< >_>','I.i'], ['>_< <_<','Ii'], ['>_< >_<','Ii'], ['>_< <_>','I.i'], ['<_> >_>','.I.i'], ['<_> <_<','Ii'], ['<_> >_<','.Ii'], ['<_> <_>','I..i'], ['>_> >_> <_>','I...i'], ['<_> >_> >_> >_> <_> <_<','.I...i'], ['<_> >_> >_> >_> <_> <_< >_< <_< >_<','..Ii'], ['>_> >_< >_> >_> >_> >_> >_> >_> <_> <_> <_<','...I.....i']] .forEach(t=>{ var i=t[0],k=t[1],r=f(i) console.log(i +' -> '+r + (r==k?' OK':' KO (exp '+k+')')) }) <pre id=O></pre> • You can save one if you .split .map( instead of .replace(/\S+/, I really like how you're storing the distance from I to i instead of position of i. I wanted to change mine to use that but I think it'd just be a mirror of yours. – Charlie Wynn Mar 4 '16 at 18:37 • it saves 2 bytes! thx @CharlieWynn .. or even 3 – edc65 Mar 4 '16 at 21:24 Retina, 61 58 bytes 3 bytes saved thanks to @FryAmTheEggman. ^ Ii (( ?)I(.*i) <|I( ?)(.*i) >$3I$1$2$4 ?i_< i i_> i  Explanation comes a bit later. Try it online! Modified code with batch test. • Ah. Putting the I's at the front does make more sense... – mbomb007 Mar 4 '16 at 18:19 Python 2, 96 92 bytes f=lambda s,I=1,i=2:s and f(s[2:],i,I+cmp(s,'=')*(0<I+cmp(s,'=')!=i))or'%*c'*2%(I,73,i-I,105)  Pretty shifty-looking solution for a shifty challenge. Input like f('>_> <_>'), output like 'I i'. Verification program (assuming tests is the multiline test case string): for test in zip(*[iter(tests.replace("[empty string]", "").splitlines())]*4): assert f(test[1]) == test[2].replace('.',' ')  The program reads each arrow one at a time, starting with I=1, i=2 and using 1-based indices. The variable names are a tad misleading since they swap roles - after every char, I becomes i and i becomes I updated. A char is only updated if it would move to neither the position of the other char nor position 0. For example, for >_> <_> >_< we do: Char I (= i from previous iteration) i ----------------------------------------------------------------------------------------- 1 2 > 2 1+1 = 2 would overlap, so remain 1 > 1 2+1 = 3 < 3 1-1 = 0 is too low, so remain 1 > 1 3+1 = 4 > 4 1+1 = 2 < 2 4-1 = 3  This gives ' Ii' as desired. Lua, 104 bytes s='Ii'for v in(...):gmatch'%S_?'do s=s:gsub(('>i$ i< ii>_I  I<_ II '):match(v..'(..)(%P+)'))end
print(s)


Usage:

\$ lua shifter.lua "<_> >_> >_> >_> <_> <_<"
I   i


Javascript (ES5), 153 125 bytes

takes an input by setting a variable a before running

I=i=0;for(b of a.split(" "))b[0]==">"?i!=I&&I++:I>0&&I--,b[2]==">"?i++:I!=i&&i--;r=Array(i).fill(".");r[I]="I";r.join("")+"i"


Somewhat ungolfed:

I=i=0;
for(b of a.split(" "))
b[0]==">"
? i!=I && I++
: I>0 && I--,
b[2]==">"
? i++
: I!=i && i--
;
r=Array(i).fill(".");
r[I]="I";
r.join("")+"i"


Mathematica, 125 bytes

Fold[StringReplace,"Ii",StringCases[#,"<_"|">_"|">"|"<"]/.{"<_"->".I"->"I.",">_"->"I."->".I","<"->".i"->"i",">"->"i"->".i"}]&


Pure function with first argument #. The idea is that each <_, >_, <, and > in the input corresponds to a string replacement rule. "<_"|">_"|">"|"<" is a string pattern that matches any of those four expressions. StringCases[#,"<_"|">_"|">"|"<"] will find all such matches. Then we replace (/.) each "<_" with the string replacement rule ".I"->"I.", each ">_" with the rule "I."->".I", and so forth. Then I want to sequentially apply each replacement rule to the string "Ii", but StringReplace will only look for matches in the parts of the string which have not been replaced, so we left Fold the function StringReplace over the list of replacement rules with starting value "Ii".

Perhaps it would be clearer with an example (here % refers to the previous cell's output):