/* * Jaiku - a quick & dirty random Haiku generator. * * by David M. Chess, http://www.davidchess.com/ * comments to Jaiku@davidchess.com. * * Written sometime in 1999; committed to the public domain. * */ import java.awt.*; import java.awt.event.*; import java.applet.*; /* Now this implementation is done as just one single class, single threaded, and like that. Maybe just because I'm lazy, or maybe to demonstrate that Java can be used for Q&D code just like (well, maybe not *just* like) Perl. A real proper object-oriented implementation of this would probably include: - an interface RandomPoet, with a method like getRandomPoem(), returning an array of strings, one per line, - an interface class PoemUI, providing the basic user-interface to random poems, and taking an object implementing RandomPoet in the constructor, - a class GrammarDriver, which takes a set of (weighted) production rules, and drives them randomly forward until it gets to a set of terminal symbols, - a class RandomHaiku, implementing RandomPoet, by giving GrammarDriver a set of productions corresponding to the various "nounphraseplural(n)" hacks below, - an applet to wrap it all together, instantiating a RandomHaiku and passing it to PoemUI for processing. But really how much advantage would we get from all that? While writing this, I was reminded: - How much more painful it is to write quick-and-dirty code in a bondage-and-discipline language like Java than in a forgiving language like Perl, - How much sense it'd make if HTML font tags had a "drop-shadow" attribute. */ public class Jaiku extends Applet implements ActionListener { Button b = null; int space = 0; int start = 0; Dimension d = null; String line1 = null; String line2 = null; String line3 = null; Color textcolor = null; Color shadowcolor = null; Font thisfont = null; public void init() { /* First just process the incoming params */ String param = null; String buttonLabel = getParameter("button"); if (buttonLabel == null) buttonLabel = " "; /* "font" param can have values like "SansSerif", */ /* "Serif", "Monospaced", or actual font names. */ String fontname = getParameter("font"); if (fontname == null) fontname = "SansSerif"; /* "fontsize" is in points */ param = getParameter("fontsize"); int fontsize = (param == null) ? 12 : Integer.parseInt(param); /* It's always bold; that could be made a param also. */ thisfont = new Font(fontname,java.awt.Font.BOLD,fontsize); /* bgr,bgg,bgb is red,blue,green for the background */ param = getParameter("bgr"); int bgr = (param == null) ? 255 : (Integer.parseInt(param)); param = getParameter("bgg"); int bgg = (param == null) ? 255 : (Integer.parseInt(param)); param = getParameter("bgb"); int bgb = (param == null) ? 255 : (Integer.parseInt(param)); this.setBackground(new Color(bgr,bgg,bgb)); /* fgr,fgg,fgb is red,blue,green for the text */ param = getParameter("fgr"); int fgr = (param == null) ? 0 : (Integer.parseInt(param)); param = getParameter("fgg"); int fgg = (param == null) ? 0 : (Integer.parseInt(param)); param = getParameter("fgb"); int fgb = (param == null) ? 0 : (Integer.parseInt(param)); textcolor = new Color(fgr,fgg,fgb); /* shr,shg,shb is red,blue,green for the drop-shadow, if any */ param = getParameter("shr"); if (param != null) { int shr = (param == null) ? 128 : (Integer.parseInt(param)); param = getParameter("shg"); int shg = (param == null) ? 128 : (Integer.parseInt(param)); param = getParameter("shb"); int shb = (param == null) ? 128 : (Integer.parseInt(param)); shadowcolor = new Color(shr,shg,shb); } this.setForeground(textcolor); /* Make the very first poem */ makelines(); /* Set up the button and so on */ d = this.getSize(); b = new Button(buttonLabel); b.addActionListener(this); this.add(b); } /* Display the current poem when needed */ public void paint(Graphics g) { Graphics useg = g; if (space==0) { space = g.getFontMetrics().getHeight() + 2; start = d.height / 2; } useg.setFont(thisfont); useg.clearRect(0,0,d.width,d.height); if (shadowcolor!=null) { useg.setColor(shadowcolor); useg.drawString(line1,21,start+1); useg.drawString(line2,21,start+space+1); useg.drawString(line3,21,start+space+space+1); } useg.setColor(textcolor); useg.drawString(line1,20,start); useg.drawString(line2,20,start+space); useg.drawString(line3,20,start+space+space); } /* When anything at all happens, make a new poem */ public void actionPerformed(ActionEvent ev) { makelines(); this.repaint(); } /* In case anyone calls this... */ public String getAppletInfo() { return "Title: Jaiku\nAuthor: David chess, Jaiku@davidchess.com\nGenerates random 5-7-5 quasi-poems."; } /* That takes care of all the applet and UI mechanics; from here on in we're doing the poetry. So to speak! This is the stuff that would be in the grammar and the GrammarDriver in a Real Implementation(tm). It's all pretty self-explanatory. I'm constantly adding another word or two to this bit... */ private void makelines() { double r = Math.random(); if (r<0.4) { line1 = phrase(5); line2 = phrase(7); line3 = phrase(5); } else if (r<0.55) { line1 = nounphraseplural(5); line2 = verbphraseplural(7); line3 = phrase(5); } else if (r<0.7) { line1 = adverbphrase(5); line2 = phrase(7); line3 = phrase(5); } else if (r<0.85) { line1 = phrase(5); line2 = phrase(7); line3 = adverbphrase(5); } else { line1 = phrase(5); line2 = adverbphrase(7); line3 = phrase(5); } } private String adverbphrase(int sy) { if (sy==1) return "here"; if (sy==2 && Math.random()<0.1) return "by night"; if (sy==2) return prep(1) + " " + nounphraseplural(1); if (sy==3 && Math.random()<0.1) return "in autumn"; if (sy==3 && Math.random()<0.1) return "in winter"; if (sy>3 && Math.random()<0.3) return sconj(1) + " " + phrase(sy-1); int sy1 = 1+(int)(Math.random()*2); return prep(sy1) + " " + nounphraseplural(sy-sy1); } private String sconj(int sy) { switch (sy) { case 1: switch ((int)(4*Math.random())) { case 0: return "and"; case 1: return "if"; case 2: return "while"; case 3: return "when"; } } return "?"; } private String prep(int sy) { switch(sy) { case 1: switch ((int)(4*Math.random())) { case 0: return "on"; case 1: return "with"; case 2: return "by"; case 3: return "for"; } case 2: switch ((int)(5*Math.random())) { case 0: return "above"; case 1: return "beside"; case 2: return "beyond"; case 3: return "over"; case 4: return "under"; } } return "?"; } private String phrase(int sy) { int nsy = 1+(int)(Math.random()*(sy-1)); return nounphraseplural(nsy) + " " + verbphraseplural(sy-nsy); } private String nounphraseplural(int sy) { double r = Math.random(); if (sy==1) return nounplural(1); if (r<0.35) return nounplural(sy); if (r<0.45) return "my "+ nounplural(sy-1); if (r<0.55) return "the "+ nounplural(sy-1); if (r<0.60) return "these "+ nounplural(sy-1); if (r<0.65) return "those "+ nounplural(sy-1); if (sy<3) return nounplural(sy); if (sy>3 && r<0.80) switch ((int)(4*Math.random())) { case 0: return "piles of " + nounplural(sy-3); case 1: return "a dozen " + nounplural(sy-3); case 2: return "a thousand " + nounplural(sy-3); case 3: return "a million " + nounplural(sy-3); } int sy1 = 1+(int)(Math.random()*(sy-3)); return nounphraseplural(sy1) + " " + conj(1) + " " + nounphraseplural(sy-(1+sy1)); } private String conj(int sy) { if (Math.random()<0.5) return "and"; else return "or"; } private String nounplural(int sy) { double r = Math.random(); if (sy==1) return simplenounplural(sy); if (sy<4 && r<0.5) return simplenounplural(sy); int sy1 = 1+(int)(Math.random()*(sy-1)); if (sy1>=4 && Math.random()<0.4) { return nounplural(sy-sy1) + " " + adjphraseas(sy1); } else { if (sy-sy1>3) sy1 = sy-3; return adjphrase(sy1) + " " + simplenounplural(sy-sy1); } } private String adjphraseas(int sy) { int sy1 = 1+(int)(Math.random()*(sy-3)); return "as " + adjphrase(sy1) + " as " + simplenounplural(sy-(sy1+2)); } private String adjphrase(int sy) { return adjphrase(sy,true); } private String adjphrase(int sy, boolean color_ok) { int sy1; switch(sy) { case 1: switch ((int)(15*Math.random())) { case 0: return (color_ok) ? "red" : "bright"; case 1: return (color_ok) ? "blue" : "dark"; case 2: return (color_ok) ? "green" : "thin"; case 3: return "good"; case 4: return "old"; case 5: return "young"; case 6: return "new"; case 7: return "sad"; case 8: return "proud"; case 9: return "sweet"; case 10: return "dry"; case 11: return "soft"; case 12: return "pale"; case 13: return "bright"; case 14: return "dark"; } case 2: switch ((int)(12*Math.random())) { case 0: return (color_ok) ? "purple" : "flaming"; case 1: return "ancient"; case 2: return "tired"; case 3: return "fervent"; case 4: return "eager"; case 5: return "happy"; case 6: return "joyful"; case 7: return "hopeful"; case 8: return "broken"; case 9: return "sour"; case 10: return "fevered"; case 11: return "endless"; } case 3: switch ((int)(5*Math.random())) { case 0: return "autumnal"; case 1: return "wonderful"; case 2: return "beautiful"; case 3: return adjphrase(1,false) + " " + adjphrase(2,color_ok); case 4: return adjphrase(2,false) + " " + adjphrase(1,color_ok); } default: sy1 = 1+(int)(Math.random()*(sy-1)); return adjphrase(sy1,false) + " " + adjphrase(sy-sy1,color_ok); } } private String simplenounplural(int sy) { double r = Math.random(); switch (sy) { case 1: switch ((int)(21*Math.random())) { case 0: return "clouds"; case 1: return "birds"; case 2: return "boys"; case 3: return "men"; case 4: return "waves"; case 5: return "trees"; case 6: return "doves"; case 7: return "winds"; case 8: return "dreams"; case 9: return "ghosts"; case 10: return "joys"; case 11: return "roads"; case 12: return "cats"; case 13: return "streams"; case 14: return "hearts"; case 15: return "fish"; case 16: return "flames"; case 17: return "stars"; case 18: return "stones"; case 19: return "bells"; case 20: return "rocks"; } case 2: switch ((int)(20*Math.random())) { case 0: return "seasons"; case 1: return "women"; case 2: return "girls"; case 3: return "children"; case 4: return "horses"; case 5: return "echoes"; case 6: return "herons"; case 7: return "beaches"; case 8: return "sorrows"; case 9: return "shadows"; case 10: return "rivers"; case 11: return "tigers"; case 12: return "meadows"; case 13: return "mountains"; case 14: return "valleys"; case 15: return "regrets"; case 16: return "dancers"; case 17: return "nightmares"; case 18: return "bridges"; case 19: return "winters"; } case 3: switch ((int)(7*Math.random())) { case 0: return "thunderstorms"; case 1: return "memories"; case 2: return "reveries"; case 3: return "fallen trees"; case 4: return "sunflowers"; case 5: return "promises"; case 6: return "melon seeds"; } case 4: return "bottles of air"; case 5: return "dandelion seeds"; } return "?"; } private String verbphraseplural(int sy) { int sy1; if (sy==1) return verbintransitiveplural(1); if (sy<5 & Math.random()<0.3) return verbintransitiveplural(sy); if (Math.random()<0.2) { sy1 = 1+(int)(Math.random()*(sy-1)); return verbintransitiveplural(sy1) + " " + adverbphrase(sy-sy1); } if (sy>3 & Math.random()<0.2) return "still " + simpleverbintransitiveplural(sy-2) + " here"; if (sy>3 & Math.random()<0.2) return "are " + nounphraseplural(sy-1); if (sy>=5 & Math.random()<0.3) return "are " + adjphraseas(sy-1); sy1 = 1+(int)(Math.random()*(sy-1)); return verbtransitiveplural(sy1) + " " + nounphraseplural(sy-sy1); } private String verbtransitiveplural(int sy) { double r = Math.random(); if (r<0.5) return simpleverbtransitiveplural(sy); if (sy<2) return simpleverbtransitiveplural(sy); int sy1 = 1+(int)(Math.random()*(sy-1)); if (sy1>4) sy1=4; return simpleadverb(sy1) + " " + simpleverbtransitiveplural(sy-sy1); } private String simpleadverb(int sy) { switch (sy) { case 1: switch ((int)(4*Math.random())) { case 0: return "may"; case 1: return "still"; case 2: return "must"; case 3: return "would"; } case 2: switch ((int)(4*Math.random())) { case 0: return "never"; case 1: return "always"; case 2: return "seldom"; case 3: return "sometimes"; } case 3: switch ((int)(4*Math.random())) { case 0: return "no longer"; case 1: return "usually"; case 2: return "eagerly"; case 3: return "hastily"; } case 4: return "eternally"; } return "?"; } private String simpleverbtransitiveplural(int sy) { double r = Math.random(); switch (sy) { case 1: switch ((int)(9*Math.random())) { case 0: return "love"; case 1: return "watch"; case 2: return "touch"; case 3: return "see"; case 4: return "taste"; case 5: return "lift"; case 6: return "trust"; case 7: return "cross"; case 8: return "pass"; } case 2: switch ((int)(7*Math.random())) { case 0: return "follow"; case 1: return "elude"; case 2: return "summon"; case 3: return "worship"; case 4: return "subdue"; case 5: return "embrace"; case 6: return "adore"; } case 3: switch ((int)(3*Math.random())) { case 0: return "contemplate"; case 1: return "enliven"; case 2: return "admire"; } case 4: return verbtransitiveplural(1) + " and " + simpleverbtransitiveplural(2); case 5: return verbtransitiveplural(2) + " and " + simpleverbtransitiveplural(2); case 6: return verbtransitiveplural(3) + " and " + simpleverbtransitiveplural(2); case 7: return verbtransitiveplural(3) + " and " + simpleverbtransitiveplural(3); } return "?"; } private String verbintransitiveplural(int sy) { double r = Math.random(); if (r<0.5) return simpleverbintransitiveplural(sy); if (sy<2) return simpleverbintransitiveplural(sy); int sy1 = 1+(int)(Math.random()*(sy-1)); return simpleadverb(sy1) + " " + simpleverbintransitiveplural(sy-sy1); } private String simpleverbintransitiveplural(int sy) { int sy1; if (sy>3) { sy1 = 1+(int)(Math.random()*(sy-2)); if (sy1>3) sy1 = 3; return verbintransitiveplural(sy-(sy1+1)) + " and " + simpleverbintransitiveplural(sy1); } switch (sy) { case 1: switch ((int)(12*Math.random())) { case 0: return "sleep"; case 1: return "run"; case 2: return "live"; case 3: return "glow"; case 4: return "wait"; case 5: return "dance"; case 6: return "turn"; case 7: return "rest"; case 8: return "play"; case 9: return "weep"; case 10: return "sing"; case 11: return "bend"; } case 2: switch ((int)(8*Math.random())) { case 0: return "follow"; case 1: return "smile"; case 2: return "wonder"; case 3: return "embrace"; case 4: return "settle"; case 5: return "whisper"; case 6: return "vanish"; case 7: return "wander"; } case 3: switch ((int)(3*Math.random())) { case 0: return "awaken"; case 1: return "remember"; case 2: return simpleverbintransitiveplural(1) + " and " + simpleverbintransitiveplural(1); } } return "?"; } }