11
\$\begingroup\$

Write a program/function that can help users make simple drawings by moving a cursor contained within a grid of characters, and by selecting which characters should be at a specific position.

Expected Input and Output

The user will be able to work on an area of 80 x 25 characters and will start at position (0, 0). You must then execute commands given as input, each command represented by a specific character. You can assume that the input will only contain a valid set of commands. Your program must be able to execute the following commands:

  • w,a,s,d : Move the cursor up, left, down or right if the cursor will still be within the boundaries, do nothing otherwise. Your program must work correctly even if the user tries to leave the drawing area.
  • 0,1,2,3,4 : Replace the character at the cursor's position by the corresponding character : ░▒▓█.
  • ? : Read the next input character and save it. You can assume that only printable ASCII characters can be inputted (code 32 to 126 inclusive).
  • ! : Read the character at the cursor's position and save it. ! can therefore save printable ASCII characters and ░▒▓█.
  • 5 : Replace the character at the cursor's position by the last character received with the ? or ! command.

This is , so the shortest answer in bytes will win.

Test Cases

It will obviously be hard to display the final output, since it requires 25 lines, so these examples will only display the first five lines. Ignore the / and - symbols, they are just here as delimiters for readability.

Input: 1s1s1d2w2w2d3s3s3d4w4w4d3s3s3d2w2w2d1s1s1
Final Output:

░▒▓█▓▒░ /
░▒▓█▓▒░ /
░▒▓█▓▒░ /
 /
 /
--------------------------------------------------------------------------------/

Input: 2dsaw!s5s5d5d5d5d5d5d5s5s5a5a5a5a5w5w3w5w5a5a3
Final Output:

▓▒▒ /
▒ ▒ /
▒▒▓▒▒▒▒ /
 ▒ ▒ /
 ▒▒▒▒▒ /
--------------------------------------------------------------------------------/

Input: aaaaasd?H5d?e5d?l5d5d?a5d?!5a?o5s4d4aa4a4a4a4
Final Output:

 /
 Hello! /
 ██████ /
 /
 /
--------------------------------------------------------------------------------/
asked Jan 16 at 19:26
\$\endgroup\$
1

5 Answers 5

8
\$\begingroup\$

JavaScript (ES8), 173 bytes

Expects an array of characters and returns a matrix of characters.

a=>a.map(c=>~a?1/c?m[y][x]=" ░▒▓█"[c]||a:c<{}?a=c>"!"?-1:m[y][x]:c>"s"?y-=!!y:c>"d"?y+=y<24:x+=c>"a"?x<79:-!!x:a=c,m=[...1e9+{}].map(_=>[..."".padEnd(80)]),x=y=0)&&m

Try it online!

Commented

a => a.map(c => // for each character c in the input array a[]:
 ~a ? // if a is not -1:
 1 / c ? // if c is a digit:
 m[y][x] = // update m[y][x]:
 " ░▒▓█"[c] // with either a 'grayscale' character
 || a // or a (if c = 5)
 : // else:
 c < {} ? // if c is not a letter:
 a = // update a:
 c > "!" ? // if c = "?":
 -1 // use -1
 : // else (c = "!"):
 m[y][x] // use the character at the current position
 : // else (c is a letter):
 c > "s" ? // if c = "w":
 y -= !!y // decrement y if it's not 0
 : // else:
 c > "d" ? // if c = "s":
 y += y < 24 // increment y if it's less than 24
 : // else:
 x += // update x:
 c > "a" ? // if c = "d":
 x < 79 // increment x if it's less than 79
 : // else (c = "a"):
 -!!x // decrement x if it's not 0
 : // else (a = -1):
 a = c, // copy c in a
 m = [...1e9 + {}] // start with a matrix m[] made of 25 rows ...
 .map(_ => //
 [..."".padEnd(80)] // ... of 80 columns, filled with spaces
 ), //
 x = y = 0 // start with x = 0 and y = 0
) && m // end of map() -> return m[]
answered Jan 16 at 20:43
\$\endgroup\$
8
\$\begingroup\$

Bash on vt510-rm, (削除) 349 292 (削除ここまで) 251 bytes

-57 bytes by cedilla and myself by factorising the echo calls into a function, and replacing ((y>0))&&z A&&((y--)) with ((y>0&&y--))&&z A, as well as removing unneeded newlines.

-41 bytes by cedilla by further factorising the movements

p()(printf 2ドル\33円[11ドル)
m(){((1ドル<2ドル&&3ドル))&&p 4ドル;}
r="read -sn1 k"
b=\ ░▒▓█
s=$b
f()($r&&(case $k in
w)m 0 y y-- A;;s)m y 25 ++y B;;d)m x 80 ++x C;;a)m 0 x x-- D;;[0-5])p D ${s:$k:1};;?)$r;s=$b$k;;!)p ";1;$y;$y;$x;$x*y";$r;s=$b${k:6};;esac
f))
f

Don't try this online, it requires a terminal that supports DECRQCRA. I don't have one, but as far as I can tell, this should work (maybe). DECRQCRA is a vt510-rm escape code that asks the terminal to do a checksum of all the characters in a rectangular area. if you ask it to check an area of 1x1, it returns the sum of 1 character (so that character). It is largely unsupported because that means that applications would be able to read what previous apps launched from the same terminal wrote, and is thus a security risk. For this reasons, command line programs that need to reference what they've previously outputted need to use ncurses or otherwise keep an array of what the ouput looks like, on modern terminals.

only ! requires the exotic terminal, the rest works on normal modern terminals:

test case 1

my shell catches commands that don't end at the beggining of a line (and prints the ugly %), so i've taken the liberty of adding sd to the test case so we could see it in its entirety

! is untested, I can emulate it with ?▒ here: test case 2

test case 3

if you don't pipe in a test case, read will read from stdin, which means you get to paint manually which is actually pretty fun :D enter image description here

answered Jan 17 at 15:48
\$\endgroup\$
6
  • 1
    \$\begingroup\$ Impressive answer, really nice to see it work as a full program. You can at least save 3 bytes by storing 033円[1 as a variable. \$\endgroup\$ Commented Jan 17 at 20:36
  • 1
    \$\begingroup\$ @WeirdGlyphs indeed. i was running out of time to spend on this and decided to call it quits when i noticed that. it is definitely a byte save, but each usage actually costs 3 bytes rather than 2, because you can't use $qA, that would get interpreted as variable "qA" so you'd need $q\A. i think it still saves bytes overall though. My biggest gripe is !, I can't even make sure it works properly (or if i even got the syntax right), because it's so undocumented since nobody uses it nor implements it. \$\endgroup\$ Commented Jan 18 at 0:05
  • \$\begingroup\$ I used ${q} in order to determine the new byte count. It's probably also possible to remove some newlines from your code, since some of them are optional. As for the escape sequences, I don't think that any terminal supports character reading commands nowadays. Tried everything I was able to think of with XTerm, nothing worked. \$\endgroup\$ Commented Jan 18 at 1:32
  • \$\begingroup\$ Your new code fails to execute the provided examples on a regular terminal, partially because the conditions for s and d fail if x or y are equal to 0 (Suffix ++ evaluates to the previous value, not the current one). You can easily fix that by using prefix ++ instead for s and d: s)((y<25&&++y))&&z B;;d)((x<80&&++x))&&z C;;. Please test your code next time before posting it (or add draft somewhere if it is not finished). The last test also fails, because it reads the ! given as input for the ? commands as it it was the command !. \$\endgroup\$ Commented Jan 31 at 18:03
  • \$\begingroup\$ @WeirdGlyphs, with my last edits i fixed it (and someone from IRC golfed it). the last test probably fails because your shell tries interpreting ! when you enter the command, notice the single quotes in my screenshot. \$\endgroup\$ Commented Feb 7 at 13:56
5
\$\begingroup\$

Charcoal, 80 bytes

UO80¦25 ≔⪪ ░▒▓█ 1θFS¿υ§≔θ⊟υι≡ιwM›j0↑aM›i0←sM‹j24↓dM‹i79→?⊞υ5!§≔θ5KKP§θIι

Try it online! Link is to verbose version of code. Note that TIO's deverbosifier inserts an unnecessary "y". Explanation:

UO80¦25 

Create the grid.

≔⪪ ░▒▓█ 1θ

Create the list of drawing characters for the commands 0-5. (Note that non-code page characters are allowed but cost 3 or 4 bytes each; this has been included in the byte count.)

FS

Loop over the input characters.

¿υ§≔θ⊟υι

If the last character was a ? then replace the 5 character with the current character.

≡ι

Otherwise, switch on the current character.

wM›j0↑

If it's a w then move up unless the cursor is in the top row.

aM›i0←

If it's an a then move left unless the cursor is in the left column.

sM‹j24↓

If it's an s then move down unless the cursor is in the bottom row.

dM‹i79→

If it's a d then move right unless the cursor is in the right column.

?⊞υ5

If it's a ? then prepare to save the next character as the output for the 5 command.

!§≔θ5KK

If it's a ! then save the character under the cursor as the output for the 5 command.

P§θIι

Otherwise replace the character under the cursor with the appropriate character for the commands 0-5.

18 bytes to prevent the cursor from exiting the grid may seem a lot, but trying to share code between the directions also takes 18 bytes, of which 9 is used translating from wasd to Charcoal direction codes which could be removed if uldr could be used instead.

answered Jan 17 at 1:21
\$\endgroup\$
4
\$\begingroup\$

Java (OpenJDK 8), (削除) 263 (削除ここまで) (削除) 261 (削除ここまで) (削除) 251 (削除ここまで) 250 bytes

char[]g=new char[2000];Arrays.fill(g,' ');for(int i=0,p=0,c,e=80;i<s.length;)if((c=s[i++])>47&c<54)g[p]=(" ░▒▓█"+s[0]).charAt(c-48);else if(c<e)s[0]=c<34?g[p]:s[i++];else p+=c>100?c>115?p<e?0:-e:p>1975?0:e:c>99?p%e>78?0:1:p%e<1?0:-1;return g;

Try it online!

  • -2 bytes thanks to @Weird Glyphs
  • As suggested by @jdt, 5 bytes can be saved by using Java 11+ and replacing the array initialisation and fill with var g=" ".repeat(2000).toCharArray();,
  • -1 byte thanks to @jdt

Commented

char[] p(char[] s) {
 char[] g = new char[2000]; // output 25*80=2000 characters
 Arrays.fill(g, ' '); // fill with spaces
 for(int p = 0, // array (p)osition
 i = 0, // input (i)ndex
 c, // current (c)haracter
 e = 80; // (e)ighty literal
 i < s.length;) // for each character in input array
 if ((c = s[i++]) > 47 & c < 54) // if c = "0","1","2","3","4" or "5"
 g[p] = (" ░▒▓█" + s[0]) // add stored value to grayscale characters
 .charAt(c - 48); // converto to number and get output
 else if (c < e) // if c = "?" or "!"
 s[0] = // store in input array
 c < 34 ? // if c = "!"
 g[p] // character at current position
 : // else (c = "?")
 s[i++]; // next input character
 else // if c = "w","a","s" or "d"
 p += // update (p)osition with
 c > 100 ? // if c = "w" or "s"
 c > 115 ? // if c = "w" [up]
 p < e ? // if y = 0
 0 // don't move
 : // else (y > 0)
 -e // move up by a full row (-80 chars)
 : // else (c = "s") [down]
 p > 1975 ? // if y = 24 ((25-1)*80=1975)
 0 // don't move
 : // else (y < 24)
 e // move down by a full row (+80 chars)
 : // else (c = "a" or "d")
 c > 99 ? // if c = "d" [right]
 p % e > 78 ? // if x = 79
 0 // don't move
 : // else (x < 79)
 1 // move right
 : // else (c = "a") [left]
 p % e < 1 ? // if x = 0
 0 // don't move
 : // else (x > 0)
 -1; // move left
 return g; // return array
}
answered Jan 17 at 14:34
\$\endgroup\$
6
  • 3
    \$\begingroup\$ Nice program! You can remove the space between char[] and g in order to save one byte. On top of that, your program uses the value 80 five times, and you can replace c<64 by c<80, since there is no valid command between these values except ! and ?. 80 now appears 6 times, so you can save one extra byte by storing the value 80 as a variable. \$\endgroup\$ Commented Jan 17 at 15:29
  • 1
    \$\begingroup\$ var g=" ".repeat(2000).toCharArray(); with Java 11: onecompiler \$\endgroup\$ Commented Jan 20 at 13:59
  • \$\begingroup\$ @jdt I considered this but didn't want to change the Java version after posting the answer, I've added a comment \$\endgroup\$ Commented Jan 20 at 15:04
  • 1
    \$\begingroup\$ save 1 more byte with for(int p=0,i=0,c,e=80;i<s.length;)... \$\endgroup\$ Commented Jan 20 at 18:01
  • 1
    \$\begingroup\$ Suggest 1-p%e/79 instead of p%e>78?0:1 \$\endgroup\$ Commented Feb 10 at 6:35
2
\$\begingroup\$

CASIO BASIC (CASIO fx-9750GIII), (削除) 424 (削除ここまで) 408 bytes

I had to replace the characters with some that are close enough, so sorry about that.

"ABCDEFGHIJKLMNOPQRSTUVWXYZ"→Str 2
{80,25→Dim Mat A
" ×ばつしかく"→Str 9
1→Q~R
For 1→N To StrLen(Str 1
StrMid(Str 1,N,1→Str 8
If Not StrCmp("?",Str 8
Then StrMid(Str 1,N+1,1→Str 7
Isz N
Else If Not StrCmp("!",Str 8
Then Mat A[Q,R→Str 7
Mat A[Q,R>9⟹StrMid(Str 2,Mat A[Q,R]-10,1→Str 7
Else If StrSrc("01234",Str 8)
Then Exp(Str 8)+1
Ans→Mat A[Q,R
Text 1+6R,1+6Q,StrMid(Str 9,Ans,1)
IfEnd
R-Not StrCmp("W",Str 8)+(R<1)+Not StrCmp("S",Str 8)-(R>25→R
Q-Not StrCmp("A",Str 8)+(Q<1)+Not StrCmp("D",Str 8)-(Q>80→Q
If Not StrCmp("5",Str 8
Then Text 1+6R,1+6Q,Str 7
10+StrSrc(Str 2,Str 7)→Mat A[Q,R
IfEnd
IfEnd
IfEnd
Next
answered Feb 4 at 18:33
\$\endgroup\$

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.