GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W puzzle.g GAP library Thomas Breuer
##
##
#Y Copyright (C) 1998, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany
#Y (C) 1998 School Math and Comp. Sci., University of St. Andrews, Scotland
##
#############################################################################
##
#F Puzzle( <m>, <n>[, <options>] )
##
## `Puzzle' returns a sheet that shows a $<m> \times <n>$ rectangle
## with $<m><n> - 1$ numbered squares and one free square indicated by
## a red box in the lower right corner.
## Clicking on a numbered square in the same row or column as the red box
## will move the squares between the red box and the pointer towards the
## place of the red box, and put the red box under the pointer.
## The aim is to rearrange the numbered squares such that they are ordered.
##
## If the optional argument <options> is present then it must be a record
## with the following components.
## \beginitems
## `b' (default 30) &
## the dimension of the squares,
##
## `l' (default 2) &
## the width of the separating lines,
##
## `font' (default `FONTS.normal') &
## the font chosen for the text, and
##
## `color' (default `COLORS.red') &
## the color chosen for the free square).
## \enditems
##
## The puzzle provides also an example for the effects caused by
## `FastUpdate' (see~"FastUpdate").
## For a sheet <sheet> returned by `Puzzle',
## call `FastUpdate( <sheet>, true )', move the red box in <sheet>,
## and you will notice that you can make moved numbers disappear;
## this is due to the fact that after the text has been moved to the
## place of the red box, this box is moved away, and the system thinks
## that nothings needs to be drawn in this place.
## (Note that {\XGAP} does not support the idea that a graphic object
## lies (partially) above or under another graphic object.)
## The correct status of <sheet> can be recovered by calling
## `FastUpdate( <sheet>, false )'.
##
BindGlobal( "Puzzle", function( arg )
local m, # number of rows
n, # number of columns
options, # options record
b, # dimension of squares
l, # line width
bl, # `b + l'
font, # font chosen for text (numbers)
red, # the colour of the free square
hori, # width of the sheet
vert, # height of the sheet
sheet, # graphic sheet containing the puzzle
i, # loop over rows
j, # loop over columns
free, # the red box
boxpos, # list with position of the free square
pi, # random permutation
matrix, # matrix containing the text objects
x; # random element in `numbers'
# Get and check the arguments.
if Length( arg ) = 2 and IsPosInt( arg[1] ) and IsPosInt( arg[2] ) then
m:= arg[1];
n:= arg[2];
options:= rec( b:= 30,
l:= 2,
font:= FONTS.normal,
color:= COLORS.red );
elif Length( arg ) = 3 and IsPosInt( arg[1] ) and IsPosInt( arg[2] )
and IsRecord( arg[3] ) then
m:= arg[1];
n:= arg[2];
options:= arg[3];
else
Error( "usage: Puzzle( <m>, <n>[, <options>] )" );
fi;
b := options.b;
l := options.l;
bl := b + l;
font := options.font;
red := options.color;
# Compute the dimensions of the sheet.
if b mod 2 = 1 then
b:= b + 1;
fi;
hori := n * bl + l;
vert := m * bl + l;
# Construct the sheet
sheet := GraphicSheet(
Concatenation( String( m ), "x", String( n ), " puzzle" ),
hori,
vert );
# Draw the horizontal and vertical ``lines''.
for i in [ 0 .. m ] do
Box( sheet, 0, i * bl - 1, hori, l );
od;
for j in [ 0 .. n ] do
Box( sheet, j * bl - 1, 0, l, vert );
od;
# Draw a red box in the lower right corner square.
free:= Box( sheet, ( n - 1 ) * bl + l - 1,
( m - 1 ) * bl + l - 1, b, b );
Recolor( free, red );
boxpos:= [ m, n ];
# Choose an initial numbering of the squares except the free one.
pi:= Random( AlternatingGroup( m * n - 1 ) );
matrix:= [];
x:= 1;
for i in [ 0 .. m-1 ] do
matrix[ i+1 ]:= [];
for j in [ 0 .. n - 1 - QuoInt( i, m-1 ) ] do
matrix[ i+1 ][ j+1 ]:= Text( sheet, font,
j * bl + b/2,
i * bl + b/2,
String( x^pi ) );
x:= x + 1;
od;
od;
# Clicking the left mouse button in a square of the puzzle will
# move the free square to this position.
InstallCallback( sheet, "LeftPBDown", function( sheet, x, y )
local i, j, k, z;
# Compute in which square the cursor is.
i:= QuoInt( y - l, bl ) + 1;
j:= QuoInt( x - l, bl ) + 1;
if i = boxpos[1] then
k:= boxpos[2];
# The free square is in the same row as the cursor.
# Move the texts between the two towards the cursor.
if j < k then
for z in [ k-1, k-2 .. j ] do
matrix[i][ z+1 ]:= matrix[i][z];
MoveDelta( matrix[i][ z+1 ], bl, 0 );
od;
elif k < j then
for z in [ k, k+1 .. j-1 ] do
matrix[i][z]:= matrix[i][ z+1 ];
MoveDelta( matrix[i][z], - bl, 0 );
od;
fi;
# Put the free square under the cursor.
MoveDelta( free, bl * ( j - k ), 0 );
Unbind( matrix[i][j] );
boxpos[2]:= j;
elif j = boxpos[2] then
k:= boxpos[1];
# The free square is in the same column as the cursor.
# Move the texts between the two towards the cursor.
if i < k then
for z in [ k-1, k-2 .. i ] do
matrix[ z+1 ][j]:= matrix[z][j];
MoveDelta( matrix[ z+1 ][j], 0, bl );
od;
elif k < i then
for z in [ k, k+1 .. i-1 ] do
matrix[z][j]:= matrix[ z+1 ][j];
MoveDelta( matrix[z][j], 0, - bl );
od;
fi;
# Put the free square under the cursor.
MoveDelta( free, 0, bl * ( i - k ) );
Unbind( matrix[i][j] );
boxpos[1]:= i;
fi;
end );
# Return the sheet.
return sheet;
end );
#############################################################################
##
#E