GAP (Browse) - Chapter 6: Examples of Applications based on NCurses.BrowseGeneric

Goto Chapter: | Top | 1 | 2 | 3 | 4 | 5 | 6 | Bib | Ind

### 6. Examples of Applications based on `NCurses.BrowseGeneric`

This chapter introduces the operation `Browse` (6.1-1) and lists several examples how the function `NCurses.BrowseGeneric` (4.3-1) can be utilized for rendering GAP related data or for playing games. Each section describes the relevant GAP functions and briefly sketches the technical aspects of the implementation; more details can be found in the GAP files.

Only Section 6.3 describes a standard application in the sense of the introduction to Chapter 4., perhaps except for a special function that is needed to compare table entries. The other examples in this chapter require some of the programming described in Chapter 5..

#### 6.1 The Operation `Browse`

##### 6.1-1 Browse

`> Browse`( obj[, arec] ) | ( operation )

This operation displays the GAP object obj in a nice, formatted way, similar to the operation `Display` (Reference: Display). The difference is that `Browse` is intended to use `ncurses` facilities.

Currently there are methods for character tables (see `Browse` (6.2-1)) and for tables of marks (see `Browse` (6.3-1)).

#### 6.2 Character Table Display

The GAP library provides a `Display` (Reference: Display) method for character tables that breaks the table into columns fitting on the screen. Browse provides an alternative, using the standard facilities of the function `NCurses.BrowseGeneric` (4.3-1), i. e., one can scroll in the matrix of character values, searching and sorting are provided etc.

The `Browse` method for character tables can be called instead of `Display`. For convenience, one can additionally make this function the default `Display` method for character tables, by assigning it to the `Display` component in the global record `CharacterTableDisplayDefaults.User`, see Reference: Printing Character Tables; for example, one can do this in one's `.gaprc` file, see Reference: The .gaprc file. (This can be undone by unbinding the component `CharacterTableDisplayDefaults.User.Display`.)

##### 6.2-1 Browse

`> Browse`( tbl[, options] ) | ( method )

This method displays the character table tbl in a window. The optional record options describes what shall be displayed, the supported components and the default values are described in Reference: Printing Character Tables.

The full functionality of the function `NCurses.BrowseGeneric` (4.3-1) is available.

    gap> BrowseData.SetReplay( Concatenation(
    >         # scroll in the table
    >         "DRULdddddrrrrrlluu",
    >         # select an entry and move it around
    >         "seddrruuuddlll",
    >         # search for the pattern 135 (six times)
    >         "/135", [ NCurses.keys.ENTER ], "nnnnn",
    >         # deselect the entry, select the first column
    >         "qLsc",
    >         # sort and categorize by this column
    >         "sc",
    >         # select the first row, move down the selection
    >         "srdddd",
    >         # expand the selected category, scroll the selection down
    >         "xd",
    >         # and quit the application
    >         "Q" ) );
    gap> Browse( CharacterTable( "HN" ) );
    gap> BrowseData.SetReplay( false );

Implementation remarks: The first part of the code in the `Browse` method for character tables is almost identical with the code for extracting the data to be displayed from the input data in the GAP library function `CharacterTableDisplayDefault`. The second part of the code transforms these data into a browse table. Character names and (if applicable) indicator values are used as row labels, and centralizer orders, power maps, and class names are used as column labels. The identifier of the table is used as the static header. When an irrational entry is selected, a description of this entry is shown in the dynamic footer.

Due to an extension of the standard modes in `BrowseData` (5.4-1) by two new actions, a pager can be opened that gives an overview of all all irrationalities in the table, and an overview of all those irrationalities that have been shown on the screen in the current call, respectively. The corresponding user inputs are the I and the i key. (The names assigned to the irrationalities are generated column-wise. If one just scrolls through the table, without jumping, then these names coincide with the names generated by the defaults `Display` method for character tables; this is in general not the case for example when a row-wise search in the table is performed.)

In order to keep the required space small also for large character tables, caching of formatted matrix entries is disabled, and the strings to be displayed are computed on demand with a `Main` function in the `work` component of the browse table. For the same reason, the constant height one for all table rows is set in advance, so one need not inspect a whole character if only a few values of it shall be shown.

Special functions are provided for sorting (concerning the comparison of character values, which can be integers or irrationalities) and categorizing the table by a column (the value in the category row involves the class name of the column in question).

The code can be found in the file `lib/ctbldisp.g` of the package.

#### 6.3 Table of Marks Display

The GAP library provides a `Display` method for tables of marks that breaks the table into columns fitting on the screen. Similar to the situation with character tables, see Section 6.2, but with a much simpler implementation, Browse provides an alternative based on the function `NCurses.BrowseGeneric` (4.3-1).

`Browse` can be called instead of `Display` for tables of marks, cf. Reference: Printing Tables of Marks.

##### 6.3-1 Browse

`> Browse`( tom[, options] ) | ( method )

This method displays the table of marks tom in a window. The optional record options describes what shall be displayed, the supported components and the default values are described in Reference: Printing Tables of Marks.

The full functionality of the function `NCurses.BrowseGeneric` (4.3-1) is available.

    gap> BrowseData.SetReplay( Concatenation(
    >         # scroll in the table
    >         "DDRRR",
    >         # search for the (exact) value 100 (three times)
    >         "/100",
    >         [ NCurses.keys.DOWN, NCurses.keys.DOWN, NCurses.keys.RIGHT ],
    >         [ NCurses.keys.DOWN, NCurses.keys.DOWN, NCurses.keys.DOWN ],
    >         [ NCurses.keys.RIGHT, NCurses.keys.ENTER ], "nn",
    >         # no more occurrences of 100, confirm
    >         [ NCurses.keys.ENTER ],
    >         # and quit the application
    >         "Q" ) );
    gap> Browse( TableOfMarks( "A10" ) );
    gap> BrowseData.SetReplay( false );

Implementation remarks: Rows and columns are indexed by their positions. The identifier of the table is used as the static header, there is no footer.

In order to keep the required space small also for large tables of marks, caching of formatted matrix entries is disabled, and the strings to be displayed are computed on demand with a `Main` function in the `work` component of the browse table. For the same reason, the constant height one for the table rows is set in advance. (For example, the table of marks of the group with identifier `"O8+(2)"`, with 11171 rows and columns, can be shown with `Browse` in a GAP session requiring about 100 MB.)

The code can be found in the file `lib/tomdisp.g` of the package.

#### 6.4 Table of Contents of AtlasRep

The GAP package AtlasRep (see [WP+04]) is an interface to a database of representations and related data. The table of contents of this database can be displayed via the function `DisplayAtlasInfo` (AtlasRep: DisplayAtlasInfo) of this package. The Browse package provides an alternative based on the function `NCurses.BrowseGeneric` (4.3-1); one can scroll, search, and fetch representations for later use.

##### 6.4-1 BrowseAtlasInfo

`> BrowseAtlasInfo`( ) | ( function )

Returns: the list of "clicked" representations.

This function shows the table of contents of the GAP package AtlasRep in a browse table, cf. Section AtlasRep: Accessing Data of the AtlasRep Package in the package manual. When one "clicks" on one of the table rows or entries then a browse table with an overview of the available representations for this group is shown, and "clicking" on one of its rows adds this representation to the list of return values of `BrowseAtlasInfo`.

The full functionality of the function `NCurses.BrowseGeneric` (4.3-1) is available.

The following example shows how `BrowseAtlasInfo` can be used to fetch permutation representations of the alternating groups A_5 and A_6: We search for the group name `"A5"` in the overview table, and the first cell in the table row for A_5 becomes selected; hitting the Enter key causes a new window to be opened, with an overview of the available representations for A_5; moving down by one row and hitting the Enter key again causes the second representation to be added to the result list, the second window is closed, and we are back in the overview table; we move the selection down twice (to the row for the group A_6), and choose the first representation for this group; finally we leave the table, and the return value is the list with the data for the two representations.

    gap> d:= [ NCurses.keys.DOWN ];;  r:= [ NCurses.keys.RIGHT ];;
    gap> c:= [ NCurses.keys.ENTER ];;
    gap> BrowseData.SetReplay( Concatenation(
    >        "/A5",         # Find the string A5 ...
    >        d, d, r,       # ... such that just the word matches,
    >        c,             # start the search,
    >        c,             # click the table entry A5,
    >        d,             # move down by one row,
    >        c,             # click the row for this representation,
    >        d, d,          # move down by two rows,
    >        c,             # click the table entry A6,
    >        c,             # click the first row,
    >        "Q" ) );       # and quit the application.
    gap> tworeps:= BrowseAtlasInfo();;
    gap> BrowseData.SetReplay( false );
    gap> if fail in tworeps then
    >      Print( "no access to the Web ATLAS\n" );
    >    else
    >      Print( List( tworeps, x -> x.identifier[1] ), "\n" );
    >    fi;
    [ "A5", "A6" ]

Implementation remarks: The first browse table shown has a static header, no footer and row labels, one row of column labels describing the type of data summarized in the columns. When a row is selected, the "click" functionality opens a new window (via a second level call to `NCurses.BrowseGeneric` (4.3-1)), in which a browse table with the list of available representations for the given group is shown; in this table, "click" results in adding the selected representation to the result list, and leaving the second level table, So one returns to the first browse table and can choose further representations, perhaps of other groups. When the first level table is left, the list of chosen representations is returned.

This function is available only if the GAP package AtlasRep is available.

The code can be found in the file `lib/atlasbrowse.g` of the package.

#### 6.5 Access to GAP Manuals -- a Variant

A Browse adapted way to access several manuals is to show the hierarchy of books, chapters, sections, and subsections as collapsible category rows, and to regard the contents of each subsection as a data row of a matrix with only one column.

This application is mainly intended as an example with table cells that exceed the screen, and as an example with several category levels.

##### 6.5-1 BrowseGapManuals

`> BrowseGapManuals`( [start] ) | ( function )

This function displays the contents of the GAP manuals (the main GAP manuals as well as the loaded package manuals) in a window. The optional argument start describes the initial status, admissible values are the strings `"inline/collapsed"`, `"inline/expanded"`, `"pager/collapsed"`, and `"pager/expanded"`.

In the `inline` cases, the parts of the manuals are shown in the browse table, and in the `pager` case, the parts of the manuals are shown in a different window when they are "clicked", using the user's favourite help viewer, see Reference: Changing the Help Viewer.

In the `collapsed` case, all category rows are collapsed, and the first row is selected; typical next steps are moving down the selection and expanding single category rows. In the `expanded` case, all category rows are expanded, and nothing is selected; a typical next step in the `inline/expanded` case is a search for a string in the manuals. (Note that searching in quite slow: For viewing a part of a manual, the file with the corresponding section is read into GAP, the text is formatted, the relevant part is cut out from the section, perhaps markup is stripped off, and finally the search is performed in the resulting strings.)

If no argument is given then the user is asked for selecting an initial status, using `NCurses.Select` (3.1-2).

The full functionality of the function `NCurses.BrowseGeneric` (4.3-1) is available.

    gap> BrowseData.SetReplay( "xdxdzzzQ" );  # expand a Tutorial section
    gap> BrowseGapManuals( "inline/collapsed" );
    gap> BrowseData.SetReplay( Concatenation(
    >        "/Browse", [ NCurses.keys.ENTER ],  # search for "Browse"
    >        "xdxddxdzzz",                       # expand a section
    >        "Q" ) );                            # and quit
    gap> BrowseGapManuals( "inline/collapsed" );
    gap> BrowseData.SetReplay( false );

Implementation remarks: The browse table has a dynamic header showing the name of the currently selected manual, no footer, no row or column labels, and exactly one column of fixed width equal to the screen width. The category rows are precomputed, i. e., they do not arise from a table column; this way, the contents of each data cell can be computed on demand, as soon as it is shown on the screen, in particular the category hierarchy is computed without reading the manuals into GAP. Also, the data rows are not cached. There is no return value. The heights of many cells are bigger than the screen height, so scrolling is a mixture of scrolling to the next cell and scrolling inside a cell. The different initial states are realized via executing different initial steps before the table is shown to the user.

For the variants that show the manuals in a pager, the code temporarily replaces the `show` function of the default viewer `"screen"` (see Reference: Changing the Help Viewer) by a function that uses `NCurses.Pager` (3.1-4). Note that in the case that the manual bit in question fits into one screen, the default `show` function writes this text directly to the screen, but this is used already by the browse table.

The implementation should be regarded as a sketch.

For example, the markup available in the text file format of GAPDoc manuals (using Esc sequences) is stripped off instead of being transferred to the attribute lines that arise, because of the highlighting problem mentioned in Section 2.2-3.

Some heuristics used in the code are due to deficiencies of the manual formats.

For the inline variant of the browse table, the titles of chapters, sections, and subsections are not regarded as parts of the actual text since they appear already as category rows; however, the functions of the GAP help system deliver the text together with these titles, so these lines must be stripped off afterwards.

The category hierarchy representing the tables of contents is created from the `manual.six` files of the manuals. These files do not contain enough information for determining whether several functions define the same subsection, in the sense that there is a common description text after a series of manual lines introducing different functions. In such cases, the browse table contains a category row for each of these functions (with its own number), but the corresponding text appears only under the last of these category rows, the data rows for the others are empty. (This problem does not occur in the GAPDoc manual format because this introduces explicit subsection titles, involving only the first of several function definitions.)

Also, index entries and sectioning entries in `manual.six` files of manuals in GAPDoc format are not explicitly distinguished.

The code can be found in the file `lib/manual.g` of the package.

#### 6.6 Overview of the GAP Bibliography

The GAP documentation contains a bibliography of GAP related publications, see [xxx]. Browse provides access to this information in GAP, using the standard facilities of the function `NCurses.BrowseGeneric` (4.3-1), i. e., one can scroll in the list, search for entries, sort by year, sort and categorize by authors etc.

The Browse package contains a (perhaps outdated) version of this bibliography. One can get an updated version as follows.

`wget -N http://www.gap-system.org/Doc/Bib/gap-publishednicer.bib`

##### 6.6-1 BrowseBibliography

`> BrowseBibliography`( [bibfiles] ) | ( function )

Returns: a list of strings representing BibTeX entries.

This function shows the list of BibTeX entries in the files given by the strings in the list bibfiles. If no argument is given then the file `bibl/gap-publishednicer.bib` in the Browse package directory is taken. The return value is a list of strings that contain the BibTeX entries that have been "clicked" in visual mode.

The full functionality of the function `NCurses.BrowseGeneric` (4.3-1) is available.

    gap> # sort and categorize by year, scroll down, expand a category row
    gap> BrowseData.SetReplay( "scrrscsedddddxdddddQ" );
    gap> BrowseBibliography();;
    gap> # sort & categorize by authors, expand all category rows, scroll down
    gap> BrowseData.SetReplay( "scscXseddddddQ" );
    gap> BrowseBibliography();;
    gap> # sort and categorize by journal, search for a journal name, expand
    gap> BrowseData.SetReplay( Concatenation( "scrrrsc/J. Algebra",
    >        [ NCurses.keys.ENTER ], "nxdddQ" ) );
    gap> BrowseBibliography();;
    gap> BrowseData.SetReplay( false );

Implementation remarks: The browse table has a static header, no footer and row labels, one row of column labels is given by the descriptions of the table columns (authors, title, year, journal). For categorizing by authors, a special `CategoryValues` function is provided that distributes the authors to different category rows, such that each entry appears once for each of its authors in the categorized table. When a data row or an entry in this row is selected, "click" adds the corresponding BibTeX entry to the result list. The width of the title column is preset, in order to avoid computing all values in advance; note that the contents of this column is formatted as a paragraph, using the function `FormatParagraph`.

For three columns, the sort parameters are customized as follows: The authors column does not become hidden when the table is categorized according to this column, sorting by year yields a descending order, and the category rows arising from the three columns show the numbers of the data rows that belong to them.

This function requires some of the utilities provided by the GAP package GAPDoc (see [LN06]), such as `FormatParagraph`, `NormalizedWhitespace`, `NormalizeNameAndKey`, `ParseBibFiles`, and `PrintBibAsBib`.

The code can be found in the file `lib/gapbibl.g` of the package.

#### 6.7 A Puzzle

We consider an m by n rectangle of squares numbered from 1 to m n - 1, the bottom right square is left empty. The numbered squares are permuted by successively exchanging the empty square and a neighboring square such that in the end, the empty cell is again in the bottom right corner.

7 | 13 | 14 | 2
1 | 4 | 15 | 11
6 | 8 | 3 | 9
10 | 5 | 12 |

The aim of the game is to order the numbered squares via these moves.

For the case m = n = 4, the puzzle is known under the name "Sam Loyd's Fifteen", see [B] and [OR] for more information and references.

##### 6.7-1 BrowsePuzzle

`> BrowsePuzzle`( [m, n[, pi]] ) | ( function )

Returns: a record describing the initial and final status of the puzzle.

This function shows the rectangle in a window.

The arguments m and n are the dimensions of the rectangle, the default for both values is 4. The initial distribution of the numbers in the squares can be prescribed via a permutation pi, the default is a random element in the alternating group on the points 1, 2, ..., m n - 1. (Note that the game has not always a solution.)

In any case, the empty cell is selected, and the selection can be moved to neighboring cells via the arrow keys.

The return value is a record with the components `dim` (the pair `[ m, n ]`), `init` (the initial permutation), `final` (the final permutation), and `steps` (the number of steps that were needed).

    gap> BrowseData.SetReplay( Concatenation(
    >        BrowsePuzzleSolution.steps, "Q" ) );
    gap> BrowsePuzzle( 4, 4, BrowsePuzzleSolution.init );;
    gap> BrowseData.SetReplay( false );

An implementation using mouse clicks instead of key strokes is available in the GAP package XGAP (see [CN04]).

Implementation remarks: The game board is implemented via a browse table, without row and column labels, with static header, dynamic footer, and individual `minyx` function. Only one mode is needed in which one cell is selected, and besides the standard actions for quitting the table, asking for help, and saving the current window contents, only the four moves via the arrow keys are admissible.

Some standard `NCurses.BrowseGeneric` (4.3-1) functionality, such as scrolling, selecting, and searching, are not available in this application.

The code can be found in the file `lib/puzzle.g` of the package.

#### 6.8 Peg Solitaire

Peg solitaire is a board game for one player. The game board consists of several holes some of which contain pegs. In each step of the game, one peg is moved horizontally or vertically to an empty hole at distance two, by jumping over a neighboring peg which is then removed from the board.

We consider the game that in the beginning, exactly one hole is empty, and in the end, exactly one peg is left.

##### 6.8-1 PegSolitaire

`> PegSolitaire`( [format][,][nrholes][,][twoModes] ) | ( function )

This function shows the game board in a window.

If the argument format is one of the strings `"small"` or `"large"` then small or large pegs are shown, the default is `"small"`.

Three shapes of the game board are supported, with 33, 37, and 45 holes, respectively; this number can be specified via the argument nrholes, the default is 33. In the cases of 33 and 45 holes, the position of both the initial hole and the destination of the final peg is the middle cell, whereas in the case of 37 holes, the initial hole is in the top left position and the final peg has to be placed in the bottom right position.

If a Boolean twoModes is entered as an argument then it determines whether a browse table with one or two modes is used; the default `false` yields a browse table with only one mode.

In any case, one cell of the board is selected, and the selection can be moved to neighboring cells via the arrow keys. A peg in the selected cell jumps over a neighboring peg to an adjacent hole via the `j` key followed by the appropriate arrow key.

    gap> for n in [ 33, 37, 45 ] do
    >      BrowseData.SetReplay( Concatenation(
    >          PegSolitaireSolutions.( String( n ) ), "Q" ) );
    >      PegSolitaire( n );
    > od;
    gap> BrowseData.SetReplay( false );

For more information such as variations of the game and references, see [K]. Also the solutions stored in the variable `PegSolitaireSolutions` have been taken from this web page.

Implementation remarks: The game board is implemented via a browse table, without row and column labels, with static header, dynamic footer, and individual `minyx` function. In fact, two implementations are provided. The first one needs only one mode in which one cell is selected; moving the selection and jumping with the peg in the selected cell in one of the four directions are the supported user actions. The second implementation needs two modes, one for moving the selection and one for jumping.

Some standard `NCurses.BrowseGeneric` (4.3-1) functionality, such as scrolling, selecting, and searching, are not available in this application.

The code can be found in the file `lib/solitair.g` of the package.

#### 6.9 Rubik's Cube

We visualize the transformations of Rubik's magic cube in a model that is given by "unfolding" the faces and numbering them as follows.

Clockwise turns of the six layers (top, left, front, right, back, and down) are represented by the following permutations.

    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)
    > ];;

GAP computations analyzing this permutation group have been part of the announcements of GAP 3 releases. For a GAP 4 equivalent, see [S]. For more information and references (not GAP related) about Rubik's cube, see [K].

##### 6.9-1 BrowseRubiksCube

`> BrowseRubiksCube`( [format][,][pi] ) | ( function )

This function shows the model of the cube in a window.

If the argument format is one of the strings `"small"` or `"large"` then small or large cells are shown, the default is `"small"`.

The argument pi is the initial permutation of the faces, the default is a random permutation in the cube group, see Reference: Random.

Supported user inputs are the keys t, l, f, r, b, and d 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 `NCurses.attrs.has_colors` (2.2-1), the input s 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.

The return value is a record with the components `inputs` (a string describing the user inputs), `init`, and `final` (the initial and final permutation of the faces, respectively). (The `inputs` component can be used for the replay feature, see the example below.)

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.

    gap> choice:= List( [ 1 .. 30 ], i -> Random( [ 1 .. 6 ] ) );;
    gap> input:= List( "tlfrbd", INT_CHAR ){ 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> BrowseData.SetReplay( false );

Implementation remarks: The cube is implemented via a browse table, without row and column labels, with static header, dynamic footer, and individual `minyx` 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.

Switching between the two display formats is implemented via a function `work.Main`, so this relies on not caching the formatted cells in `work.main`.

Row and column separators of the browse table are whitespace of height and width one. The separating lines are drawn using an individual `SpecialGrid` function in the browse table. Note that the relevant cells do not form a rectangular array.

Some standard `NCurses.BrowseGeneric` (4.3-1) functionality, such as scrolling, selecting, and searching, are not available in this application.

The code can be found in the file `lib/rubik.g` of the package.

#### 6.10 Changing Sides

We consider a 5 by 5 board of squares filled with two types of stones, as follows. The square in the middle is left empty.

X | X | X | X | X
O | X | X | X | X
O | O |  | X | X
O | O | O | O | X
O | O | O | O | O

The aim of the game is to exchange the two types of stones via a sequence of single steps that move one stone to the empty position on the board. Only those moves are allowed that increase or decrease one coordinate by 2 and increase or decrease the other by 1; these are the allowed moves of the knight in chess.

This game has been part of the MacTutor system [OR00].

##### 6.10-1 BrowseChangeSides

`> BrowseChangeSides`( ) | ( function )

This function shows the game board in a window.

Each move is encoded as a sequence of three arrow keys; there are 24 admissible inputs.

    gap> for entry in BrowseChangeSidesSolutions do
    >      BrowseData.SetReplay( Concatenation( entry, "Q" ) );
    >      BrowseChangeSides();
    > od;
    gap> BrowseData.SetReplay( false );

Implementation remarks: The game board is implemented via a browse table, without row and column labels, with static header, dynamic footer, and individual `minyx` 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 moves via combinations of the four arrow keys are admissible.

The separating lines are drawn using an individual `SpecialGrid` function in the browse table.

Some standard `NCurses.BrowseGeneric` (4.3-1) functionality, such as scrolling, selecting, and searching, are not available in this application.

The code can be found in the file `lib/knight.g` of the package.

#### 6.11 Sudoku

We consider a 9 by 9 board of squares. Some squares are initially filled with numbers from 1 to 9. The aim of the game is to fill the empty squares in such a way that each row, each column, and each of the marked 3 by 3 subsquares contains all numbers from 1 to 9. A proper Sudoku game is defined as one with a unique solution. Here is an example.

| 1 | 5
9 |  |
4 |  | 6
| 5 |
5 |  |
| 2 |
3 |  |
6 |  | 4
8 |  |
8 |  |
9 |  |
| 5 | 3
| 4 |
|  | 9
|  | 5
|  | 7
1 |  |
|  | 2
8 |  |

The Browse package contains functions to create, play and solve these games. There are basic command line functions for this, which we describe first, and there is a user interface `PlaySudoku` (6.11-7) which is implemented using the generic browse functionality described in Chapter 4..

##### 6.11-1 Sudoku.Init

`> Sudoku.Init`( [arg] ) | ( function )

Returns: A record describing a Sudoku board or `fail`.

This function constructs a record describing a Sudoku game. This is used by the other functions described below. There a several possibilities for the argument arg.

arg is a string

  The entries of a Sudoku board are numbered row-wise from 1 to 81. A board is encoded as a string as follows. If one of the numbers 1 to 9 is in entry i then the corresponding digit character is written in position i of the string. If an entry is empty any character, except `'1'` to `'9'` or `'|'` is written in position i of the string. Trailing empty entries can be left out. Afterwards `'|'`-characters can be inserted in the string (for example to mark line ends). Such strings can be used for arg.

arg is a matrix

  A Sudoku board can also be encoded as a 9 by 9-matrix, that is a list of 9 lists of length 9, whose (i,j)-th entry is the (i,j)-th entry of the board as integer if it is not empty. Empty entries of the board correspond to unbound entries in the matrix.

arg is a list of integers

  Instead of the matrix just described the argument can also be given by the concatenation of the rows of the matrix (so, a list of integers and holes).

    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;

##### 6.11-2 Sudoku.Place

`> Sudoku.Place`( game, i, n ) | ( function )

`> Sudoku.Remove`( game, i ) | ( function )

Returns: The changed game.

Here game is a record describing a Sudoku board, as returned by `Sudoku.Init` (6.11-1). The argument i is the number of an entry, counted row-wise from 1 to 81, and n is an integer from 1 to 9 to be placed on the board. These functions change game.

`Sudoku.Place` tries to place number n on entry i. It is an error if entry i is not empty. The number is not placed if n is already used in the row, column or subsquare of entry i. In this case the component `game.impossible` is bound.

`Sudoku.Remove` tries to remove the number placed on position i of the board. It does not change the board if entry i is empty, or if entry i was given when the board game was created. In the latter case `game.impossible` is bound.

    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;
    gap> Sudoku.Place(game, 1, 3);; # 3 is already in first row
    gap> IsBound(game.impossible);
    true
    gap> Sudoku.Place(game, 1, 2);; # 2 is not in row, col or subsquare
    gap> IsBound(game.impossible);
    false

##### 6.11-3 Sudoku.RandomGame

`> Sudoku.RandomGame`( [seed] ) | ( function )

Returns: A pair `[str, seed]` of string and seed.

The optional argument seed, if given, must be an integer. If not given some random integer from the current GAP session is used. This function returns a random proper Sudoku game, where the board is described by a string `str`, as explained in `Sudoku.Init` (6.11-1). With the same seed the same board is returned.

The games computed by this function have the property that after removing any given entry the puzzle does no longer have a unique solution.

    gap> Sudoku.RandomGame(5833750);
    [ " 1         2     43  2   68   72    8     6 2   1 9 8  8 3   \
    9     47 3   7  18  ", 5833750 ]
    gap> last = Sudoku.RandomGame(last[2]);
    true

##### 6.11-4 Sudoku.SimpleDisplay

`> Sudoku.SimpleDisplay`( game ) | ( function )

Displays a Sudoku board on the terminal. (But see `PlaySudoku` (6.11-7) for a fancier interface.)

    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;
    gap> Sudoku.SimpleDisplay(game);
     3 |  6|8
     85|  1| 69
      9|7  | 53
    -----------
       |   |79
     6 | 47|
    45 | 2 |
    -----------
    89 |  2| 1
     4 |  8| 7
       |   |

##### 6.11-5 Sudoku.OneSolution

`> Sudoku.OneSolution`( game ) | ( function )

Returns: A completed Sudoku board that solves game, or `fail`.

Here game must be a Sudoku board as returned by `Sudoku.Init` (6.11-1). It is not necessary that game describes a proper Sudoku game (has a unique solution). It may have several solutions, then one random solution is returned. Or it may have no solution, then `fail` is returned.

    gap> Sudoku.SimpleDisplay(Sudoku.OneSolution(Sudoku.Init("  3")));
    493|876|251
    861|542|739
    527|193|648
    -----------
    942|618|573
    156|739|482
    738|425|916
    -----------
    289|354|167
    375|961|824
    614|287|395

##### 6.11-6 Sudoku.UniqueSolution

`> Sudoku.UniqueSolution`( game ) | ( function )

Returns: A completed Sudoku board that solves game, or `false`, or `fail`.

Here game must be a Sudoku board as returned by `Sudoku.Init` (6.11-1). It is not necessary that game describes a proper Sudoku game. If it has several solutions, then `false` is returned. If it has no solution, then `fail` is returned. Otherwise a board with the unique solution is returned.

    gap> s := "      5  | 154 6 2 |9   5 3  |6 4      |   8     |8  9   53\
    > |     5   | 4   7  2|  91  8  ";;
    gap> sol := Sudoku.UniqueSolution(Sudoku.Init(s));;
    gap> Sudoku.SimpleDisplay(sol);
    438|219|576
    715|436|928
    962|758|314
    -----------
    694|573|281
    153|862|749
    827|941|653
    -----------
    281|695|437
    546|387|192
    379|124|865

##### 6.11-7 PlaySudoku

`> PlaySudoku`( [arg] ) | ( function )

Returns: A record describing the latest status of a Sudoku board.

This function allows one to solve Sudoku puzzles interactively. There are several possibilities for the optional argument arg. It can either be a string, matrix or list of holes and integers as described in `Sudoku.Init` (6.11-1), or a board as returned by `Sudoku.Init` (6.11-1). Furthermore arg can be an integer or not be given, in that case `Sudoku.RandomGame` (6.11-3) is called to produce a random game.

The usage of this function is self-explanatory, pressing the ? key displays a help screen. Here, we mention two keys with a particular action: Pressing the h key you get a hint, either an empty entry is filled or the program tells you that there is no solution (so you must delete some entries and try others). Pressing the s key the puzzle is solved by the program or it tells you that there is no or no unique solution.

Implementation remarks: The game board is implemented via a browse table, without row and column labels, with static header, dynamic footer, and individual `minyx` function. Two modes are supported, with the standard actions for quitting the table and asking for help; one cell is selected in each mode. The first mode provides actions for moving the selected cell via arrow keys, for changing the value in the selected cell, for getting a hint or the (unique) solution. (Initial entries of the matrix cannot be changed via user input. They are shown in boldface.) The second mode serves for error handling: When the user enters an invalid number, i. e., a number that occurs already in the current row or column or subsquare, then the application switches to this mode, which causes that a message is shown in the footer, and the invalid entry is shown in red and blinking; similarly, error mode is entered if a hint or solution does not exist.

The separating lines are drawn using an individual `SpecialGrid` function in the browse table, since they cannot be specified within the generic browse table functions.

Some standard `NCurses.BrowseGeneric` (4.3-1) functionality, such as scrolling, selecting, and searching, are not available in this application.

The code can be found in the file `lib/sudoku.g` of the package.

##### 6.11-8 Sudoku.HTMLGame

`> Sudoku.HTMLGame`( game ) | ( function )

`> Sudoku.LaTeXGame`( game ) | ( function )

Returns: A string with HTML or LaTeX code, respectively.

The argument of these functions is a record describing a Sudoku game. These functions return code for including the current status of the board into a webpage or a LaTeX document.

Top of Book | Previous Chapter | Next Chapter

Goto Chapter: | Top | 1 | 2 | 3 | 4 | 5 | 6 | Bib | Ind

generated by GAPDoc2HTML