Asking for comments on this script

  • Thread starter Rafael Villarroel Flores
  • Start date
R

Rafael Villarroel Flores

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
% <[email protected]>
\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);
 
R

Randal L. Schwartz

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.
 
T

Tad McClellan

Rafael Villarroel Flores said:
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();
 
T

Tore Aursand

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.
 
J

Jon Ericson

Rafael Villarroel Flores said:
\makeatletter
\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.
 
D

Darrin Edwards

Rafael Villarroel Flores <[email protected]> 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!
 
J

Jon Ericson

Darrin Edwards said:
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
 
R

Rafael Villarroel

Jon Ericson said:
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
 
R

Rafael Villarroel

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';
\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
% <[email protected]>
\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
\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{"
}
}
++$ply;
}
print OUT "}\\hspace{\\stretch{1}} \\textbf{[\\thisresult]}\n\n";
}

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top