#!/usr/local/bin/perl # # Rock Scissors Paper # a basic artificial life demo # using the unix file system as "environ" # # by miff, 3/2000 # # written for the "Creative Perl Daemon" contest. # # here's the scoop: # -we spawn 3 daemons, rock, scissors, and paper # -given a base dir, and using all the dirs below it # that we dont specifically exclude in @nouse, we will # allow rock, scissors, and paper to "claim" any dir. # as its territory. NOTE! make sure you have write access # to all subdirs in question! (you can use root user and / # if you like - or use someuser and ~someuser) # -rock will determine its own life force by the number of # .rocks that are existing at the beginning of any cycle; # rock then tries to write more .rocks - it succeeds if # the dir is empty of (.rock/.scissors/.paper) and gets # one more ".rock" territory. if it encounters ".scissors" # rock gets the terrirory and an extra life point bonus. # If it encounters ".paper" in a dir, rock loses a life point # and moves on. # -the other elements behave similarly # -the number of life points determines the number of "turns" # an element has before a mandatory sleep event. # -if any element has no territories left, it dies. # -to watch what is going on, tail -f /tmp/RSPlog # -use ps aux | grep ROCK to see a process, killall -9 rsp to # stop them all! # # mad shouts and 40z to all my homiez # and # the obligatory plug: mike patton rules. # # EFNet #perl sucks. # # miff@9mm.com # #use NOTHING. we aint submitting other ppl's code... my $life = 4; #set an initial life to 4 - meaning we will be able to #write 4 of "me" (rock/scis/pap) initially. my $alive = 1; #lets hope we stay alive! #put the dir that you want to be "root" of your env here: my $basedir = '/home/miff'; #PUT ALL DIRS YOU DONT WANT TO USE HERE --> my @nouse = ('/dev','/proc','/cdrom','/floppy','/usr/src','/var/lib'); my $sleeptime = 5; #sleep for 5 seconds between cycles my $logfile = '/tmp/RSPlog'; # lets all write to the same log my $this_rsp; #which rock/scis/paper are we? my $this_desc; #R/S/P description (for log)? my $we_beat; #which rock/scis/paper do we beat? my $beats_us; #which rock/scis/paper beats us my @elements = ('ROCK','PAPER','SCISSORS'); #and, to comply with the "rules", we have to set umask and set initial working dir: # damn rules. rules are not for perl people. i'll comply so my entry doesnt get # booted out of the contest, but under strong protest. chdir ("/"); umask 0; my @alldirs; my @tmpdirs = split "\n", `find '$basedir' -type d `; my $d,$nu; #now remove the no use dirs: foreach $d (@tmpdirs) { my $ok = 1; foreach $nu (@nouse) { if ($d =~ /$nu/) { $ok = 0; } } if ($ok) { push @alldirs, $d; } } #lets clear the logfile and some old rsps if they exist! print "Clearing prior environment\n"; system ("cat /dev/null > $logfile"); system ("rm `find '$basedir' -name .rock` "); system ("rm `find '$basedir' -name .scissors` "); system ("rm `find '$basedir' -name .paper` "); #TIME TO SPAWN OUR ROCK, SCISSORS, AND PAPER!! # note to judge: we use a double fork. my $element; foreach $element (@elements) { print "forking for $element \n"; my $spawn_id = fork(); die "fork failed: $!" unless defined $spawn_id; if ($spawn_id) { #we are the parent waitpid($spawn_id,0); } else { #we are the child my $dfork = fork(); die "double fork failed $!" unless defined $dfork; if ($dfork) { #we are the intermediary - must die!!! exit 0; } #critical! clear out @elements! $this_desc = $element; @elements = ''; } } #now we are a happily forked rock, scissor, or paper! #(defined by $element) if ($this_desc eq "ROCK") { $this_rsp = ".rock"; $we_beat = ".scissors"; $beats_us = ".paper"; } elsif ($this_desc eq "PAPER") { $this_rsp = ".paper"; $we_beat = ".rock"; $beats_us = ".scissors"; } elsif ($this_desc eq "SCISSORS") { $this_rsp = ".scissors"; $we_beat = ".paper"; $beats_us = ".rock"; } else { print "all processes launched; tail -f $logfile for progress! \n"; exit(0); #parent exits here. } $0 = $this_desc; #set our proc name! open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '>/dev/null'; open STDERR, '>/dev/null'; #do an initial write: open LOGFILE, ">>$logfile"; print LOGFILE "$this_desc born with $life life points. \n"; close LOGFILE; #set up randomizer srand(time ^ $$); my @provinces; #the dirs that i have staked a claim to my $loops; #how many "turns" did we stay alive? #MASTER LOOP!! while ($alive) { $loops++; my $added = 0; my $rejected = 0; my $converted = 0; my @nprov = ''; #check how many dirs we still claim: foreach $d (@provinces) { if (-e $d) { #ok! push @nprov, $d; $life++; } } @provinces = @nprov; #trim out the lost territories if ($life < 1) { $alive = 0; #im slain! } #NOW WE LIVE AND TAKE ACTION! while ($life > 0) { #pick a random dir: my $randdir = @alldirs[int(rand($#alldirs))]; my $newrsp = "$randdir/$this_rsp"; #now check what is in it!: if (-e "$newrsp") { #do nothing. } elsif (-e "$randdir/$we_beat") { #we found someone we can beat: my $removal = "$randdir/$we_beat"; unlink $removal; system ("touch '$newrsp' "); push @provinces, $newrsp; $added++; $converted++; $life++; # a little life bonus! } elsif (-e "$randdir/$beats_us") { #we got beat down! $rejected++; $life--; } else { #we found an empty dir! system ("touch '$newrsp' "); push @provinces, $newrsp; $added++; } $life--; } #do a quick log write... open LOGFILE, ">>$logfile"; my $message = "$this_desc has run $loops loops. \n" . " added $added territories,\n". " beat down $converted enemies,\n". " and got rejected $rejected times.\n"; print LOGFILE "$message"; close LOGFILE; #now sleep and recover some energy: sleep ($sleeptime + int(rand(4))); # add some randomness } #the only way to get out of the loop is to be dead.: open LOGFILE, ">>$logfile"; print LOGFILE "*******$this_desc has died after $loops iterations. **********\n"; close LOGFILE; exit(0);