GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W rubik.g GAP 4 package `browse' Thomas Breuer
##
#Y Copyright (C) 2006, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
##
#############################################################################
##
#F BrowseRubiksCube( [<format>][<pi>] )
#F BrowseRubiksCube( <output> )
##
## <#GAPDoc Label="Rubik_section">
## <Section Label="sec:rubikscube">
## <Heading>Rubik's Cube</Heading>
## <Index Subkey="Rubik's Cube">game</Index>
##
## We visualize the transformations of Rubik's magic cube in a model that is
## given by <Q>unfolding</Q> the faces and numbering them as follows.
##
## <Alt Only="LaTeX">
## <!-- BP rubik -->
## <![CDATA[
## \begin{center}
## \begin{tabular}{cccccccccccc}
## \cline{4-6}
## & & & \multicolumn{1}{|c}{1} & \multicolumn{1}{c}{2} &
## \multicolumn{1}{c|}{3} & & & & & & \\
## & & & \multicolumn{1}{|c}{4} & \multicolumn{1}{c}{top} &
## \multicolumn{1}{c|}{5} & & & & & & \\
## & & & \multicolumn{1}{|c}{6} & \multicolumn{1}{c}{7} &
## \multicolumn{1}{c|}{8} & & & & & & \\
## \hline
## \multicolumn{1}{|c}{9} & \multicolumn{1}{c}{10} &
## \multicolumn{1}{c}{11} & \multicolumn{1}{|c}{17} &
## \multicolumn{1}{c}{18} & \multicolumn{1}{c}{19} &
## \multicolumn{1}{|c}{25} & \multicolumn{1}{c}{26} &
## \multicolumn{1}{c}{27} & \multicolumn{1}{|c}{33} &
## \multicolumn{1}{c}{34} & \multicolumn{1}{c|}{35} \\
## \multicolumn{1}{|c}{12} & \multicolumn{1}{c}{left} &
## \multicolumn{1}{c}{13} & \multicolumn{1}{|c}{20} &
## \multicolumn{1}{c}{front} & \multicolumn{1}{c}{21} &
## \multicolumn{1}{|c}{28} & \multicolumn{1}{c}{right} &
## \multicolumn{1}{c}{29} & \multicolumn{1}{|c}{36} &
## \multicolumn{1}{c}{back} & \multicolumn{1}{c|}{37} \\
## \multicolumn{1}{|c}{14} & \multicolumn{1}{c}{15} &
## \multicolumn{1}{c}{16} & \multicolumn{1}{|c}{22} &
## \multicolumn{1}{c}{23} & \multicolumn{1}{c}{24} &
## \multicolumn{1}{|c}{30} & \multicolumn{1}{c}{31} &
## \multicolumn{1}{c}{32} & \multicolumn{1}{|c}{38} &
## \multicolumn{1}{c}{39} & \multicolumn{1}{c|}{40} \\
## \hline
## & & & \multicolumn{1}{|c}{41} & \multicolumn{1}{c}{42} &
## \multicolumn{1}{c|}{43} & & & & & & \\
## & & & \multicolumn{1}{|c}{44} & \multicolumn{1}{c}{down} &
## \multicolumn{1}{c|}{45} & & & & & & \\
## & & & \multicolumn{1}{|c}{46} & \multicolumn{1}{c}{47} &
## \multicolumn{1}{c|}{48} & & & & & & \\
## \cline{4-6}
## \end{tabular}
## \end{center}
## ]]>
## <!-- EP -->
## </Alt>
## <Alt Only="HTML">
## <![CDATA[
## </p><div style="text-align:center;">
## <img src="rubik.png" alt="[unfolded Rubik's cube image]"/>
## </div><p>
## ]]>
## </Alt>
## <Alt Only="Text">
## <Verb>
## ┌──────────────┐
## │ 1 2 3 │
## │ 4 top 5 │
## │ 6 7 8 │
## ┌──────────────┼──────────────┼──────────────┬──────────────┐
## │ 9 10 11 │ 17 18 19 │ 25 26 27 │ 33 34 35 │
## │ 12 left 13 │ 20 front 21 │ 28 right 29 │ 36 back 37 │
## │ 14 15 16 │ 22 23 24 │ 30 31 32 │ 38 39 40 │
## └──────────────┼──────────────┼──────────────┴──────────────┘
## │ 41 42 43 │
## │ 44 down 45 │
## │ 46 47 48 │
## └──────────────┘
## </Verb>
## </Alt>
##
## Clockwise turns of the six layers (top, left, front, right, back, and
## down) are represented by the following permutations.
## <P/>
## <Example><![CDATA[
## gap> cubegens := [
## > ( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19),
## > ( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35),
## > (17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11),
## > (25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24),
## > (33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27),
## > (41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40)
## > ];;
## ]]></Example>
##
## &GAP; computations analyzing this permutation group have been part of
## the announcements of &GAP; 3 releases.
## For a &GAP; 4 equivalent, see <Cite Key="RubiksCubeGAPWeb"/>.
## For more information and references (not &GAP; related)
## about Rubik's cube, see <Cite Key="RubiksCubeWeb"/>.
##
## <ManSection>
## <Func Name="BrowseRubiksCube" Arg="[format][pi]"/>
##
## <Description>
## This function shows the model of the cube in a window.
## <P/>
## If the argument <A>format</A> is one of the strings <C>"small"</C> or
## <C>"large"</C> then small or large cells are shown,
## the default is <C>"small"</C>.
## <P/>
## The argument <A>pi</A> is the initial permutation of the faces,
## the default is a random permutation in the cube group,
## see <Ref Sect="Random" BookName="ref"/>.
## <P/>
## Supported user inputs are the keys <B>t</B>, <B>l</B>, <B>f</B>,
## <B>r</B>, <B>b</B>, and <B>d</B> for clockwise turns of the six layers,
## and the corresponding capital letters for counter-clockwise turns.
## If the terminal supports colors, according to the global variable
## <Ref Var="NCurses.attrs.has_colors"/>, the input <B>s</B> switches
## between a screen that shows only the colors of the faces and a screen
## that shows the numbers; the color screen is the default.
## <P/>
## The return value is a record with the components
## <C>inputs</C> (a string describing the user inputs),
## <C>init</C>, and <C>final</C> (the initial and final permutation of the
## faces, respectively).
## (The <C>inputs</C> component can be used for the replay feature,
## see the example below.)
## <P/>
## In the following example, a word in terms of the generators is used to
## initialize the browse table, and then the letters in this word are used
## as a series of input steps, except that in between, the display is
## switched once from colors to numbers and back.
## <P/>
## <Example><![CDATA[
## gap> choice:= List( [ 1 .. 30 ], i -> Random( [ 1 .. 6 ] ) );;
## gap> input:= List( "tlfrbd", IntChar ){ choice };;
## gap> BrowseData.SetReplay( Concatenation(
## > input{ [ 1 .. 20 ] },
## > "s", # switch to number display
## > input{ [ 21 .. 25 ] },
## > "s", # switch to color display
## > input{ [ 26 .. 30 ] },
## > "Q" ) );; # quit the browse table
## gap> BrowseRubiksCube( Product( cubegens{ choice } ) );;
## gap> BrowseRubiksCube( "large", Product( cubegens{ choice } ) );;
## gap> BrowseData.SetReplay( false );
## ]]></Example>
##
## <E>Implementation remarks</E>:
## The cube is implemented via a browse table,
## without row and column labels,
## with static header, dynamic footer, and individual <C>minyx</C> function.
## Only one mode is needed,
## and besides the standard actions for quitting the table, asking for help,
## and saving the current window contents,
## only the twelve moves and the switch between color and number
## display are admissible.
## <P/>
## Switching between the two display formats is implemented via a function
## <C>work.Main</C>,
## so this relies on <E>not</E> caching the formatted cells in
## <C>work.main</C>.
## <P/>
## Row and column separators of the browse table are whitespace of height
## and width one.
## The separating lines are drawn using an individual <C>SpecialGrid</C>
## function in the browse table.
## Note that the relevant cells do not form a rectangular array.
## <P/>
## Some standard <Ref Func="NCurses.BrowseGeneric"/> functionality,
## such as scrolling, selecting, and searching,
## are not available in this application.
## <P/>
## The code can be found in the file <F>app/rubik.g</F> of the package.
## </Description>
## </ManSection>
## </Section>
## <#/GAPDoc>
##
BindGlobal( "BrowseRubiksCube", function( arg )
local gens, cube, format, i, pi, showcolors, coord, minyx, numbers,
col, colors, grid, turn, mode, table;
# Write down the permutations and the group.
gens:= [
( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19), # t
( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35), # l
(17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11), # f
(25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24), # r
(33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27), # b
(41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40), # d
];
cube:= GroupWithGenerators( gens );
# Get and check the arguments.
format:= "small";
for i in [ 1 .. Length( arg ) ] do
if IsPerm( arg[i] ) then
pi:= arg[i];
elif IsString( arg[i] ) and arg[i] in [ "small", "large" ] then
format:= arg[i];
else
Error( "usage: BrowseRubiksCube( [<format>][<pi>] )" );
fi;
od;
if not IsBound( pi ) then
pi:= Random( cube );
elif not IsPerm( pi ) or not pi in cube then
Error( "<pi> must be a permutation in <cube>" );
fi;
showcolors:= NCurses.attrs.has_colors;
coord:= [ [1, 4],[1, 5],[1, 6],[2, 4],[2, 6],[3, 4],[3, 5],[3, 6], # t
[4, 1],[4, 2],[4, 3],[5, 1],[5, 3],[6, 1],[6, 2],[6, 3], # l
[4, 4],[4, 5],[4, 6],[5, 4],[5, 6],[6, 4],[6, 5],[6, 6], # f
[4, 7],[4, 8],[4, 9],[5, 7],[5, 9],[6, 7],[6, 8],[6, 9], # r
[4,10],[4,11],[4,12],[5,10],[5,12],[6,10],[6,11],[6,12], # b
[7, 4],[7, 5],[7, 6],[8, 4],[8, 6],[9, 4],[9, 5],[9, 6], # d
[2, 5],[5, 2],[5, 5],[5, 8],[5,11],[8, 5], # layer centers
];
if format = "small" then
minyx:= [ 25, 37 ];
numbers:= List( [ 1 .. 48 ], i -> [ String( i, 2 ) ] );
Append( numbers, List( [ " t", " l", " f", " r", " b", " d" ],
c -> [ NCurses.attrs.BOLD, true, c ] ) );
col:= List( [ "red", "green", "yellow", "blue", "cyan", "white" ],
color -> [ NCurses.ColorAttr( "white", color ), true,
" " ] );
else
minyx:= [ 43, 61 ];
numbers:= List( [ 1 .. 48 ], i -> [ " ",
Concatenation( String( i, 3 ), " " ),
" " ] );
Append( numbers,
List( [ " t ", " l ", " f ", " r ", " b ", " d " ],
c -> [ " ", [ NCurses.attrs.BOLD, true, c ],
" " ] ) );
col:= List( [ "red", "green", "yellow", "blue", "cyan", "white" ],
color -> ListWithIdenticalEntries( 3,
[ NCurses.ColorAttr( "white", color ), true, " " ] ) );
fi;
colors:= Concatenation( List( col,
c -> ListWithIdenticalEntries( 8, c ) ) );
Append( colors, col );
grid:= function( t, data )
local ht, wt, win, tp, lt, i, j;
ht:= Length( numbers[1] ) + 1;
wt:= Length( numbers[1][1] ) + 1;
win:= t.dynamic.window;
tp:= data.topmargin + 3;
lt:= data.leftmargin;
NCurses.Grid( win, tp, tp + 3 * ht, lt + 3 * wt, lt + 6 * wt,
tp + [ 0 .. 3 ] * ht, lt + [ 3 .. 6 ] * wt );
NCurses.Grid( win, tp + 3 * ht, tp + 6 * ht, lt, lt + 12 * wt,
tp + [ 3 .. 6 ] * ht, lt + [ 0 .. 12 ] * wt );
NCurses.Grid( win, tp + 6 * ht, tp + 9 * ht, lt + 3 * wt, lt + 6 * wt,
tp + [ 6 .. 9 ] * ht, lt + [ 3 .. 6 ] * wt );
for i in [ 3, 6 ] do
for j in [ 3 .. 6 ] do
NCurses.wmove( win, tp + i * ht, lt + j * wt );
NCurses.waddch( win, NCurses.lineDraw.PLUS );
od;
od;
end;
# Supported actions are
# - quarter turns via the six generators and their inverses
# - switching between color and number display
# - entering the help mode,
# - and leaving the table
turn:= function( t, gen, input, exp )
pi:= gens[ gen ]^exp * pi;
t.dynamic.Return.final:= pi;
Add( t.dynamic.Return.input, input );
t.dynamic.changed:= true;
return t.dynamic.changed;
end;
# Construct the mode.
mode:= BrowseData.CreateMode( "rubik", "", [
# standard actions
[ [ "E" ], BrowseData.actions.Error ],
[ [ "q", [ [ 27 ], "<Esc>" ] ], BrowseData.actions.QuitMode ],
[ [ "Q" ], BrowseData.actions.QuitTable ],
[ [ "?", [ [ NCurses.keys.F1 ], "<F1>" ] ],
BrowseData.actions.ShowHelp ],
[ [ [ [ NCurses.keys.F2 ], "<F2>" ] ], BrowseData.actions.SaveWindow ],
# moves via the keys tlfrbd and TLFRBD
[ [ "t" ], rec( helplines:= [ "turn the top layer clockwise" ],
action:= t -> turn( t, 1, 't', -1 ) ) ],
[ [ "l" ], rec( helplines:= [ "turn the left layer clockwise" ],
action:= t -> turn( t, 2, 'l', -1 ) ) ],
[ [ "f" ], rec( helplines:= [ "turn the front layer clockwise" ],
action:= t -> turn( t, 3, 'f', -1 ) ) ],
[ [ "r" ], rec( helplines:= [ "turn the right layer clockwise" ],
action:= t -> turn( t, 4, 'r', -1 ) ) ],
[ [ "b" ], rec( helplines:= [ "turn the back layer clockwise" ],
action:= t -> turn( t, 5, 'b', -1 ) ) ],
[ [ "d" ], rec( helplines:= [ "turn the down layer clockwise" ],
action:= t -> turn( t, 6, 'd', -1 ) ) ],
[ [ "T" ], rec( helplines:= [ "turn the top layer counter-clockwise" ],
action:= t -> turn( t, 1, 'T', 1 ) ) ],
[ [ "L" ], rec( helplines:= [ "turn the left layer counter-clockwise" ],
action:= t -> turn( t, 2, 'L', 1 ) ) ],
[ [ "F" ], rec( helplines:= [ "turn the front layer counter-clockwise" ],
action:= t -> turn( t, 3, 'F', 1 ) ) ],
[ [ "R" ], rec( helplines:= [ "turn the right layer counter-clockwise" ],
action:= t -> turn( t, 4, 'R', 1 ) ) ],
[ [ "B" ], rec( helplines:= [ "turn the back layer counter-clockwise" ],
action:= t -> turn( t, 5, 'B', 1 ) ) ],
[ [ "D" ], rec( helplines:= [ "turn the down layer counter-clockwise" ],
action:= t -> turn( t, 6, 'D', 1 ) ) ],
# switch between color and number display
[ [ "s" ], rec( helplines:= [ "switch between color and number display" ],
action:= function( t )
if NCurses.attrs.has_colors then
showcolors:= not showcolors;
t.dynamic.changed:= true;
else
BrowseData.AlertWithReplay( t,
"Your terminal does not support colors.",
NCurses.attrs.BOLD );
fi;
end ) ],
] );
# Construct the browse table.
table:= rec(
work:= rec(
align:= "ct",
minyx:= minyx,
header:= [ "", [ NCurses.attrs.UNDERLINE, true, "Rubik's cube" ],
"" ],
main:= [],
Main:= function( t, i, j )
i:= Position( coord, [ i, j ] );
if i = fail then
return "";
fi;
i:= i ^ pi;
if showcolors then
return colors[i];
else
return numbers[i];
fi;
end,
m:= 9,
n:= 12,
sepRow:= " ",
sepCol:= " ",
footer:= function( t )
local stepsline, scr, inputline;
if IsOne( t.dynamic.Return.final ) then
stepsline:= [ Concatenation(
String( Length( t.dynamic.Return.input ) ), " steps " ),
NCurses.ColorAttr( "red", -1 ), "(done)" ];
else
stepsline:= Concatenation(
String( Length( t.dynamic.Return.input ) ), " steps" );
fi;
scr:= Sum( t.work.widthCol, 0 );
if Length( t.dynamic.Return.input ) = 0 then
inputline:= "input: ";
elif Length( t.dynamic.Return.input ) + 7 < scr then
inputline:= Concatenation( "input: ", t.dynamic.Return.input );
else
inputline:= t.dynamic.Return.input;
inputline:= inputline{ [ Length( inputline ) - scr + 10
.. Length( inputline ) ] };
inputline:= Concatenation( "input: ...", inputline );
fi;
return [ "", stepsline, inputline ];
end,
availableModes:= [ mode ],
cacheEntries:= false,
SpecialGrid:= grid,
),
dynamic:= rec(
activeModes:= [ mode ],
Return:= rec( input:= "",
init:= pi,
final:= pi,
),
),
);
# Show the browse table, and return its return value.
return NCurses.BrowseGeneric( table );
end );
#############################################################################
##
#E