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 code-golf, 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! /
██████ /
/
/
--------------------------------------------------------------------------------/
-
1\$\begingroup\$ related: codegolf.stackexchange.com/questions/275906/… \$\endgroup\$Themoonisacheese– Themoonisacheese2025年01月17日 14:21:47 +00:00Commented Jan 17 at 14:21
5 Answers 5
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
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[]
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:
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
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
-
1\$\begingroup\$ Impressive answer, really nice to see it work as a full program. You can at least save 3 bytes by storing
033円[1as a variable. \$\endgroup\$Weird Glyphs– Weird Glyphs2025年01月17日 20:36:32 +00:00Commented 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\$Themoonisacheese– Themoonisacheese2025年01月18日 00:05:00 +00:00Commented 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\$Weird Glyphs– Weird Glyphs2025年01月18日 01:32:02 +00:00Commented Jan 18 at 1:32 -
\$\begingroup\$ Your new code fails to execute the provided examples on a regular terminal, partially because the conditions for
sanddfail 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\$Weird Glyphs– Weird Glyphs2025年01月31日 18:03:39 +00:00Commented 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\$Themoonisacheese– Themoonisacheese2025年02月07日 13:56:15 +00:00Commented Feb 7 at 13:56
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.
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;
- -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
}
-
3\$\begingroup\$ Nice program! You can remove the space between
char[]andgin order to save one byte. On top of that, your program uses the value 80 five times, and you can replacec<64byc<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\$Weird Glyphs– Weird Glyphs2025年01月17日 15:29:18 +00:00Commented Jan 17 at 15:29 -
1\$\begingroup\$
var g=" ".repeat(2000).toCharArray();with Java 11: onecompiler \$\endgroup\$jdt– jdt2025年01月20日 13:59:59 +00:00Commented 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\$mastaH– mastaH2025年01月20日 15:04:04 +00:00Commented 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\$jdt– jdt2025年01月20日 18:01:43 +00:00Commented Jan 20 at 18:01 -
1\$\begingroup\$ Suggest
1-p%e/79instead ofp%e>78?0:1\$\endgroup\$ceilingcat– ceilingcat2025年02月10日 06:35:55 +00:00Commented Feb 10 at 6:35
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