24
\$\begingroup\$

Inspired by this listing from the Commodore 64 User's Guide:

10 PRINT "{CLR/HOME}"
20 POKE 53280,7 : POKE 53281,13
30 X = 1 : Y = 1
40 DX = 1 : DY = 1
50 POKE 1024 + X + 40 * Y, 81
60 FOR T = 1 TO 10 : NEXT
70 POKE 1024 + X + 40 * Y, 32
80 X = X + DX
90 IF X <= 0 OR X >= 39 THEN DX = -DX
100 Y = Y + DY
110 IF Y <= 0 OR Y >= 24 THEN DY = -DY
120 GOTO 50

Make a similar program in your chosen language/platform to bounce a ball-alike object around your terminal, screen, canvas or other visual display area.

You don't have to mimic the C64's PETSCII graphics exactly, a simple O or o will do, nor do you have to use the GOTO command if it exists in your language still. As long as your ball starts at the top of your canvas and travels diagonally until it hits a canvas limit, and then bounces accordingly, as follows:

  • Travelling downwards and right and hits the bottom of the screen area, bounces up and continues right;
  • Travelling up and right and hits the right-most boundary, and bounces left and up;
  • Travelling left and up and hits the top, bounces left and down;
  • Travelling left and down and reaches the left-most boundary, bounces right and down;
  • Hits any corner and reverses direction;

Then we're all good.

You don't have to move the ball 8-pixels at a time either, like is happening in the BASIC listing on the C64; you can move one character block or one pixel at a time, whichever you think is most appropriate.

To see this BASIC listing working, you can type it in with this online Commodore 64 emulator providing your browser supports JavaScript.

\$\endgroup\$
25
  • 2
    \$\begingroup\$ JavaScript canvas. D'uh. \$\endgroup\$ Feb 17, 2017 at 14:05
  • \$\begingroup\$ I'm not sure what you call a screen nowadays. You used to have just the screen and border area viewed through your television set or VDU... and now you've got terminals, windows, canvases, stdout etc... it's all very confusing to me. \$\endgroup\$ Feb 17, 2017 at 14:09
  • \$\begingroup\$ It'd be better if we had a constant pixel size value. \$\endgroup\$ Feb 17, 2017 at 14:11
  • 4
    \$\begingroup\$ Can we assume the screen size of 1x1 and print o forever? \$\endgroup\$ Feb 18, 2017 at 5:27
  • 1
    \$\begingroup\$ possible duplicate of ASCII Ball in Box Animation \$\endgroup\$
    – Titus
    Mar 2, 2017 at 14:07

27 Answers 27

17
\$\begingroup\$

Bash + Unix utilities, 125 117 bytes

for((x=y=u=v=1;;x+=u,y+=v,u=(x<1||x>=`tput cols`-1)?-u:u,v=(y<1||y>=`tput lines`-1)?-v:v)){
tput cup $y $x
sleep .1
}

Animation of sample run:

Animation of sample run

\$\endgroup\$
1
  • 6
    \$\begingroup\$ It hit the exact corner! :O \$\endgroup\$
    – mbomb007
    Feb 21, 2017 at 16:12
13
\$\begingroup\$

CP-1610 assembly, 6764 62 DECLEs = 78 bytes

This code is intended to be run on an Intellivision. It's using one of its hardware sprites, known as a MOB (for Mobile Object).

A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This program is 62 DECLEs long, starting at $4800 and ending at $483D.

Hexadecimal dump + source

                            ROMW  10            ; use 10-bit ROM
                            ORG   $4800         ; start program at address $4800

                    FRAME   EQU   $17E          ; frame #

                            ;; ------------------------------------------------ ;;
                            ;;  main entry point                                ;;
                            ;; ------------------------------------------------ ;;
                    main    PROC

4800 0001                   SDBD                ; load Interrupt Service Routine
4801 02B8 002B 0048         MVII  #isr,   R0    ; into R0

4804 0240 0100              MVO   R0,     $100  ; update ISR
4806 0040                   SWAP  R0
4807 0240 0101              MVO   R0,     $101

4809 02B9 0208              MVII  #$0208, R1    ; initialize R1 = X
480B 02BA 0108              MVII  #$0108, R2    ; initialize R2 = Y
480D 02BB 0001              MVII  #1,     R3    ; initialize R3 = DX
480F 009C                   MOVR  R3,     R4    ; initialize R4 = DY

4810 0002                   EIS                 ; enable interrupts

                            ;; ------------------------------------------------ ;;
                            ;;  main loop                                       ;;
                            ;; ------------------------------------------------ ;;
4811 0280 017E      @@loop  MVI   FRAME,  R0    ; R0 = current frame #

4813 0340 017E      @@spin  CMP   FRAME,  R0    ; wait for next frame
4815 0224 0003              BEQ   @@spin

4817 00D9                   ADDR  R3,     R1    ; X += DX

4818 0379 02A0              CMPI  #$2A0,  R1    ; reached right border?
481A 0204 0003              BEQ   @@updDx

481C 0379 0208              CMPI  #$208,  R1    ; reached left border?
481E 002F                   ADCR  PC

481F 0023           @@updDx NEGR  R3            ; DX = -DX

4820 00E2                   ADDR  R4,     R2    ; Y += DY

4821 037A 0160              CMPI  #$160,  R2    ; reached bottom border?
4823 0204 0003              BEQ   @@updDy

4825 037A 0108              CMPI  #$108,  R2    ; reached top border?
4827 002F                   ADCR  PC

4828 0024           @@updDy NEGR  R4            ; DY = -DY

4829 0220 0019              B     @@loop        ; loop forever

                            ENDP

                            ;; ------------------------------------------------ ;;
                            ;;  ISR                                             ;;
                            ;; ------------------------------------------------ ;;
                    isr     PROC

482B 01DB                   CLRR  R3            ; clear a bunch of STIC registers
482C 02BC 0020              MVII  #$20,   R4

482E 0263           @@clear MVO@  R3,     R4    ; (including background color,
482F 037C 0032              CMPI  #$32,   R4    ; border color, etc.)
4831 0226 0004              BLE   @@clear

4833 0259                   MVO@  R1,     R3    ; update X register of MOB #0
4834 0242 0008              MVO   R2,     $8    ; update Y register of MOB #0
4836 02BB 017E              MVII  #$017E, R3    ; update A register of MOB #0
4838 0243 0010              MVO   R3,     $10   ; (using a yellow "O")

483A 0298                   MVI@  R3,     R0    ; increment frame #
483B 0008                   INCR  R0
483C 0258                   MVO@  R0,     R3

483D 00AF                   JR    R5            ; return from ISR

                            ENDP

Output

output

\$\endgroup\$
11
\$\begingroup\$

TI-BASIC, 71 70

1->A
1->B
1->C
1->D
While 1
ClrHome
Output(B,A,0
A+C->A
B+D->B
If A<2 or A>15
~C->C
If B<2 or B>7
~D->D
End

Quite literal translation, I wouldn't be surprised if there are tricks to make it smaller.

The screen is 16x8 and 1-indexed so the constants are different.

~ is the SourceCoder way to write the negation symbol.

gif of bouncing O

It looks smoother on hardware.

\$\endgroup\$
3
  • \$\begingroup\$ Are you sure this is 70 bytes? It looks like less than that. \$\endgroup\$
    – 12Me21
    Oct 6, 2017 at 20:35
  • \$\begingroup\$ @12Me21 how many bytes do you count? I get 80 bytes if I save this on a calculator and 10 bytes for an empty program which agrees with my count. \$\endgroup\$
    – harold
    Oct 6, 2017 at 20:47
  • \$\begingroup\$ Oh, I guess I counted wrong then. \$\endgroup\$
    – 12Me21
    Oct 6, 2017 at 20:50
10
\$\begingroup\$

HTML (Microsoft Edge/Internet Explorer), 81 bytes

Pretend it's 1998 with these nested <marquee> tags:

<marquee behavior=alternate direction=down><marquee behavior=alternate width=99>O

Tested in Microsoft Edge, though from what I've read IE should also still support marquees. Decidedly does not work in Chrome.

Setting direction=up would save 2 bytes, but break the rule that the ball has to start at the top of the canvas.

\$\endgroup\$
9
  • \$\begingroup\$ Unfortunately, this is an invalid answer as the ball does not travel diagonally, as required by the challenge. \$\endgroup\$ Feb 20, 2017 at 1:47
  • \$\begingroup\$ Did you try it in Microsoft Edge? Chrome doesn't seem to support the direction attribute. \$\endgroup\$ Feb 20, 2017 at 2:42
  • \$\begingroup\$ Huh, my apologies - it does work in Edge. I can confirm that it does not work in Chrome, and I can attest that it does work in Firefox and Internet Explorer. Three out of four isn't bad (and you only need one for this answer to be valid). +1 \$\endgroup\$ Feb 20, 2017 at 2:49
  • 1
    \$\begingroup\$ +1 for marquee, that's pretty creative! \$\endgroup\$
    – Metoniem
    Feb 20, 2017 at 9:27
  • \$\begingroup\$ Worked in Chrome for me. \$\endgroup\$
    – ckjbgames
    Feb 20, 2017 at 15:03
7
\$\begingroup\$

Befunge, 209 bytes

>10120130pppp>"l52?[J2["39*,,,,39*,,,,,,v
v+56/+55\%+55:+1g01*83-"6!"7\-"6!?"8-*86<
>00g1+:55+%\55+/"+!6"-48*,68>*#8+#6:#,_v$
v:+g03g01p02+-\g02*2!-*64\*2!:p00:+g02g<$
>10p:!2*\"O"-!2*30g\-+30p"2"::**>:#->#1_^

This assumes a screen size of 80x25, but you can easily tweak the range by replacing the "O" (79) on the last line and the *64 (24) on the second last line (note that the second last line is executed right to left). The speed can also be adjusted by replacing the "2" (50) on the last line.

\$\endgroup\$
7
\$\begingroup\$

Java, 184 176 bytes

class A{public static void main(String[]a)throws Exception{for(int X=1,Y=1,x=1,y=1;;System.out.print("\033["+X+";"+Y+"H"),Thread.sleep(50),X+=x=X%25<1?-x:x,Y+=y=Y%85<1?-y:y);}}

This makes use of ANSI Escape Sequences to relocate the cursor, which is the object that bounces around a 85 x 25 terminal display. Save in a file named A.java.

Ungolfed

class Terminal_Bouncing_Ball {
    public static void main(String[] args) throws InterruptedException {
        int X = 0, Y = 0, dx = 1, dy = 1;
        while (true) {
            System.out.print(String.format("\033[%d;%dH",X,Y));
            Thread.sleep(50);
            dx = (X < 1) ? 1 : (X > 71) ? -1 : dx;
            dy = (Y < 1) ? 1 : (Y > 237) ? -1 : dy;
            X += dx;
            Y += dy;
        }
    }
}

Demo

Example

\$\endgroup\$
1
  • \$\begingroup\$ This is code golf, so you'll want to remove Thread.sleep(50). And your golfed and ungolfed programs don't match. \$\endgroup\$
    – Jakob
    Sep 4, 2017 at 15:33
4
\$\begingroup\$

6502 machine code (C64), 90 89 91 bytes

+2 bytes because it needs a load address (not PIC because of self modification)

00 C0 20 44 E5 CA D0 FD C6 FC D0 F9 A2 20 86 FC A9 D8 85 9E A9 03 85 9F A4 CA
18 A5 9E 69 28 85 9E 90 02 E6 9F 88 10 F2 A4 C9 8A 91 9E C9 20 D0 08 E6 C9 E6
CA A2 51 10 D7 A5 C9 F0 04 C9 27 D0 08 AD 2F C0 49 20 8D 2F C0 A5 CA F0 04 C9
18 D0 B4 AD 31 C0 49 20 8D 31 C0 D0 AA

Online demo

Usage: sys49152

I tried hard to reduce size (e.g. NOT using IRQs for timing but stupid empty loops instead), still impossible to reach the level of Titus' golfed C64 BASIC :o oh, well. But it looks less flickering ;)

Explanation: (vice disassembly)

00 C0       .WORD $C000         ; load address
20 44 E5    JSR $E544           ; clear screen
CA          DEX
D0 FD       BNE $C003           ; inner wait (256 decrements)
C6 FC       DEC $FC
D0 F9       BNE $C003           ; outer wait (32 decrements in zeropage)
A2 20       LDX #$20            ; wait counter and screen code for "space"
86 FC       STX $FC             ; store wait counter
A9 D8       LDA #$D8            ; load screen base address ...
85 9E       STA $9E             ; ... -40 (quasi row "-1") ...
A9 03       LDA #$03            ; ... into vector at $9e/$9f
85 9F       STA $9F
A4 CA       LDY $CA             ; load current row in Y
18          CLC                 ; clear carry flag
A5 9E       LDA $9E             ; add ...
69 28       ADC #$28            ; ... $28 (40 cols) to ...
85 9E       STA $9E             ; ... vector
90 02       BCC $C023
E6 9F       INC $9F             ; handle carry
88          DEY                 ; count rows down
10 F2       BPL $C018
A4 C9       LDY $C9             ; load current col in Y
8A          TXA                 ; copy screen code from X to A
91 9E       STA ($9E),Y         ; store at position of screen
C9 20       CMP #$20            ; screen code was "space"
D0 08       BNE $C037           ; if not, ball was drawn
E6 C9       INC $C9             ; next column   | opcodes are modified
E6 CA       INC $CA             ; next row      | here for directions
A2 51       LDX #$51            ; screen code for "ball"
10 D7       BPL $C00E           ; and back to drawing code
A5 C9       LDA $C9             ; load current column
F0 04       BEQ $C03F           ; if zero, change X direction
C9 27       CMP #$27            ; compare with last column (39)
D0 08       BNE $C047           ; if not equal, don't change X direction
AD 2F C0    LDA $C02F           ; load opcode for X direction
49 20       EOR #$20            ; toggle between ZP INC and DEC
8D 2F C0    STA $C02F           ; store back
A5 CA       LDA $CA             ; load current row
F0 04       BEQ $C04F           ; if zero, change Y direction
C9 18       CMP #$18            ; compare with last row (24)
D0 B4       BNE $C003           ; if not equal, don't change Y direction
AD 31 C0    LDA $C031           ; load opcode for Y direction
49 20       EOR #$20            ; toggle between ZP INC and DEC
8D 31 C0    STA $C031           ; store back
D0 AA       BNE $C003           ; -> main loop

Just for fun, here's a more professional variant using a sprite for the ball and flashing the border when hit in 385 bytes (containing the sprite data that's used in place):

00 C0 AD 15 D0 F0 30 A9 CC 85 FC A9 04 20 A2 C0 A9 97 8D 00 DD A9 15 8D 18 D0 
A9 00 8D 15 D0 8D 1A D0 A2 81 8E 0D DC A2 31 8E 14 03 A2 EA 8E 15 03 58 A6 D6 
4C F0 E9 A9 04 85 FC A9 CC 20 A2 C0 A2 31 86 01 A2 10 A9 D0 85 FC B1 FB C6 01 
91 FB E6 01 C8 D0 F5 E6 FC CA D0 F0 A9 37 85 01 A9 94 8D 00 DD A9 35 8D 18 D0 
8D 27 D0 A2 05 8E F8 CF A2 01 8E 15 D0 8E 1A D0 8E 12 D0 86 FD 86 FE A2 18 8E 
00 D0 A2 1B 8E 11 D0 A2 32 8E 01 D0 A2 7F 8E 0D DC AE 0D DC AE 20 D0 86 FB A2 
C1 8E 14 03 A2 C0 D0 8A 85 FE 8D 88 02 A9 00 85 FB 85 FD A2 04 A0 00 78 B1 FB 
91 FD C8 D0 F9 E6 FC E6 FE CA D0 F2 60 A6 FB 8E 20 D0 CE 19 D0 A5 FD F0 20 AD 
00 D0 18 69 04 8D 00 D0 90 03 EE 10 D0 C9 40 D0 2C AD 10 D0 29 01 F0 25 20 38 
C1 C6 FD F0 1E AD 00 D0 38 E9 04 8D 00 D0 B0 03 CE 10 D0 C9 18 D0 0C AD 10 D0 
29 01 D0 05 20 38 C1 E6 FD A5 FE F0 14 AD 01 D0 18 69 04 8D 01 D0 C9 E6 D0 19 
20 38 C1 C6 FE F0 12 AD 01 D0 38 E9 04 8D 01 D0 C9 32 D0 05 20 38 C1 E6 FE 4C 
31 EA A9 01 8D 20 D0 60 00 00 00 7E 00 03 FF C0 07 FF E0 1F FF F8 1F FF F8 3F 
FF FC 7F FF FE 7F FF FE FF FF FF FF FF FF FF FF FF FF FF FF 7F FF FE 7F FF FE 
3F FF FC 1F FF F8 1F FF F8 07 FF E0 03 FF C0 00 7E 00 00 00 00 

Online demo -|- browse ca65 assembler source

Start and stop the bouncing ball with sys49152.

  • This leaves the C64 BASIC running, that's done by moving the VIC-II address space up to $C000, which requires copying the screen contents and the character set (font).
  • It hooks into the system IRQ and to avoid flicker, changes the source of this IRQ to the VIC-II graphics chip, so updates are always done between frames.
  • Glitches:
    1. RUN/STOP + RESTORE is broken, don't try.
    2. With the VIC-II as IRQ source, the cursor blinks slightly slower and TI$ will lag behind as well.
    3. when stopping while the border is flashed (very unlikely but possible), it stays white -- you have to restore it manually.
\$\endgroup\$
13
  • 1
    \$\begingroup\$ It´s not totally independent, is it? I see two absolute LDAs and two STAs. Great work nonetheless! \$\endgroup\$
    – Titus
    Sep 4, 2017 at 21:54
  • \$\begingroup\$ Damn you're right :o I forgot the self modification! I'll update as soon as I'm on the PC. \$\endgroup\$ Sep 5, 2017 at 5:16
  • 1
    \$\begingroup\$ @Titus fixed ... and just for fun, added a "better" variant :) \$\endgroup\$ Sep 5, 2017 at 10:22
  • \$\begingroup\$ Have you considered packing the sprite? (Hmm ... use the charset ROM?) And I´d prefer inc $d020 over jsr flash ;) hitshimselfwithalargetrout It´s marvellous! \$\endgroup\$
    – Titus
    Sep 5, 2017 at 22:26
  • 1
    \$\begingroup\$ @Titus would save 2 bytes, yes. As for the load address, it's part of a valid .prg file and from my meta question here I take I have to include it ... could probably leave it out if the code was position independent. \$\endgroup\$ Sep 7, 2017 at 16:15
4
\$\begingroup\$

Clojure, 398 380 375 bytes

(ns g(:require[quil.core :as q]))(def w 1e3)(def h 1e3)(def f 100)(def b(atom{:x f :y f :n 1 :m 1}))(q/defsketch . :size[w h]:setup #(do(q/text-font(q/create-font""f))(q/fill 255 255 255)):draw #(let[s 9{x :x y :y n :n m :m}@b c(+ x(* n s))u(+ y(* m s))](q/background 0 0 0)(reset! b{:x c :y u :n(if(< 0 c(- w f))n(* -1 n)):m(if(<(+ 0 f)u h)m(* -1 m))})(q/text"O"(:x @b)(:y @b))))

-18 bytes by changing the font name to an empty string to default it, inlining the boundary checks, and fixing the bottom boundary issue (which you can see in the GIF). Fixing that actually saved bytes.

-5 bytes by changing to a more succinct destructuring syntax and shrinking the ball by a pixel.

Uses Quil.

I tried to switch to functional mode, but it required a lot of extra code and ended up being more expensive.

(ns bits.golf.ball-bounce
  (:require [quil.core :as q]))

(def width 1000)
(def height 1000)

(def font-size 100)

; Mutable state holding the properties of the ball. n and m are the directions on the x and y axis.
(def ball (atom {:x 300 :y 600 :n 1 :m 1}))

(q/defsketch b
  :size [width height] ; Window size

  :setup #(do
            (q/text-font (q/create-font "Arial" font-size)) ; Set the font
            (q/fill 255 255 255)) ; And the text color

  :draw
  #(let [speed 9
         ; Deconstruct the state
         {:keys [x y n m]} @ball
         next-x (+ x (* n speed))
         next-y (+ y (* m speed))

         ; I'm adding/subtracting the font-size so it stays in the window properly
         x-inbounds? (< 0 next-x (- width font-size))
         y-inbounds? (< (+ 0 font-size) next-y height)]

     ; Wipe the screen so the ball doesn't smear
     (q/background 0 0 0)

     ; Reset the state
     (reset! ball
             {:x next-x
              :y next-y
              :n (if x-inbounds? n (* -1 n))
              :m (if y-inbounds? m (* -1 m))})

     ; Draw the ball
     (q/text "O" (:x @ball) (:y @ball))))

Ball Bouncing GIF

(Note, the new version doesn't bounce early along the bottom of the screen like it does in the GIF.)

\$\endgroup\$
1
  • \$\begingroup\$ I just realized I have (+ 0 font-size) in there. That's embarrassing. I'll fix that in the next version. Should save me like 5 bytes. \$\endgroup\$ Feb 20, 2017 at 2:45
4
\$\begingroup\$

Racket 247 bytes

(let*((w 500)(h(* w 0.6))(x 100)(y 0)(d 10)(e d)(G(λ(t)(set! x(+ x d))(when(or(> x w)(< x 0))
(set! d(* d -1)))(set! y(+ y e))(when(or(> y h)(< y 0))(set! e(* e -1)))
(underlay/xy(rectangle w h"solid""white")x y(circle 10"solid""black")))))(animate G))

Ungolfed:

(require 2htdp/image
         2htdp/universe) 

(let* ((wd 500)            ; define variables and their initial values
       (ht 300)
       (x 100)
       (y 0)
       (dx 10)
       (dy 10)

       (imgfn              ; define function to draw one frame; called repeatedly by animate fn; 
        (λ (t)             ; t is number of ticks till now- sent by animate fn; ignored here;

                           ; update location (x and y values):
          (set! x (+ x dx))
          (when (or (> x wd) (< x 0))
            (set! dx (* dx -1)))             ; invert direction at edges
          (set! y (+ y dy))
          (when (or (> y ht) (< y 0))
            (set! dy (* dy -1)))             ; invert direction at edges

                           ; draw image: 
          (underlay/xy
           (rectangle wd ht "solid" "white") ; draw background
           x y                               ; go to location (x,y)
           (circle 10 "solid" "black")       ; draw ball
          ))))

  (animate imgfn))         ; animates the images created by imgfn (default rate 28 times/sec)

Output:

enter image description here

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Playing racquetball with Racket! \$\endgroup\$
    – ckjbgames
    Feb 19, 2017 at 21:52
  • \$\begingroup\$ That's a good one! \$\endgroup\$
    – rnso
    Feb 20, 2017 at 0:35
  • \$\begingroup\$ "Racket" is derived from "Scheme" programming language: after Scheme (a devious plan) there is Racket (a scam or swindle)! \$\endgroup\$
    – rnso
    Feb 21, 2017 at 9:29
  • \$\begingroup\$ @mso Even better! \$\endgroup\$
    – ckjbgames
    Feb 21, 2017 at 13:42
4
\$\begingroup\$

Simons´ BASIC (C64), 66 65 bytes

One byte saved thanks @ShaunBebbers.

I need only one line here, because Simons´ Basic has a modulo function.
AfaIk, this requires a physical C64 and a Simons´ BASIC module
(or any other BASIC extension that has a mod function).

0fori=0to623:print"{CLR}":poke1024+40*abs(mod(i,48)-24)+abs(mod(i,78)-39),81:next:goto

Type in these 69 characters:

0fOi=0TO623:?"{CLR}":pO1024+40*aB(mod(i,48)-24)+aB(mod(i,78)-39),81:nE:gO

{CLR} is PETSCII 147, which clears the screen. Use Shift+CLR/HOME to type it in.

bytecount

When saved to disk, it takes 65 bytes, because the commands are tokenized:
for, to, poke, abs, next and goto are one byte each; mod takes two bytes.
That makes 59 bytes of code plus 4 bytes for pointers and 2 bytes for the line number.

For reference, see Mapping the C64 and search for $800 (BASIC Program Text).
(You can find the Video Screen Memory Area at $400.)

breakdown

The program loops I from 0 to 623 (=LCM of 48 and 78 minus 1). In the loop

  • the screen is cleared
  • I gets mapped to 39..0..38 respectively 24..0..23
  • and the blob (PETSCII 81) is put at the corresponding position in the video memory
    (like the original program does).

When the loop is done, the program is restarted by jumping to line 0.

C64 BASIC, 77 76 bytes

0fori=0to623:print"{CLR}"
1poke1024+40*abs(i-48*int(i/48)-24)+abs(i-78*int(i/78)-39),81:next:goto

Unfortunately I need two lines, because even with all possible abbreviations it would take 83 characters - too many to use the C64 line editor:

0fOi=0to623:?"{CLR}":pO1024+40*aB(i-48*int(i/48)-24)+aB(i-78*int(i/78)-39),81:nE:gO

(A hex editor could be used to create a longer line - which would make it 73 bytes.)

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Commodore command separators are : and not ; \$\endgroup\$ Feb 22, 2017 at 10:06
  • 1
    \$\begingroup\$ Also if you start at line zero, you can simply use goto in your two-liner version, as goto without a number assumes goto 0 on BASIC 2 \$\endgroup\$ Feb 22, 2017 at 10:08
  • \$\begingroup\$ If you want to get more commands on your C64 BASIC listing, enter it into a C128 in 128 mode, save it to disk and load it back into C64 mode, the C128 has a 160 character limit by default so this barrier can be broken by using Commodore keyword abbreviations. \$\endgroup\$ Feb 22, 2017 at 12:37
  • \$\begingroup\$ @ShaunBebbers nice to know. It´s been so long. I also wanted to implement this in machine code ... trying to recap on the kernel routines; not sure when I have to backup what registers; the complete kernel listing is online; I just cannot take the time to dig further. Would You like to complete this? \$\endgroup\$
    – Titus
    Feb 22, 2017 at 15:23
  • \$\begingroup\$ I was going to make a MC version, though I think submitting it to my own challenge would be over-indulgent even for me. The fastest way would be to write the byte directly to the screen from $0400 to $07e7; or use sprites. Using the Kernal with $ffd2 (output accumulator) would work as you can set the X and Y pos on the cursor easily enough (I don't remember the call for that), but you might have to avoid the last character position in case it forces a line feed. \$\endgroup\$ Feb 22, 2017 at 15:38
3
\$\begingroup\$

Jelly, 37 bytes

“ñc‘Ọ24ḶŒḄṖ⁸ị⁷x⁸µ80ḶŒḄṖ⁸ị⁶x⁸‘œS.1
Ç1¿

With some help from this answer for getting the loop and escape characters right. Currently it bounces around in a 80x24 screen, but that can be easily modified in the code.

The coördinates in each direction can be represented as elements of two lists [0, 1,..., 24, 23,..., 1] and [0, 1,..., 80, 79,..., 1], let's call them Y and X, that are infinitely repeated. This infinite repetition can be emulated using modular indexing -- using in Jelly. Example: in the ith iteration the ball is at position (X[i%|X|], Y[i%|Y|]) = (iịY, iịX). The moving ball is just the cursor that is put into position by emitting iịY newlines and iịX spaces.

Demo

https://i.gyazo.com/b8eac64097cb6d3a18185877c2f4c945.gif

Explanation

“ñc‘Ọ24ḶŒḄṖ⁸ị⁷x⁸µ80ḶŒḄṖ⁸ị⁶x⁸‘œS.1        Monadic helper link - argument i.
                                         Resets the terminal, prints Y[i] newlines,
                                         X[i] spaces and returns i + 1.
“ñc‘                                     Set the output to [27, 99]
    Ọ                                    Convert to characters and print (\x1bc)
                                          -> Cursor is at position (0,0)
     24Ḷ                                 Lowered range of 24. Yields [0,...,23].
        ŒḄ                               Bounce. Yields [0,...,23,22,...,0].
          Ṗ                              Pop. Yields [0,...,23,22,...,1] = Y.
           ⁸ị                            Modular index i (⁸) into Y. The current
                                         value is the Y coordinate, y.
              x                          Repeat y times
             ⁷                           the newline character ('\n').
               ⁸                         Output that (y times '\n') and continue
                                         with value i.
                                          -> Cursor is at position (0, y)
                µ                        Monadic chain separation.
                 80ḶŒḄṖ                  Same as above, but this time yielding X.
                       ⁸ị                Modular index i into X, yielding the
                                         value for x.
                          x              Repeat x times
                         ⁶               the whitespace character.
                           ⁸             Output that (x times ' ') and continue
                                         with value i.
                                         -> Cursor is at position (x, y), the
                                            final position.
                             œS.1        Wait 0.1 seconds.
                            ‘            Return i + 1.

Ç1¿                                      Main (niladic) link.
 1¿                                      While true.
Ç                                        Call the helper link. The first time
                                         there is no argument and i will be [],
                                         which is cast to 0 when used as integer
                                         (e.g. try ‘¶Ç). After that, the previous
                                         return value (i + 1) is used.
\$\endgroup\$
2
\$\begingroup\$

SmileBASIC, 85 74 bytes

SPSET.,9M=MAINCNT
SPOFS.,ASIN(SIN(M/5))*122+192,112+71*ASIN(SIN(M/3))EXEC.

The position of the ball can be modelled with 2 triangle waves, and the shortest way I could find to produce them in SmileBASIC was arcsine(sine(x)). (the algorithm using MOD was longer since SB uses MOD instead of %)

\$\endgroup\$
2
\$\begingroup\$

CSS/HTML, 200 + 7 = 207 bytes

p{position:relative}a{position:absolute;animation:infinite linear alternate;animation-name:x,y;animation-duration:7.9s,2.3s}@keyframes x{from{left:0}to{left:79ch}}@keyframes y{from{top:0}to{top:24em}}
<p><a>O

This version shows you the size of the canvas and also gives the animation a more pixilated feel:

p {
  position: relative;
  width: 80ch;
  height: 25em;
  border: 1px solid black;
}
a {
  position: absolute;
  animation: infinite alternate;
  animation-name: x, y;
  animation-duration: 7.9s, 2.3s;
  animation-timing-function: steps(79), steps(23);
}
@keyframes x {
  from {
    left: 0;
  } to {
    left: 79ch;
  }
}
@keyframes y {
  from {
    top: 0;
  } to {
    top: 24em;
  }
}
<p><a>O</a></p>

\$\endgroup\$
2
\$\begingroup\$

Dyalog APL, 44 bytes

{⎕SM∘←0,G←⍺+⍵⋄G∇⍵×1-2×⊃1 G∨.≥G⎕SD⊣⎕DL.1}⍨1 1

Explanation:

  • {...}⍨1 1: call the given function with ⍺=⍵=1 1
    • ⎕SM∘←0,G←⍺+⍵: store ⍺+⍵ in G, display a 0 at that location in the ⎕SM window.
    • ⎕DL.1: wait 1/10th of a second
    • ⊃1 G∨.≥G⎕SD: check if G is at the ⎕SM window boundary (1≥G or G≥⎕SD, ⎕SD is the screen dimensions)
    • 1-2×: map [1,0] onto [¯1,1], to flip the direction of travel
    • ⍵×: multiply the current direction of travel by that
    • G∇: recursion, let G be the new location () and ⍵.... be the new direction ().
\$\endgroup\$
3
  • \$\begingroup\$ Is this supposed to continuously open and close terminals as it runs? It's quite difficult to stop this from running once it's started, as the terminal closes and reopens every tenth of a second (at least on Windows). \$\endgroup\$
    – ren
    Feb 21, 2017 at 18:05
  • 1
    \$\begingroup\$ @wptreanor: fixed \$\endgroup\$
    – marinus
    Feb 21, 2017 at 18:08
  • \$\begingroup\$ cool, excellent work! \$\endgroup\$
    – ren
    Feb 21, 2017 at 18:11
2
\$\begingroup\$

PHP, 112 97 94 103 102 bytes

for(;;usleep(1e5),$i%=624)echo($r=str_repeat)(A^K,99),$r(A^a,abs($i%78-39)),O,$r(A^K,abs($i++%48-24));

bounces a capital O on a 40x25 grid, starting at the top right corner;
prints 99 newlines to clear the screen.

Run with -nr.

A^K = chr(10) = newline
A^a = chr(32) = space

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Hi Titus it's me again. for($d=$e=-1;;usleep(1e5))echo($r=str_repeat)(A^K,99),$r(A^a,$x+=$d*=$x%79?1:-1),O,$r(A^K,$y+=$e*=$y%24?1:-1);. The modulo is false at 0 and N and reverts the direction. Sadly we have to init $d and $e to -1 but still get some savings. $x%79<=>.5 also works for the same bytes. \$\endgroup\$
    – Christoph
    Feb 21, 2017 at 7:03
  • 1
    \$\begingroup\$ Hey @Christoph welcome back. Strange: when I copied Your stuff it had 116 bytes instead of 110. But it inspired me to something a lot shorter. \$\endgroup\$
    – Titus
    Feb 21, 2017 at 15:56
  • \$\begingroup\$ We're definitely a good team ;) Strange thing on the copying I've got no idea why. \$\endgroup\$
    – Christoph
    Feb 21, 2017 at 16:05
2
\$\begingroup\$

C + curses, 190 bytes

#include<curses.h>
w;h;x;y;d;main(e){initscr();curs_set(0);getmaxyx(stdscr,h,w);for(d=e;;usleep(20000),mvaddch(y,x,32)){mvaddch(y+=d,x+=e,48);if(!y||y>h-2)d=-d;if(!x||x>w-2)e=-e;refresh();}}

Explanation:

#include<curses.h>
w;h;x;y;d;
main(e)
{
    initscr();
    curs_set(0);
    getmaxyx(stdscr,h,w);

    // initialize distances to 1 (e is 1 when called without arguments)
    // wait for 1/5 second, then write space character at current pos
    for(d=e;;usleep(20000),mvaddch(y,x,32))
    {
        // advance current pos and write ball character (`0`)
        mvaddch(y+=d,x+=e,48);

        // check and change direction:
        if(!y||y>h-2)d=-d;
        if(!x||x>w-2)e=-e;

        // trigger output to screen:
        refresh();
    }
}
\$\endgroup\$
2
\$\begingroup\$

QBasic, 71 bytes

DO
CLS
LOCATE 24-ABS(i MOD 46-23),80-ABS(i MOD 158-79)
?"O";
i=i+1
LOOP

This version loops forever, although it might break when i gets too large--I haven't tested. Also, how fast it moves depends on the hardware you use, but on the emulator at Archive.org it runs too fast to see the motion. Therefore, here's a somewhat friendlier version that terminates once the ball gets back to the top left corner:

FOR i=0TO 3634
CLS
LOCATE 24-ABS(i MOD 46-23),80-ABS(i MOD 158-79)
?"O";
z=COS(1)
NEXT

Change the COS(1) to COS(1)+COS(1)+COS(1) (etc.) to slow it down further. Try it here!

Explanation

Our bounding box is QBasic's standard text mode screen, 80 characters wide by 24 characters tall, 1-indexed. As several other answers have observed, we can calculate the correct x and y coordinates, given a frame number, using modular arithmetic:

Frame i i MOD 46 (i MOD 46)-23 ABS((i MOD 46)-23) y = 24-ABS(...) x = 80-ABS(...)
0 0 -23 23 1 1
1 1 -22 22 2 2
22 22 -1 1 23 23
23 23 0 0 24 24
24 24 1 1 23 25
45 45 22 22 2 46
46 0 -23 23 1 47
47 1 -22 22 2 48
79 33 10 10 14 80
80 34 11 11 13 79

After that it's just an issue of clearing the screen with CLS, moving to the proper coordinates with LOCATE, and printing "O".

\$\endgroup\$
1
\$\begingroup\$

Python 2, 176 168 Bytes

This assumes a terminal size of 80x24. Definitely not optimal but I'm new to golfing so yeah.

import time;x=y=d=e=1
while 1:
 m=[[' 'for i in' '*80]for j in' '*24];x+=d;y+=e;m[y][x]='O';time.sleep(.1)
 if x%79<1:d=-d
 if y%23<1:e=-e 
 for r in m:print''.join(r)

Thanks to R. Kap for suggesting the x%79<1 instead of x<1or x>79 and ditto for y.

\$\endgroup\$
1
  • \$\begingroup\$ You can save a few bytes by replacing x<1or x>78 with x%79<0 and y<1or y>22 with y%23<1. \$\endgroup\$
    – R. Kap
    Feb 20, 2017 at 3:26
1
\$\begingroup\$

Rebol/View, 284 266 bytes

rebol[]p: 3x9 d:[3 3]view layout[b: box blue 99x99 effect[draw[circle p 2]]rate :0.01 feel[engage: func[f a e][if a = 'time[case/all[p/x < 2[d/1: abs d/1]p/y < 2[d/2: abs d/2]p/x > 98[d/1: negate d/1]p/y > 98[d/2: negate d/2]]p/x: p/x + d/1 p/y: p/y + d/2 show b]]]]

Ungolfed:

rebol []

p: 3x9     ;; starting position
d: [3 3]   ;; direction

view layout [
    b: box blue 99x99 effect [
        draw [
            circle p 2
        ]
    ]

    rate :0.01 feel [
        engage: func [f a e] [
            if a = 'time [
                case/all [
                    p/x < 2  [d/1: abs d/1]
                    p/y < 2  [d/2: abs d/2]
                    p/x > 98 [d/1: negate d/1]
                    p/y > 98 [d/2: negate d/2]
                ]
                p/x: p/x + d/1
                p/y: p/y + d/2
                show b
            ]
        ]
    ]
]
\$\endgroup\$
1
\$\begingroup\$

C 294 bytes

#include<graphics.h> f(){int d=0;g,x,y,a=0,b=0;initgraph(&d,&g,NULL);x=30;y=30;while(1){x+=6;y+=7;if(y<60)b=0;if(x<60)a=0;if((y>getmaxy()-40)) b=!b;if((x>getmaxx()-40))a=!a;if(b){y-=18;x+=3;}if(a){x-=15;y+=2;}usleep(10000);setcolor(4);cleardevice();circle(x, y,30);floodfill(x,y,4);delay(45);}}

Ungolfed version:

#include<graphics.h>
void f()
{
 int d=DETECT,g,x,y,r=30,a=0,b=0;
 initgraph(&d,&g,NULL);
 x=30;
 y=30;

 while(1)
 {
   x+=6;
   y+=7;

   if(y<60)
     b=0;
   if(x<60)
     a=0;     

   if((y>getmaxy()-40))
        b=!b;

   if((x>getmaxx()-40))
        a=!a;

    if(b)
    {       
        y-=18;
        x+=3;
    }

    if(a)
    {       
       x-=15;
       y+=2;               
    } 
    usleep(10000);
    setcolor(RED);
    cleardevice();
    circle(x,y,r);
    floodfill(x,y,RED);
    delay(45);

  }   

}

Explanation

  • So in order to begin with this, I had to get graphics.h in my /usr/include directory. Therefore, i searched and this is what I found. It is a TurboC Graphics implementation using SDL for Linux. One could also use OpenGL. In windows, I guess it is already installed, not sure about MacOS.
  • void initgraph(int *graphdriver, int *graphmode, char *pathtodriver); initialises the system and puts it in a graphics mode, in this case graphics driver is automatically detected. Please refer to this link for more details.
  • x and y are coordinates that determines the ball's position.
  • a and b are flags, a is set to zero when the x value drops below 60 and b is set to zero when y drops below 60.
  • The flags are toggled when x and y exceeds boundary values of window, and the coordinates are accordingly adjusted.
  • I put a usleep so that my CPU does not get stressed out.
  • One should normally use a closegraph() call, in order to close the window. But it's missing here.

Must be compiled with the linker flag -lgraph

It runs smoother on real hardware. :)

Bouncing Red Ball

\$\endgroup\$
2
  • \$\begingroup\$ Are import statements necessary to run this program? \$\endgroup\$
    – user41805
    Feb 20, 2017 at 18:44
  • \$\begingroup\$ @KritixiLithos Yes sir; Updated! you need to include graphics.h. This answer askubuntu.com/questions/525051/… was helpful. \$\endgroup\$
    – Abel Tom
    Feb 20, 2017 at 18:48
1
\$\begingroup\$

MATL, 42 bytes

1thXH_XI`Xx8E70hZ"79HZ}&(DH4M\1>EqI*XIH+XH

This uses a 70×16 screen and character O. If you wait for a few bounces you'll see the ball hitting a corner.

Try at MATL Online!

Screen size can be easily modified in the code. The relevant part is 8E70, which pushes 8, doubles it, and pushes 70. For example, for a 80×25 screen replace by 5W80, which pushes 5, squares it, and pushes 80 (or replace by 25 80, but that requires one more byte).

Also, adding tD at the end of the code shows the current position in real time (vertical, then horizontal, 1 1 is upper left). As an example, for a 80×18 screen,

1thXH_XI`Xx9E80hZ"79HZ}&(DH4M\1>EqI*XIH+XHtD

Try it too!

Explanation

This uses an infinite loop. Position is kept in clipboard H as a 1×2 vector, and direction is kept in clipboard I as a 1×2 vector with entries 1 or -1.

Each iteration clears the screen, defines a matrix of spaces, writes an O at the relevant position, and displays it. Then the position and directio need to be updated.

Position is 1-based, and thus the edges of the screen are 1 and the maximum screen size. So if position modulo screen size gives 0 or 1 in either the first or second components, which means we have reached a vertical or horizontal edge respectively, that component of the direction vector is negated. After that, the new direction is added to the current position to obtain the new position.

\$\endgroup\$
1
\$\begingroup\$

Here's the ZX Spectrum listing.

  10 FOR n=0 to 7
  20 READ a: POKE USR "a"+n, a
  30 NEXT n
  40 DATA 60,126,243,251,255,255,126,60
  50 LET x=10:LET y=10:LET vx=1: LET vy=1
  60 PRINT AT y,x;"\a"
  70 IF x<1 OR x>30 THEN LET vx=-vx
  80 IF y<1 OR x>20 THEN LET vy=-vy
  90 LET x=x+vx: LET y=y+vy
 100 PRINT AT y-vy,x-vx;" ": GO TO 60
\$\endgroup\$
3
  • \$\begingroup\$ Nice first entry DrlB - could you please include a byte count. I assume this will work on any Speccy including 16K machines? \$\endgroup\$ Sep 4, 2017 at 12:08
  • \$\begingroup\$ Hi this is 201 bytes, you could omit the first 4 lines but then you get just a bouncing "a" character but it saves you 64 bytes. I will try to optimize. This nothing fancy at all and will work on any Spectrum model :) \$\endgroup\$
    – DrIB
    Sep 8, 2017 at 1:40
  • \$\begingroup\$ Ok I've managed to cut it down to 185 by condensing the lines a bit without dropping the ball graphics. It's a bit less readable though but it's faster. \$\endgroup\$
    – DrIB
    Sep 8, 2017 at 5:00
1
\$\begingroup\$

Lua (LÖVE 2D), 130 bytes

x,y,a,b=0,0,1,1
function love.draw()a=(x<0 or x>800)and-a or a
b=(y<0 or y>600)and-b or b
x=x+a
y=y+b
love.graphics.points(x,y)end

Lua is not the best language when it comes to code golf, but here you go! A few points worth mentioning:

  • The default canvas size is 800 x 600. It can be changed in the configuration file, but I didn't see any size restrictions, so I left it as is.

  • love.draw() is LÖVE's drawing function and it has a predetermined name. Alternative LÖVE functions that could be used would be love.update(dt) and love.run() -- the first being longer, in bytes, and the latter being shorter, yes, but without a built-in infinite loop. Thus, draw() seems to be our best bet here.

  • The above version uses love.graphics.points to draw the ball. Although shorter, I am not sure it is allowed. Here is a GIF of how it runs:

Animated screenshot - point

As you can see (or perhaps can't), there is a single pixel moving on the screen. While that saves up bytes, it's not the most satisfying of results.

So I've made an alternative 131 bytes solution:

x,y,a,b=0,0,1,1
function love.draw()a=(x<0 or x>795)and-a or a
b=(y<0 or y>595)and-b or b
x=x+a
y=y+b
love.graphics.print(0,x,y)end

This one uses love.graphics.print -- which prints text -- and a 0 as a ball, making it much more visible and appealing.

Animated screenshot - zero

\$\endgroup\$
1
\$\begingroup\$

CHIP-8, 36 34 28 bytes

FF29 'LDF I,vF //load digit sprite for the value of vF (should be 0)

4000 'SNE v0,0 //if x is 0...
6201 'LD v2,1 //set x velocity to 1
403C 'SNE v0,3C //if x is 3C...
62FF 'LD v2,FF //set x velocity to -1
4100 'SNE v1,0 //if y is 0...
6301 'LD v3,1 //set y velocity to 1
411B 'SNE v1,1B //if y is 1B...
63FF 'LD v3,FF //set y velocity to -1

D015 'DRW v0,v1,5 //draw sprite
D015 'DRW v0,v1,5 //draw sprite again to clear it.
8024 'ADD v0,v2 //add x velocity to x
8134 'ADD v1,v3 //add y velocity to y

1202 'JMP 202 //jump to second instruction

No fancy tricks here...

Requires an interpreter that draws sprites correctly (only one sprite can be drawn per frame, which slows down the program enough for you to be able to see it).

Low quality video

\$\endgroup\$
1
\$\begingroup\$

PostScript (GhostScript), 259 bytes

I don't think PostScript was built for animation... improvements welcome!

[/x{4}/y{196}/X{2}/Y{-2}/b{x y 4 currentgray add 0 360 arc fill}/p{/x x X add def}/q{/y y Y add def}/g{setgray}>>begin[/PageSize[320 200]>>setpagedevice{1 g
b p q
x 316 gt x 4 lt or{/X X -1 mul def}if
y 196 gt y 4 lt or{/Y Y -1 mul def}if
0 g b flushpage}loop

Needs GhostScript for flushpage.

animation

(It runs smoother when the screen recorder isn't also running.)

\$\endgroup\$
1
\$\begingroup\$

Commodore C64 BASIC - 75 71 tokenised BASIC bytes, 77 73 characters with keyword abbreviations

The following answer is taken from the YouTube channel 8-BIT Show and Tell, so Robin Harbron deserves the credit, along with credit to the YouTube channel JosipRetroBits.

 0?"{CLEAR}":DEFFNA(B)=ABS(T-INT(T/B)*B-B/2):POKE1024+FNA(48)*40+FNA(78),81:T=T+1:GOTO

Older solution

 0?"{CLEAR}":DEFFNA(B)=ABS(T-INT(T/B)*B-B/2):Z=1024+FNA(48)*40+FNA(78):POKEZ,81:T=T+1:GOTO

The listing is above one logical (Commodore C64) line in Commodore BASIC, so it must be typed with keyword abbreviations, as in the screen shot below.

A full explanation of what is going on is linked above.

Commodore C64 BASIC Bouncing Ball

\$\endgroup\$
0
\$\begingroup\$

ZX Spectrum BASIC - 179 bytes

Here it is just condensed a little bit. It's 179 bytes with the ball graphics included

  10 LET a=10: LET b=10: LET c=1: LET d=-1: FOR e=0 TO 7: READ f: POKE USR "a"+e, f: NEXT e
  20 DATA 60, 126,243,251,255,255,126,60
  30 PRINT AT b,a;"\a"
  40 IF a<1 OR a>30 THEN LET c=-c
  50 IF b<1 OR b>20 THEN LET d=-d
  60 LET a=a+c: LET b=b+d: PRINT AT b-d, a-c;" ": GO TO 30
\$\endgroup\$
1
  • \$\begingroup\$ Please look at the mark-up used for answers, also by using the character o or O you may be able to save some bytes in the symbolic listing; you are also able to edit previous answers when you have improved solutions, rather than re-answering the same question \$\endgroup\$ Sep 8, 2017 at 8:48

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.