#! /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;
}
syntax highlighted by Code2HTML, v. 0.9.1