Skip to content

Instantly share code, notes, and snippets.

@maettig
Forked from 140bytes/LICENSE.txt
Created April 15, 2012 13:04
Show Gist options
  • Save maettig/2392672 to your computer and use it in GitHub Desktop.
Save maettig/2392672 to your computer and use it in GitHub Desktop.
decodeMinecraftColors in 140byte.es

You can use color codes in the Minecraft chat. I tried to create a parser in 140byt.es that converts a Minecraft chat line to HTML. I found two possible solutions, both 150 bytes. One with a lookup table and another with an algorithmic aproach (which I think is very cool).

Here are some things I learned while golfing this down:

  • Do you know String.slice() is the new String.substring()? It's shorter and more reliable. Be aware of the few different behaviors.
  • To convert a hexadecimal string to a number (“hex2dec”) you can use parseInt('FF', 16) or eval('0x' + 'FF') or ('0x' + 'FF') * 1 or '0x' + 'FF' | 0. Take care of the operator precedence.
  • The eight main colors are ordered like they are binary numbers: 0002, 0012, 0102, 0112, 1002, 1012, 1102, 1112. Thats why I can do this: 616 → 1102 → convert to a binary and parse as a hexadecimal string → 11016 → multiply with A16 → AA016 → add 55516 → FF516.
  • The most reliable solution would be to use /&[\dA-F].*/i in a loop. Solutions with [^&] stop coloring on additional ampersands (I can live with this). Solutions with &. either fail (not acceptable) or go black for invalid colors (may be acceptable on a bright background).
  • String.fontcolor() is a fascinating relic from the 90s. You should never use it. ;-)
function f(a, b)
{
return b //if called from the replace function
? b.fontcolor('#' + //surround fetched string by a <font> tag
'00000A0A00AAA00A0AFA0AAA55555F5F55FFF55F5FFF5FFF' //lookup table
.substr(('0x' + a[1]) * 3, 3)) //convert hex color code to dec
: a.replace(/&[\dA-F]([^&]+)/gi, f) //fetch color code followed by non-ampersands
}
function f(a,b){return b?b.fontcolor('#'+'00000A0A00AAA00A0AFA0AAA55555F5F55FFF55F5FFF5FFF'.substr(('0x'+a[1])*3,3)):a.replace(/&[\dA-F]([^&]+)/gi,f)}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2012 Thiemo Mättig <http://maettig.com>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "decodeMinecraftColors",
"description": "Converts a Minecraft chat line to HTML.",
"keywords": [
"color",
"fontcolor",
"html",
"parse"
]
}
<!DOCTYPE html>
<style type="text/css">
pre { background: #222; color: #FFF; display: inline-block; padding: 2em; }
</style>
<script type="text/javascript">
var decodeMinecraftColors = function f(a,b){return b?b.fontcolor('#'+'00000A0A00AAA00A0AFA0AAA55555F5F55FFF55F5FFF5FFF'.substr(('0x'+a[1])*3,3)):a.replace(/&[\dA-F]([^&]+)/gi,f)}
// My second solution, also 150 bytes:
//function f(a,b,c){return c?c.fontcolor('#'+(('0x'+('0x1'+b|0).toString(2))*10+(b>'7')*1365).toString(16).slice(2)):a.replace(/&([\dA-F])([^&]+)/gi,f)}
var test = '!0000!100A!20A0!30AA!4A00!5A0A!6FA0 (or #AA0)!7AAA!8555!955F!a5F5!b5FF!cF55!dF5F!eFF5 &this should be yellow (or white)!fFFF &this should be white';
test = test.replace(/!(.)((...)[^!]*)/g, '&$1<span style="background:#$3">NOT</span> $1 should become #$2\n');
test = decodeMinecraftColors(test);
document.write('<pre>' + test + '</pre>');
</script>
@maettig
Copy link
Author

maettig commented Apr 17, 2012

Just for the record, here is a possible solution that relies on external CSS.

.c0{color:#000}.c1{color:#00A}.c2{color:#0A0}.c3{color:#0AA}.c4{color:#A00}.c5{color:#A0A}.c6{color:#FA0}.c7{color:#AAA}
.c8{color:#555}.c9{color:#55F}.ca{color:#5F5}.cb{color:#5FF}.cc{color:#F55}.cd{color:#F5F}.ce{color:#FF5}.cf{color:#FFF}
function(a,b){for(;b=/&([\dA-F])(.*)/i.exec(a);)a=a.replace(b[0],'<span class="c'+b[1].toLowerCase()+'">'+b[2]+'</span>');return a}

Its possible to make this even smaller by replacing span with c and removing all " and .toLowerCase() (requires .ca,.cA and so on in the CSS). But this wont make it a 140byt.es solution because of the external data.

@xpansive
Copy link

I've probably spent a good 3 hours trying to get a single byte off this, but nothing seems to work. Good job :)

@maettig
Copy link
Author

maettig commented Apr 30, 2012

Thanks a lot for trying. One thing we can do is to remove the case-insensitive modifier: /&[\da-f]([^&]+)/g (149 bytes). Another idea is to rely on a little bit of CSS (143 bytes + 44 bytes CSS).

font[color="#"],font[color="#5"]{color:#FFF}
function f(a,b,c){return c?c.fontcolor('#'+(('0x'+('0x1'+b|0).toString(2))*10+(b>'7')*1365).toString(16).slice(2)):a.replace(/&(.)([^&]+)/g,f)}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment