#!/usr/bin/env perl # okc-whine --- generate nice guy rants # Author: Noah Friedman # Created: 2009-04-01 # Public domain # $Id: okc-whine,v 1.4 2009/05/09 19:17:30 friedman Exp $ # Commentary: # Government agencies are encouraged to integrate this software into # weapons control systems and other instruments of destruction. # Code: $^W = 1; # enable warnings package SentenceGenerator; use strict; sub new { my $type = shift; my %args = (@_); my $class = ref ($type) || $type; bless \%args, $class; return \%args; } sub string { my $self = shift; ucfirst ($self->substitute ($_[0] || $self->{start} || "sentence")); } sub substitute { my ($self, $category) = @_; $category = $1 if $category =~ /^[!*](.*)/; my $list = $self->{grammar}->{$category}; return $self->iterate_list ($self->random_item ($list)) if $list; return $category; } sub random_item { my ($self, $list) = @_; my $n = int (rand (@$list)); return $list->[$n]; } # Iterate over string STR, replacing all substrings beginning # with a '*' or '!' with a random selection from the appropriate list. sub iterate_list { my ($self, $str) = @_; my @list = $self->split_string ($str); for my $elt (@list) { next if length ($elt) <= 1; my $c = substr ($elt, 0, 1); if ($c eq '*') { $elt = $self->substitute ($elt); } elsif ($c eq '!') { $elt = ucfirst ($self->substitute ($elt)); } } join ("", $self->fixup_plurals (@list)); } # Tokenize sentence so substitution words are separated. # The character `^' can be used to join suffixes to the end of a # substitution token, but do not show up in the resulting list. # e.g. "Many *word^s with *sub, with 2^32 at *end." # => ("Many " "*word" "s with " "*sub" ", with 2^32 at " "*end" ".") sub split_string { #my $self = $_[0]; local $_ = $_[1]; my @list; my $p = 0; pos ($_) = $p; while (1) { last unless /([*!][_a-z-]+)/ig; if ($p < pos ($_)) { my $stop = pos ($_) - length ($1); push @list, substr ($_, $p, $stop - $p); } push @list, $1; pos ($_) += 1 if (substr ($_, pos ($_), 1) eq '^'); $p = pos ($_); } push @list, substr ($_, $p) if $p < length ($_); return @list; } # Rudimentary pluralization correction sub fixup_plurals { my ($self, @list) = @_; for (my $i = 0; $i < (@list - 1); $i++) { next unless substr ($list[$i+1], 0, 1) eq 's'; next unless length ($list[$i]) > 2; local $_ = $list[$i]; if (/y$/) { $list[$i] =~ s/([^ou])y$/$1ie/; } elsif (/(?:[xs]|ch)$/) { $list[$i] .= 'e'; } elsif (/^(?:wo)?man$/) # irregular form { $list[$i] =~ s/an$/en/; $list[$i+1] =~ s/^s//; } elsif (/^person$/) # irregular form { $list[$i] = 'people'; $list[$i+1] =~ s/^s//; } } return @list; } # Take a long string and word-wrap it to no more than $width columns sub fold { my ($self, $string, $width) = @_; my @line; local $_ = $string . " "; while (/\G(.{0,$width}\S)\s+/go) { push @line, $1; } join ("\n", @line); } package main; use strict; my $okc_grammar = { sentence => [ "Why do *generalization?", "Why do I see so many *person^s complaining about how *generalization, on this site?", "Can't a *good_adj_person find a *good_adj_person who just wants *want_list?", "I think *generalization, and *person^s say that they really want *want_list, but *generalization.", "If you think *generalization, you should see what it's like when someone *mean_actions your *an_object and leaves you with nothing but *an_object.", "See how you like being stuck with a *an_object, then you'll really be complaining. I just think you're all spoiled.", "Isn't this a dating site?", "I mean, jeez *person^s!", "!ima_niceguy, who wants *want_list and I can't find someone who is willing to *action_to me.", "I'm not trying to *do_something or make anyone *do_something, but people always react as though I'm the *bad_adj one.", "Why do *person^s always *do_something when I *do_something?", "Why won't *person^s just *do_something?", "I don't get why so many people are uptight about how *generalization.", "Why don't you all just pull your *any_object^s out of your *any_object^s and get over yourself?", "I just don't think it's fair that *generalization, when *ima_niceguy and I can't get a single *person to *action_to me or even *action_to me.", "Is it too much to expect that *generalization?", "I think all *any_adj *person^s should *action_to me. It's only polite.", "When I *do_something, *any_adj *person^s should *action_to me.", ], generalization => [ "*person^s never write back", "all *person^s want the *person with *want_list", "everyone goes for the *person with *want_list", "*person^s say that they really want *want_list", "*person^s think *want_list is a big deal", "all these *person^s on this site should have all the *any_object^s", "*sex_adj *person^s *do_verb other *sex_adj *person^s just to get the attention of *person^s", ], ima_niceguy => [ "I have a lot of *quality to offer", "I'm a *good_adj_person", "I'm just a *good_adj_person", ], want_list => [ "*quality", "a fast *fast_object", "a *sex_adj *sex_object", "*want_list, or *want_list", # recursive ], good_adj_person => [ "*good_adj *person", "*sex_pref *gender", "*good_adj, *good_adj_person", # recursive "really *good_adj_person", # recursive ], person => [ "man", "guy", "boy", "jerk", "woman", "gal", "girl", "ho", "bitch", "slut", "whore", ], gender => [ "male", "female", "transgendered", "hermaphrodite", ], sex_pref => [ "homosexual", "heterosexual", "bisexual", "trisexual", "asexual", "transsexual", ], an_object => [ "*quality", "*fast_object", "*sex_object", ], any_object => [ "*fast_object", "*sex_object", ], quality => [ "good sense of humor", "good looks", "youth", "money", ], fast_object => [ "bike", "bitchin' Camero", "Porche", "Trans Am", "Ferarri", "Dodge Dart", "computer", ], sex_object => [ "ass", "body", "cunt", "dick", "figure", "hard disk", "penis", "sphincter", "ten-incher", "vagina", ], sex_adj => [ "enormous", "erotic", "expandable", "hard-core", "huge", "impertinent", "indecent", "loose", "prepubescent", "prurient", "pulsing", "rapacious", "tight", "tiny", "tumescent", "virgin", "voluptuous", ], good_adj => [ "academic", "adventurous", "ambidextrous", "ambitious", "amusing", "authentic", "charismatic", "chivalrous", "convenient", "funny", "garden-variety", "grammatical", "honest", "laid-back", "nice", "philanthropic", "young", ], bad_adj => [ "abusive", "adulterous", "alcoholic", "bloated", "disgusting", "egocentric", "fat", "flatulent", "geriatric", "hypocritical", "idiotic", "ignorant", "incompetent", "insipid", "mean", "misanthropic", "nasty", "old", "pathetic", "perverted", "ponderous", "preposterous", "presumptuous", "puerile", "repellent", "repugnant", "sadistic", "selfish", "shallow", "ugly", ], any_adj => [ "*bad_adj", "*good_adj", "*sex_adj", ], mean_actions => [ "fucks over", "pisses on", "dumps", "craps on", "punches", "abandons", "attacks", ], action_to => [ "talk to", "fuck", "go down on", "laugh at", "reply to", "drink with", "have dinner with", "go to the movies with", "write bad checks for", "write back to", ], do_verb => [ "attack", "cruise", "cheat on", "insult", "stalk", "message", "woo", "wink", "proposition", "talk dirty to", "have sex with", "kiss", "ogle", ], do_something => [ "*do_verb *bad_adj *person^s", "*do_verb *sex_pref *person^s", "*do_verb *any_adj *person^s", ], }; sub main { my $whine = SentenceGenerator->new (grammar => $okc_grammar); my $width = 75; my $count = 5; # Generate a screed of $count sentences my $rant = join (" ", map { $whine->string } (1 .. $count )); # word-wrap it and ship it! print $whine->fold ($rant, $width), "\n"; } main (@ARGV); # eof