#! /usr/bin/perl -T -W # # schart.pl, version 1 # # Prints state charts (a juggling notation for siteswaps) # Written on 24-25th March 2005 by Daniel Clemente (n142857@at@gmail/com), # while learning Perl ("man perlintro") # # More help at http://www.danielclemente.com/linux/schart.html # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2. # use strict; use Getopt::Std; my %opts; getopts('htxm:n:', \%opts) or &usage(); &usage() if $opts{h}; die("Please tell me the maximum throw height. (See $0 -h)\n") unless defined $opts{m}; my $max_throw=$opts{m}; die("${max_throw}??? ". "Could the maximum throw type be a natural number, please?\n") unless $max_throw =~ /^\d+$/; if (defined $opts{n}) { die("I want the number of balls to be a natural\n") unless $opts{n} =~ /^\d+$/; } my $only_n_balls=$opts{n}; # This flag says we want the table made of "throw types" for each state, # not "next state" for each throw type my $tttable=$opts{t}; my $putXs=$opts{x}; # Finished parsing options my $last_state=2**$max_throw-1; # Number of characters needed to print any state in decimal form my $last_state_width=length $last_state; print "Juggling state chart\nfor throws up to $max_throw, ", (defined $only_n_balls) ? "but only for $only_n_balls balls\n\n" : "including pickups (+) and drops (-)\n\n" ; print " || State", " "x($max_throw+2+$last_state_width+2-6), "|"; unless (defined $tttable) { # The default behaviour # We want + and - columns if dealing with a variable number of balls unless (defined $only_n_balls) { print " ", &state_decimal('+'), " |"; print " ", &state_decimal('-'), " |"; } print " ", &state_decimal($_), " |" foreach (0..$max_throw); # Print a lot of dashes (------...) print "|\n%|", "-"x( ($max_throw+1+(defined $only_n_balls?0:2)) * ($last_state_width+3) + ($max_throw==0?1:$max_throw) + 2 + $last_state_width + 2 + 1 ), "||\n"; } else { # Column headers are states my $number_of_columns=0; foreach (0..$last_state) { # Don't show states which don't have the desired number of balls if (defined $only_n_balls) { next if &number_of_ones($_) != $only_n_balls; } # We will need this to print the dashes on the next line $number_of_columns++; print " ", &state_decimal($_), " |"; } # Print a lot of dashes (------...) print "|\n%|", "-"x( ($number_of_columns)*($last_state_width+3) + ($max_throw==0?1:$max_throw) + 2 + $last_state_width + 2 + 1 ), "||\n"; } my $state; for($state=0; $state<=$last_state; $state++) { # For every row if (defined $only_n_balls) { next if &number_of_ones($state) != $only_n_balls; } # A hash to store the result state for each throw in the current state my %transitions; # Ends in 0: we have no balls to throw; we can take one (+) or 'throw' a 0 if ( ($state & 1) == 0) { $transitions{0}=$state>>1; # 'Pop' it from the queue $transitions{'+'}=$state|1; } else { # We have a ball; maybe we can throw a 1, 2, 3, 4, ... or drop it (-) $transitions{'-'}=$state-1; # Switch off the LSB (least signifiant bit) foreach my $throw_type (1..$max_throw) { my $ends=2**$throw_type; # Has a 1 where the ball would land $transitions{$throw_type}=($state|$ends)>>1 if ( ($state & $ends) == 0); # Only if no other balls will be there } } # Now print the columns with the transitions for the current state print " | ".&state_binary($state)." (".&state_decimal($state).") |"; unless (defined $tttable) { # Each row has states foreach my $column ('+', '-', 0..$max_throw) { if (defined $only_n_balls) { next if $column eq '+' || $column eq '-'; } # This will be printed if we don't know the transition my $number=" "x$last_state_width; $number=&state_decimal($transitions{$column}) if defined $transitions{$column}; print " ", $number, " |"; } } else { # Each row has throw types my $state2; foreach my $state2 (0..$last_state) { # For every column # Some columns may be missing in the table if (defined $only_n_balls) { next if &number_of_ones($state2) != $only_n_balls; } # Is there any $throw_made so that $transitions{$throw_made}==$state2 ? my $throw_made; foreach (keys %transitions) { if ($transitions{$_}==$state2) { $throw_made=$_; last; # We have already found it } } my $number=" "x$last_state_width; # Default value is blank if (defined $throw_made) { unless ( defined $only_n_balls && ($throw_made eq '+' || $throw_made eq '-') ) { $number=&state_decimal($throw_made) } } $number=&state_decimal('X') if (defined $putXs && $state eq $state2 && $number=~/^[ ]*$/); print " ", $number, " |"; } } print "|\n"; # Row printed } print "\n"; exit 0; # And here ends the program sub state_binary { # Pad with zeros to the left return sprintf("%0${max_throw}b", shift); } sub state_decimal { # Pad with spaces to the left # Note that states and throw types are treated equally (same padding) return " "x($last_state_width-1).$_[0] if ($_[0] =~ /^[+-X]$/); return sprintf("%${last_state_width}u", shift); } sub number_of_ones { my $total=0; my $n=shift; while($n>0) { $total+=($n&1); $n>>=1; } return $total; } sub usage { print STDERR << "EOF"; Usage: $0 -m max_throw [-n balls] [-t] [-x] [-h] -m max_throw : which throw heights to consider (from 0 to max_throw). -n balls : only show states with that number of balls; no drops or pickups. -t : show the throw types inside the table, and use the "final state" as the horizontal axis. Easier to understand, but tables are giant (2^max_throw rows and 2^max_throw columns). -x : when -t is on, add X marks as reference points -h : show this help Example : $0 -m5 -n3 -t -x You can use txt2tags to convert the output to other formats, like TeX and HTML. schart.pl, version 1. March 2005, Daniel Clemente. License: GPL More help at http://www.danielclemente.com/linux/schart.html EOF exit; }