ASCII Ball in Box Animation

11

1

Write a program that displays a moving ASCII ball * inside a rectangle (20 by 10 chars including border). The ball must not cross the rectangle, e.g. bump off when it hits the wall. The scene must be cleared and redrawn every 0.1 seconds and the ball must move 1 char in x and y direction every frame. The shortest program written in any language wins.

Example output (frame 1)

+------------------+
|*                 |
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
+------------------+

Example output (frame 2)

+------------------+
|                  |
| *                |
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
+------------------+

Example output (frame 8)

+------------------+
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
|       *          |
+------------------+

Example output (frame 9)

+------------------+
|                  |
|                  |
|                  |
|                  |
|                  |
|                  |
|        *         |
|                  |
+------------------+

Patrick Oscity

Posted 2012-04-26T17:43:55.043

Reputation: 765

4Does printing 99 newlines qualify as clearing the screen? – Ventero – 2012-04-27T13:52:19.670

Not on my monitor with 1080x1920 resolution :) – mellamokb – 2012-04-27T14:10:37.857

related: https://codegolf.stackexchange.com/q/110410/55735

– Titus – 2018-11-29T14:13:50.487

Answers

7

Ruby 1.9, 115 characters

The movement logic is quite similar to Danko's answer.

This version has been tested on Linux only.

p=0
loop{u=(?|+?\s*18+"|
")*8
u[165-21*(7-p%14).abs-(17-p%34).abs]=?*
p+=1
puts"\e[2J",r=?++?-*18+?+,u,r
sleep 0.1}

Ventero

Posted 2012-04-26T17:43:55.043

Reputation: 9 842

With Ruby 1.9.3 under Windows 7, puts"\e[2J" just prints ←[2J (and a newline) on the screen. – r.e.s. – 2012-04-27T01:55:30.377

@r.e.s. See my edit for a version which should run on Windows (can't test it myself unfortunately). – Ventero – 2012-04-27T02:06:37.790

Using cls doesn't work for me, but system ("cls") does. – r.e.s. – 2012-04-27T04:14:55.087

@r.e.s. use `cls`. Literal backticks. – Mark Reed – 2012-04-27T04:34:59.280

@MarkReed - That's what I tried to write .. Anyway, it doesn't work. Windows seems to require system("cls"). – r.e.s. – 2012-04-27T05:43:02.590

damn this one's good! – Patrick Oscity – 2012-04-27T09:02:29.517

you can actually go down to 118 if you put the first two lines inside the loop. this way you don't have to remove the ball from the box any more. you can also save one character by omitting the newline after loop{. – Patrick Oscity – 2012-04-27T13:36:12.393

@padde Good ideas, thanks! – Ventero – 2012-04-27T13:48:03.217

that's 118 in total, not 117 sorry for the confusion – Patrick Oscity – 2012-04-27T14:00:44.833

@padde I'm counting 117 characters. Make sure your editor doesn't add a trailing newline. – Ventero – 2012-04-27T14:02:56.973

i used vim + wc, still get 118, double checked. I guess it doesn't matter (at least not now), but hey its codegolf! – Patrick Oscity – 2012-04-27T14:28:12.663

@padde Vim does indeed add a trailing newline. You can verify this by looking at the file with hd. – Ventero – 2012-04-27T14:31:39.203

@Ventero You are right, sorry for that! – Patrick Oscity – 2012-04-27T17:31:40.430

4

Powershell, 144 characters

Based on Joey's excellent answer, using the fact that the ball coordinates are a function of the frame index (i), so if you have something like x=n-abs(n-(i mod (2*n))), x will go from 0 to n, back to 0, and so on...

for(){cls
($l="+$('-'*18)+")
7..0|%{$j=$_
"|$(-join(17..0|%{'* '[$j-[Math]::abs(7-$i%14)-or$_-[Math]::abs(17-$i%34)]}))|"}
$l;$i++;sleep -m 100}

Danko Durbić

Posted 2012-04-26T17:43:55.043

Reputation: 10 241

Nice one. Although I was kinda proud of my if(-1,18-eq$x){$a*=-1;$x+=2*$a}if(-1,8-eq$y){$b*=-1;$y+=2*$b} which replaced four ifs earlier ;-). I was sure there had to be a formula, though. – Joey – 2012-04-27T05:14:00.160

3

Ruby (179 174 147)

EDIT got rid of some more chars:

l=?++?-*18+?++?\n
c=?|+?\s*18+?|+?\n
p=22
u=v=1
loop{f=l+c*8+l
f[p]=?*
puts"\e[2J"+f
p+=(u=f[p+u]==' '?u:-u)+21*(v=f[p+21*v]==' '?v:-v)
sleep 0.1}

EDIT shaved off some chars, now 174:

l="+#{'-'*18}+\n";f=l+"|#{' '*18}|\n"*8+l;x=y=u=v=1
loop{f[21*y+x]='*';$><<"\e[2J\e[f"+f;f[21*y+x]=' '
u=f[21*y+x+u]==' '?u:-u;v=f[21*(y+v)+x]==' '?v:-v
x+=u;y+=v;sleep 0.1}

Ungolfed:

l="+#{'-'*18}+\n"           # top and bottom lines 
f=l+"|#{' '*18}|\n"*8+l     # complete box as string
x=y=u=v=1                   # x,y position; u,v next move
loop {                      #
  f[21*y+x]='*'             # add ball to box
  $> << "\e[2J\e[f"+f       # clear screen and print box with ball
  f[21*y+x]=' '             # remove ball from box
  u=f[21*y+x+u]==' '?u:-u   # next move in x direction
  v=f[21*(y+v)+x]==' '?v:-v # next move in y direction
  x+=u                      # perform move
  y+=v                      # --"--
  sleep 0.1                 # .zZZ...
}                           #

Patrick Oscity

Posted 2012-04-26T17:43:55.043

Reputation: 765

Shouldn't sleep .1 work too? – Joey – 2012-04-26T20:44:52.547

Nope, not in 1.9. SyntaxError: (irb):1: no .<digit> floating literal anymore; put 0 before dot. But i'll get back to that if i need it in the future, thanks! – Patrick Oscity – 2012-04-26T20:47:35.377

If you're on 1.9 you can use char literals to shorten a few things, e.g. ?* instead of '*', etc. – Joey – 2012-04-26T21:08:23.813

Using Ruby 1.9.3 under Win7, this prints rectangles one after (below) the other, each having ←[2J←[f+------------------+ as the first line. – r.e.s. – 2012-04-27T01:35:02.057

then change $> << "\e[2J\e[f"+f to 'cls';$><<f (use backticks for cls) – Patrick Oscity – 2012-04-27T08:16:36.640

3

Python 2, 234

I'm sure this can be golfed more, but I gotta go so here's what I have sofar. will work more on it later

import os,time
a,b,c,d,e,n='+- |*\n'
w=d+c*18+d+n
s=a+b*18+a+n
x,y=0,0
g,h=17,7
j,k=1,1
while 1:
 if 0>x or x>g:j*=-1;x+=j
 if 0>y or y>h:k*=-1;y+=k
 os.system('cls');print s+w*y+d+c*x+e+c*(g-x)+d+n+w*(h-y)+s;x+=j;y+=k;time.sleep(0.1)

note: works on Windows command console. Other operating systems may use a different command than cls to clear the screen, such as clear

Blazer

Posted 2012-04-26T17:43:55.043

Reputation: 1 902

does print "\e[H\e[2J" work on windows? – Patrick Oscity – 2012-04-26T21:00:45.670

@padde - It doesn't seem to work when I run your Ruby program under Windows 7 (see my comment to your post). – r.e.s. – 2012-04-27T01:41:21.020

2

QBasic (QB64), 178 173 bytes

a$="+------------------+
?a$
for c=1to 8
?"|                  |
next
?a$
do
x=19-abs(17-(i mod 34))
y=9-abs(7-(i mod 14))
locate y,x
?"*
_delay .1
locate y,x
?" "
i=i+1
loop

-5 bytes thanks to DLosc

wastl

Posted 2012-04-26T17:43:55.043

Reputation: 3 089

Nice! For the infinite loop, you can use DO ... LOOP in place of WHILE 1 ... WEND and save 5 bytes. – DLosc – 2018-06-25T01:11:57.017

2

JavaScript (275 283)

s=Array(19).join(' ');n='\n';z=Array(9).join('|'+s+'|'+n).split(n,8);
x=y=0;a=b=1;t='+'+s.replace(/ /g,'-')+'+';
setInterval(function(){u=z[y];z[y]=u.replace(eval('/( {'+x+'}) /'),'$1*');
$('#o').text(t+n+z.join('\n')+n+t);z[y]=u;x+=a;y+=b;if(!x|x==17)a=-a;if(!y|y==7)b=-b},100)

Demo: http://jsfiddle.net/eKcfu/2/

I wrote this up pretty quickly so I'm sure there's still quite a bit of room for improvement. Suggestions are welcome :)

Edit 1: Remove unnecessary separate function call, embed directly in setInterval.

mellamokb

Posted 2012-04-26T17:43:55.043

Reputation: 5 544

2

PowerShell, 184 185 215

Only semi-golfed as my brain isn't working properly when I'm sick ...

A few nice tricks in it, though.

for($a=$b=1){cls
($l="+$('-'*18)+")
0..7|%{$j=$_
"|$(-join(0..17|%{'* '[$j-$y-or$_-$x]}))|"}
$l
$x+=$a;$y+=$b
if(-1,18-eq$x){$a*=-1;$x+=2*$a}if(-1,8-eq$y){$b*=-1;$y+=2*$b}sleep -m 100}

[Edit]: Looping over the field is much shorter.

Joey

Posted 2012-04-26T17:43:55.043

Reputation: 12 260

2

Haskell, 212 characters

import System
main=mapM_ f$s 19`zip`s 9
s n=[2..n]++[n-1,n-2..3]++s n
f p=r"clear">>putStr(unlines[[" |-+*"!!(19#i+2*(9#j)+4*e((i,j)==p))|i<-[1..20]]|j<-[1..10]])>>r"sleep 0.1"
b#n=e$n<2||n>b
e=fromEnum
r=system

Uses a more functional approach for calculating the coordinates, by making the infinite sequence for each coordinate separately and then zipping them together (lines 2 and 3). The rest is drawing code.

hammar

Posted 2012-04-26T17:43:55.043

Reputation: 4 011

1

Powershell, 139 bytes

Inspired by Danko Durbić's answer.

for(){cls
,"+$('-'*18)+
"*2-join("|$(' '*18)|
"*8-replace"^(\W{$(21*[Math]::abs(7-$i%14)+[Math]::abs(17-$i++%34))}.) ",'$1*')
sleep -m 100}

This script uses the -replace operator to draw * inside the rectangle.

Less golfed script to explain how it works:

for(){
    cls
    $y=[Math]::abs(7-$i%14)
    $x=[Math]::abs(17-$i++%34)
    $b="+$('-'*18)+`n"
    $m="|$(' '*18)|`n"*8-replace"^(\W{$(21*$y+$x)}.) ",'$1*'
    ,$b*2-join($m)          # draw it
    sleep -m 100
}

mazzy

Posted 2012-04-26T17:43:55.043

Reputation: 4 832

1

Perl 5, 141 characters

print"\e[H\e[2J",$h="+"."-"x18 ."+
",(map{"|".$"x$q,(abs$t%14-7)-$_?$":"*",$"x(17-$q),"|
"}0..7),$h,select'','','',0.1while$q=abs$t++%34-17,1

Does not start on the upper left corner as the example output does, but that is not stated as a requirement.

Kevin Reid

Posted 2012-04-26T17:43:55.043

Reputation: 1 693

1

Ruby 1.9, 162 characters

35 chars shy of @Ventero's answer, but I was impressed that I could get it down this far while still using a relatively straightforward approach to the actual logic. The ^[ is a literal ESC (1 char).

x=y=0
v=h=1
s=' '
loop{puts"^[[2J"+b="+#{?-*18}+",*(0..7).map{|i|"|#{i!=y ?s*18:s*x+?*+s*(17-x)}|"},b
y+=v
y+=v*=-1if y<0||y>7
x+=h
x+=h*=-1if x<0||x>17
sleep 0.1}

Mark Reed

Posted 2012-04-26T17:43:55.043

Reputation: 667

1

R, 233 characters

s=c("\n+",rep("-",18),"+");for (j in 1:8){cat(s,sep="");cat(rep(c("\n|",rep("",17),"|"),j-1));cat(c("\n|",rep(" ",j-1),"*",rep(" ",18-j),"|"),sep="");cat(rep(c("\n|",rep("",17),"|"),8-j));cat(s,sep="");Sys.sleep(0.1);system("clear")}

Paolo

Posted 2012-04-26T17:43:55.043

Reputation: 696

1

Another bash entry - 213 204 chars

Not really a prize winner, but it was fun nonetheless. It uses vt100 char sequences for the drawing. (the code reported here uses 215 chars for readability, 2 chars can be removed by escaping, e.g. '*' -> \*

e(){ printf "\e[$1";}
e 2J;e H
h='+------------------+'
echo $h
for((;++j<9;));do printf '|%18s|\n';done
echo $h
e '3;2H*'
while :;do
e 'D '
((i/17%2))&&e D||e C
((++i/7%2))&&e A||e B
e 'D*'
sleep .1
done

Dan Andreatta

Posted 2012-04-26T17:43:55.043

Reputation: 211

0

PHP, 196 186 148 bytes

I removed avoiding an integer overflow to save 6 bytes. It will run for 29 billion years before overflow; still 6.8 years on a 32 bit system. I´d say that´s acceptable.

Calculating the position instead of adjusting it saved a lot, preparing the complete template at once another lot.

for(;++$i;$s[-3-21*abs($i%14-7)-abs($i%34-17)]="*",print$f(_,9e5,"
").$s.$z,usleep(1e5))$s=($f=str_pad)($z=$f("+",19,"-")."+
",189,$f("|",19)."|
");
").$z;

Run with -nr. Requires PHP 7.1.

breakdown:

for(;++$i;      # infinite loop:
                    # 2. set asterisk at appropriate position
    $s[-3-21*abs($i%14-7)-abs($i%34-17)]="*";
                    # 3. clear screen: print 900k newlines
    print$f(_,9e5*0+2,"\n")
            .$s.$z, # 4. print field
    usleep(1e5))    # 5. sleep 100000 microseconds
                    # 1. create template
    $s=($f=str_pad)($z=$f("+",19,"-")."+\n",189,$f("|",19)."|\n");

Titus

Posted 2012-04-26T17:43:55.043

Reputation: 13 814

0

TI Basic, 169 167 bytes

1→X
1→Y
1→S
1→T
While 1
ClrHome
Output(Y,X,"*
S(X>1 and X<20)+(X=1)-(X=20→S
T(Y>1 and Y<10)+(Y=1)-(Y=10→T
X+S→X
Y+T→Y
For(N,1,20,19
For(O,1,10,9
Output(O,N,"+
End
End
For(N,2,19
For(O,1,10,9
Output(O,N,"-
End
End
For(O,2,9
Output(O,1,"|
Output(O,20,"|
End
End

Horribly slow, but it works.

phase

Posted 2012-04-26T17:43:55.043

Reputation: 2 540

(X=20)→S can be (X=20→S, and you can take advantage of Ans to save a few bytes in the final For( loop. I'm positive lines 8 and 9 can be golfed, but I'm not sure how at the moment. – M. I. Wright – 2015-06-24T22:56:30.780

@M.I.Wright Where would I put the Ans? – phase – 2015-06-24T22:59:26.820

"| \ Output(O,1,Ans \ Output(O,20,Ans should work. – M. I. Wright – 2015-06-24T23:00:58.687

@M.I.Wright But isn't that replacing a two character string with a three character string? And adding the two character string above it, making it +4? – phase – 2015-06-24T23:02:33.963

What do you mean? Ans is a one-byte token, typed with 2nd (-). That'll end up saving one byte, since the | token is two bytes on the calculator.

– M. I. Wright – 2015-06-24T23:04:12.177

@M.I.Wright Is there an online byte counter anywhere? I was just going off of character count. – phase – 2015-06-24T23:08:39.553

Let us continue this discussion in chat.

– M. I. Wright – 2015-06-24T23:16:43.827

You can golf this using http://codereview.stackexchange.com/questions/82847/ti-basic-bouncing-ball-animation

– lirtosiast – 2015-07-01T18:28:06.207

dim(identity(1->A While 1 Ans->B Ans+∟A->A ClrHome ∟Bcos(πʳ(Ans=1 or Ans>{19,9 For(N,1,20 For(O,1,10 Output(O,N,sub("* |-+",2+(N=1 or N=20)+2not(fPart(log(O))-min(Ans={O,N End End End

should be significantly shorter. Sorry about the formatting. – lirtosiast – 2015-07-01T19:02:11.503

0

Bash 278 300, 296

h="+------------------+"
w="|                  |"
g(){
echo -e "\e[$2;$1H$3"
}
g 1 1 $h
for i in {1..8}
do
echo "$w"
done
echo $h
x=4
y=4
p=1
q=1
for((;;))
do
((x==19&&p==1||x==2&&p==-1))&&p=-$p
((y==9&&q==1||y==2&&q==-1))&&q=-$q
x=$((x+p))
y=$((y+q))
g $x $y \*
sleep .1
g $x $y " "
done

The \e in the line echo -e "\e[$2;$1H$3" can be produced by

echo -e "\x1b"

to replace it. As binary 0x1b it is 3 chars shorter; I count just 1 for "\e", because only the layouting software forces me to use \e.

user unknown

Posted 2012-04-26T17:43:55.043

Reputation: 4 210

An anonymous user suggested edits to remove the $ symbols inside ((...)) and replace x=$(($x+$p)) with ((x+=p)) and similarly on the following line. (They also suggested using \e for the escape character). – Peter Taylor – 2012-04-27T11:20:12.390

I would suggest in addition that \* might work as a replacement for "*". – Peter Taylor – 2012-04-27T11:28:56.327

@PeterTaylor: Thanks to the anonymous user. I incorporated suggestion no 1, and use no 2 in modified form, and yours. – user unknown – 2012-04-27T11:34:57.423

@userunknown: You're rep of 1,337 needs to be permanently locked in place :P – mellamokb – 2012-04-27T19:08:39.263

@mellamokb: I would prefer 0xCAFE, #5CA7A or -32768. – user unknown – 2012-04-27T19:41:33.503

1@mellamokb: It's gone. – user unknown – 2012-04-28T23:49:17.147