% \iffalse
%<*copyright>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% acromemory.sty package,          2006-10-11          %%
%% Copyright (C) 2006  D. P. Story                      %%
%%   dpstory@acrotex.net                                %%
%%   storyd@owc.edu                                     %%
%%                                                      %%
%% This program can redistributed and/or modified under %%
%% the terms of the LaTeX Project Public License        %%
%% Distributed from CTAN archives in directory          %%
%% macros/latex/base/lppl.txt; either version 1 of the  %%
%% License, or (at your option) any later version.      %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%</copyright>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{acromemory}
%<package> [2006/10/31 v1.0 AcroMemory (dps)]
%<*driver>
\documentclass{ltxdoc}
\usepackage[dviwindo,colorlinks,hyperindex]{hyperref}
\pdfstringdefDisableCommands{\let\\\textbackslash}%
\EnableCrossrefs \CodelineIndex
\OnlyDescription  % comment out for implementation details
\begin{document}
  \GetFileInfo{acromemory.sty}
  \title{The \texttt{AcroMemory} Package\texorpdfstring{\\}{: }
  A member of the AeB Pro family}
  \author{D. P. Story\\
    Email: \texttt{storyd@owc.edu}}
  \date{processed \today}
  \maketitle
  \tableofcontents
  \let\Email\texttt
  \DocInput{acromemory.dtx}
  \PrintIndex
\end{document}
%</driver>
% \fi
% \section{What is the AeB Pro Family?}
%   Through the years, I have tried to make my AeB software
%   ({Acro\negthinspace\TeX} eDucation Bundle) compatible with
%   \textsf{pdftex} and \textsf{dvipdfm}; however, during that time,
%   I've developed a number of techniques that require the use of
%   Acrobat and distiller. Therefore, I have set off in a new direction and
%   will be publishing a new line of {\LaTeX} packages, one that require the use
%   of Acrobat.
%
%   The current package, \textsf{AcroMemory}, requires the use of Acrobat Pro~7.0 or later.
% \section{Introduction}
%
% At the instigation of my erstwhile friend, J\"{u}ergen, I present to you \textsf{AcroMemory}, and
% for the life of me, I can't remember why.
%
% Oh, yes, \textsf{AcroMemory} is a memory game in which you find the matching tiles. There are two versions
% ---available as options of this package---for your enjoyment,
% \texttt{acromemory1} and \texttt{acromemory2} (the default).
% \begin{itemize}
%    \item \texttt{acromemory1}: Here you have a single game board, a rectangular region divided
%            by rows and columns. The total number of tiles should
%            be even, each tile should have a matching twin. The
%            game begins with all the tiles hidden. the user clicks a tile,
%            then another. If the tiles do not match, they become become hidden again
%            (you did remember the position of those tiles, didn't you?); otherwise,
%            they remain visible and are now read-only. The game is complete when the user, with
%            a lot of time on his/her hands, matches all tiles. There is a running tabulation kept
%            on the number of tries.  There is also a button which resets the game and randomizes the
%            tiles.
%   \item \texttt{acromemory2}: For this game you have two identical rectangular images subdivided in%to tiles
%           (or slices) arrayed in rows and columns. The tiles for
%           one of the two images has been randomly re-arranged. The object of the game is to find all the
%           matching tiles by choosing a tiles from one image, and tile from the other image. As in the
%           first case, if the selected tiles do not match, they are hidden after an short interval of time
%           (you did remember the position of those tiles, didn't you?); otherwise, they remain visible and
%           are now read-only. The game is over when all tiles are matched, when this occurs, end-of-game
%           special effects occur that will dazzle the senses. There is an option to view a small image to
%           help you locate the matching tiles on the non-randomized; useful if the image is complex. There
%           is no reset button at this time, to play again, the user must close and open the document.
% \end{itemize}
% The demo files are \texttt{acromemory1.tex} and \texttt{acromemory2.tex}. These files show how to
% lay out the various elements of this package.
%
% \section{Creating the Image Tiles}
%
% There are a couple of ways you can package your image tiles: (1) place all tiles (all of identical size)
% into a single PDF and use the \texttt{iconfile} option to tell AcroMemory you are using this method; (2)
% each image file is a separate PDF. In the latter case, there is a naming convention that must be followed;
% the files must be named \texttt{<basename>\_01.pdf}, \texttt{<basename>\_02.pdf}, \dots,
% \texttt{<basename>\_10.pdf}, \texttt{<basename>\_11.pdf}, \dots. You'll note the consistent two digit
% numbering system is used.

% For \texttt{acromemory2}, slicing of the image is at the very heart
% of this game. You can slice an image in to rectangular tiles using
% any of several applications: \textsf{Adobe Illustrator},
% \textsf{Photoshop} and \textsf{ImageReady}, for example. But these are
% expensive applications and you have already bought \textsf{Acrobat Pro}. For this purpose,
% AeB Pro provides the \textsf{AcroSlicing} batch sequence.
%
%    \begin{macrocode}
%<*package>
\RequirePackage{xkeyval}
%    \end{macrocode}
%    \begin{macro}{acromemory1}
% One playing board, where you try to match identical icons.
%    \begin{macrocode}
\DeclareOptionX{acromemory1}{\acromemoryitrue}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{acromemory2}
% Two playing boards, one board randomized the other not. Try to find the matching icons,
% one from each of the two boards.
%    \begin{macrocode}
\DeclareOptionX{acromemory2}{\acromemoryifalse}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{acromemory2}
% Two playing boards, one board randomized the other not. Try to find the matching icons,
% one from each of the two boards.
%    \begin{macrocode}
\DeclareOptionX{iconfile}{\def\iconsInOneFile{true}}
\def\iconsInOneFile{false}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{includehelp}
% Only valid when \texttt{acromemory2} is taken, this option allows you to provide a
% figure showing the completed puzzle.
%    \begin{macrocode}
\DeclareOptionX{includehelp}{\includehelptrue}
%    \end{macrocode}
%    \end{macro}
%    \begin{macrocode}
\newif\ifincludehelp \includehelpfalse
\newif\ifacromemoryi \acromemoryifalse
\ProcessOptionsX
\RequirePackage{comment}
\ifacromemoryi
    \def\RanIdentifier{\@gobble}
    \includecomment{acromemory1}
    \excludecomment{acromemory2}
    \excludecomment{needhelp}
\else
    \def\RanIdentifier{R\@gobble}
    \includecomment{acromemory2}
    \excludecomment{acromemory1}
    \ifincludehelp
        \includecomment{needhelp}
    \else
        \excludecomment{needhelp}
    \fi
\fi
%    \end{macrocode}
% \section{Main Macro Code}
% When setting up your AcroMemory document, you need to provide this package with
% certain information, as listed below.
%    \begin{macro}{\theTotalTiles}
%    \begin{macro}{\theNumRows}
%    \begin{macro}{\theNumCols}
%    \begin{macro}{\theImportPath}
%    \begin{macro}{\theIconExt}
%    \begin{macro}{\theTeXImageWidth}
%    \begin{macro}{\provideDimensions}
%\begin{itemize}
%   \item \cs{theTotalTiles}: The total number of tiles in the game board. For example,
%   \verb!\theTotalTiles{20}!. This parameter is required.
%   \item \cs{theNumRows}: The number of rows in the game board. For example,
%   \verb!\theNumRows{5}!. This parameter is required.
%   \item \cs{theNumCols}: The number of columns in the game board. For example,
%   \verb!\theNumCols{4}!. This parameter is required.
%   \item \cs{theImportPath}: The import path to the basename of the image. The path
%   should use the path specification as defined in the PDF Ref, and the file name should
%   have no extension. For example, \verb!\theImportPath{myFig/myimages}!. Required.
%   There is an optional argument that is typically used when the iconfile is in effect
%   with the acromemory2 option,  and an image of the game board is different from the
%    path given by the optional argument; for example,
%\begin{verbatim}
%   \theImportPath[dpsweb/dpsweb]{dpsweb/dpsweb_package}
%\end{verbatim}
%   The required argument points the packaged icons, the optional argument points to
%   a file showing the entire image
%   \item \cs{theIconExt}: The extension of the image file(s), Required if different
%   from \texttt{pdf}.
%   \item \cs{theTeXImageWidth}: The scaled width of the rectangular game board. The
%   game board will be rescaled so that its width is equal to the value specified
%   by the argument of this command, e.g., \verb!\theTeXImageWidth{2in}!.
%   \item\cs{provideDimensions}: If the dimension of the game board is known, the width
%   and height can be entered with this command using the two parameters. For example,
%   \verb!\provideDimensions{2in}{2.5in}! (width, height).
%\end{itemize}
%    \begin{macrocode}
\def\theTotalTiles#1{\def\nTotalTiles{#1}}
\def\theNumRows#1{\def\nRows{#1}}
\def\theNumCols#1{\def\nCols{#1}}
\newcommand{\theImportPath}[2][]{%
    \ifacromemoryi
        \def\imageImportPath{#2}
        \def\importpath{#2}
    \else
        \def\argi{#1}\ifx\argi\@empty
            \def\imageImportPath{#2}
            \def\importpath{#2}
        \else
            \def\imageImportPath{#1}
            \def\importpath{#2}
        \fi
    \fi
}
\def\theIconExt#1{\def\iconsExt{#1}}
\def\iconsExt{pdf}
\def\theTeXImageWidth#1{\def\texImageWidth{#1}%
    {\dimen0 = \texImageWidth \xdef\imageWidth{\strip@pt\dimen0 }}%
}
\newcommand{\provideDimensions}[3][]{%
    \def\argi{#1}\ifx\argi\@empty
        {\dimen0 = #2 \xdef\trueImageWidth{\strip@pt\dimen0 }%
         \dimen0 = #3 \xdef\trueImageHeight{\strip@pt\dimen0 }}%
    \else
        \def\trueImageWidth{#1}\def\trueImageHeight{#2}%
    \fi
    \def\bProvideDimen{true}%
}
\def\bProvideDimen{false}%
\def\trueImageWidth{0}\def\trueImageHeight{0}%
%    \end{macrocode}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \begin{macro}{\bDebug}
% A debugging command. When executed in the preamble, more is written to the Acrobat console
% as the document is  opened the first time, also, the icons are initially visible so you can
% see the layout, and quickly play the game. This was used in development extensively to help
% develop the JavaScript.
%    \begin{macrocode}
\def\bDebug{\def\memDebug{true}}
\def\memDebug{false}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{\helpImage}
%    \begin{macro}{\rolloverHelpButton}
%    \begin{macro}{\setHelpImageWidth}
%    \begin{macro}{\theHelpCaption}
%  When \texttt{acromemory2} options and the \texttt{includehelp}
%  options are taken, these commands are available. The command
%  \cs{helpImage} will contain an icon of the puzzle, and it width
%  is set by the command \cs{setHelpImageWidth}. The image is normally
%  hidden until the user rolls over the \cs{rolloverHelpButton}. The
%  icons appears with an caption under it, the content of the caption
%  can be entered using \cs{theHelpCaption}.
%    \begin{macrocode}
\newcommand{\helpImage}[1][]{{%
    \ifincludehelp{\setbox0 = \hbox{%
        \includegraphics[draft,width=\helpImageWidth]%
        {\imageImportPath}}%[\Ff\FfReadOnly]
    \dimen0=\ht0 \advance\dimen0by14bp\ht0=\dimen0
    \pushButton[\BC{}\BG{}\S{S}#1]{memoryhelp}{\the\wd0}{\the\ht0}}\fi
}}
\newcommand{\rolloverHelpButton}[3][]{%
    \ifincludehelp
        \pushButton[\CA{Help}\BC{0 0 1}\BG{0.89 0.9 0.9}
        \AA{\AAMouseEnter{\JS{%
        var f = this.getField("memoryhelp");\r
        oIcon = f.buttonGetIcon(1);\r
        f.buttonPosition = position.iconTextV;\r
        f.buttonSetIcon(oIcon,0);\r
        f.buttonSetCaption({cCaption: "\helpCaption"});\r
        f.textColor=color.blue;\r
        }}%
        \AAMouseExit{\JS{%
        var f = this.getField("memoryhelp");\r
        f.buttonPosition = position.iconOnly;\r
        f.buttonSetIcon(nullIcon,0);
        }}}#1]{checkhelp}{#2}{#3}%
    \fi
}
\newcommand{\setHelpImageWidth}[1]{\def\helpImageWidth{#1}}
\def\helpImageWidth{1in}
\def\theHelpCaption#1{\def\helpCaption{#1}}
\theHelpCaption{A little help}
%    \end{macrocode}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \begin{macro}{\messageBox}
% A message text field, as the user works the puzzle, the progress is reported
% to this field.
%    \begin{macrocode}
\newcommand{\messageBox}[3][]{%
    \textField[#1\Ff\FfMultiline]{MsgBox}{#2}{#3}}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{\playItAgain}
% For the \texttt{acromemory1} option, this button can be placed to reset
% the game board, the icons are rearranged hand hidden again.
%    \begin{macrocode}
\newcommand{\playItAgain}[3][]{\ifacromemoryi{\small
    \pushButton[\CA{Play again}#1\A{\JS{playagain();}}]%
    {playAgain}{#2}{#3}}%
    \fi
}
%    \end{macrocode}
%    \end{macro}
%    \begin{macro}{\ulCornerHere}
%    \begin{macro}{\LulCornerHere}
%    \begin{macro}{\RulCornerHere}
%    \begin{macro}{\reserveSpaceByDimension}
%    \begin{macro}{\reserveSpaceByFile}
% This group of buttons appear together. \cs{ulCornerHere} is used to
% set the upper left corner of the game board in the case of the
% \texttt{acromemory1} option; while \cs{LulCornerHere} and \cs{RulCornerHere}
% do the same thing for the \texttt{acromemory2} option. One of the two commands
% \cs{reserveSpaceByDimension} or \cs{reserveSpaceByFile} immediately follow
% the `corner' commands.
%    \begin{macrocode}
\def\ulCornerHere{\makebox[0pt][l]%
    {\pushButton[\autoCenter{n}]{ulcorner}{0pt}{0pt}}}
\def\LulCornerHere{\makebox[0pt][l]%
    {\pushButton[\autoCenter{n}]{Lulcorner}{0pt}{0pt}}}
\def\RulCornerHere{\makebox[0pt][l]%
    {\pushButton[\autoCenter{n}]{Rulcorner}{0pt}{0pt}}}
%    \end{macrocode}
% The two arguments are $\texttt{\#1} = \mbox{width}$ and $\texttt{\#2} = \mbox{height}$.
%    \begin{macrocode}
\def\reserveSpaceByDimension#1#2{%
    \hbox{\ifpreview\setlength\fboxrule{0.4pt}\setlength\fboxsep{0pt}%
    \@tempdima=#1\advance\@tempdima by-\fboxrule
    \@tempdimb=#2\advance\@tempdimb by-\fboxrule
    \fbox{\parbox[t][\@tempdimb][t]{\@tempdima}{\kern0pt\hfill\vfill}}%
    \else\parbox[t][#2][t]{#1}{\kern0pt\hfill\vfill}\fi}%
}
%    \end{macrocode}
% The optional argument can be used to insert a file that has the same aspect ratio as
% the puzzle, the default is the one specified by the optional argument of
% \cs{theImportPath}, which, if not specified, is the same as the required argument
% of \cs{theImportPath}
%    \begin{macrocode}
\newcommand{\reserveSpaceByFile}[1][\imageImportPath]%
    {{\setbox0=\hbox{\includegraphics[draft,width=\texImageWidth]{#1}}%
    \reserveSpaceByDimension{\wd0}{\ht0}}}
%    \end{macrocode}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
%    \end{macro}
% \section{Document JavaScript for \textsf{AcroMemory}}
% Most of the work of this package is done with document JavaScript, and here
% it is.
%\par\medskip\noindent
%\StopEventually{JavaScript listing suppressed,
% comment out \cs{OnlyDescription} at the beginning of this file to see
% the JavaScript.}
%    \begin{macrocode}
\begin{insDLJS*}[_MemLoaded]{memjs}
\begin{newsegment}{AcroMemory 1: Global Data and Initialization}
// Global Data:
_MemLoaded=true;
var randomDPS = new Array(\nTotalTiles+1);
var imageNames = new Array();
imageNames.push("null");

var dpsl = randomDPS.length;
var timeout = 10;
var shutdown, rAE;
var ok2Continue = true;
var nRows = \nRows;
var nCols = \nCols;
var nCorrect = 0;
var nAttempts = 0;

for (i=1; i<=\nTotalTiles; i++) randomDPS[i]=i;

if ( typeof nullIcon == "undefined" ) {
    this.addIcon("nullIcon", this.createIcon("", 0, 0));
    var nullIcon = this.getIcon("nullIcon");
}
var debug = \memDebug;
\end{newsegment}

\begin{acromemory1}
\begin{newsegment}{AcroMemory 2: Initialize Pic Names}
var nttl = \nTotalTiles/2;
for ( var i = 1; i <= nttl; i++)
{
    imageNames.push("pic"+i +".0");
    imageNames.push("pic"+i +".1");
}
var currentChoice = "";
var currentTile1 = 0;
var currentTile2 = 0;
var currentIconName = "";
\end{newsegment}
\end{acromemory1}
\begin{acromemory2}
\begin{newsegment}{AcroMemory 2: Initialize Pic Names}
var nttl = \nTotalTiles;
for ( var i = 1; i <= nttl; i++)
{
    imageNames.push("pic."+i);
}
var LcurrentChoice = 0;
var LcurrentTile = 0;
var RcurrentChoice = 0;
var RcurrentTile = 0;
\end{newsegment}
\end{acromemory2}
\begin{newsegment}{AcroMemory 3: Bubble Sort}
// Clear DPS:

function clearDPS()
{
    for ( var i=1; i<=\nTotalTiles; i++ )
    {
        var f = this.getField("Mem\RanIdentifier button."+i);
        f.buttonSetIcon(nullIcon);
    }
}

// Mixup DPS:
function mixupDPS()
{
    var i, rand;
    for (i=1; i<= \nTotalTiles; i++)
    {
        var rand = Math.random();
        rand *= dpsl*dpsl;
        rand = Math.ceil(rand);
        rand = rand \% dpsl;
        if (rand == 0 ) rand = 1;
        temp = randomDPS[i];
        randomDPS[i]=randomDPS[rand];
        randomDPS[rand]=temp;
    }
}

// Show DPS:
function showDPS()
{
    for ( var i=1; i<=\nTotalTiles; i++ )
    {
        var oIcon = this.getIcon(imageNames[randomDPS[i]]);
        var f = this.getField("Mem\RanIdentifier button."+i);
        f.buttonSetIcon(oIcon);
    }
}

// Sortout DPS: begin bubble sort
function sortoutDPS()
{
    outerLoop(randomDPS.length-1);
}
function outerLoop(i)
{
     if ( ok2Continue && (i >= 0) ) shutdown = app.setTimeOut("app.clearTimeOut(shutdown); innerLoop("+i+",1);", timeout);
}
function innerLoop(i,j)
{
    if ( j <= i )
    {
       if (randomDPS[j-1] > randomDPS[j])
       {
            var temp = randomDPS[j-1];
            randomDPS[j-1] = randomDPS[j];
            randomDPS[j] = temp;
            var oIcon = this.getIcon(imageNames[randomDPS[j-1]]);
            var f = this.getField("Mem\RanIdentifier button."+(j-1));
            f.buttonSetIcon(oIcon);
            var oIcon = this.getIcon(imageNames[randomDPS[j]]);
            var f = this.getField("Mem\RanIdentifier button."+j);
            f.buttonSetIcon(oIcon);
        }
        j++
        if ( ok2Continue ) shutdown = app.setTimeOut("app.clearTimeOut(shutdown); innerLoop("+i+","+j+");", timeout);
    }
    else
    {
        i--;
        outerLoop(i);
    }
}
function randomizePuzzle() {
    mixupDPS();
    for ( var i=1; i<=\nTotalTiles; i++) {
        var g = this.getField("Mem\RanIdentifier button."+i);
        var oIcon = this.getIcon(imageNames[randomDPS[i]]);
        g.buttonSetIcon(oIcon,1);
        if (debug) g.buttonSetIcon(oIcon,0);
    }
}
\end{newsegment}
\begin{acromemory1}
\begin{newsegment}{AcroMemory 4: Tile Processing}
function selectTile() // right side randomly arranged
{
    var f = event.target;
    var oIcon = f.buttonGetIcon(1);
    f.buttonSetIcon(oIcon,0);
    var fname = f.name;
    var re1 = /Membutton\.(\d+)/;
    var index = re1.exec(fname);
    if (debug) console.println("index = " + index[1]);
    var thisiconName = imageNames[randomDPS[index[1]]];
    if (debug) console.println("thisiconName = " + thisiconName);
    var re = /pic(\d+)\.(\d)/;
    var image = re.exec(thisiconName)
    if (debug) console.println("selected: " + image[1] + "." + image[2]);
    if ( currentChoice == "" ) {
        currentChoice = fname;
        currentTile1=image[1];
        currentTile2=image[2];
        currentIconName = thisiconName;
        return;
    }
    if ( (image[1] == currentTile1) && (image[2] != currentTile2) )
    { // right choice
        nCorrect++;
        nAttempts++
        f.readonly = true;
        var g = this.getField(currentChoice);
        g.readonly = true;
        reportProgress(nCorrect,nAttempts);
        resetCounters();

    } else { // wrong choice
        nAttempts++
        reportProgress(nCorrect,nAttempts);
        rAE = app.setTimeOut("resetAfterError(\""+currentChoice+"\",\""+fname+"\")", 1000);
        resetCounters();
    }

}
function resetCounters ()
{
    currentChoice = "";
    currentTile1 = 0;
    currentTile2 = 0;
    currentIconName = "";
}
function resetAfterError(l,r)
{
    try { app.clearTimeOut(rAE); } catch(e) {};
    var f = this.getField(l);
    var g = this.getField(r);
    if (!debug) g.buttonSetIcon(nullIcon,0);
//    g.strokeColor=color.black;
    if (!debug) f.buttonSetIcon(nullIcon,0);
//    f.strokeColor=color.black;
}
function executePostGameEffects() {return;}
function playagain()
{
    for ( var i=1; i<=\nTotalTiles; i++) {
        var g = this.getField("Membutton."+i);
        g.buttonSetIcon(nullIcon,0);
    }
    g = this.getField("Membutton");
    g.readonly=false;
    resetCounters();
    nCorrect = 0;
    nAttempts = 0;
    reportProgress(nCorrect,nAttempts);
    randomizePuzzle();
}
\end{newsegment}
\end{acromemory1}
\begin{acromemory2}
\begin{newsegment}{AcroMemory 4: Tile Processing}
function selectRandomTile(nCnt,n) // right side randomly arranged
{
    if ( RcurrentChoice != 0 ) return;
    RcurrentChoice = nCnt;
    RcurrentTile = n;
    nAttempts++;
    var f = event.target;
    f.strokeColor = ["RGB", 0, .6, 0];
    var oIcon = f.buttonGetIcon(1);
    f.buttonSetIcon(oIcon,0);
    if ( LcurrentChoice != 0 ) {
        if (debug) console.println("LcurrentChoice = " + LcurrentChoice + ", RcurrentChoice = " + RcurrentChoice);
        if ( LcurrentChoice == nCnt ) {// right answer
            // need to make right side hidden and readonly
            // need to make this button readonly
            var g = this.getField("MemLbutton."+LcurrentChoice);
            g.strokeColor=color.transparent;
            g.readonly = true;
            f.strokeColor=color.transparent;
            f.readonly = true;
            if (++nCorrect == \nTotalTiles ) // game complete
                executePostGameEffects();
            reportProgress(nCorrect,nAttempts);
            resetCounters();
        } else { // wrong answer
            // need to set current choices back to zero
            reportProgress(nCorrect,nAttempts);
            rAE = app.setTimeOut("resetAfterError("+LcurrentTile+","+RcurrentTile+")", 1000);
            resetCounters();
        }
    }
}
function selectNonRandomTile(nCnt,n) // left side, arranged in natural order
{
    if ( LcurrentChoice != 0 ) return;
    LcurrentChoice = nCnt;
    LcurrentTile = n;
    var f = event.target;
    f.strokeColor = ["RGB", 0, .6, 0];
    var oIcon = f.buttonGetIcon(1);
    f.buttonSetIcon(oIcon,0);
    if ( RcurrentChoice != 0 ) {
        if (debug) console.println("LcurrentChoice = " + LcurrentChoice + ", RcurrentChoice = " + RcurrentChoice);
        if ( RcurrentChoice == nCnt ) {// right answer
            // need to make right side hidden and readonly
            // need to make this button readonly
            var g = this.getField("MemRbutton."+RcurrentTile);
            g.strokeColor=color.transparent;
            g.readonly = true;
            f.readonly = true;
            f.strokeColor=color.transparent;
            if (++nCorrect == \nTotalTiles ) // game complete
                executePostGameEffects();
            reportProgress(nCorrect,nAttempts);
            resetCounters();
        } else { // wrong answer
            // need to set current choices back to zero
            reportProgress(nCorrect,nAttempts);
            rAE = app.setTimeOut("resetAfterError("+LcurrentTile+","+RcurrentTile+")", 1000);
            resetCounters();
        }
    }
}
function resetCounters ()
{
    LcurrentChoice = 0;
    RcurrentChoice = 0;
    LcurrentTile = 0;
    RcurrentTile = 0;
}
function resetAfterError(l,r)
{
    try { app.clearTimeOut(rAE); } catch(e) {};
    var f = this.getField("MemLbutton."+l);
    var g = this.getField("MemRbutton."+r);
    if (!debug) g.buttonSetIcon(nullIcon,0);
    g.strokeColor=color.black;
    if (!debug) f.buttonSetIcon(nullIcon,0);
    f.strokeColor=color.black;
}
function executePostGameEffects() {
    sortoutDPS();
    var fL = this.getField("MemLbutton.1");
    var fR = this.getField("MemRbutton.1");
    var LulCorner = fL.rect;
    var RulCorner = fR.rect;
    var mWidth = LulCorner[2]-LulCorner[0];
    var mHeight = LulCorner[1]-LulCorner[3];
    var nCnt = 0;
    for ( var i=0; i<nRows; i++) {
        for ( var j=0; j<nCols; j++ ) {
            nCnt++;
            try {
                var g = this.getField("MemLbutton."+nCnt);
                g.rect  = [ LulCorner[0]+j*mWidth, LulCorner[1]-i*mHeight, LulCorner[0]+(j+1)*mWidth, LulCorner[1]-(i+1)*mHeight ]
                g.lineWidth = 0;
                g.strokeColor = color.transparent;

                var h = this.getField("MemRbutton."+nCnt);
                h.rect  = [ RulCorner[0]+j*mWidth, RulCorner[1]-i*mHeight, RulCorner[0]+(j+1)*mWidth, RulCorner[1]-(i+1)*mHeight ]
                h.lineWidth = 0;
                h.strokeColor = color.transparent;

            } catch(e) { console.println("set properties: " + e.toSource()) }
        }
    }
}
\end{newsegment}
\end{acromemory2}
\begin{newsegment}{AcroMemory 5: Reporting}
function reportProgress(nCorrect,nAttempts) {
    var Msg = this.getField("MsgBox")
    if ( Msg != null ) {
    Msg.value = "Number matched = " + nCorrect
        + "\n Number of attempts = " + nAttempts;
    }
}
try { randomizePuzzle(); } catch(e) {}
\end{newsegment}
\end{insDLJS*}

\begin{acromemory1}
\begin{execJS}{acromemjs}
var l = \nTotalTiles/2;
var pt2bpScaleFactor = 72/72.27;
if (\iconsInOneFile) {
    for ( var i = 0; i < l; i++)
    {
        try {
            aebTrustedFunctions(this, aebImportIcon, {cName: "pic"+(i+1) +".0", cDIPath: "\importpath.pdf", nPage: i }, this);
            aebTrustedFunctions(this, aebImportIcon, {cName: "pic"+(i+1) +".1", cDIPath: "\importpath.pdf", nPage: i }, this);
        }
        catch(e) {console.println("exception: " + e.toSource())}
    }
} else {
    var index, j;
    for ( var i = 0; i < l; i++)
    {
        try {
            j = i+1;
            index = ( j < 10 ) ? "0"+j : ""+j;
            if (debug) console.println("Importing: \importpath_"+index+".\iconsExt")
            aebTrustedFunctions(this, aebImportIcon, {cName: "pic"+j+".0", cDIPath: "\importpath_"+index+".\iconsExt"}, this);
            aebTrustedFunctions(this, aebImportIcon, {cName: "pic"+j+".1", cDIPath: "\importpath_"+index+".\iconsExt"}, this);
        }
        catch(e) {console.println("import exception: " + e.toSource())}
    }
}

// Now lay out the icon fields.
var f = this.getField("ulcorner");
var ulCorner = f.rect;
var nPage = f.page;
this.removeField("ulcorner");

if (\bProvideDimen) {
    var width = \trueImageWidth*pt2bpScaleFactor;
    var height =\trueImageHeight*pt2bpScaleFactor;
} else {
    var doc = aebTrustedFunctions( this, aebAppOpenDoc, { cPath: "\importpath.pdf", oDoc: this });
    var aRect = doc.getPageBox({cBox:"Crop"})
    doc.closeDoc(true);
    var width = aRect[2]-aRect[0];
    var height = aRect[1]-aRect[3];
}
var scaleFactor = (\imageWidth*pt2bpScaleFactor)/width;

var scaledWidth = width*scaleFactor;
var scaledHeight = height*scaleFactor;

if (debug) {
    console.println("scaleFactor = " + scaleFactor);
    console.println("scaledWidth = " + scaledWidth);
    console.println("scaledHeight = " + scaledHeight);
}

var mWidth = scaledWidth/nCols
var mHeight = scaledHeight/nRows

var nCnt = 0;

for ( var i=0; i<nRows; i++) {
    for ( var j=0; j<nCols; j++ ) {
        nCnt++;
        try {
        var g = this.addField({
            cName: "Membutton."+nCnt,
            cFieldType: "button",
            nPageNum: nPage,
            oCoords: [ ulCorner[0]+j*mWidth, ulCorner[1]-i*mHeight, ulCorner[0]+(j+1)*mWidth, ulCorner[1]-(i+1)*mHeight ]
        });
        } catch(e) { console.println( e.toSource() ); }
        try {
            g.highlight=highlight.p
            g.buttonPosition = position.iconOnly;
            g.lineWidth = 1;
            g.strokeColor = color.black;
            g.setAction("MouseDown", "selectTile();")
        } catch(e) { console.println("set properties: " + e.toSource()) }
        try {
          var pNCnt = nCnt + 1;
          var index = parseInt( pNCnt/2)
          var oIcon = this.getIcon("pic"+index+"."+ ( pNCnt \% 2 ));
          g.buttonSetIcon(oIcon,1);
          if (debug) g.buttonSetIcon(oIcon,0);
        } catch(e) { console.println("set properties: " + e.toSource()) }
    }
}
try { randomizePuzzle(); } catch(e) { console.println("execJS--randomizePuzzle exceptions: " + e.toSource()) }
var isRandomized = true;
\end{execJS}
\end{acromemory1}

\begin{acromemory2}
\begin{execJS}{execjs}
var index;
var pt2bpScaleFactor = 72/72.27;
if (\iconsInOneFile) {
    for ( var i = 1; i <= \nTotalTiles; i++)
    {
        index = ( i < 10 ) ? "0"+i : ""+i;
        if (debug) console.println("\importpath_"+index+".\iconsExt")
        try { aebTrustedFunctions(this, aebImportIcon, {cName: "pic."+i, cDIPath: "\importpath.pdf", nPage: (i-1) }, this);
        }
        catch(e) {console.println("exception: " + e.toSource())}
    }
} else {
    for ( var i = 1; i <= \nTotalTiles; i++)
    {
        index = ( i < 10 ) ? "0"+i : ""+i;
        if (debug) console.println("\importpath_"+index+".\iconsExt")
        try { aebTrustedFunctions(this, aebImportIcon, {cName: "pic."+i, cDIPath: "\importpath_"+index+".\iconsExt"}, this);
        }
        catch(e) {console.println("exception: " + e.toSource())}
    }
}
// Now lay out the icon fields.
var f = this.getField("Lulcorner");
var ulCorner = f.rect;
var nPage = f.page;
this.removeField("Lulcorner");

if (\bProvideDimen) {
    var width = \trueImageWidth*pt2bpScaleFactor;
    var height =\trueImageHeight*pt2bpScaleFactor;
} else {
    var doc = aebTrustedFunctions( this, aebAppOpenDoc, { cPath: "\imageImportPath.pdf", oDoc: this });
    var aRect = doc.getPageBox({cBox:"Crop"})
    doc.closeDoc(true);
    var width = aRect[2]-aRect[0];
    var height = aRect[1]-aRect[3];
}
var scaleFactor = (\imageWidth*pt2bpScaleFactor)/width;

var scaledWidth = width*scaleFactor;
var scaledHeight = height*scaleFactor;

if (debug) {
    console.println("scaleFactor = " + scaleFactor);
    console.println("scaledWidth = " + scaledWidth);
    console.println("scaledHeight = " + scaledHeight);
}

var mWidth = scaledWidth/nCols
var mHeight = scaledHeight/nRows

%console.println("mWidth = " + mWidth);
%console.println("mHeight = " + mHeight);

var nCnt = 0;

for ( var i=0; i<nRows; i++) {
    for ( var j=0; j<nCols; j++ ) {
        nCnt++;
        try {
        var g = this.addField({
            cName: "MemLbutton."+nCnt,
            cFieldType: "button",
            nPageNum: nPage,
            oCoords: [ ulCorner[0]+j*mWidth, ulCorner[1]-i*mHeight, ulCorner[0]+(j+1)*mWidth, ulCorner[1]-(i+1)*mHeight ]
        });
        } catch(e) { console.println( e.toSource()); }
        try {
            g.highlight=highlight.p
            g.buttonPosition = position.iconOnly;
            g.lineWidth = 1;
            g.strokeColor = color.black;
            g.setAction("MouseDown", "selectNonRandomTile("+nCnt+","+nCnt+");")
        } catch(e) { console.println("set properties: " + e.toSource()) }
          var oIcon = this.getIcon("pic."+nCnt);
          g.buttonSetIcon(oIcon,1);
          if (debug) g.buttonSetIcon(oIcon,0);
    }
}
// Now lay out the icon fields.
var f = this.getField("Rulcorner");
var ulCorner = f.rect;
this.removeField("Rulcorner");

var nCnt = 0;

for ( var i=0; i<nRows; i++) {
    for ( var j=0; j<nCols; j++ ) {
        nCnt++;
        try {
        var g = this.addField({
            cName: "MemRbutton."+nCnt,
            cFieldType: "button",
            nPageNum: nPage,
            oCoords: [ ulCorner[0]+j*mWidth, ulCorner[1]-i*mHeight, ulCorner[0]+(j+1)*mWidth, ulCorner[1]-(i+1)*mHeight ]
        });
        } catch(e) { console.println( e.toSource()); }
        try {
        g.highlight=highlight.p
        g.buttonPosition = position.iconOnly;
        g.lineWidth = 1;
        g.strokeColor = color.black;
        g.setAction("MouseDown", "selectRandomTile(randomDPS["+nCnt+"],"+nCnt+");")
        } catch(e) { console.println("set properties: " + e.toSource()) }
    }
}
try { randomizePuzzle(); } catch(e) { console.println("randomizePuzzle exceptions: " + e.toSource()) }
var isRandomized = true;
\end{execJS}
\end{acromemory2}
\begin{needhelp}
\begin{execJS}{helpjs}
try {
    aebTrustedFunctions(this, aebImportIcon, {cName: "helpicon", cDIPath: "\imageImportPath.\iconsExt"}, this);
    var f = this.getField("memoryhelp");
    var oIcon = this.getIcon("helpicon");
    f.buttonPosition = position.iconOnly;
    f.buttonSetIcon(oIcon,1);
    f.buttonSetCaption({cCaption: "\helpCaption"});
} catch(e) {console.println("exception: " + e.toSource())}
\end{execJS}
\end{needhelp}
%</package>
%    \end{macrocode}
%  \Finale
\endinput