24
\$\begingroup\$

You are given a string [A-Za-z|]+ that contains exactly one occurrence of | denoting the current position of the cursor and a sequence of moves [><#]+ where:

  • >: moves the cursor one position to the right
  • <: moves the cursor one position to the left
  • #: moves the cursor one position to the left, deletes the character to its right and shifts back any text after (simulates a backspace)

If the cursor is the first character of the string, moving left (<) or deleting (#) should keep the string unchanged. Similarly, if the cursor is the last character of the string, moving right (>) should keep the string unchanged.

Your goal is to simulate the sequence of moves on the provided string.

Test cases

String Moves Expected result
This is a test| #<<<<<######> Tha| tes
Curs|or end test >>>>>>>>>>>>><> Cursor end test|
Cursor s|tart test <<<<<<<<<<<>< |Cursor start test
Delete all| ##############><<< |
| >><##> |

Steps for the 1st test case

  • Input: This is a test|
  • Moves: #<<<<<######>

Steps:

Move String
This is a test| (Input)
(#) This is a tes|
(<) This is a te|s
(<) This is a t|es
(<) This is a |tes
(<) This is a| tes
(<) This is |a tes
(#) This is|a tes
(#) This i|a tes
(#) This |a tes
(#) This|a tes
(#) Thi|a tes
(#) Th|a tes
(>) Tha| tes (Output)

If you want to see the intermediate steps for the other test cases you can check this spreadsheet. If you want to run the simulation on your own inputs, you can make a copy of the spreadsheet and change the corresponding fields in the 'simulator' tab.

This is so shortest answer in bytes wins.

asked Jul 23, 2024 at 8:43
\$\endgroup\$
2
  • 1
    \$\begingroup\$ As the rest of the string can only contain [A-Za-z ], may we choose a different character to represent the cursor to avoid the | choking RegEx based solutions? \$\endgroup\$ Commented Jul 23, 2024 at 9:35
  • \$\begingroup\$ I believe at the time this was written there were already a few regex solutions so I'd say it's better to keep it like this. \$\endgroup\$ Commented Jul 24, 2024 at 10:28

20 Answers 20

6
\$\begingroup\$

Retina 0.8.2, 53 bytes

+`\|(.?)(.*	)>|(.?)\|(.*	)<|.?\|(.*	)#
1ドル|2ドル3ドル4ドル5ドル
	

Try it online! Link includes test cases. Takes input separated by tabs (normally would have used newlines but that makes writing a test suite harder). Explanation: The replacement stage simply shuffles the | around depending on the next operation, with the + prefix repeating until no more operations remain, then finally the tab is deleted.

answered Jul 23, 2024 at 9:43
\$\endgroup\$
6
\$\begingroup\$

Perl 5 -p, 71 bytes

%C=qw(< (.)\|/|1ドル/ > \|(.)/1ドル|/ # .\|/|/);eval join";s/",@C{<>=~/^|./g}

Try it online!

same length:

%C=qw(< s/()(.)\| > s/\|(.) # s/.\|);$"='/1ドル|2ドル/;';eval"@C{<>=~/$|./g}"

Try it online!

answered Jul 23, 2024 at 9:32
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Nice approach! I tried to do this myself and ended up with a pretty similar solution for 63: dom111.github.io/code-sandbox/… Main changes are using a list instead of a hash and using cmp to get 0, 1 or -1 from "<" and using getc with a while!eof \$\endgroup\$ Commented Jul 23, 2024 at 13:30
  • 1
    \$\begingroup\$ Worth noting that a port of @Neil's answer comes in at 57: dom111.github.io/code-sandbox/… \$\endgroup\$ Commented Jul 23, 2024 at 13:30
4
\$\begingroup\$

Python 3.8 (pre-release), 137 bytes

f=lambda s,n=0,*m:n and f([(x:=s[:(c:=s.find('|'))and~-c])+'|'+s[c-1:c]+s[c+1:],0,s[:c]+s[c+1:c+2]+'|'+s[c+2:],x+s[c:]][ord(n)%4],*m)or s

Try it online!

answered Jul 23, 2024 at 9:14
\$\endgroup\$
3
  • 1
    \$\begingroup\$ 106 with regex \$\endgroup\$ Commented Jul 23, 2024 at 23:05
  • \$\begingroup\$ @nocomment Clever regex! 101 bytes (omitting f=) \$\endgroup\$ Commented Jul 24, 2024 at 2:59
  • \$\begingroup\$ Nice suggestions! I'll refrain from posting a regex submission since Albert.Lang has a nice one now. \$\endgroup\$ Commented Jul 24, 2024 at 9:04
4
\$\begingroup\$

Python, 92 bytes

-1 thanks to @xnor

lambda s,c:[s:=re.sub((2*"((\.|))")[i<"="::2],r"2円1円"[:ord(i)&6],s)for i in c][-1]
import re

Attempt This Online!

Python, 93 bytes

lambda s,c:[s:=re.sub((2*"((\.|))")[i<"="::2],r"2円"+r"1円"*(i>"#"),s)for i in c][-1]
import re

Attempt This Online!

Inspired by other regex based answers.

answered Jul 24, 2024 at 6:20
\$\endgroup\$
1
  • 1
    \$\begingroup\$ That's a crazy cyclic [::2]! For the second arg, looks like r"2円1円"[:ord(i)&6] saves a byte \$\endgroup\$ Commented Jul 24, 2024 at 8:45
4
\$\begingroup\$

Google Sheets, 120 bytes

Expects the string in A2 and the moves in B2:

=REDUCE(A2,SEQUENCE(LEN(B2)),LAMBDA(a,i,REGEXREPLACE(a,"(.?)\|(.?)",SWITCH(MID(B2,i,1),">","1ドル2ドル|","<","|1ドル2ドル","|2ドル")))) 

enter image description here

answered Jul 24, 2024 at 11:12
\$\endgroup\$
3
\$\begingroup\$

Zsh, (削除) 103 (削除ここまで) 92 bytes

Saved 11 bytes using r[1]= to remove the first character of $r and l[-1]= to remove the last character of $l.

IFS=\|
read l r
for m;case $m {\<)r=$l[-1]$r l[-1]=;;\>)l+=$r[1] r[1]=;;*)l[-1]=;}
<<<$l\|$r

(削除) Try it online! (削除ここまで) Try it online!

Takes string on stdin and motions as argv.

IFS=\| # set internal field separator for reading string
read l r # read string from stdin, splitting on $IFS into $l and $r
for m; # motions as separate arguments
 case $m {
 \<)
 r=$l[-1]$r l[-1]= ;;
 \>)
 l+=$r[1] r[1]= ;;
 *) # catchall, rather than escaping \#
 l[-1]=
 }
<<<$l\|$r
answered Jul 24, 2024 at 17:49
\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node.js), 80 bytes

g=(x,[c,...d])=>c?g(x.replace(/(.?)\|(.?)/,c<g?'|2ドル':c>'='?'1ドル2ドル|':'|1ドル2ドル'),d):x

Try it online!

answered Jul 23, 2024 at 10:01
\$\endgroup\$
2
\$\begingroup\$

Uiua, 40 bytes

⍜⊙↻⊂@|∧⊃(+-⊸¬=@>|⍜⊙↻↘=@#⊙-⊙1):⊙:⊙▽⊃⊗⊸≠@|

Try it out!

Explanation

⊙▽⊃⊗⊸≠@| # Get the (0 indexed) position of the cursor `|`
 # and remove it from the string
:⊙: # Rearrange items before looping
∧⊃( # For each character in the moves string do: 
 +-⊸¬=@> # Add 1 to the cursor position if the move is `>`
 # otherwise subtract 1
| ⍜⊙↻↘=@#⊙-⊙1 # If the move is `#` backspace remove the character
 # 1 index before the cursor position 
) # End loop
⍜⊙↻⊂@| # Insert the cursor back into the string at cursor position
answered Jul 24, 2024 at 17:03
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Golf, and nice first answer! \$\endgroup\$ Commented Jul 26, 2024 at 2:00
2
\$\begingroup\$

GNU C, (削除) 214 (削除ここまで) 208 bytes

  • -6 bytes by replacing all *(x+y) by x[y]
char*c(char*s,char*i){char*r=malloc(strlen(s)),*z,o,t;r=strcpy(r,s);for(;*i;++i){z=strchr(r,'|');o=(*i==60&&z-r)?-1:(*i==62&&z[1])?1:0;t=*z;*z=z[o];z[o]=t;if(*i==35&&z-r)memmove(z-1,z,strlen(z)+1);}return r;}

Try it on programiz.com

answered Jul 25, 2024 at 22:17
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Golf, and nice first answer! Be sure to check out our Tips for golfing in C page for ways you can golf your program! It looks like this is 214 bytes, since you don't have to include the trailing newline. \$\endgroup\$ Commented Jul 25, 2024 at 22:23
  • \$\begingroup\$ Thanks, I'll check it out right away! \$\endgroup\$ Commented Jul 25, 2024 at 22:28
2
\$\begingroup\$

Python, (削除) 100 (削除ここまで) (削除) 96 (削除ここまで) (削除) 87 (削除ここまで) 86 bytes

-4 bytes thanks to @xnor
-9 bytes thanks to @no comment

def f(s,o):
 for i in o:s[(j+i%5or 1)-1:0]=s.pop(j:=s.index('|'))[i%2>0<j==s.pop(j-1)]

Attempt This Online!

Modifies the input in-place

answered Jul 24, 2024 at 4:36
\$\endgroup\$
4
  • \$\begingroup\$ It looks like you can replace the and with == \$\endgroup\$ Commented Jul 24, 2024 at 8:31
  • \$\begingroup\$ Or better, do i%2>0<j== \$\endgroup\$ Commented Jul 24, 2024 at 8:37
  • \$\begingroup\$ 90: def f(s,o): for i in o:s.pop(j:=s.index('|'));i%2>0<j==s.pop(j-1);s[max(0,j+i%5-1):0]='|' \$\endgroup\$ Commented Jul 24, 2024 at 15:17
  • 1
    \$\begingroup\$ 87: def f(s,o): for i in o:s[max(0,j+i%5-1):0]=s.pop(j:=s.index('|'))[i%2>0<j==s.pop(j-1)] \$\endgroup\$ Commented Jul 24, 2024 at 15:27
2
\$\begingroup\$

Haskell, (削除) 133 (削除ここまで) 131 bytes

  • -2 bytes thanks to @DLosc
f=(intercalate"|".).foldl h.splitOn"|"
h[x,y:z]'>'=[x++[y],z]
h x@[[],_]_=x
h[x,y]'<'=h[x,last x:y]'#'
h[x,y]'#'=[init x,y]
h x _=x

Try it online!

  1. The string is split at | into two strings
  2. For each move the last character of the first string and / or the first of the second are modified (helper function h)
  3. The two strings are joined with a | in the mid
answered Jul 29, 2024 at 21:48
\$\endgroup\$
0
2
\$\begingroup\$

sed 4.2.2 -E, 65 bytes

The input string and sequence of moves should be separated by a tab. Code contains literal tab characters (which SE has replaced with spaces below, alas).

:
s/(.)?\|(.* )</|1円2円/
s/\|(.)?(.* )>/1円|2円/
s/.?\|(.* )#/|1円/
t

Try it online!

answered Jul 24, 2024 at 16:36
\$\endgroup\$
1
\$\begingroup\$

Charcoal, 50 bytes

×ばつ<⌕⮌θ|+#S≡ι<«¿θ⊞υ⊟θ»>«¿υ⊞θ⊟υ»≔∧θ⊟θηFθι|Wυ⊟υ

Try it online! Link is to verbose version of code. Explanation:

≔⪪S1θ

Input the text string and split it into a list of characters. At this point the program's cursor is still at the end of the string.

×ばつ<⌕⮌θ|+#S≡ι

Find the number of characters after the |, and prefix cursor commands to move the cursor back to the | and delete it to the command string, then loop over the resulting commands, switching over each character.

<«¿θ⊞υ⊟θ»

For a < try to move a character from the text string to the predefined empty list.

>«¿υ⊞θ⊟υ»

For a > try to move a character back to the text string.

≔∧θ⊟θη

Otherwise just try to delete a character from the text string.

Fθι|Wυ⊟υ

Reconstitute the string from the lists.

answered Jul 23, 2024 at 21:52
\$\endgroup\$
1
\$\begingroup\$

Setanta, 193 bytes

gniomh(s,m){s=roinn@s("|")t:=s[1]s=s[0]le i idir(0,fad@m){i=m[i]u:=fad@s ma i=="<"&u{t=s[-1]+t s=cuid@s(0,u-1)}ma i==">"&fad@t{s+=t[0]t=cuid@t(1,fad@t)}ma i=="#" s=cuid@s(0,u-1)}toradh s+"|"+t}

Try on try-setanta.ie

answered Jul 23, 2024 at 22:33
\$\endgroup\$
1
\$\begingroup\$

05AB1E, (削除) 35 (削除ここまで) 34 bytes

Çvā<VÐ'|kDy61.S+‚YÃUy6›iXèë'|æ}RXǝ

Inputs in the order \$moves,string\$.

Try it online or verify all test cases or see all intermediate steps by adding a trailing =.

Explanation:

Ç # Convert the first (implicit) input-moves to a list of codepoint-integers
 v # Pop and loop over each codepoint `y`:
 ā # Push a list in the range [1,length] (without popping)
 # (using the implicit second input-string in the first iteration)
 < # Decrease it to 0-based range [0,length)
 V # Pop and store it in variable `Y`
 Ð # Triplicate the current string
 '|k '# Pop one, and get the (0-based) index of "|"
 D # Duplicate this index
 y61.S # Compare the current codepoint `y` with 61
 # (1 if >61 and -1 if <61: ">" is 1 and "<"/"#" are -1)
 + # Add that to the duplicated index
 ‚ # Pair the two together
 YÃ # Only keep indices that are in range based on `Y`
 U # Pop and store this pair (or singleton) in variable `X`
 y6›i # If `y` is an arrow (codepoint > 36):
 Xè # Get the character(s) of the string at index/indices `X`
 ë # Else:
 '|æ '# Push pair ["","|"] (powerset of "|")
 }R # After the if-else statement: Reverse the pair
 Xǝ # Insert it/them at index/indices `X` back into the string
 # (after the loop, the result is output implicitly)
answered Jul 23, 2024 at 13:05
\$\endgroup\$
1
\$\begingroup\$

jq, 102 bytes

reduce.m[]as$m(.s;{">":sub("\\|(?<c>.)";.c+"|"),"<":sub("(?<c>.)\\|";"|"+.c),"#":sub(".\\|";"|")}[$m])

Try it online!

Input as {"s":"the| current buffer","m":[">","<","#","#"]}. The TIO link splits the motions in the header for testing convenience.

On each motion, compute each possible change as values in a JSON object, then select the correct result.

reduce .m[] as $m ( # Repeat for each motion, where the motion is bound to $m
 .s; # Initial input is the string
 { # Replace current input with...
 ">": sub("\\|(?<c>.)";.c+"|"),
 "<": sub("(?<c>.)\\|";"|"+.c),
 "#": sub(".\\|";"|")
 }[$m] # ...value at key $m
)

If the motions need to be passed as a single string, then +5 bytes by using reduce(.m/"")[]as $m instead.

answered Aug 5, 2024 at 11:42
\$\endgroup\$
1
\$\begingroup\$

tinylisp 2, 153 bytes

(d G(\(A M B)(: R(=(h M)62)(? M(G(? R(,(] 1 B)A)(t A))(t M)(? R(t B)(,(](=(h M)60)A)B)))(,(~ A)(,"|"B
(\(S M)(: I(first-index S 124)(G(~(] I S))M(t([ I S

The first line defines a helper function; the second line is an anonymous function that takes the starting string and the string of moves and returns the result string. Try It Online!

Ungolfed/explanation

The main function splits the string into two halves at the | (ASCII 124) and passes them with the move-string to the helper function, reversing the left half so the business end is in front:

(def apply-moves
 (lambda (string moves)
 (let pipe-index (first-index string 124)
 (_apply-moves
 (reverse (take pipe-index string))
 (tail (drop pipe-index string))
 moves))))

The helper function recurses while there are moves remaining:

  • On a > (ASCII 62), concatenate the first character from the right half (if any) to the left half; otherwise, take the tail of the left half.
  • On a >, take the tail of the right half; otherwise, on a < (ASCII 60), concatenate the first character from the left half (if any) to the right half; otherwise, leave the right half unchanged.
  • Take the tail of the move-string.

When the move-string is empty, reverse the left half and concatenate it to | and the right half.

(def _apply-moves
 (lambda (left right moves)
 (if moves
 (_apply-moves
 (if (= (head moves) 62)
 (concat (take 1 right) left)
 (tail left))
 (if (= (head moves) 62)
 (tail right)
 (if (= (head moves) 60)
 (concat (take 1 left) right)
 right))
 (tail moves))
 (concat
 (reverse left)
 (concat "|" right)))))
answered Aug 19, 2024 at 19:47
\$\endgroup\$
0
\$\begingroup\$

StackControl 1.1, 96 characters

So basicly i unpack string on to stack and move on it directly, a lot of space get taked to prevent cursor go of the edge of the stack

[0 0R 0 0⟧←←←:"|"⊗⍃⇆("|"≠)⊚⍄R⇆(→⇆)⟲(:"<"=(,:#→¿←)(:">"=(,→:#←¿)(:"#"=(,:#,?)?)??)??)∴"|"]→⟦⇆⊍⌦⌦⌫⌫W
answered Aug 5, 2024 at 19:17
\$\endgroup\$
0
\$\begingroup\$

Uiua, 32 bytes

∧(⍜⊙⊜しろいしかく⍚⨬⋅@|⇌±⊙⊸⦷⟜⍥⇌⊙"\W|")⊗⊙"#>"

Try it online!

I am not editing my previous answer because this solution is radically different.

∧(⍜⊙⊜しろいしかく⍚⨬⋅@|⇌±⊙⊸⦷⟜⍥⇌⊙"\W|")⊗⊙"#>"
 
 ⊗⊙"#>" # enumerate the instructions
∧( ) # for each instruction:
 ⊙"\W|" # make a pattern string with `|`
 # and wildcard (like `*` in regex)
 ⟜⍥⇌ # reverse the pattern if instruction is `>`
 ⊙⊸⦷ # find all matches of the pattern
 ⍜⊙⊜しろいしかく⍚ # operate on each match
 ⨬ ± # if instruction is `#`
 ⋅@| # delete preceding character
 ⇌ # else move the cursor
answered Aug 19, 2024 at 19:39
\$\endgroup\$
0
\$\begingroup\$

C (gcc), 134 bytes

j;f(c,i,q)char*c,*i,*q;{for(q=strchr(c,'|');*i;i++)*i%2?q-c?strcpy(q-1,q),q--:7:*(q-=j=q-c|*i%3?1-*i%3:0)?j?*q^=q[j]^=*q^=q[j]:0:q--;}

Try it online! Utilizes UB, but if it works, it worksTM. Reusable function which takes two char* inputs, and outputs by modifying its first parameter. All char* are NULL-terminated. Uses K&R syntax to declare the function, which also declares (but does not accept for the purposes of this challenge) a third char* parameter.

Extra test cases generated using this script, which (arbitrarily) uses Jitse's Python answer as a ground truth.

Experience

I find golfing conditional code somewhat challenging, so I wanted to share some progress for the main conditional deciding whether or not we should move the cursor. I initially had:

j=1-*i%3;q==c&&j>0?j=0:1; // 25 bytes
j=*i%3;j=q==c&&!j?0:1-j; // 24 bytes (flips j's sign, refactored following code)
j=*i%3;j=q-c||j?1-j:0; // 22 bytes
j=*i%3;j=q-c|j?1-j:0; // 21 bytes (we don't need short circuiting)
j=q-c|*i%3?1-*i%3:0; // 20 bytes (cheaper to reuse *i%3)

This last line also allows us to inline *(q-=j) that we had to use when we had multiple statements to *(q-=j=...).

Readable and commented version

j; // temp integer used to represent cursor movement
f(c, i, q)
 char *c, // the text to operate on
 *i, // cursor instructions
 *q; // dummy temp buffer
{
 // q is a pointer to our cursor
 for(q = strchr(c, '|'); *i; i++) {
 // decide between '#' and '<>'
 // '<' = 60 '>' = 62 '#' = 35
 // 60 % 2: 0 62 % 2: 0 35 % 2: 1
 if(*i % 2) {
 // delete
 if(q - c) {
 // only delete if our cursor is not at the start
 strcpy(q - 1, q); //shift characters after cursor left
 q--; // update the cursor's position to reflect shift
 }
 }
 else {
 // move
 // '<' = 60 '>' = 62
 // 60 % 3: 0 62 % 3: 2
 if(q - c | *i % 3) {
 // if we are at the start (q - c), only execute '>' moves
 // if we are not at the start, execute any cursor moves*
 j = 1 - *i % 3;
 }
 else {
 // otherwise, the cursor will not move
 j = 0;
 }
 // move the cursor pointer accordingly
 q -= j;
 if(*q) {
 // *we check to see if the cursor result points at a null byte
 if(j) {
 // only swap if we actually moved, since the XOR trick
 // zeroes *q if *q and q[j] are the same address
 
 // if *q is not null, we execute the cursor movement
 // XOR swap trick thanks to Karl Napf
 // https://codegolf.stackexchange.com/a/111495/31957
 *q ^= q[j] ^= *q ^= q[j];
 // even though j is -1 or 1, we can use q[j] since
 // q[j] <=> *(q + j)
 // thus, q[-1] is the char to the left of the cursor
 }
 }
 else {
 // *q is null, we've moved too far right, and must undo,
 // lest our null-terminated string be ruined
 q--;
 }
 }
 }
}
answered Aug 21, 2024 at 3:46
\$\endgroup\$
2
  • \$\begingroup\$ 130 bytes \$\endgroup\$ Commented Aug 21, 2024 at 6:33
  • \$\begingroup\$ @ceilingcat nice stuff!! I'm not sure why strcpy(q,q--) didn't work for me in testing, I know I tried it - but it seems that the +1 after that is superfluous? That would give 128 assuming I'm not missing anything \$\endgroup\$ Commented Aug 21, 2024 at 6:42

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.