9
\$\begingroup\$

Ascii Pong

The challenge is to recreate the classic game "pong" in ASCII characters in the shortest code possible.

Requirements/specifications

  • The "screen" should be 45x25 characters.
  • White space should actually be white space.
  • The paddles should be 9 equals signs: ========= and should be on the top-most and bottom-most rows (I know the original is played side-to-side instead of top-to-bottom, but I think this works better for ascii pong).
  • The ball can be a lower or upper case o or a zero.
  • Two input buttons of any kind (it is fine if the user presses a key that makes a letter show up in the input, that is fine, too) to move the player's paddle left and right one or two character at a time.
  • The ball needs to ricochet at the appropriate angle upon hitting a paddle or a wall (hint: negate the x or y difference value).
  • The score should be displayed somewhere in the output. The score is how many times the player successfully hits the ball.
  • If the ball misses the player's paddle, terminate the program.
  • There should be some sort of AI (even if the AI's paddle just matches the ball's x value).
  • The ball can't travel in a straight line vertically or horizontally.

The start screen/first frame should look something like this:

                  =========                  











                      o                      











                  =========                  
score: 0

Scoring This is , so the shortest code wins ... however, there are some bonuses to decrease your character count:

  • -30 characters: Change trajectory of ball depending on where it hits on the paddle
  • -10 characters: Make the game speed up over time
  • -20 characters: Make the AI beatable
  • -20 characters: Avoid any situation where the player stays still and the game goes on forever without winning or losing (caused by certain trajectories and AI patterns)
  • -20 characters: Make the ball start moving at a (semi-)random trajectory
  • -25 characters: Add a reset option

Here is an un-golfed example with no bonuses in JavaScript:

//init
var x = 22,
    y = 12,
    xd = Math.random() < 0.5 ? -1 : 1,
    yd = 1,
    player = opponent = 18,
    score = 0,

//interval that runs 10x per second (minus the runtimeof one cycle)
interval = setInterval(function() {
  //move ball
  x += xd;
  y += yd;

  //move opponent
  opponent = x - 4;

  //collision with walls
  if(x <= 0 || x >= 44) xd = -xd;

  //collision with paddles
  if(y == 1) {
    if(x >= opponent && x < opponent + 9) yd = -yd;
    else {
      //you win
      clearInterval(interval);
      document.body.innerHTML = "You win!<br>Your score was: " + score;
      return;
    }
  }
  else if(y == 23) {
    if(x >= player && x < player + 9) {
      yd = -yd;
      score++;
    }
    else {
      //you lose
      clearInterval(interval);
      document.body.innerHTML = "You lose!<br>Your score was: " + score;
      return;
    }
  }

  draw();
}, 100);

function draw() {
  var body = document.body;
  body.innerHTML = "";
  for(var i = 0; i < 25; i++) {
    for(var j = 0; j < 45; j++) {
      //white space is default
      var char = " ";
      
      //ball
      if(j == x && i == y) char = "o";
      
      //paddles
      if(i === 0) {
        if(j >= opponent && j < opponent + 9) char = "=";
      }
      else if(i == 24) {
        if(j >= player && j < player + 9) char = "=";
      }
      
      body.innerHTML += char;
    }
    body.innerHTML += "<br>";
  }
  body.innerHTML += "score: " + score;
}

//key press listener for left and right movement
window.onkeydown = function() {
  if (window.event.keyCode == 37) player -= 2;
  else if(window.event.keyCode == 39) player += 2;
};
<body style="font-family: monospace; white-space: pre">
  
</body>

Let the games begin!

\$\endgroup\$
10
  • 3
    \$\begingroup\$ Okay can somebody at least explain why they downvoted this? I am genuinely curious because I thought this would be fun but I don't have much experience. \$\endgroup\$ Aug 6, 2015 at 23:01
  • 3
    \$\begingroup\$ Printing out ASCII stuff is okay but with keyboard interaction we'd have to build a whole app. Those type of challenges are frowned upon because many languages don't support i/o like that. Implementing an AI, physics. Almost like a full scale application. If you take a look at other challenges they have one task which takes an input (or not) and produces a relevant output. Adding an AI, physics, and keyboard knteraction is just way to much for a challenge \$\endgroup\$
    – Downgoat
    Aug 6, 2015 at 23:07
  • 1
    \$\begingroup\$ @vihan the AI for pong is extremely simple, it just has to follow the x value of the ball. Even making it beatable is not that hard, just limit the speed od the paddle. The only real issue with the concept is the keyboard interaction, which can be managed in a reasonable variety of languages. The brief is however rather vague and open (given that we are sticklers for unambiguity and fairness around here), and the vast array of bonuses don't help that. \$\endgroup\$ Aug 6, 2015 at 23:52
  • 1
    \$\begingroup\$ relevant: meta.codegolf.stackexchange.com/q/5521/15599 \$\endgroup\$ Aug 6, 2015 at 23:56
  • 2
    \$\begingroup\$ Keyboard interaction had to be solved in (Re)Implementing Tetris too, but that didn't diminished the task's popularity. (Yes, I know, that was actually popularity-contest, this is not.) The only thing personally I find disturbing, is the amount of bonuses. But certainly not has to be up to my taste. \$\endgroup\$
    – manatwork
    Aug 7, 2015 at 7:23

2 Answers 2

4
\$\begingroup\$

Perl, 760 611 592 535 515 (640-30-10-20-20-20-25)

A console solution with all the bonus stuff.
It should work on any terminal that understands the ANSI escape codes (\e[...). Tested on cygwin.

Keyboard controls:
Left: 4
Right : 6
Reset : 8

use Term::ReadKey;ReadMode 4;END{ReadMode 0}$|++;$a=$d=45;$b=25;$h=1;$j=$l=17;$p='='x9;sub P{print"\e[@_"}sub L{$%=$l+pop;if($%>0&$%+7<$a){P"25H\e[K\e[25;$%H$p ";$l=$%}}sub U{$%=$j+pop;if($%>0&$%+7<$a){P"1H\e[K\e[1;$%H$p ";$j=$%}}sub I{$}=int rand 42;$g=8;$i=1;P"2J\ec";L}I;while(1){if($c>0){$c--}else{$c=98;$d-=$d>6}if($c%9==0){U -1if$}<$j+4;U 1if$}>$j+6};if($c%$d==0){$m=$}+$h;$n=$g+$i;$h*=-1if$m<1||$m>$a;if($n>24&$}>=$l&$}<$l+9){$i*=-1;$h=-1if$m<$l+5;$h=1if$m>$l+5;$s++}if($n<2&$}>=$j&$}<$j+9){$i*=-1}P"$g;1H\e[K";$}+=$h;$g+=$i;P"$g;$}HO"}if($k=ReadKey -1){I,if$k>6;L$k<=>5}P"26;0Hscore:$s";exit,if$g>=$b;select($\,$\,$\,0.01);I,if$g<2}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Hey Luk, loving this! Thought I'd share a few ways you might be able to save some more chars... Since you're using print more than 4 times, it should be cheaper to set up a new sub and call that sub p{print@_}. If you don't mind other keys being used as well as those you specific, you can replace if(defined($k=ReadKey(-1))){L(-1)if's'eq$k;L(1)if'd'eq$k;I,if'r'eq$k} with L 114<=>ord$k if$k=ReadKey -1;. This just converts <r:-1,r:0,>r:1 which might not be what you're after. If you are happy with this, you could use [4][5][6] as the keys with: L$k<=>5 if$k=ReadKey -1;. \$\endgroup\$ Aug 27, 2015 at 11:56
  • 1
    \$\begingroup\$ Also looks like you can replace a few if blocks with a postfix if check or replace with ternary for some more savings, but that might prove tricky with the nesting... You have another block $d--if$d>6 which you can change to $d-=$d>6 for a 2-byte saving which might work in a few more places too and another small saving is ;$p=H.'='x9 .$" instead of $p="H========= ". Hope some of these are useful! \$\endgroup\$ Aug 27, 2015 at 12:00
  • 1
    \$\begingroup\$ Thanks, glad that you like it. Your tips helped to golf it down further with 50 bytes! That trick with the ord is wicked. The $d-=$d>6 trick looked so weird. I also discovered that when printing something like "$aH" is a problem, that "${a}H" works like it would in bash. :) \$\endgroup\$
    – LukStorms
    Aug 27, 2015 at 21:14
  • 1
    \$\begingroup\$ Ah yeah! A couple of ways around that using non-alphabetic variable names ($} or something) or using barewords ($a.H), not sure if that'll help you in this case or not. Something I did notice, is in sub P if you only ever have one argument to it, you could have print"\e[@_" instead of .pop since there won't be a field separator to worry about. Also, if you're using the numpad instead of letters, you don't need to use ord, as $k would be 4 or 6 so you could do 5<=>$k instead. \$\endgroup\$ Aug 28, 2015 at 6:31
  • 1
    \$\begingroup\$ Thanks again. Didn't think perl would accept variables like that O_o. But it does work and a few bytes were resqued by that. But don't try using a $! variable. Weird stuff happens then. \$\endgroup\$
    – LukStorms
    Aug 29, 2015 at 12:52
1
\$\begingroup\$

JavaScript, 618 bytes + HTML, 99 bytes

Well here is my golfed JavaScript answer even if it isn't feasible in other languages:

var x,y,xd,yd,p,o,s,n,i,j,c,k;function a(){x=22;y=12;xd=Math.random()<.5?-1:1;yd=1;p=o=18;s=0;n=setInterval(function(){x+=xd;y+=yd;o=x-4;if(x<=0||x>=44)xd=-xd;if(y==1){if(x>=o&&x<o+9)yd=-yd;else{clearInterval(n);b.innerHTML="You Win!<br>Score: "+s;return}}else if(y==23){if(x>=p&&x<p+9){yd=-yd;s++;}else{clearInterval(n);b.innerHTML="You Lose!<br>Score: "+s;return}}d();},100);}function d(){b.innerHTML="";for(i=0;i<25;i++){for(j=0;j<45;j++){c=" ";if(j==x&&i==y)c="o"; if(i==0&&j>=o&&j<o+9)c="=";if(i==24&&j>=p&&j<p+9)c="=";b.innerHTML+=c;} b.innerHTML+="<br>";}b.innerHTML+="score: "+s;}onkeydown=function(){ k=window.event.keyCode;if(k==37)p-=2;if(k==39)p+=2;};
<button onclick="a()">start</button><div id="b"style="font-family:monospace;white-space:pre"></div>

-20 and -25 for bonuses

\$\endgroup\$
2
  • \$\begingroup\$ Hm, this doesn't look very golfed to me. You could still get rid of two-letter variable names, substitute a few && tokens for &, eliminate plenty of ; tokens, get rid of unnecessary spaces after { and even get rid of some quote characters in the HTML. And probably a lot more, even. This is just a guess, but you might want to try to avoid UglifyJS and minify by hand. :) \$\endgroup\$
    – Chiru
    Aug 8, 2015 at 8:24
  • 6
    \$\begingroup\$ Quite cryptic posted this way. The reader has to search the 663 characters code for the key handling, then possibly STFW to find out which keys have code 37 and 39. (Anyway, there is no reaction on left and right arrow key in the latest Firefox I use.) So not being able to test it I will never find out for which of the 3 available bonuses you earned the -20. \$\endgroup\$
    – manatwork
    Aug 8, 2015 at 14:26

Your Answer

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

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