The tileset (if you need it): http://img.ctrlv.in/img/14/10/19/5443f44c7eb78.png
You can produce your own tileset, subset or superset of this one.
You can use your tileset as a separate image file or include it in your code (for example, in base64). If it's separate, add its size in bytes to your score.

\$\begingroup\$@BetaDecay I could but... the frontier is very thin between including an image and producing a similar image programatically. Also, I'm curious to see someone else make another hardcoded solution in less than 4012 chars. Theoretically, the hardcoded image could fit in less than 2400 Unicode characters.\$\endgroup\$
– xemOct 19 '14 at 6:39

6

\$\begingroup\$@BetaDecay NES developpers were the code-golfers of the 80's ;) All the SMB game (dozens of sprites, palettes, sounds, musics, physics engine, game engine, AI, ...) fits in 32kb. It blows my mind. This challenge is a tribute to it, btw.\$\endgroup\$
– xemOct 19 '14 at 10:21

1

\$\begingroup\$@xem: Another one: The castle has complete brick blocks (f.e. at (202,10)) that differs from the other brick blocks (f.e. at (168,8)) which have orange lines at the top. Updated tileset: img.ctrlv.in/img/14/10/19/5443f44c7eb78.png\$\endgroup\$
– schnaaderOct 19 '14 at 17:26

1

\$\begingroup\$@Will Sorry, I don't know what PPM means, but basically, yeah, your program can use any file, gzipped or not, passed in argument or used directly using its path. Your score will be the size of your program (in chars) + the size of your files (in bytes)\$\endgroup\$
– xemOct 21 '14 at 15:20

8 Answers
8

x86 Machine Code, 172916191468 1382 Bytes

How it works: Tiles are generated by a combination of RLE compression, 2bit images, and procedural code. Once the tiles are generated in memory, the program then creates a matrix of tile indexes. This is first loaded with the repeating background. After that, the pipes, floating blocks, pyramids, and the flag pole are drawn procedurally. The pipes, hills, bushes, and pyramids can extend below the ground, but are covered up when the rock tiles get written next. Finally, the castle tile values are simply copied into the correct location.
To generate the image file, the BMP header and palette is stored in the file as data, and is written first. The program then runs through the matrix, writing the appropriate row from the corresponding tile for each position.

Usage: Run mario.com, it will generate "m.bmp", a standard BMP image file. The file is created as a hidden file, since that ended up being less bytes.

1205 -> 1186: function n renamed to N; new function n that operates on an array with delta coding

1186 -> 1171: hills and warps can be drawn "big" anytime, the lower parts get overdrawn by stone blocks; use d for both clouds and bushes; removed some unnecessary semicolons

This is a procedural attempt. There are patterns everywhere, one of the magic numbers is 48 (tile gap between clouds, bushes and mountains). The tileset is encoded as Base64 data url string and used as a CSS stylesheet. In Javascript, the 212x14 array m is filled with tile indices. See the commented unminified version for more details.

\$\begingroup\$Very impressive for a first draft, and thanks for putting so many efforts into it. Here are a few optimizations you could use: remove <html>, <head>, <body> tags, type="text/javascript", (or text/css) attribute, minify your JS code (use closure-compiler.appspot.com/home), then pack it ( use siorki.github.io/regPack.html) then obfuscate it in Unicode chars (use xem.github.io/obfuscatweet). But please, if you do so, keep the "unminified" version in your answer so thah people can read it. To count your score in number of chars, use mothereff.in/byte-counter. ;)\$\endgroup\$
– xemOct 19 '14 at 18:36

1

\$\begingroup\$Semicolons are automatically inserted at the end of lines, so you can safely remove those.\$\endgroup\$
– NinjaBearMonkeyOct 19 '14 at 19:29

Python3 163815761616 1513 bytes

581 code + 932 data

↑ This top is the original level. In the middle is the PPM generated by this script. At the bottom is a diff, and this shows that the palette and the map do not agree exactly! I put this down to data glitch and not my script ;)

# 759808 is 212*14 tiles at 16*16 each - the size of the map
import lzma;B,E,R,X=bytearray,759808,range,16
D=B(lzma.decompress(open("i","rb").read())+B(E)) # put canvas on end of data array
i,P=8218,lambda x,y:8622+y*54272+x*X # 8622 is length of decompressed data
def Q(s,n,d): # copies a 16x16 tile from s to d
# n is the stride for the source s, which is different for put and copy
for m in R(0,256,X):e=d+m*212;D[e:e+X]=D[s+m*n:s+m*n+X]
for j in R(4): # there are 4 command sections
j&=1;i+=1+j;r=D[i-1]<<8 # j is now truthy if this is a copy buffer
for k in R(D[i]): # this many commands
a,b,w,h,c,d=D[i+1:i+7];q=h>32 # unpack it; put doesn't use all these
# do the put/copy
for z in R(((a,h-32)[q],w*h)[j]):Q((D[(i+4+z,i+5)[q]]<<8,P(a+z%w,b+z//w))[j],1+211*j,
(P(b+z,w),P(c+(z%w,w-z%w-4)[r&1],d+z//w))[j])
i+=((3+a,5)[q],6)[j&1];r>>=1 # move i to next command
for y in R(2,11):Q(28<<8,1,P(198,y)) # special case flagpole
o=open("o","wb");o.write(b"P6 3392 224 255 ") # header for PPM
for c in D[8622:]:o.write(D[8192+c*3:][:3]) # decompress palette to RGB

(bytes counted without comments, line-unwrapped and second level indent being \t)

Data (base64-encoded to be pasted; decode and save as a file named "i"):

The data file is a custom format I made for this problem and is stored as LZMA.

First all 32 tiles are serialized. There are 9 colours in the palette, and this takes 8219 bytes uncompressed. (I found that trying to compress the tiles at 4-bits-per-pixel didn't help compression at all. I did not try and brute-force the best ordering of the tiles, and I probably lose a few points here.)

There are 212x14=2968 blocks making up the map.

Then the instructions to recreate the map are now encoded.

First comes a section of "put" commands that place some sequence of tiles from the tile-palette on the map at a particular x,y. As there are 32 tiles, I specify a run of the same tile using a number bigger than 32 rather than the tile index.

Then comes a section of "copy" commands that copy some rectangle of the current map to some other place. There is a special bit-mask that marks if the copy should be mirrored.

(I said short, but that's actually nearly half the total command buffer needed to make the whole map; the vast majority of the bytes in the data are the 32 source tiles themselves)

Then comes a second section of "put" commands and finally another section of "copy" commands.

Because these can overwrite each other, I can build copy parts that I later erase or change.

I can doubtless shave a few more bytes off it by - for example - turning puts into small copies and by doing the eval trick on a gzipped source-code, or playing with PNG (which is DEFLATE with image-specific filters) too. But I like things verbose as they are.

\$\begingroup\$Great work! About the diff, if the images of the OP are wrong (which is totally possible, I made that with paint), can you please provide better images for the map (or better values for the palette, idk). Thanks :)\$\endgroup\$
– xemOct 22 '14 at 7:53

\$\begingroup\$Also, all the clouds and bushes appear in the diff. Do you think it's because of colors or because of an offset of 1-2 pixels between the two images? Looks like an offset to me\$\endgroup\$
– xemOct 22 '14 at 7:56

\$\begingroup\$@xem I don't think the problem is so bad and I don't think it needs urgent fixing. In your map the tile at 3,11 seems to be missing from the palette. There seems to be some noise in the map around 20,12 and the general outline of trees and clouds may be an off-by-one alignment glitch in the palette? There's only 9 colours, so it can't be any kind of blending artifact.\$\endgroup\$
– WillOct 22 '14 at 7:58

\$\begingroup\$the bricks are totally my fault yeah :D. I tried to hide a goomba and made a 1px offset. I didn't touch bushes and clouds at all though. But I think your image is righter than mine, so I take it :p\$\endgroup\$
– xemOct 22 '14 at 8:02

\$\begingroup\$I also failed hiding mario and all the other goombas in front of mountains, actually, d'uh. Thanks!\$\endgroup\$
– xemOct 22 '14 at 8:04

\$\begingroup\$Let's get dirty. Here are some tips to lose a few dozen chars: you can remove the quotes in <body style='width:3392px'> and in <div class='...'>. You can write your <style> element at the end of your <body>. You can omit </style> and </body>. You can remove the leading "image/png" and the trailing "ggg==" in your base64. You can omit the <style> element altogether, and get rid of the longhand notation "background-position" and the div classes, by setting an inline style to each div, like this: <div style='background:url(data:;base64,...)-Mpx -Npx;width:16px;height:16px;float:left'>\$\endgroup\$
– xemOct 22 '14 at 12:49

1

\$\begingroup\$Good work! I thought my procedural attempt would beat coding the map, now I have something to catch up with ;)\$\endgroup\$
– schnaaderOct 22 '14 at 14:07

JavaScript: 3620 (ouch)3429 3411

Updates

Update #1: Removed var definitions and put the variable declarations inside brackets of first use. Removed getElementById() as it is also available on load as a varaible by ID. Using cloneNode() instead of createElement('CANVAS'). Renamed main from xMx to M. Removed the scaling feature :(, (still available in the example.)

Added some comments to the expanded code. (That code is not updated with the removals. The line below, ("Mini code"), is.)

Update #2: Removed the main function M() as a whole and let the code run at root. This would require the code be placed inside a load wrapper or at end of document.

Blah blah:

– Demo at end of post.

Using canvas as base for an attempt to solve this. Ended up with about 6000 chars, but after some fiddling (and a custom compression - with fiddling and tweaks on that as well) I'm now at the stated number. Still high, but then again the quality is good ;-).

It also includes a scale option, first argument to the xMx(), aka "main" function. 1 = normal size (as in 16 bit tiles). There is not much room for tweaking, so if one use fractions some of the tiles does not fit best together. At whole numbers it should be OK. [1]

But: warning, going up, quickly eats resources and makes for a huge canvas. (It is all painted in one go.) When the original width is 3392 pixels it quickly becomes huge. [1]

[1] As of update #1 this has been removed. It is present in the demo.

Stats:

Main code : 870

Compression:

Main data : 2176 (17,480 bits)

Key : 128 ( 1,024 bits)

Code : 236

Whole she bang : 2540

Decompressed data : 5608 (44,864 bits)

Total : 3410 [1]

[1]: +1 byte, ";", between the main / data code.

The compressed data is one array, Å, and one "object", Ø. Where Å is holding the tile placement and the Ø holding the data for painting the tiles. The Ø should have been an array as well – but as my code started out with something like this:

var tiles = {
brick: [
...
],
qmark: [
...
],
};

It ended up as is. I'll perhaps see if I can't fix this. Ideally I would have made it parseable with JSON instead of eval(), but that would not be a point in a contest like this. As it is now it can not be JSON parsed due to missing quotes around the id parts.

Demo

Here is a fiddle where one can view the code in action. The code in the fiddle I have expanded somewhat (And added some HTML / CSS styling around the world – but the image of itself is self sustained.):

\$\begingroup\$woah, That's great! +1 for the nice presentation in the provided link. +1 for the homemade packer. If only you could avoid using non-ASCII chars (like Å,Ø), you could use tools like xem.github.io/obfuscatweet to compress (much) more, because the score is counted in chars.\$\endgroup\$
– xemOct 21 '14 at 16:38

\$\begingroup\$You don't need to use var to declare variables; they are automatically declared when you define them, so you should be able to remove var D,C,c,S,s,R,F,Z, from your code. (also you can replace /.{2}/ with /../.)\$\endgroup\$
– NinjaBearMonkeyOct 21 '14 at 22:48

\$\begingroup\$@hsl: Yes, thank you. I thought of doing the removal of var but left it as I had to run when I posted it. Done now. The RegExp was a nice one, somehow did not manage to see that ;)\$\endgroup\$
– RuniumOct 22 '14 at 0:35

\$\begingroup\$@xem: Thank you. There is no problem using the Unicode trick with ÆØÅ as those are within the first set (0x80+0xNN) where 0xNN is the interesting part. I was not sure, however, if that was "legal" as it depends on how one view it. A code point above 0x7f has a length of 2. As such the code would be equal in length (+1 byte if odd - or, actually longer by 45 bytes due to the ev(a|i)l routine). If it is "legal" then OK.\$\endgroup\$
– RuniumOct 22 '14 at 0:40

\$\begingroup\$You're right, those chars are okay: eval(unescape(escape('𨑬𩑲𭀨𘣆񆃅𘠩').replace(/uD./g,'')))works as intended. And yes, your score is counted in characters so you can totally use tricks like Unicode compression and eval.You could have a score near 1750 with that!\$\endgroup\$
– xemOct 22 '14 at 7:42

Python 13311326131412721268 1217 bytes

(code: 219214202186 182, data: 362+750=1112363+723=1086 346+689=1035)

The idea here is that I first generate a map one 16th the size in each dimension, where each pixel is colored by the index in the tile map. To ensure that the same pallette was used, I first combined the given tile map and the map into a single image. From this and the tile map (both minified pngs) we can now regenerate the original map. (The code for this process can be seen below the submission code). It should be noted that I saved the tile map as a file "t" and the generated map as an image "m"

EDIT: I started exploring how the ordering of the tiles influenced the compression, and for the sake of exploration I generated 10000 random permutations and compressed them with this result:
This was all with the tiles in one line, as that reduced the code quite a bit. I tried similar things with different configurations (2*16, 4*8), but none with a better average result.

Assuming that png would compress better with greater continuous similar regions, I built a tool that would let me move the tiles around, and using what I perceived to be a more continuous image I got the tile image down to 723 from 750b.

EDIT2: Afte rmuch (much) more analysis on how png actually works, and a lot of experimenting (still ongoing), the images have now been compressed even further. Link below updated. I'll write more about this analysis later when it is complete.

\$\begingroup\$I know that this is a non-optimal solution, but why have you answered your own question so soon?\$\endgroup\$
– Beta DecayOct 18 '14 at 21:22

1

\$\begingroup\$An additional option would be to use a sprite sheet and an array of where the sprites go. o_O\$\endgroup\$
– CompassOct 18 '14 at 21:46

7

\$\begingroup\$@BetaDecay to get rid of this "hardcoded" approach asap and let people do something more creative, like, use a sprite sheet and an array. :p\$\endgroup\$
– xemOct 19 '14 at 6:28

1

\$\begingroup\$And I would disagree with that. It's pretty easy to encode binary data as Unicode via simple methods, and thus we'll all end up writing uninteresting extra code in order to pretend binary data is really Unicode. That is the opposite of being creative in my opinion.\$\endgroup\$
– Sir_LagsalotOct 21 '14 at 16:44

1

\$\begingroup\$Then the task really becomes two unrelated tasks - Encode the image data as small as possible, then convert the compressed data to as few Unicode characters as possible. I think the second task hinders being creative on the first, and would be better off as it's own challenge.\$\endgroup\$
– Sir_LagsalotOct 21 '14 at 17:52

Your Answer

If this is an answer to a challenge…

…Be sure to follow the challenge specification. However, please refrain from exploiting obvious loopholes. Answers abusing any of the standard loopholes are considered invalid. If you think a specification is unclear or underspecified, comment on the question instead.

…Try to optimize your score. For instance, answers to code-golf challenges should attempt to be as short as possible. You can always include a readable version of the code in addition to the competitive one.
Explanations of your answer make it more interesting to read and are very much encouraged.

…Include a short header which indicates the language(s) of your code and its score, as defined by the challenge.

More generally…

…Please make sure to answer the question and provide sufficient detail.

…Avoid asking for help, clarification or responding to other answers (use comments instead).