http://www.perlmonks.org?node_id=89269

I'm obsessed. I admit it. I play pinball like a madman. hours and hours a day of it; I never stop. Sometimes, when I go away from the chatterbox and miss stuff, it's because I'm playing a game or two. More often, it's five or six. But the other day I reached a new low: I stopped playing. I made perl do it for me.

To explain a little better, I own a pinball machine (Medieval Madness, Williams 1997), so I really do play an insane amount of pinball. And I've gotten fairly high scores; but not every game is a new high; not every game is exciting. So i wanted to come up with a meta-game to play over the course of many pinball games. originally, it started as just "how long can i keep getting a better score than i did the previous game?" -- but monotonic increase does not encourage playing to the best of ones ability (because a really good game can break a streak early), and i didn't like that.

So I came up with a few other ideas, and i wanted to test them out -- but they'd take hundreds of games to see if they were fair and interesting. Which is, finally, where perl comes in.

first, i needed to model a single game of pinball. now, my top score is 178 million, and some games come in as low as (approximately) 178 thousand, but it's not a linear scale; the average is not 89 million, or even 17.8 million, but about 10 million. and, of course, i'v emade 178 million my top score in under 2000 games; given 10000 or 100k games, there's no hard upper limit. so just using rand() was right out of the question. on the other hand, i didn't want to map out every switch on the table and figure out probabilities for everything. so a model is in order. it turns out that the score increases faster thee longer you play, with a few large jumps which also get bigger as you go. i poked and tweaked, and here is what i ended up with:

sub score { # score is reported in millions. my $t = .1; # there is an actual minimum score. my $b = 0; # this counts the balls. my $round = 0; while (++$round) { # it's a 3 ball table, so ... last if $b > 3; # get some points, $t += int(rand $round*10)/10; # risk draining (++$b && redo) if (int rand int sqrt (9*$round)); # possibly get an extra ball $b-- unless int rand(5); # get the big points if we made it this far $t += 2 * $round; # the rest of this isn't interesting # let me know if something too unlikely happens... print ("Divine Intervention: $t points in $round rounds.\n") and last if not $round % 20; # ... and stop it if it gets out of hand print ("Apocolypse!") and last if $round > 400; } # point out statistically unlikely games; # otoh, they should range up to 200 ~ .01% of the time, # and some real games do fail to get off the ground. warn "Great game: $t in $round!\n" if ($round > 12 or $t > 150); warn "Horrid try: $t in $round!\n" if ($round < 1 or $t < .2); return $t; }

a simple "run this a million times and tell me the average" eventually revealed that it was doing pretty much what i wanted. so i wrote a few meta-games, and ended up with this as my basic game:

### # # this sub was originally separated out so i could try averages vs. # weighting the averages toward the most recent game. # i chose to weight them because it won't allow any one game to be too + hard. sub game { my $historic = shift; my $game = score; return (($historic+$game)/2); } ### the meta game itself. # # basically, each round you are trying to keep your (weighted) average + score above # the target (set at the start of the metagame, and incremented with e +ach game). # $spares is the number of games you can "do over"; # pretend that didn't happen and play that game again. sub meta { my ($historic, $target, $spares) = @_; my $games = 0; my $archive = 0; do { { $archive = $historic; ($historic) = game($historic); if ($historic < $target and $spares) { --$spares; $historic = $archive; redo; } } } while (++$games and $historic > $target and ++$target); return ($archive, --$target, $games); }

i usually play meta(score+score, 10, 0);

.

Clarification: this isn't a whole program, this is just a couple subs. if you want to see what it's doing, you need a wrapper of some sort. this one will play the meta-game as i currently play it on my real machine, and tell you some stuff:

sub show_meta { my ($first, $target, $spares) = @_; my ($lastgood, $made, $games) = meta (@_); print <<OUTPUT; First two games averaged to $first. Played for $games (using $spares spares) (Making target $made with $lastgood and then having a bad game) OUTPUT } show_meta (((score + score)/2), 10, 0);
of course, if you really want to see what's going on, you can add prints into the game and meta subroutines.

.

Replies are listed 'Best First'.
Re: Virtually Pinball
by jorg (Friar) on Jun 18, 2001 at 18:13 UTC
    I tried getting your code to work on perl 5.6 win2k but it didn't produce any output, oh well maybe i'm not using it correctly ..

    Anyways I think it is a shame that not more pinball games are developed for the gaming market (PC that is). The last ever decent pinball game i played on PC was Pinball Fantasies, it absolutely rocked!
    I recently bought the UT engine powered Adventure Pinball but it just wasn't up there..

    Any monks out there that can recommend classic pinball games I could try ? (they *don't* have to be written in Perl :)

    Jorg

    "Do or do not, there is no try" -- Yoda

      Check out http://www.thefew.com/vptables/

      You'll need Visual Pinball and VPinMAME. Visual Pinball is a pinball construction program with many available tables. VPinMAME is an interface that lets original arcade pinball roms control the Visual Pinball machine. The above site should be a good starting point for you.