ASCII Art Programming

From EggeWiki
Revision as of 00:30, 17 February 2010 by Brianegge (talk | contribs) (Created page with 'ASCII art programming is a rare form of programming, where your program is itself artwork. Artwork can be crafted by hand, but it's easier to create the code programatically. F…')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

ASCII art programming is a rare form of programming, where your program is itself artwork. Artwork can be crafted by hand, but it's easier to create the code programatically. For this example, I'll show you how to create ASCII Art programs in Java.

To start, create a small program in Java, and compile it. Second, find a simple image and use an online ASCII Art Generator to turn in into an ASCII image.

For example, I have this ASCII art:

                 ;D########D;                   
                j################f                
             .######################,             
            W#G####################E##            
          E#:########################.##          
         E# ########################## #W         
        #W;############################LD#        
       #K ##############################.j#       
      #W.################################jf#.     
     ## ################################## K#     
    j# K################################### KE    
    # ,####################################f #.   
   ## #################j  i################# D#   
   #.G############EK#;      .##L############# #   
  fW ############ #E          t# W########### jW  
  #: ########### #G            i# ###########, #  
 :# D########## ##              E# ########### #j 
 G# ##########; #                #  ########## W# 
 #D ########## DW                i# ##########.t# 
 #t #########K #.                 # j#########j # 
 #.i#########. #                  #. #########E # 
 # j#########  #                  #i #########K # 
 # f#########  #                  #t #########W # 
 # f#########  #                  #i #########W # 
 #.i#########i #                  #  #########E # 
 #;.#########K #;                 # j#########L # 
 #D ########## t#                KK ##########.t# 
 i# ##########E #E              :# t########## WE 
  # G##########;L#              ## ##########W #, 
  #: ###########;;#            #E ###########. #  
  ;W ############GE#,        :##t############ LL  
   #:i#############W##j    ,################E #   
   E# ###################################### W#   
    #. ####################################: #    
    .# t##################################E #j    
     E# W################################# ##     
      ## ################################ E#      
       EW ############################## G#       
        ## ############################ K#        
         i#;K#########################:#L         
          i#Gi######################Gj#f          
            f######################W#E            
              K#####################              
                 L##############G                 
                     tGEKKKDf.   

And this Java program: <geshi lang="java5"> package org.egge;

import java.util.Calendar;

public class CountDown {

private static final Calendar lastDay = Calendar.getInstance(); public CountDown() { lastDay.set(2010, 1, 19); Calendar today = Calendar.getInstance(); int workDays = 0; int totalDays = 0; while(today.compareTo(lastDay) < 0) { if (today.get(Calendar.DAY_OF_WEEK) >= Calendar.MONDAY && today.get(Calendar.DAY_OF_WEEK) <= Calendar.FRIDAY) { if (!(today.get(Calendar.DAY_OF_MONTH) == 26 && today.get(Calendar.MONTH) == 0)) workDays++; } totalDays++; today.add(Calendar.DAY_OF_MONTH, 1); } System.out.println("Brian has " + totalDays + " calendar days and " + workDays + " working days left."); } } </geshi>

Next, take your compiled class file, gzip it, and then base 64 encode it. Now, run the following Ruby script, to replace the text in the ASCII art with your compiled class.

<geshi lang="ruby">

  1. !/usr/bin/env ruby

if ARGV.empty? then

 puts "#{$0} ascii-file base64-source"

end

def getb64(file)

 c = file.getc
 if not c then
   return '='.unpack('c*')[0]
 end
 char = [c].pack('c*')
 if char =~ /\n/ then
   return getb64(file)
 end
 c 

end

b64 = File.open(ARGV[1]) File.new(ARGV[0]).each_byte do |c|

 char = [c].pack('c*')
 if char =~ /[^ \n]/ then
   c = getb64(b64)
 end
 STDOUT.putc c

end </geshi>

Take the output, and quote add quotes around it using sed.

<geshi lang="bash"> ./subascii.rb dollar <(gzip -c ../bin/org/egge/CountDown.class | base64) | sed 's/^/ "/; s/$/" +/' </geshi>

Lastly, modify the following program, so the classloader uses your image/program.

<geshi lang="java5"> import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream;

public class Macquarie extends ClassLoader { private static final String DOLLAR = " " + " H4sICNUeRUsA " + " A0NvdW50RG93bi5jbG " + " FzcwB9VFtPE0EU/oZedikLlF " + " IuctEqgi0Viqh4KaBSQFfLxUCI " + " Pg7tWBaW3WY7lZDoG3/C+KLxwWcxKU " + " YT H33wJ/hjxDNtMZgSt9mdM+fyne +c " + " OdOfv79+BzCJpxqaGCKuV0iJQkGkMm7Zkf " + " Pu nqPBzxDe5i95yuZOIbWyuS1ykkGzeUnO8 " + " 32GaLZqLUvLTmW4LZw899IM+nTOthxLzjL44okN " + " Bn /GzYsQWtCqwaBcjVEG2tDO0FIQ0nRKkjs5 wd " + " Ad T5yVoBkMHQYCCDK0Zy1HLJd3N4W3zjdtCopk 3R " + " y 3N7hnqX1dGZyu8gnBh24DUeiKTK+Bc+gjjiVBV QX " + " jp mkmNpRhwMAgzpOhoAz +uJkwlTpm4CIukZrn8 wy " + " B+In7ZQPDGGFozrm7Re 6JdZehJ34W84TZjDh G " + " NS T+aezafkmKXQ NJ XC V0t0xJu2rhlp ta " + " 9S xHrklP8N20h nG CP hUoyViYK1t2X n " + " g6 JhhCc1S3E9v ip Vg IY5hUxV4nuD qd " + " U1 FpRX4MNw1M4 R a Vz4tFIskwQ OU " + " 2O NdTpHXcYei M5 eo FxfJ8vxTjTr6a " + " LK 2gphnGzkr2 H8 h ZBbnnejukrS H " + " a4oUcV5D3DTzA H E2 UdGtRNHPxR q " + " x 0CPNY1LBAS G f0 zcBD1QStqF S " + " 2 Q0cqt6ySGt / Go afxDUg3r6Z b " + " V 5xozsmTmXS 8 0p Xcru1b1yTP 7 " + " SzxYn2+Qmtu2c u J RUtt2v6CjS s " + " yNDR0qnTVGPrU 0J L UBB0avQRKuy S " + " t6 tG+IPQZ4U9 Vh wh 9Q7SSmkIC6Kxq " + " FU j0JJD1wE8/4 PX oE boOEe7QfvnD nR " + " X 0EFDWN+Ob/YipZ Fi roD/wHkM1KR z8 " + " gN 6krqS+d2hJBkh6 g+ BBEzvwsaTur+ B " + " Ck qAqGHp7/OMQV75h7 HkkdYRrgxXciNyu4 O6 " + " AEmZIqOBeBRlF11elO4t 2+upEqRlhIh+lu9ENA 7 " + " 1o RT/d72GyT5BlDh1YoIIekccy2Z6R1yv0VEscrR VE " + " Np PWKCw8pvLprwAFPEGWWtALgSWSfBSrPPzHBO7 X " + " wD S0MGrkSJNqz0q1Xat/AFWUiNflBAAA====== == " + " == ================================== == " + " == ================================ == " + " == ============================== == " + " == ============================ == " + " ================================ " + " ============================== " + " ========================== " + " ====================== " + " ================ " + " ========= ";


@Override public Class<?> loadClass(String name) throws ClassNotFoundException { ByteArrayOutputStream bo = new ByteArrayOutputStream(); try { byte[] decodeBuffer = new sun.misc.BASE64Decoder().decodeBuffer(DOLLAR.replace(" ", "")); GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(decodeBuffer)); int i; while((i = gzip.read()) != -1) { bo.write(i); } } catch (IOException e) { throw new RuntimeException(e); } byte[] b = bo.toByteArray(); return name.equals("org.egge.CountDown") ? defineClass(name, b, 0, b.length)  : findSystemClass(name); }

public static void main(String[] args) throws Throwable { new Macquarie().loadClass("org.egge.CountDown").newInstance(); } } </geshi>

A few hints: The size of the gzipped, base64 encoded file must be smaller than the non-whitespace characters in your image. Since base64 ignores the equals sign, this can be used to fill out the rest of the image. Unfortunately, Java doesn't come with a base64 method as part of the library, but there is a publicly accessible one in the sun.misc package. Even Sun has public methods they'd prefer to be 'module private' or non-exported. Come up with some code? Send me a link, I've love to see it.

See Also