Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Asking for comments on this script

0 views
Skip to first unread message

Rafael Villarroel Flores

unread,
Apr 21, 2004, 3:26:23 PM4/21/04
to

The following script already does what I want it to do, but I would
appreciate if the experts here share any comment they may have that
would help improve my Perl skills


#!/usr/bin/perl

# The purpose of this script is to typeset annotations done by the
# chess program Crafty (ftp://ftp.cis.uab.edu/pub/hyatt/), in LaTeX
# (http://www.latex-project.org/). Crafty already has a command to
# produce LaTeX annotations, but this script gives much prettier
# results!

# It requires the Perl module Chess::PGN::Parse, the non-standard
# LaTeX packages skak, chess-workshop-symbols, wrapfig, fullpage and
# needspace, which are available at CTAN, crafty and latex (of
# course), and the pgn-extract utility.

# To use it: starting with, say, games.pgn, use Crafty's annotate
# feature to get games.pgn.can. Then you will want to preprocess this
# file using pgn-extract
# (ftp://ftp.cs.ukc.ac.uk/pub/djb/Extract/About.html) in order to add
# an ECO (opening classification code) tag to each game and get the
# file back into standard PGN notation. You get then gamesann.pgn. You
# should then delete all instances of the comments of the like of
# {annotating both black and white moves.} {using a scoring margin of
# +0.20 pawns.} {search time limit is 1.00}, since they confuse the
# Chess::PGN::Parse module. Say you have now a file gamesann.pgn, you
# are now ready to apply this script, say 'perl skakify.pl
# gamesann.pgn', to obtain gamesann.pgn.tex which you can now process
# with LaTeX

use warnings;
use strict;
use Chess::PGN::Parse;

my $preamble='\documentclass[twocolumn]{article}
\usepackage[T1]{fontenc}
\usepackage{fullpage,ifthen}
\usepackage{skak,chess-workshop-symbols}
\usepackage{wrapfig,needspace}
\setlength{\columnsep}{7mm}
\setlength{\parindent}{0pt}
\setlength{\intextsep}{0pt}

\makeatletter
\newcommand{\@event}{} \newcommand{\@site}{} \newcommand{\@dategame}{}
\newcommand{\@round}{} \newcommand{\@whiteplayer}{}
\newcommand{\@blackplayer}{} \newcommand{\thisresult}{}
\newcommand{\@eco}{}

\newcommand{\event}[1]{\renewcommand{\@event}{#1}}
\newcommand{\site}[1]{\renewcommand{\@site}{#1}}
\newcommand{\dategame}[1]{\renewcommand{\@dategame}{#1}}
\newcommand{\round}[1]{\renewcommand{\@round}{#1}}
\newcommand{\whiteplayer}[1]{\renewcommand{\@whiteplayer}{#1}}
\newcommand{\blackplayer}[1]{\renewcommand{\@blackplayer}{#1}}
\newcommand{\result}[1]{\renewcommand{\thisresult}{#1}}
\newcommand{\eco}[1]{\renewcommand{\@eco}{#1}}

\setlength{\parindent}{0pt}

\newcounter{gameno}
\setcounter{gameno}{1}

\newcommand{\gameheader}%
{\parbox{\linewidth}{%
\begin{center}
\textbf{\thegameno.}
\end{center}
\begin{flushleft}
$\circ$ \textsc{\@whiteplayer}\par%
$\bullet$ \textsc{\@blackplayer}\par%
\smallskip%
\@event \ifthenelse{\equal{\@round}{}}%
{}{, Round \@round}\hspace{\stretch{1}}\textit{\@eco}\par%
\@site\hspace{\stretch{1}}\@dategame\par
\end{flushleft}
\nopagebreak
}% END PARBOX
\addtocontents{toc}{\thegameno. \@whiteplayer--\@blackplayer\dotfill\thepage\par}
\addtocounter{gameno}{1}
\renewcommand{\@event}{} \renewcommand{\@site}{} \renewcommand{\@dategame}{}
\renewcommand{\@round}{} \renewcommand{\@whiteplayer}{}
\renewcommand{\@blackplayer}{}
}% END GAMEHEADER

% this next macro by Donald Arsenau, see
% <4008a73c$0$22624$61ce...@news.syd.swiftdsl.com.au>
\def\wrapfill{\par
\ifx\parshape\WF@fudgeparshape
\nobreak
\ifnum\c@WF@wrappedlines>\@ne
\advance\c@WF@wrappedlines\m@ne
\vskip\c@WF@wrappedlines\baselineskip
\global\c@WF@wrappedlines\z@
\fi
\allowbreak
\WF@finale
\fi
}
\makeatother

\begin{document}
\tinyboard
\notationOff
\tableofcontents
';

my $enddoc='\end{document}';

my $pgnfile = shift || die "filename required\n";
my $pgn = new Chess::PGN::Parse $pgnfile
or die "can't open $pgnfile\n";
my $output = "$pgnfile.tex";

sub printboard
{
print OUT "\n\n\\smallskip\n".
"\\needspace{50pt}\n".
"\\begin{wrapfigure}[8]{r}{78pt}\n".
"\\showboard\n".
"\\end{wrapfigure}\n";
}

# In this subroutine, we want to convert a string like: "({7:+0.42} 10. ... c6 11. O-O-O
# $14) ({0:+0.00} 10. ... dxc4 $10)" into
# \textit{[7:+0.42]} \movecomment{ 10... c6 11. O-O-O }\wbetter

# \textit{[0:+0.00]} \movecomment{ 10... dxc4 }\equalp

sub fix_variation
{
$_=$_[0];
s/\({/{/g;
s/}/}\(/g;
s/{/\\textit{\[/g;
s/}/\]}/g;
s/\[ /\[/g;
s/ \]/\]/g;
s/\(/ \\movecomment{/g;
s/\)/}\n/g;
s/}\n \\textit{/} \n\n \\textit{/g;
s/ }/}/g;
s/\$18}/}\\wdecisive/g;
s/\$16}/}\\wupperhand/g;
s/\$14}/}\\wbetter/g;
s/\$19}/}\\bdecisive/g;
s/\$17}/}\\bupperhand/g;
s/\$15}/}\\bbetter/g;
s/\$10}/}\\equalp/g;
return $_;
}

open(OUT, ">$output");

print OUT "$preamble";

while ($pgn->read_game()){
$pgn->parse_game({save_comments => 'yes'});
my $comments = $pgn->comments;
print OUT "\\event{",$pgn->event,"}\n"; # NOT "\\event{$pgn->event}\n";
print OUT "\\site{",$pgn->site,"}\n";
print OUT "\\dategame{",$pgn->date,"}\n";
print OUT "\\round{",$pgn->round,"}\n";
print OUT "\\whiteplayer{",$pgn->white,"}\n";
print OUT "\\blackplayer{",$pgn->black,"}\n";
print OUT "\\result{",$pgn->result,"}\n\n";
print OUT "\\eco{",$pgn->eco,"}\n\n";
print OUT '\\gameheader
\newgame
\mainline{';
my $total_plys= @{$pgn->moves};
my $ply =0;
while ($ply<$total_plys) {
my $move = int($ply/2)+1;
if (!($ply % 2)) # ply corresponds to a white move
{
print OUT "$move. @{$pgn->moves}[$ply] ";
if ($$comments{"${move}w"})
{
print OUT "}";
&printboard;
print OUT &fix_variation($$comments{"${move}w"});
print OUT "\\wrapfill\n\n";
}
}
else
{
if ($$comments{"${move}w"}) {
print OUT "\\smallskip\n\\mainline{$move... @{$pgn->moves}[$ply] "}
else {
print OUT "@{$pgn->moves}[$ply] "
}
if ($$comments{"${move}b"})
{
print OUT "}";
&printboard;
print OUT &fix_variation($$comments{"${move}b"});
print OUT "\\wrapfill\n";
print OUT "\\smallskip\n\\mainline{"
}
}
++$ply;
}
print OUT "}\\hspace{\\stretch{1}} \\textbf{[\\thisresult]}\n\n";
}

print OUT "\n$enddoc";
close(OUT);

Randal L. Schwartz

unread,
Apr 21, 2004, 3:56:50 PM4/21/04
to
>>>>> "Rafael" == Rafael Villarroel Flores <rvf_...@fastmail.fm> writes:

Rafael> The following script already does what I want it to do, but I would
Rafael> appreciate if the experts here share any comment they may have that
Rafael> would help improve my Perl skills

This program *screams* for Template Toolkit. See www.tt2.org for
details.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<mer...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!

Tad McClellan

unread,
Apr 21, 2004, 5:21:19 PM4/21/04
to
Rafael Villarroel Flores <rvf_...@fastmail.fm> wrote:
>
> The following script already does what I want it to do, but I would
> appreciate if the experts here share any comment they may have that
> would help improve my Perl skills

> my $preamble='\documentclass[twocolumn]{article}

[snip 70 lines of data]

> ';


Yikes!

The maintenance programmer is supposed to scan dozens of lines
until he gets to the couple of black pixels that make the single quote?

A "here-document" would be much easier to see. (see perlop.pod)


> print OUT "\n\n\\smallskip\n".
> "\\needspace{50pt}\n".
> "\\begin{wrapfigure}[8]{r}{78pt}\n".
> "\\showboard\n".
> "\\end{wrapfigure}\n";


I don't like to hide the concat operators from myself, so I'd write that:

print OUT "\n\n\\smallskip\n"
. "\\needspace{50pt}\n"
. "\\begin{wrapfigure}[8]{r}{78pt}\n"
. "\\showboard\n"
. "\\end{wrapfigure}\n";

(or use a here-doc again)


> s/\({/{/g;


s/ \( { / { /gx; # easier to see what it does


> open(OUT, ">$output");


You should always, yes *always*, check the return value from open().


> print OUT "$preamble";


perldoc -q vars

What's wrong with always quoting "$vars"?

So that should be:

print OUT $preamble; # quotes don't do anything, so they should not be there


> while ($ply<$total_plys) {


Whitespace is not a scarce resource, feel free to use as much as
you want to make your code easier to read an maintain:

while ( $ply < $total_plys ) {

(see perlstyle.pod)

> if (!($ply % 2)) # ply corresponds to a white move


I'd have written that differently:

if ( $ply % 2 == 0 ) # even moves are white


> &printboard;


perldoc -q function

What's the difference between calling a function as &foo and foo()?

Then change that to:

printboard();


--
Tad McClellan SGML consulting
ta...@augustmail.com Perl programming
Fort Worth, Texas

Tore Aursand

unread,
Apr 21, 2004, 6:53:38 PM4/21/04
to
On Wed, 21 Apr 2004 14:26:23 -0500, Rafael Villarroel Flores wrote:
> The following script already does what I want it to do, but I would
> appreciate if the experts here share any comment they may have that
> would help improve my Perl skills
> [...]

One thing you _definitely_ should look into, is how to use templates to
seperate - uhm - ordinary (external) text from Perl code.

Go to <http://www.cpan.org/> and do a search for 'template'. There are
too many to mention.


--
Tore Aursand <to...@aursand.no>
"Then there was the man who drowned crossing a stream with an average
depth of six inches." (W.I.E. Gates)

Jon Ericson

unread,
Apr 22, 2004, 4:44:05 PM4/22/04
to
Rafael Villarroel Flores <rvf_...@fastmail.fm> writes:

> \makeatletter
<snip>
> \makeatother

Other people have given you perl style advice, but I wanted to mention
a couple of LaTeX hints. Using \makeatletter in a document is a good
sign that you should write a document class or package.

> sub printboard
> {
> print OUT "\n\n\\smallskip\n".
> "\\needspace{50pt}\n".
> "\\begin{wrapfigure}[8]{r}{78pt}\n".
> "\\showboard\n".
> "\\end{wrapfigure}\n";
> }

It would be easier to write a LaTeX macro for this[1]:

\newcommand{\printboard}
{

\smallskip
\needspace{50pt}


\begin{wrapfigure}[8]{r}{78pt}

\showboard
\end{wrapfigure}
}

As a bonus, the LaTeX source would be much easier to read.

This seems like an excellent opportunity to use PerlTeX[2]. From the
abstract:

PerlTEX is a combination Perl script (perltex) and LATEX2" style
file (perlmacros) that, together, give the user the ability to
define LATEX macros in terms of Perl code. Once defined, a Perl
macro becomes indistinguishable from any other LATEX macro. PerlTEX
thereby combines LATEX’s typesetting power with Perl’s
programmability.

Basically this would this would turn your script inside out, much like
a template solution. You include perl code within your LaTeX
document[3]:

\perlnewcommand{\fixvariation}[1]
{ $_=$_[0];
#{{{{{{{{{{{{{{{{ Make sure the curlies balance.


s/\({/{/g;
s/}/}\(/g;
s/{/\\textit{\[/g;
s/}/\]}/g;
s/\[ /\[/g;
s/ \]/\]/g;
s/\(/ \\movecomment{/g;
s/\)/}\n/g;
s/}\n \\textit{/} \n\n \\textit{/g;
s/ }/}/g;
s/\$18}/}\\wdecisive/g;
s/\$16}/}\\wupperhand/g;
s/\$14}/}\\wbetter/g;
s/\$19}/}\\bdecisive/g;
s/\$17}/}\\bupperhand/g;
s/\$15}/}\\bbetter/g;
s/\$10}/}\\equalp/g;
return $_;
}

And you would use something like:

\fixvariation{{7:+0.42} 10. ... c6 11. O-O-O

$14) ({0:+0.00} 10. ... dxc4 $10)}

Jon

Footnotes:
[1] This code is *untested*.

[2] Available at www.ctan.org (note the `t').

[3] I did do some testing on this macro. It takes a bit of fiddling
to get everything right, but not as much as I expected. The
important thing, as far as I can tell, is to make sure the
curlies balance. I don't have the chess packages, so I can't help
with those.

Darrin Edwards

unread,
Apr 22, 2004, 8:02:47 PM4/22/04
to
In article <rcgd65z...@Jon-Ericson.sdsio.prv>, Jon Ericson wrote:
> Rafael Villarroel Flores <rvf_...@fastmail.fm> writes:
[OP describing a perl script to output latex]

Wow, I thought I was crazy trying to combine perl + latex, guess I am
not the only one. :)

(My attempts, not for the weak-stomached:
http://home.uchicago.edu/~dcedward/software/)

> This seems like an excellent opportunity to use PerlTeX[2].

> [2] Available at www.ctan.org (note the `t').

I _really_ want to thank you for mentioning this. I've been dreaming
of such a thing for years, and had no idea someone else not only had
the same itch, but had successfully scratched it so to speak.

Thanks again!

--
Darrin

Jon Ericson

unread,
Apr 22, 2004, 8:42:22 PM4/22/04
to
Darrin Edwards <edw...@nouce.trurl.bsd.uchicago.edu> writes:

> I _really_ want to thank you for mentioning this. I've been
> dreaming of such a thing for years, and had no idea someone else not
> only had the same itch, but had successfully scratched it so to
> speak.

Don't thank me. Thank Scott Pakin, who wrote PerlTeX.

Jon

Rafael Villarroel

unread,
Apr 23, 2004, 11:49:04 AM4/23/04
to
Jon Ericson <Jon.E...@jpl.nasa.gov> writes:

> Rafael Villarroel <rvf_...@fastmail.fm> writes:
>
>> \makeatletter
> <snip>
>> \makeatother
>
> Other people have given you perl style advice, but I wanted to mention
> a couple of LaTeX hints. Using \makeatletter in a document is a good
> sign that you should write a document class or package.
>

Yes I know, in fact, I had that code in a separate package, but I
decided to include it in the script because it seemed easier for me to
maintain and distribute only one file.

> This seems like an excellent opportunity to use PerlTeX[2]. From the
> abstract:
>
> PerlTEX is a combination Perl script (perltex) and LATEX2" style
> file (perlmacros) that, together, give the user the ability to
> define LATEX macros in terms of Perl code. Once defined, a Perl
> macro becomes indistinguishable from any other LATEX macro. PerlTEX
> thereby combines LATEX’s typesetting power with Perl’s
> programmability.
>

[snip example of PerlTeX use]

This seems very interesting, I knew about the existence of PerlTeX but
I did not have a use for it at that moment. However, since the
combination of abilities to use both Perl and TeX in chess fans (to
the extent of installing a nonstandard and complex package like
PerlTeX) seems to be rare, I think I will prefer not to depend on it.

Thanks for your time taken in answering my post, and to all the other
people!

Rafael

Rafael Villarroel

unread,
Apr 23, 2004, 2:49:41 PM4/23/04
to

Thanks a lot for your time taken, Tad. Just for the record, here is a
new version in which I tried to take care of your suggestions

----------------------- skakify.pl -------------------
#!/usr/bin/perl

# The purpose of this script is to typeset annotations done by the
# chess program Crafty (ftp://ftp.cis.uab.edu/pub/hyatt/), in LaTeX
# (http://www.latex-project.org/). Crafty already has a command to
# produce LaTeX annotations, but this script gives much prettier
# results!

# It requires the Perl module Chess::PGN::Parse, the non-standard
# LaTeX packages skak, chess-workshop-symbols, wrapfig, fullpage and
# needspace, which are available at CTAN, crafty and latex (of
# course), and the pgn-extract utility.

# To use it: starting with, say, games.pgn, use Crafty's annotate
# feature to get games.pgn.can. Then you will want to preprocess this
# file using pgn-extract
# (ftp://ftp.cs.ukc.ac.uk/pub/djb/Extract/About.html) in order to add
# an ECO (opening classification code) tag to each game and get the
# file back into standard PGN notation. You get then gamesann.pgn. You
# should then delete all instances of the comments of the like of
# {annotating both black and white moves.} {using a scoring margin of
# +0.20 pawns.} {search time limit is 1.00}, since they confuse the
# Chess::PGN::Parse module. Say you have now a file gamesann.pgn, you
# are now ready to apply this script, say 'perl skakify.pl
# gamesann.pgn', to obtain gamesann.pgn.tex which you can now process
# with LaTeX

# Thanks to people of comp.lang.perl.misc for suggestions

use warnings;
use strict;
use Chess::PGN::Parse;

# We could put most of the LaTeX commands in a separate package. I
# prefer not to do that in order maintain and move around only one
# file. Main program starts at line 163.

my $preamble = <<'PREAMBLE';

\setlength{\parindent}{0pt}

\bigskip

PREAMBLE


my $enddoc='\end{document}';

my $pgnfile = shift || die "filename required\n";
my $pgn = new Chess::PGN::Parse $pgnfile
or die "can't open $pgnfile\n";
my $output = "$pgnfile.tex";

sub printboard
{
print OUT <<'PRINTBOARDCOMMANDS';

\smallskip
\needspace{50pt}


\begin{wrapfigure}[8]{r}{78pt}

\showboard
\end{wrapfigure}
PRINTBOARDCOMMANDS
}

# In this subroutine, we want to convert a string like: "({7:+0.42} 10. ... c6 11. O-O-O
# $14) ({0:+0.00} 10. ... dxc4 $10)" into
# \textit{[7:+0.42]} \movecomment{ 10... c6 11. O-O-O }\wbetter

# \textit{[0:+0.00]} \movecomment{ 10... dxc4 }\equalp

sub fix_variation
{
$_=$_[0];
s/ \( { / { /gx;
s/ } / } \( /gx;

s/{/\\textit{\[/g;
s/}/\]}/g;

s/\(/ \\movecomment{/g;
s/\)/}\n/gx;
s/}\n \\textit{/}\\par\n\\textit{/g;
s/\$18 ?}/}\\wdecisive/g;
s/\$16 ?}/}\\wupperhand/g;
s/\$14 ?}/}\\wbetter/g;
s/\$19 ?}/}\\bdecisive/g;
s/\$17 ?}/}\\bupperhand/g;
s/\$15 ?}/}\\bbetter/g;
s/\$10 ?}/}\\equalp/g;
s/ +\]/\]/g;
s/\[ +/\[/g;
s/ +}/}/g;
s/{ +/{/g;
s/^ \\/\\/g;
return $_;
}

################### MAIN PROGRAM

open(OUT, ">$output") or die "Could not open $output: $!\n";

print OUT $preamble;

while ($pgn->read_game()){
$pgn->parse_game({save_comments => 'yes'});
my $comments = $pgn->comments;
print OUT "\\event{",$pgn->event,"}\n"

. "\\site{",$pgn->site,"}\n"
. "\\dategame{",$pgn->date,"}\n"
. "\\round{",$pgn->round,"}\n"
. "\\whiteplayer{",$pgn->white,"}\n"
. "\\blackplayer{",$pgn->black,"}\n"
. "\\result{",$pgn->result,"}\n"
. "\\eco{",$pgn->eco,"}\n\n"
. "\\gameheader\n"
. "\\newgame\n"
. "\\mainline{";
my $total_plys = @{$pgn->moves};
my $ply = 0;


while ( $ply < $total_plys ) {

my $move = int($ply/2) + 1;
if ( $ply % 2 == 0) # even plies correspond to white moves


{
print OUT "$move. @{$pgn->moves}[$ply] ";

if ( $$comments{"${move}w"} )
{
print OUT "}";
printboard();
print OUT fix_variation($$comments{"${move}w"});
print OUT "\\wrapfill\n";
}
}
else
{
if ( $$comments{"${move}w"} ) {


print OUT "\\smallskip\n\\mainline{$move... @{$pgn->moves}[$ply] "}
else {
print OUT "@{$pgn->moves}[$ply] "
}

if ( $$comments{"${move}b"} )
{
print OUT "}";
printboard();
print OUT fix_variation($$comments{"${move}b"});
print OUT "\\wrapfill\n"
. "\\smallskip\n\\mainline{"

0 new messages