Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Your program must take as input a line of characters, like this:

@+.0

And output the characters sorted by how dark they are, like this:

.+0@

Requirements:

  • You must use a monospaced font for darkness detection.

  • You must find out how many pixels each character takes up. You must actually draw the character and count pixels, i.e. you can't just hardcode pixel amounts.

    • As a more concrete rule: if you switched fonts, your program should still work. Furthermore, your program should be able to switch fonts by simply changing a variable or value or string in the code.
  • If you use antialiasing, you must count pixels as percentages of a fully black pixel. For example, an rgb(32, 32, 32) pixel will count as 1/8 of a full pixel. Disregard this rule if your characters are not antialiased.

  • After counting pixels, you must sort the characters by the amount of pixels, and output them in order.

  • This is , so the shortest code in bytes will win.

share|improve this question
6  
Strangely enough, this is also appropriate for RPG.se! –  corsiKa Mar 7 at 18:12
1  
What, no correction for display gamma in the darkness calculation? –  Ilmari Karonen Mar 7 at 18:46
5  
Can we use a white font? If so, I think I have this one wrapped up. –  Paul Mar 7 at 22:46
    
@Paul Lol, no you may not. –  Doorknob Mar 8 at 1:45

12 Answers 12

up vote 19 down vote accepted

Mathematica, 112 110 108 bytes

This can still likely be golfed further. Assumes the string is in variable s.

And now uses a correct syntax to sort one list by another list.
Lucky test cases -> "Oh yeah, that works" -> Facepalm
Thanks for the sharp eyes, David Carraher.

Update: Replaced OCR A with Menlo because I realized that on OSX the OCR A font family name is actually OCR A Std. So I was sorting a default font instead of the real deal. Menlo is also monospaced with the same byte count, so no net gain or loss.

I've put up a hosted CDF export of the notebook, so you can see the code in action if you wish. I'm still figuring out how to add some interactivity to web-hosted CDFs, so for now it's just static.

c=Characters@s;Last/@Sort[Transpose@{Total[1-#&/@ImageData@Rasterize@Style[#,FontFamily->"Menlo"],3]&/@c,c}]

Output for s = FromCharacterCode /@ Range[33, 135]; with "Courier"

enter image description here

Output for same, but with FontFamily "Monospace":

enter image description here

Note that the final results are shown in MM's internal font, not in the font being sorted. Hence, you see the differences in the font chosen reflected in the sort. The CDF link shows both, though, for the completists.

Ungolfed code:

s = FromCharacterCode /@ Range[33, 135];
c = Characters@s;
Last /@ Sort[
    Transpose@{Total[1 - # & /@ 
        ImageData@Rasterize@Style[#, FontFamily -> "Menlo"], 3] & /@ c, c}]
share|improve this answer
1  
Examine for this input: "" <> (FromCharacterCode /@ Range[33, 135]) –  David Carraher Mar 7 at 14:56
    
Default family or not, the specs required "your program should be able to switch fonts by simply changing a variable". Because of anti-aliasing values, it is possible to get some sorts that look wrong, but I'll have another look at the full range and see if anything is wrong. –  Jonathan Van Matre Mar 7 at 15:23
1  
what a terrific language! just found out about the new programming language effort by Wolfram the other day, looking forward to that. –  the0ther Mar 7 at 17:15
1  
You can save two bytes by changing Characters[] to Characters@ and Reverse[] to Reverse@. –  Michael Stern Mar 7 at 17:41
2  
I don't want to post a similar answer, but I tried myself and it came up shorter StringJoin@SortBy[Characters@"@+.0",ImageData@Binarize@Rasterize@Style[#,FontFa‌​mily->"Monospace"]~Total~2&] –  swish Mar 7 at 19:57

Bash + ImageMagick: 164 147 148 characters

while read -n1 c
do
o=`convert -size 20x15 xc: +antialias -font cour.ttf -draw "text 0,10 '$c'" xpm:-`
o=${o//[^ ]}
a[${#o}]+=$c
done
echo "${a[@]}"

Sample run:

bash-4.1$ echo -n '@+.0' | bash graysort.sh
. + 0 @

Separators are inserted between grayness groups. Characters with identical grayness level are not separated:

bash-4.1$ echo -n 'abcdefghijklmnopqrstuvwxyz' | bash graysort.sh
i cl jortz esv ax u df bgnpq y hk w m
share|improve this answer
    
+1 for sorting using bash array indices instead of sort –  DigitalTrauma Mar 7 at 16:16

QBasic, 259 bytes

SCREEN 1
DIM a(255)
FOR i = 32 TO 255
    CLS
    PRINT CHR$(i);
    FOR p = 0 TO 64
        a(i) = a(i) + POINT(p MOD 8, p \ 8)
    NEXT p
NEXT i
FOR p = 0 TO 96
    FOR i = 32 TO 255
        IF a(i) = p THEN PRINT CHR$(i);
    NEXT i
NEXT p

I did this for fun, so it's technically non-compliant to the rules in one way. It doesn't take a list of characters, but instead prints all characters from 32-255 and uses that instead. If you really want to see a version which complies with this rule, please tell me.

It also fails another technicality: "Furthermore, your program should be able to switch fonts by simply changing a variable or value or string in the code." There is no easy way to do this from within QBasic. However, the program will of course work fine with any codepage of your choosing.

Lastly, I could squeeze away a few characters (mostly whitespace that the QBasic IDE helpfully inserts,) but it's probably not worth it since this answer stands no chance of winning anyway.

QBasic sort characters by darkness

share|improve this answer
    
Plus one for doing it for fun! –  Jonathan Van Matre Mar 7 at 23:56

Javascript + Canvas and Browser DOM (280 237 235 bytes)

Updated version with suggetions from Fors and toothbrush in comments:

function m(x){a=document.createElement('canvas').getContext('2d');a.font='9px Monaco';a.fillText(x,y=i=0,20);for(;i<3600;)y+=a.getImageData(0,0,30,30).data[i++];return y}alert(s.split('').sort(function(a,b){return m(a)-m(b)}).join(''))

More readable version:

// Scoring function - Calculates darkness for single character
function m(x) {
    a = document.createElement('canvas').getContext('2d');
    a.font = '9px Monaco';
    a.fillText(x, y = i = 0, 20);
    for (; i < 3600;) y += a.getImageData(0, 0, 30, 30).data[i++];
    return y
}
// Assume input is in variable s and alert as output. Comparison function now expression.
alert(s.split('').sort(function (a, b) {
    return m(a) - m(b)
}).join(''))

Can maybe be golfed more.

I'm new to this site, so I am unsure how input is normally read for Javascript answers. I assume input is contained in a variable named s. If this is not OK, I will update the answer and the char count.

JSFiddle of updated version.

JSFiddle of first version.

share|improve this answer
    
I really like it, however there are many possible improvements here. The variables c and s are unnecessary (a=document.createElement('canvas').getContext('2d') and y+=a.getImageData(0,0,30,30).data[i]), the element doesn't need to be appended, the zero initialisation of y can be combined with that of i (i=y=0), the post-increment of i can be combined with the adding of y (for(...;y+=...[i++]);), and the typeface Monaco is mono-spaced and has a shorter name than Courier. –  Fors Mar 7 at 19:04
    
Thanks! I'm not a seasoned golfer yet, so your comments help a lot. I have incorporated them into the code now. –  waxwing Mar 8 at 7:41
    
Javascript answers here normally read input through prompt(); but this is also fine. –  Kartik Mar 8 at 12:48
    
You can remove y=i=0; and change a.fillText(x,0,20) to a.fillText(x,y=i=0,20). –  toothbrush Mar 9 at 1:06
    
Thanks, added that as well! Two characters are two characters! –  waxwing Mar 13 at 20:10

Postscript, 381

Here's something completely different, just for fun. As most fonts are vector anyway, 'counting pixels' is a little odd, isn't it. Calculating glyph shape area, while being correct way, is not that easy. An alternative can be scanning a rectangle and counting 'hits' when a point is inside a glyph shape, and Postscript has operators for this kind of checks. Though, true, scanning and insideness-testing is just a weird way of counting pixels.

(%stdin)(r)file token pop/Courier 99 selectfont[1 index length{0}repeat]0 1 99{0 1 99{0 1 5 index length 1 sub{newpath 9 19 moveto 3 copy 7 index exch 1 getinterval false charpath infill{3 index exch 2 copy get 1 add put}{pop}ifelse}for pop}for pop}for 0 1 99 dup mul{0 1 3 index length 1 sub{dup 3 index exch get 2 index eq{3 index exch 1 getinterval print}{pop}ifelse}for pop}for

.

(%stdin) (r) file token pop
/Courier 99 selectfont
%/DejaVuSansMono 99 selectfont
%/UbuntuMono-Regular 99 selectfont
[ 1 index length {0} repeat ]   % str []
0 1 99 {
    0 1 99 {
        0 1 5 index length 1 sub {
            newpath 
            9 19 moveto
            3 copy              % str [] n m i n m i
            7 index exch        % str [] n m i n m str i
            1 getinterval       % str [] n m i n m s
            false charpath      % str [] n m i n m
            infill              % str [] n m i bool
            {3 index exch 2 copy get 1 add put} {pop} ifelse
        } for
        pop
    } for
    pop
} for
% un-comment next line to print number of 'hits' for each glyph
%
% dup {=} forall
%
% next is 'lazy sort'
0 1 99 dup mul {                % str [] i
    0 1 3 index length 1 sub {  % str [] i j
        dup 3 index exch        % str [] i j [] j
        get 2 index eq          % str [] i j bool
        {3 index exch 1 getinterval print} {pop} ifelse
    } for
    pop
} for
()=

And here are results for 3 different fonts (selection of which can be un-commented, above):

$ echo '(.-?@AByz01)' | gs -q -dBATCH d.ps
.-?1z0yA@B
$ echo '(.-?@AByz01)' | gs -q -dBATCH d.ps
.-?z1yA0B@
$ echo '(.-?@AByz01)' | gs -q -dBATCH d.ps
.-?1zyA0B@
share|improve this answer
    
Counting pixels...counting pixel intersections...tomato....tomahto.... –  Jonathan Van Matre Mar 10 at 14:13

PHP, 298 characters

I've added a few line breaks so you can see it in all its hideousness:

<?php
$s=@$_GET[s];$a=array();$v=imagecreate(16,16);$f='imagecolorallocate';
$f($v,0,0,0);for($i=0;$i<strlen($s);$i++){$c=$f($v,$i,0,1);
imagechar($v,5,2,$n=0,$s[$i],$c);for($y=16;$y--;)
for($x=16;$x--;)$n+=($c==imagecolorat($v,$x,$y));
$a[]=ord($s[$i])+($n<<8);}sort($a);foreach($a as $v)echo chr($v);

This code uses the GD fonts that come built-in with PHP. The second argument of imagechar() selects the font (numbers from 1 to 5 are valid).

Example:

Input:  !@#$%^&*-=WEIX,./'
Output: '-.,^=!/*IE%X#$&@W

If you insert the following on top of the code shown above, then you'll be able to supply the list of characters in your web browser.

<?php
define("FONT_SIZE",5);
if(@$_SERVER['PATH_INFO']=='/a.png') {
  $s = $_GET['s'];
  $im = imagecreate(strlen($s)*(FONT_SIZE+4)+4,FONT_SIZE+12);
  imagecolorallocate($im,255,255,128);
  $c = imagecolorallocate($im,0,0,0);
  imagestring($im,FONT_SIZE,2,0,$s,$c);
  header("Content-Type: image/png");
  imagepng($im);
  imagedestroy($im);
  exit();
}
$me = $_SERVER['PHP_SELF'];
$t1 = $img = "";
if ($t1=htmlspecialchars(@$_GET['s'])) {
  $t2=urlencode($_GET['s']);
  $img="<p><img src=\"$me/a.png?s=$t2\" /></p>";
}
echo <<<END_HTML
<html>
<body>
$img
<form action="$me" method="get">
<input type="text" name="s" size="40" value="$t1" />
<input type="submit" value="Go" />
</form>
END_HTML;
if(!isset($_GET['s'])) exit();
?>
share|improve this answer
1  
if you use imagecreatetruecolor you can drop the first allocate and use the function name directly on the second, for -11. [] instead of array(). and foreach($a as$v) works as well –  Einacio Mar 7 at 16:38

GTB

This has code to be the second hardest code I've ever written for a calculator. No hard-coded pixel values, it actually draws the text on a graph and loops to count each pixel.

0→I`_%I<l?_T;1,1,s;_,I,1
C;pT;{0,1,2,3,4,5},{0,1,2}→L1(I
0→I%I<l?_T;1,C;L1(I)>L1,I

Input

,O.i

Output

.,iO
share|improve this answer
    
If this works, it is the best answer so far. Whatever is GTB? –  slater Mar 8 at 17:52
    
@slater Why don't you click on the link and find out? –  Timtech Mar 8 at 18:00
    
Thank you for pointing out that your post title is indeed a link. –  slater Mar 8 at 18:07
1  
The domain to get the software necessary to decrypt your compiler is dead. Dead here too. I translated the code by hand for the fun of testing it out, but you seem to have opened 10 parens and closed only one, so I am unsure how to resolve that. The compiler in my head says: "Error::unmatched identifier" :) –  Jonathan Van Matre Mar 9 at 3:06
    
@JonathanVanMatre Don't worry; TI-84 automatically closes them for you. –  Timtech Mar 13 at 21:00

Perl (with GD) (159)

use GD;sub i{$i=new GD'Image 5,8;$B=colorExact$i 9,9,9;colorExact$i 0,0,0;char$i gdTinyFont,0,0,@_,1;$_=unpack"B*",wbmp$i 0;y/0//c}print+sort{i($a)-i($b)}@ARGV

usage:

> perl dark.pl 1 2 3 @ # . , : ~ $ M i I s S
.,~:i13Is2S$M@#

edit: shortened to 159 chars

share|improve this answer

Java - 468 450 444

public static void main(String[]a){class c implements Comparable<c>{char d;c(char e){d=e;}public int compareTo(c o){return e(d)>e(o.d)?1:-1;}int e(char f){int a=0,x,y;BufferedImage img=new BufferedImage(99,99,1);img.getGraphics().drawString(""+f,9,80);for(y=0;y<99;y++)for(x=0;x<99;x++)a+=img.getRGB(x,y);return a;}}c[]s=new c[a[0].length()];int i=0;for(char d:a[0].toCharArray())s[i++]=new c(d);Arrays.sort(s);for(c d:s)System.out.print(d.d);}

@+.0abcdefghijklmnopqrstuvwxyz -> .irl+jcvtfxyzsuonkheaqpdb0wgm@

Ungolfed:

    public static void main(String[] a) {
    a = new String[]{"@+.0abcdefghijklmnopqrstuvwxyz"};
    class c implements Comparable<c> {
        char    d;

        c(char e) {
            d = e;
        }

        @Override
        public int compareTo(c o) {
            return e(d) > e(o.d)? 1 : -1;
        }

        int e(char f) {
            int a = 0, x, y;
            BufferedImage img = new BufferedImage(99, 99, 1);
            img.getGraphics().drawString("" + f, 9, 80);
            for (y = 0; y < 99; y++)
                for (x = 0; x < 99; x++)
                    a += img.getRGB(x, y);
            return a;
        }
    }
    c[] s = new c[a[0].length()];
    int i = 0;
    for (char d : a[0].toCharArray())
        s[i++] = new c(d);
    Arrays.sort(s);
    for (c d : s)
        System.out.print(d.d);
}
share|improve this answer
    
Tip: avoid public or private modifiers as far as possible; that saves unnecessary bytes –  masterX244 Mar 8 at 20:41
    
forgot all about that –  Mark Jeronimus Mar 9 at 11:49

R, 195 characters

A=strsplit(scan(,""),"")[[1]];cat(A[order(sapply(A,function(x){png('a',a='none',fa='monospace');frame();text(0,0,x);dev.off();sum(apply(png::readPNG('a'),c(1,2),function(x)any(x!=1)))}))],sep="")

Indented with comments:

A=strsplit(scan(,""),"")[[1]] #Take characters as strings and split into single chars
cat(A[order(sapply(A,function(x){ #Apply the following function to each char and order accordingly
                 png('a',a='none',fa='monospace'); #Open empty png without antialiasing and with monospace font
                 frame(); #create empty plot
                 text(0,0,x); #add the char as text to the plot
                 dev.off(); #close png device
                 sum(apply(png::readPNG('a'), #read it back as rbga 3d matrix
                           c(1,2), #check every layer (R, G, B, A)
                           function(x)any(x!=1))) #if any are not 1, send TRUE
                 }))], #Sum all TRUEs
    sep="") #Prints to output

Example:

> A=strsplit(scan(,""),"")[[1]];cat(A[order(sapply(A,function(x){png('a',a='none',fa='monospace');frame();text(0,0,x);dev.off();sum(apply(png::readPNG('a'),c(1,2),function(x)any(x!=1)))}))],sep="")
1: @+.0
2: 
Read 1 item
.+0@
> A=strsplit(scan(,""),"")[[1]];cat(A[order(sapply(A,function(x){png('a',a='none',fa='monospace');frame();text(0,0,x);dev.off();sum(apply(png::readPNG('a'),c(1,2),function(x)any(x!=1)))}))],sep="")
1: 1234567890
2: 
Read 1 item
1723450689

The gestion of fonts in R plots being platform-dependent, I cannot guarantee that it works on PC, but it does on a Mac (OS X 10.7.5, R 2.14.2).

share|improve this answer

PHP - 485

Demo:

$ php pcg-23362.php "@+.0"
.+0@

Code:

<?php $f='x.ttf';$d=array();foreach(str_split($argv[1]) as$_){$B=imagettfbbox(50,0,$f,$_);$w=abs($B[4]-$B[0]);$h=abs($B[5]-$B[1]);$im=imagecreate($w,$h);imagecolorallocate($im,255,255,255);imagettftext($im,50,0,0,$h-$B[1],imagecolorallocate($im,0,0,0),$f,$_);$b=$w*$h;for($x=0;$x<$w;$x++)for($y=0;$y<$h;$y++){$z=imagecolorsforindex($im,imagecolorat($im,$x,$y));$color=$z['red']*$z['green']*$z['blue'];$b-=$color/0x1000000;}$d[$_]=$b / ($w * $h);}asort($d);echo implode(array_keys($d));
share|improve this answer
    
Hey, send me a copy of X.TTF...the shortest font I have is OCR A. ;-D –  Jonathan Van Matre Mar 9 at 3:08

Python + freetype-py: 147

import sys,freetype as F;f=F.Face('m.ttf');f.set_char_size(99);print(sorted([(f.load_char(c)or sum(f.glyph.bitmap.buffer),c)for c in raw_input()]))
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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