Proto space invaders

This is a graphical output challenge where the task is to give the shortest code per language.


Your code should allow the user to move the following alien around the screen/window.

enter image description here

Your code can simply load it from a local file. Feel free to convert it to another standard image format or even to fix the small pixel errors in the image that were pointed out in the comments.

The background should be white and the window/screen must be at least 400 pixels by 400 pixels. If your language doesn't support windows/screens that large then use the largest size it does support as long as that is not less than 200 by 200.

To move the alien around the screen the code should support up/down/left/right using the arrows keys on a standard keyboard.

Your code should be a full program.


The alien should stop at the borders. It should also move at a uniform rate smoothly with no visible flickering or stuttering and be shown at at least 24fps. It should take between 2 and 5 seconds to go from one side of the screen/window to the other.

Languages and libraries

You can use any language or library you like (that wasn't designed for this challenge). However, I would like to be able to test your code if possible so if you can provide clear instructions for how to run it in Ubuntu that would be very much appreciated.


The Stack Snippet at the bottom of this post generates the catalog from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 62426; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 9206; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "https://api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang, user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

  • 2
    \$\begingroup\$ Can we construct the alien ourselves? If so, do we have to include the pixel errors (i guess?) from the picture? \$\endgroup\$ – mınxomaτ Oct 31 '15 at 14:42
  • \$\begingroup\$ @mınxomaτ It isn't limited to linux! Note "if possible". \$\endgroup\$ – user9206 Oct 31 '15 at 14:48
  • \$\begingroup\$ @nimi Thanks. Added that you can load it from a local file. \$\endgroup\$ – user9206 Oct 31 '15 at 14:53
  • \$\begingroup\$ @mınxomaτ You can construct it yourself or just load it from a local file. I hadn't noticed the pixel errors but I assume that loading it will be less code in any case. \$\endgroup\$ – user9206 Oct 31 '15 at 14:54
  • \$\begingroup\$ Can you define "stop at the borders"? Does the entire alien need to be visible at all times, or can it stop partially outside the border? \$\endgroup\$ – timothymh Oct 31 '15 at 17:13

11 Answers 11


Scratch, 221 217 bytes

Click the image to view it in action. The movement is determined by keystrokes, so it will be smoother the faster your key repeat is set.

The image is included within the project, but Scratch bytes are usually counted from the golfed textual representation, per this meta post. If there is disagreement as to whether this is acceptable (or whether the movement is smooth enough) let me know and I will try to work around it.

| improve this answer | |
  • \$\begingroup\$ This answer is a lot of fun! \$\endgroup\$ – user9206 Oct 31 '15 at 19:02
  • \$\begingroup\$ How did you measure the size of this program? \$\endgroup\$ – user9206 Oct 31 '15 at 19:13
  • \$\begingroup\$ @Lembik see the second paragraph. \$\endgroup\$ – timothymh Oct 31 '15 at 19:13
  • 1
    \$\begingroup\$ generates in 217 bytes: scratchblocks.github.io/… \$\endgroup\$ – user46167 Oct 31 '15 at 23:45
  • 1
    \$\begingroup\$ @Mauris Yes, that's what the "if on edge, bounce" command takes care of. You can play the demo by clicking on the image! \$\endgroup\$ – timothymh Nov 1 '15 at 15:56

Processing 2, 219 199 241 220 219 bytes

int a,x=0,y=0;void setup(){size(500,500);}void draw(){background(-1);image(loadImage(".png"),x,y);if(keyPressed){a=keyCode;if(a==38)y--;if(a==37)x--;if(a==40)y++;if(a==39)x++;}x=constrain(x,0,436);y=constrain(y,0,454);}

Requires the image saved as .png in the same directory as the .pde

| improve this answer | |

Python 2, 262 253 246 240 bytes

from pygame import*
while c.fill((255,)*3):
 if e:x=e[0].key+1;q=x&2;b=q/2;p[b]=max(0,min(336+b*16,p[b]+(1-q)*(1-(2*x&2))))

Wow. What a lot of hackery.

Uses the module 'pygame' found at http://pygame.org.


key.set_repeat(1) - Send repeat key events through the event system every millisecond

c=d.set_mode((400,)*2) - Create the 400x400 display surface

while c.fill((255,)*3): - Effectively while 1: but clears the screen as well

e=event.get(2);c.blit(image.load("I"),p);d.flip() - Only collect keyboard events, load the image stored in a png file called I and draw it. Update the screen

if e:x=e[0].key+1;q=x&2;b=q/2;p[b]=max(0,min(336+b*16,p[b]+(1-q)*(1-(2*x&2)))) - If there was an event, work out which arrow key was pressed (does weird stuff if you press other keys), and then change the position of the surface depending on which key you pressed.

| improve this answer | |
  • \$\begingroup\$ I really like your solution. \$\endgroup\$ – user9206 Oct 31 '15 at 16:57
  • \$\begingroup\$ @Lembik So a single byte file name is allowed? (I.e. without extension?). If so, I suggest to exclude filenames completely from the byte count. \$\endgroup\$ – mınxomaτ Oct 31 '15 at 17:12
  • \$\begingroup\$ @mınxomaτ I single byte file name is allowed but I don't want to invent a new scoring scheme. \$\endgroup\$ – user9206 Oct 31 '15 at 19:01
  • \$\begingroup\$ @Lembik Processing can't handle single-byte filenames... so this is unfair. \$\endgroup\$ – TheDoctor Oct 31 '15 at 19:09
  • 2
    \$\begingroup\$ @TheDoctor This just in: Different languages are different! I propose we also disallow challenges that take input from stdin, you know, because it's unfair that it takes so many characters to do that in Python... \$\endgroup\$ – Aleksi Torhamo Nov 1 '15 at 4:40

Haskell, 410 bytes

import Graphics.Gloss
import Graphics.Gloss.Interface.Pure.Game
main=do b<-loadBMP"b";play(InWindow""(400,400)(0,0))white 200((0,0),id)(b#)e(%)
e(EventKey(SpecialKey k)Down _ _)(c,_)|k==KeyUp=(c,fmap(+1))|k==KeyDown=(c,fmap(+(-1)))|k==KeyLeft=(c,((+(-1))?))|k==KeyRight=(c,((+1)?))
e(EventKey _ Up _ _)(c,_)=(c,id)
e _ w=w
_%(c,f)=(s?(s<$>f c),f)
s=min 200.max(-200)
b#((x,y),_)=Translate x y b
f?(a,b)=(f a,b)

The picture of the alien is expected in a file named b in .bmp format.

I'm new to the Gloss library, so maybe this is not optimal. Does anyone know if I can check if a key is pressed instead of tracking KeyUp/KeyDown events?

How it works: the last four parameters of play are the world state (initialized with ((0,0),id), a function to draw a picture from a state (#), an event handler (e) and a function that changes the state over time (%).

The state is a pair of x-y-coordinates and a function how to change them whenever % is called.

# moves the bitmap (b) to the current coordinates and draw it.

e looks for either KeyDown events of the cursor keys and sets appropriate functions in the state or for KeyUp of any key to reset the function in the state to the identity function.

% applies the function from the state to the current coordinates and checks the boundaries.

| improve this answer | |
  • \$\begingroup\$ I am always amazed you can even do this sort of thing in Haskell. Thank you! \$\endgroup\$ – user9206 Oct 31 '15 at 19:00

Elm, 240 bytes

import Graphics.Element as G
import Keyboard as K
import Time
import Signal exposing(..)
c=min 800>>max 64
i(x,y)=G.container x y G.middle(G.image 64 48"http://i.stack.imgur.com/CUweU.png")
main=i<~foldp(\{x,y}(v,w)->(c v+x,c w-y))(99,99)(sampleOn(Time.fps 200)K.arrows)

Try it here. Byte count is after replacing the URL with .png.

| improve this answer | |
  • \$\begingroup\$ I might have to learn Elm now. Thank you. \$\endgroup\$ – user9206 Nov 1 '15 at 19:50

AutoIt, 269 267 bytes

Dim $1,$2
Until 0

Requires the picture to be saved as b.bmp in the script directory. If you want to use a picture with actual transparency, you have to to convert it from PNG to a 32bit Bitmap (OT: a really unappreciated format).


We need to import Misc.au3 to get access to _IsPressed. A function that accepts a key code and returns True or False when the key is pressed.

The spec of the challenge is quite cool in the way that we have to create a 400px square window. The default (denoted as -1 or Default) size parameters are 400x400. The extended windows style is set to 34078728. This forces the window to be double-buffered and drawn from bottom to top. This is necessary to eliminate flickering as per the challenge requirement. In Windows 10, this unusual (and somewhat undocumented) combination of styles breaks the window title bar (all hovering effects are disabled).

$1 and $2 are declared and will hold the x and y offset of the picture loaded by the control $0. $3 becomes a pointer to the function _IsPressed to shorten the code significantly.

Since it is not required to be able to exit the program, this script runs in an infinite loop (Until 0).

$1+=$3("27")-$3("25") abuses the variant datatype in AutoIt by dynamically casting the boolean value returned from _IsPressed to an integer that can be added or sub'ed from the x offset. Same for y. $1=($1>336)?336:($1<0)?0:$1 uses the ternary operator known from C-like languages to perform a bounds check to stop the alien at the borders.

GuiCtrlSetPos moves the picture control to the new coordinates.

Here's a screenshot with a transparent alien (you can even move diagonally):


| improve this answer | |
  • \$\begingroup\$ Your background is not white! \$\endgroup\$ – sergiol Dec 14 '17 at 13:02

Lua + LÖVE, 291 characters

function l.update()d=l.keyboard.isDown
if d("left")and x>0 then x=x-1 end
if d("right")and x<336 then x=x+1 end
if d("up")and y>0 then y=y-1 end
if d("down")and y<352 then y=y+1 end
function l.draw()g.draw(i,x,y)end

This uses an unresizable 400 x 400 window. I had no success with adjusting love.keyboard.setKeyRepeat() to speed up the key reading, so I did the recommended way, polling each key's status.

As my relation with Lua for is not the best, neither this time managed to make looping shorter than the dump hardcoding of each key's condition.

| improve this answer | |

SpecBAS - 285 255 bytes

3 LET x,y=100
5 LET x=x+KEYST(39)-KEYST(37),y=y+KEYST(40)-KEYST(38)
6 LET x=CLAMP(x,1 TO 400),y=CLAMP(y,1 TO 400)
8 GO TO 4

Loads the image - colour 15 is bright white, so that becomes transparent.

Using the original image and default SpecBAS palette made it come out a bit odd, so line 2 switches them around to match the input image. Below image shows how it looks without line 2 and after.

enter image description here

The CLAMP command limits the graphic between 1 and 400 in both directions, saves on several IF...THEN statements.

Line 9 just waits for things to catch up and prevents flickering.

It moves one pixel at a time based on a boolean check of which key is pressed, so takes slightly longer than 5 seconds to go from side to side.

| improve this answer | |

Ruby with Shoes, 252 243 characters

Shoes.app{background white

This uses a resizable window starting at default 600 x 500. If you resize the window so the invader is left out, will come back when the next movement key is pressed.

The trick to satisfy the requirements is that invader's position is changed in increments of 4, but the actual movement is made with increment of 1 at 99 frames per second.

| improve this answer | |

Tcl/Tk, 242 bytes

grid [canvas .c -w 400 -he 403 -bg #FFF -highlightt 0]
.c cr i 30 24 -i [image c photo -fi .png] -t p
lmap {k b x y} "Up 1]>25 0 -5 Right 0]<368 5 0 Down 1]<376 0 5 Left 0]>30 -5 0" {bind . <$k> "if \[lindex \[.c coo p] $b {.c move p $x $y}"}
| improve this answer | |
  • \$\begingroup\$ To run it in any Linux: you must have Tcl and Tk installed; then copy the code to a file, let's say invaders.tcl; you need also to have the image saved as .png in the same folder. To run the script type wish invaders.tcl in a shell. PS : It can be more golfed if code is instead pasted into an interactive shell, as it supports by default abbreviated commands. \$\endgroup\$ – sergiol Dec 22 '17 at 2:00

JavaScript (using paper.js), 215 bytes

v=new Raster("http://i.stack.imgur.com/CUweU.png",99,99);p=new Size(4,0)
onKeyDown=e=>{p=new Size([[0,-4],[0,4],[-4,0],[4,0]]["udlr".indexOf(e.key[0])])}

paper.js is a JS graphics framework, which means it features many useful features regarding image manipulation. To run, just copy the above into the section on the left here, then, to move the alien around, click once in the right section to give it focus. If your browser can handle it, it should run at 60fps.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy