Game Courier is a versatile web-based system for playing Chess variants on-line. It can automatically generate the boards used for most variants, and it can handle the rest by using predrawn graphic files. It can automate complex moves, and it can be programmed to enforce the rules of games. It is an open-ended system that can be made to support almost any Chess variant, but it still takes human effort to design and program the presets it uses for the games it supports. This guide describes how to design and program presets for Game Courier. If all you want to do is use already made presets, you don't need to know anything in this guide. But if you want to do anything from customizing an existing preset to designing and programming new presets from scratch, this guide tells you how.
The material in this guide is presented roughly in order of difficulty. It begins with information useful for casual users who just wish to change the appearance of a preset, and it ends with information for power users who wish to design and program their own presets. Most users won't need to go beyond the material in the first section. Also, you may want to gradually go through the sections, taking time to master each one before moving on.
If there is already a preset for a game you want to play, but you would like it to use different graphics, then all you need to do is to create a customized version of the preset for your own use. You can reuse a customized preset by saving an URL or a short form that will be given to you in Customize mode.
The first step to customizing a preset is to select one and click on the "Customize" button while in Menu mode. This will put you in Customize mode. While in this mode, you will be able to change parameters that affect the appearance of the preset. These same parameters can be edited in Edit mode, but for brevity's sake most will be described only in this section. Although you could use Edit mode to create customized clones of existing presets, there are three reasons for using the Customize mode instead:
The parameters you can change in Customize mode are as follows:
This field controls the shape of the cells or the board. The options are square, horizontal hexagonal, vertical hexagonal, circular, custom grid, and custom coordinates. When you're customizing a preset, you will rarely need to change this unless you are changing to or from the custom grid shape. So, as far as customizing goes, you don't need to know a lot about the differences between the available options. They will be described in greater detail in the section on Edit mode. One important thing to know here is that the combination of shape and rendering method (described in the next section) determines how the board is made. This table shows how the board is made for each combination, and it blacks out any combination that doesn't work.
| ASCII | Table | CSS | PNG, GIF or JPG | |
|---|---|---|---|---|
| Square | Draws ASCII diagram | Generates board from scratch; draws as table | Uses background image for board, or generates board from tile image | Generates board from scratch; draws as image |
| Horizontal Hexagonal | Generates board from scratch; draws as table with auto-generated images for cell backgrounds | Uses background image for board | ||
| Vertical Hexagonal | ||||
| Circular | ||||
| Custom Grid | Uses background image for board, or generates board from tile image | |||
| Custom | Uses background image for board | |||
This controls the method used for rendering the board. The available methods are these:
This method draws ASCII diagrams. It can be used for square boards and both types of hexagonal board, but it cannot be used with custom grids or custom coordinates. The sole advantage of this option is that it will work with any browser. It is usually unnecessary to choose this option in a preset, since Game Courier will automatically draw diagrams as ASCII art whenever it detects that someone is using the Lynx browser, which is the main text-based web browser in use. Although you probably won't ever need to make a preset using this method, it can come in handy when you want to generate an ASCII diagram for displaying on a web page. You can think of this method as the equivalent of figlet for Chess variant diagrams.
This method renders boards as HTML tables. It can be used for square boards and both types of hexagonal board, but it cannot be used with custom grids or custom coordinates. It is most naturally used for boards with square spaces. Each space is just a table cell, and the color of a space is rendered as the cell's background color. It can also be used for hexagonal boards. Since table cells are rectangular, it renders hexagonal boards by generating and arranging small PNG images that it uses as background images, by means of CSS, for table cells. So, this method for generating hexagonal boards requires a browser that supports both PNG images and CSS.
This method is recommended for square boards, because it uses minimal graphics, making it quicker to download, and it is compatible with most browsers. But it is not recommended for hexagonal boards. For one thing, it limits the colors you may choose from, because it builds a hexagonal board from multi-colored PNG images with standardized names. Also, the result doesn't always look so good, spaces come in only a few standard sizes, and the coordinates cannot always be written close to the actual board. But mainly, the PNG method, which I developed after this method, is much better suited for hexagonal boards. It doesn't require CSS, it can write all coordinates near the board, spaces can be any color from #000000 to #FFFFFF, spaces are sized to fit the pieces, and it gives you only one file to download.
This method makes use of Cascading Style Sheets. Known by the abbreviation CSS, this is available on any modern web browser, such as Internet Explorer, Firefox, Safari or Opera. This method uses a background image for the board, and it positions pieces by means of absolute positioning. It can be used with every board shape, but it does not offer the advantage of automatically generating boards from scratch. It always requires some background image. There are two ways of using this method. One way is to use an image of the whole board. This is useful for games such as Smess, Chinese Chess, or 3D Chess, whose boards cannot be generated by any method Game Courier has at its disposal. The other way is to use a small image that will tile. The most useful tile image is a square of four square spaces. This can be used to generate square boards of various sizes and shapes. But this technique is not useful for generating hexagonal boards. To use the CSS method for a hexagonal board, you would need an image of the whole board. When you're using a colorful JPG image for a board, the CSS method is better than the JPG method, because it will reuse the same images, allowing download time to speed up once all the graphics are cached.
Each of these methods renders a board as a graphic image file. The available formats are PNG, GIF, and JPG, and the rendering method chosen determines which to use. The board is generated by PHP functions and stored in a temporary directory. These methods can be used with any board shape except custom boards. They can automatically generate square, hexagonal, and circular boards from scratch, and they can make use of background images with the Custom Grid shape. These three methods function almost identically, differing mainly in the type of file generated. The GIF and PNG methods are good for small-palette images, while the JPG method is good for true color images.
The GIF format supports small-palette images of no more than 256 colors. It is good for images that don't make use of true color graphics and don't need to be resized. Resized images will be reduced to 256 colors and dithered to minimize distortion, but the result will usually be larger than a resized JPG file. The GIF format is supported by every graphical web browser.
The JPG format supports true color images well. It is usually the best method to use if you need to resize a board or use JPG images in its construction. Unlike the GIF and PNG methods, this method supports the use of the Quality field, which should be set to a value between 1 and 100. This is the quality at which a JPG image gets saved. A lower quality results in a smaller filesize but also in more image distortion. The default of 75 is usually a good balance between filesize and image distortion. The JPG method will normally create smaller true color images than the PNG method will, but they will show some distortion. When you're working with only a small palette, JPG images will be larger than GIF or PNG files. Like GIF, JPG files are supported on all graphical web browsers.
The PNG format can support images of any size palette. It compresses small-palette images better than GIF does, resulting in smaller file sizes. It renders more accurate true color images than JPG does, but it will not compress them nearly as well, resulting in larger file sizes. It is normally intended for small-palette images that don't need to be resized. Resized images will look better than with JPG or GIF, but the images will be larger. Support of the PNG format is not as omnipresent as support of the GIF or JPG formats, but it is commonly supported in modern browsers. It was created to replace GIF at a time when the patent on GIF hindered the development of free software for creating GIF images.
This changes the sets that appear in the Set dropdown menu. Various sets have been grouped together by compatibility with each other. These are sets that normally associate the same letters with the same pieces, differing only in piece design. Some sets are unique and so don't belong to any group. To select one of these sets, you should first use this to display all sets in the Sets menu.
This allows you to select a different set of pieces. Your choice will usually be limited to other sets within the same group. These are sets that are compatible with each other. When you're just customizing a preset, you will probably want a piece set that is compatible with the original set. In that case, you should limit yourself to the choices within the original set's group. But you might sometimes have a reason to choose a different set. In that case, you can use the Set Group menu to change the sets listed by the Sets menu.
Background images are used with the CSS, PNG, GIF and JPG methods. All available background images are stored in the same directory, and the menu for this field lists them all. You may select a new background image with this item. The difference between using whole board images and small tiled images has already been described under the description for the CSS method.
Note that your choice of background image affects the width and height of spaces. These are normally determined by which piece set you choose, but when you choose a background image, its values for width and height override other values. If you upload any new images to the backgrounds/ directory, you should contact me (Fergus Duniho) if its cell dimensions are not the default 50,50.
Note that the PNG and JPG methods flip the whole board image for the second player, but the CSS method does not. This is because the PNG and JPG methods can, and the CSS method can't.
This field is no longer used. It was originally for a second background image. This is useful for asymmetrical boards that appear different to each side, such as the triangular board of Klin Zha. Second background images are still used, but they are now hardcoded into Game Courier. Instead of specifying two board images in Edit mode, boards that go together are paired together in a file that gets included by Game Courier. This makes it easier for players to customize the appearance of the games they are playing. If you design two asymmetric board images, you should contact Fergus Duniho about having Game Courier recognize that they are paired together.
The ASCII, Table, GIF, PNG, and JPG methods can automatically generate boards according to a specified pattern for checkering the board. It is this pattern that is stored in this field. It is rare that you will ever need to change this parameter when you are only customizing a preset. But here is an account of it. Each digit in the board pattern represents one of the colors or patterns listed in the Colors, Hex Colors, or Patterns field. Patterns is used for ASCII diagrams, Hex Colors for hexagonal boards rendered by the table method, and Colors for everything else. 0 is for the first color or pattern, 1 for the second, and so on up to 9. The pattern begins at the left end of the top rank, going across one rank at a time. It follows the same path as the Forsythe code, which is described in the section on Edit mode. The period marks the end of a rank. When it reaches a period before it reaches the end of the rank it is on, it repeats the pattern for that rank for the rest of the rank. If it reaches the end of the whole pattern before it reaches the end of the whole board, it repeats the whole pattern as needed. Thus, something as simple as "10.01." works for Chess and most other variants. An occasional variant will use a different or more complex pattern. Cavalier Chess uses "10.21." for a three colored board, and Chessgi uses a pattern that almost describes the first two ranks in full. The normal behavior is to draw no borders around spaces, letting only the different colors of spaces distinguish them from each other. But when this parameter is set to "1.", borders will be drawn around spaces for the square-table combination. This value has been used for Shatranj and Chaturanga.
This lists up to ten colors that may be used in automatically generating boards with the Table, PNG, or JPG methods. Which colors are used where is determined by the Board parameter, described above. These colors may be entered in hexadecimal format or by name.
This lists up to ten colors that may be used in automatically generating hexagonal boards with the Table method. All colors must be specified by name, and you are limited to the following colors: red, green, blue, orange, yellow, indigo, violet, cyan, magenta, black, white, firebrick, olivedrab, and royalblue. These include the three primary colors of the spectrum (red, green, blue), the three secondary colors of the spectrum (cyan, magenta, yellow), the three primary colors of paint (red, yellow, blue), the three secondary colors of paint (orange, green, magenta), the seven colors of the rainbow (red, orange, yellow, green, blue, indigo, violet), the colors of the French, British, and American flags (red, white, blue), the traditional colors of the Chess board (black, white) and a few other colors that are suitable for Chess boards (firebrick, olivedrab, royalblue).
This lists up to ten patterns that get used for drawing ASCII diagrams. Each pattern must be a single ASCII character, and there should be no separation between them. Which patterns are used where is determined by the Board parameter, described above.
This menu selects the font used by the GIF, PNG, and JPG methods for displaying the coordinates. These methods cannot use fonts from your computer. They can only use fonts stored on our web server. I have included a selection of fonts at the root of the website, located in a directory that is not accessible to the web. The script can read these fonts, but you can't download them from the website. Some of the fonts are freely available from other websites, and most come from CD-ROMs I own.
In selecting the fonts, I tried to represent various standard types of fonts, I selected legible and good-looking fonts, and I sought out various regional, historical, and display fonts that would suit certain games or certain themes common to many variants. First, I included both serif and sans serif fonts, and I included representatives of different types of serif and sans serif fonts. Serif fonts can be oldstyle, modern, revival/transitional, slab serif, and flare serif. Except for modern, I included at least one of each. Centime and Nadine are both oldstyle fonts. Benguiat is a revival font. Courier is a slab serif font. And Flair is a flare serif font. Sans Serif fonts include gothic (or grotesque), geometric, and humanist styles. I included the most popular representative of each. Helvetica is the most popular gothic sans serif, Futura the most popular geometric sans serif, and Optima the most popular humanist sans serif. I also included some sans serifs specifically designed for legibility. These include an optical character recognition font, as well as two of Microsoft's web fonts, Trebuchet and Verdana. I included regional fonts for Germany, Japan, and China. Since Japan and China don't actually use the Latin alphabet, these fonts are not actually of these regions. They merely resemble native writing in some way. I also included historical fonts that resemble types of writing from the Middle Ages, such as Cloister, Druid, Enchantment, and Templar. And I included a few other display fonts, such as AdLib, Review, TechSchool, and Uncollage.
This lets you choose the point size for the font used by the GIF, PNG, and JPG rendering methods.
This is a percentage value that an auto-generated GIF, PNG, or JPG image should be rescaled to. The default value is 100, which doesn't change the size at all. Lower values than 100 are useful for reducing the size of a board image that would otherwise be too big for the browser window.
This is a percentage value between 0 and 100 for the quality of an auto-generated JPG image. Reducing the quality reduces the filesize, which reduces download time. The default is 75, which is commonly the default that graphic programs use when saving JPG files. It is best to play around with this value to see what gives a good filesize without starting to look bad.
This specifies the color of the border used in Table mode. It may be entered as a hexadecimal color code or by name.
This specifies the color of the text used for coordinates in Table mode. It may be entered as a hexadecimal color code or by name.
This specifies the minimum width and height of the border used in Table mode. It is measured in pixils.
You can enter Edit mode by clicking on the "Edit" button in the menu for a preset. You can also get there by clicking the "Editor" button on the index page. If you plan to make a preset for a game that is similar to an already existing template, it is best to begin with it as a template instead of starting from scratch.
Edit mode should be reserved for designing new presets, for editing presets you have already designed, and for creating fairy chess problems. If all you want to do is customize an existing preset to use graphics of your liking, then you should use Customize mode instead of Edit mode. Edit mode lets you change all the parameters that can be changed for defining a game. This includes every parameter you could change in Customize mode plus the following::
This field is for your userid. This needs to be filled in whenever you save a settings file. Each settings file stores the userid of its author so that only its original author may make any changes to it. You may not overwrite an exiting settings file unless you are its original author, which you can confirm by entering your userid and password.
This field is for your password. This field is required whenever you save a settings file. You can save a settings file only when you enter the correct password for your userid.
Settings
This field is for the basename of a settings file. The extension for the filename will be ".php", but this will be added automatically. This file will be found in a subdirectory whose name is based on the name of the game. Thus, it isn't important to include the game's name in the settings file name. Together, this basename and the game's name specify a specific settings file. When this file already exists, Game Courier will use the defaults listed in it for its default values. When you click on the "Save" button, you can create or overwrite this file. If you enter an unused value in this field without saving a settings value, it will have no effect.
This field specifies the name of the game the preset is for. This name will be displayed at various places in Game Courier. It will also be translated into a game ID that will be used for locating logs, settings files, and minirules files. The game ID is made by converting all capitals to lowercase, by converting all high ASCII (8-bit) characters into the closest equivalent low ASCII (7-bit) characters, and all spaces into underscores. When you create a minirules file for displaying the rules to your game, its basename should be the game ID for the game, and its extension should be ".php".
This field specifies a webpage where a user may go to read the rules of the game. This will usually be to one of the webpages of our main website, the Chess Variant Pages. Any of these pages may be entered without including the domain. Start the name with the slash that follows the domain name. For example, the default value is "/d.chess/chess.html", which refers to http://www.chessvariants.org/d.chess/chess.html. Note that this beginning slash is important. Without it, it would be interpreted as a relative URL pointing to http://play.chessvariants.org/pbm/d.chess/chess.html, but there is no webpage there. Yet if you want to refer to a page in the play.chessvariants.org subdomain, then this is the thing to do. Just enter the URL relative to the http://play.chessvariants.org/pbm/ directory. For example,"../erf/MiniChss.html" would point to the page at http://play.chessvariants.org/erf/MiniChss.html. If the webpage is offsite, then you must enter the full URL, including the http:// part at the beginning.
Game Courier handles boards with the following shapes:
The first five shapes cover most kinds of boards, and the last shape covers all the rest. The first five all use standard file/rank coordinates, which are represented by a file label immediately followed by a rank label, such as a1 or e4. These are the standard algebraic type of coordinates used in Chess. How this is done for boards whose cells are not rectangular is described further below. Unlike the other shapes, the Custom Board shape lets you individually specify coordinates without any underlying two-dimensional file/rank axis. This makes it useful for 3D games and for very strange boards.
Most variants will use the square cell shape. When this shape is specified, each space will be the same shape and size, a square, or at least a rectangle, whose dimensions are determined by the $height and $width variables. These are normally set to 50, but they may be changed in files for individual piece sets. This allows the size of the cells to be based on the size of the pieces. This shape uses standard file/rank coordinates. Files are vertical lines of spaces, and ranks are horizontal lines of spaces. The boards made with this shape may be of varying shapes and dimensions. This shape only constricts the shape and size of individual spaces.
Horizontal hexagonal cells are standard equilateral hexagons that naturally align along the horizontal axis. These cells have a point at the top and bottom, with vertical flat sides on the left and right. Game Courier identifies their coordinates by means of slanting files that go from the lower left to the upper right and horizontal ranks. Coordinates are given in standard file/rank format. Note that each new rank begins half a space to the right of the previous rank. Thus, a full board will be shaped like a parallelogram. For a board that is shaped like a hexagon, which is common for hexagonal chess games, various spaces must be cut out.
Vertical hexagonal cells are standard equilateral hexagons that naturally stack vertically. These cells have flat sides on the top and bottom and points on the left and right sides. Game Courier recognizes their coordinates by means of vertical files and slanting ranks that go from the lower left to the upper right. Coordinates are given in standard file/rank format. Note that each new file begins half a space above the bottom of the previous file. Thus, a full board will be shaped like a parallelogram, and various spaces must be cut out to shape the board like a hexagon, which is a common board shape for hexagonal chess variants.
Circular boards are distinguished by the shape of the board, not the shape of the cells. Instead of using cartesian coordinates, circular boards use polar coordinates. Cartesian coordinates place points on an x/y axis. Hexagonal boards merely shifted the angle between the axes. Polar coordinates distinguish a point by its angle and distance from the center. On a circular board, Game Courier distinguishes ranks by their distance from the center, and it distinguishes files by their angle. In other words, each ring of spaces counts as a rank, and each pie slice of spaces counts as a file. Because there is no good place to place rank markers on a circular board, Game Courier normally just writes file markers. Game Courier's usual convention is to number the innermost ring as rank 1, counting out from the inside. So, the higher the rank number, the further away it is from the center. Circular boards are automatically generated by the PNG, GIF and JPG methods, and the CSS method supports circular boards when an appropriate graphic image is used.
A custom grid can be used for square boards, hexagonal boards, triangular boards, and more. It can do any of these by letting you adjust the x and y intervals between ranks and files. It does these with two fields. These are Nextfile and Nextrank. Each takes at least two numeric values. By default, Nextfile is "50 0" and Nextrank is "0 50". These defaults create a board checkered with squares whose sides are 50 pixels in length. The first number is the x interval, and the second is the y interval. For square boards, a change in file changes only the x value, and a change in rank changes only the y value. But other kinds of boards change both values for either rank or file changes. For example, values of "50 25" for Nextfile and "0 50" for Nextrank can create a vertical hexagonal board.
Nextfile and Nextrank can also take more than two values, though each must always take an even number of values. When more than two values are given, the first pair is used first, then it uses each subsequent pair in turn until it goes through them all, then it repeats. Because of this, a triangular board can be done with values of "50 25 50 -25" for Nextfile and "0 50" for Nextrank. Even more complicated boards can be handled with longer strings of values.
Note that the values of Nextfile and Nextrank presume a value of (0,0) for the first space in the first rank, which is by default a1. Values for Nextfile and Nextrank should normally be positive, though negative values are also acceptable. The dimensions of the board are calculated to find the size of the box used to contain the board, and positions in the container are readjusted for negative positions.
A custom board lets you use any board you can draw a computer image of. It is the most versatile way to design any board you like, but it is also the most tedious way to make a board. So it should be used only for boards you can't do any other way. It works by taking a list that associates board coordinates with pixel coordinates. This is filled into a TEXTAREA box, and each association of coordinates should have its own line. Here's a short example:
The first word in each line should be a board coordinate. It doesn't have to be in the usual coodinate form. It may be any kind of label. The second word is the x position, and the third word is the y position. These should all be positive, and (0,0) should be understood to lie at the top left corner. This is the same point that is used as the origin for graphic images. When you enter pixil coordinates, it will often help to look at the image in a graphics program that can tell you the coordinates of wherever you point the mouse. The coordinates you enter should belong to the top left corner of the space, or if it's not rectangular, the top left corner of the rectangle the piece will be displayed in. Don't worry about centering your pieces. The PBM will read the dimensions of each piece and center pieces automatically.
When using the Custom shape, the PBM does not make use of the usual rank and file coordinate system. It ignores any values given to the Ranks, Files, and Columns fields. The Forsythe code uses the order in which you define the positions, and it determines the dimensions of the container from the dimensions of the background image. So, unlike some shapes, the custom shape does not allow the use of tiled images.
This field is for a line of Forsythe code that describes the opening position of the game. Game Courier represents the board with a kind of Forsythe code that has been modified for use with variants. Forsythe code was invented in 1883 by David Forsythe, chess editor of the Glasgow Weekly Herald, for the purpose of concisely recording positions in chess. It assumed the standard 8x8 board, and it consisted of letters, numbers, and slashes. As a whole, the notation described each position in a left-to-right, top-to-bottom order from White's perspective. This is the same direction that we read English in. Each letter represented a piece, each number a series of empty spaces, and each slash indicated the end of a rank. It used lowercase letters for Black and uppercase for White. Using this code, the opening position in Chess would look like this: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR". Game Courier uses a version of Forsythe code that has been expanded to handle a variety of different boards and pieces. It takes some inspiration from Hans Boedlander's Fairy Forsythe-Edwards Notation, but it is not the same.
Game Courier's extended version of Forsythe code works with all types of boards, not just 8x8 square Chess boards. The coordinates of most boards are reprented by ranks and files. These may not always be at right angles to each other, but that is irrelevant to the use of Forsythe code to represent positions. No matter what labels are used for ranks and files, all ranks and files are represented internally by integer array indexes, beginning with zero. So, on a Chess board a1 is internally (0,0), and h8 is internally (7,7). Given this, the Forsythe code begins by describing the highest ranked position in file 0. This is the top left space on a rectangular board. This would be (0,7) or a8 on a Chess board. A little trial and error will tell you where it is on any unusually shaped board. It then continues to describe the board one rank at a time until it finishes with rank 0. It uses the Columns parameter to know how long a rank should be. The code may represent any number of spaces, and the number of ranks is a function of the number of spaces divided by the length of a rank. With the use of the minus sign, which represents non-space, it is possible to create non-retangular boards by cutting out any unused spaces. A minus sign is used just like a piece label but has a different function. For the custom shape, which does not use ranks and files, the Forsythe code represents pieces in the order that the positions were listed. With a custom board, cutting out spaces isn't necessary, because you can omit a space simply by not creating it.
Game Courier expanded Forsythe code also allows for pieces besides those in Chess. The 52 ASCII characters that represent the letters of the alphabet are reserved for identifying pieces. Lowercase letters are normally used for Black pieces, while uppercase letters are normally used for White pieces. Longer labels may be used by enclosing them in braces, like so: {NB}. When the board is rendered as an ASCII diagram, the labels themselves are used in the diagram. But for graphic boards, the labels are used to identify piece images stored on the website. Which images are used depends on the set that has been selected. Each set has a file that tells which image to use for each label. A variety of sets are offered, because there are different designs of the same pieces available, because there are more than 26 pieces used in Chess variants, because the same letter is sometimes appropriate for different pieces, and many were made back when only individual letters could be used to represent pieces.
The remaining modifications to Forsythe code simply make it easier to read and write. The asterisk (*) fills the remainder of a rank with empty spaces. This is most useful for designating empty ranks. The slash (/) has become optional, and when it comes before the natural end of a rank, it fills the remainder of the rank with non-space. Note that the asterisk and the slash differ on more than what they fill the rest of the rank with. When an asterisk appears at the natural end of a rank, it starts a new rank, while the slash does not. Thus, "****" and "*/*/*/*/" are equivalent to each other, but "////" and "/*/*/*/*" are not equivalent to each other.
Here is a summary of the characters used in the Forsythe Code:
With this in mind, here are some alternate ways of representing the opening position in Chess:
This specifies what labels to use for the files. In this context, a file is a vertical column of spaces on a chess board. All labels must be separated by single spaces. A label may be a string of any length. The empty string is an acceptible label for a file that won't have any spaces in it. The first label will be used for the leftmost file from the first player's perspective, what is normally the a file in Chess. Labels will be assigned to files in left-to-right order. When this field is left unset, which is what you'll want to do for most games, the default behavior will be to start with a lowercase a and go through the alphabet to a lowercase z. All labels are case sensitive, and if you want any capital or numeric labels for files, or if your game requires more than 26 files, you will have to enter them in this field.
Besides regular labels, you may enter a null label by entering two spaces side by side. Since every space is parsed as a separator, two adjacent spaces will be understood to have a null string between them. A null label is used for a rank or file that is not actually used for actual spaces on the board. It is useful for separating areas of the board from each other. One special type of label is the invisible label. When you precede a label name with an exclamation point, !, some methods of rendering the board will refrain from displaying its label. An invisible label is used for spaces that will be used in the game without the players ever having to specify coordinates of those spaces. This can happen when moving pieces to and from certain spaces is handled only by automation or the * operator. For example, in Shogi, the file labels for the off-board areas have been made invisible. When a player does try to specify an invisible coordinate, the move will be treated as illegal.
The value of this field is used for every board shape except the Custom shape.
This specifies what labels to use for ranks. All labels must be separated by single spaces. A label may be a string of any length. The empty string is an acceptible label for a rank that won't have any spaces in it. Null labels and invisible labels can both be used. These have already been described in the previous section on the Files parameter. The first label will be used for the bottommost rank from the first player's perspective, what is normally the first rank in Chess. Labels will be assigned to ranks in bottom-to-top order. When this field is left unset, which is what you'll want to do for most games, the default behavior will be to use arabic numerals, starting with 1 and counting up for each successive rank. All labels are case sensitive, and if you want any alphabetic labels for ranks, you will have to enter them in this field.
The value of this field is used for every board shape except the Custom shape.
This specifies the names of the sides. The default for this is "White Black". Since all names must be separated by single spaces, spaces may not be used in the names. At present, only two-player games are supported. So only the first two names will be used. The first name is for the side that moves first, and the second name is for the side that moves second. You will normally want to leave this field unchanged unless your game uses different names for the two sides, such as Red and Blue.
This menu lets you select which player's turn it is. For any game preset, this should be set to the first name listed in the Sides field. This should be changed only for presets of fairy chess problems in which it is the second player's turn to move. For example, in a problem in which Black is to move, this could be set to Black instead of White.
This specifies how many files wide the board will be. This value is used when parsing the Forsythe code used for representing the board.
This is where code goes for either automating complicated moves or for enforcing the rules of a game. All code should be written in GAME Code, an interpreted programming language designed specifically for Game Courier. What code goes where depends upon what it does.
This code gets evaluated once at the beginning of the game before anyone moves. It is the appropriate place for code that randomly places pieces, as in Fischer Random Chess, for initializing variables, and for including include files.
This code gets evaluated before the move entered by the first player each time the first player moves. In the presets I've written, this field has often gone unused, but it sometimes has its uses.
This code gets evaluated before the move entered by the second player each time the second player moves. In the presets I've written, this field has often gone unused, but it sometimes has its uses.
This code gets evaluated immediately after each move made by the first player. It is the appropriate place for code that checks the legality of a move.
This code gets evaluated immediately after each move made by the second player. It is the appropriate place for code that checks the legality of a move.
This code gets evaluated after all moves and all post-move code has been evaluated. It will be evaluated only when the last move made so far was made by the first player. It is the appropriate place for code that checks for check, checkmate, and stalemate or other win/draw conditions. This is because the code for this is usually too time-consuming to run after every move.
This code gets evaluated after all moves and all post-move code has been evaluated. It will be evaluated only when the last move made so far was made by the second player. It is the appropriate place for code that checks for check, checkmate, and stalemate or other win/draw conditions. This is because the code for this is usually too time-consuming to run after every move.
Setting this checkbox will cause the display of pieces at the bottom of the page to leave out any pieces from the piece set that are not used in the Forsythe code used for representing the game's opening position. This helps decrease download time by not downloading unnecessary images, and it makes the display less confusing. But it is not appropriate for all games. When, for example, pieces may promote to pieces not found in the opening position, this should be left unchecked.
By default, Game Courier makes sure that the piece label you specified for a piece matches the piece you are moving. For example, it will allow "N b1-c3" as a first move in Chess, but it will not allow "B b1-c3", because the piece at b1 is a Knight, not a Bishop. While this behavior is appropriate for most games, some games need it turned off in order to distingiush between different legal moves. For example, in Fusion Chess, a simple piece may separate from and move away from a compound piece. So if a Queen is on d1 with an open file, "Q d1-d4" and "R d1-d4" would both be legal moves. This checkbox should be set only when you will use code that distinguishes between legal moves such as these and will take different actions depending on which move has been entered.
You can use Game Courier not only to play games but to display fairy chess problems. You do this in basically the same way as you would design a preset for a game. Begin with a pre-existing preset, and go into Edit mode. Change the Forsythe code in the Code field to the position of pieces in the problem. Set the Side field to whichever player is supposed to move first in the problem. Within the Pre-Game field, use the say command to display information about the problem, such as "Mate in two; White to move." If you are basing your problem on a preset that enforces the rules, and it uses variables to keep track of the King's current location, you should set these variables to the current locations of the Kings. The same goes for any other royal piece. Give your problem a name in the Settings Name field, fill in your userid and password, and save it as a settings file. Copy the URL given to you, and tack on "&submit=Move" to the end. You can then include the modified URL in a webpage or comment, and others will be able to view your problem and try out solutions. If your problem was based on a rule-enforcing preset, it should automatically tell users when they have entered a correct solution to a mate-in-one problem. Here is an example of what I've just described:
A minirules file is a brief summary of the rules that gets inserted into a page for a specific game. This file's name should be the same as the game's name, except that it should be all lowercase, all spaces should be underscores, any nonstandard alphabetic characters should be replaced with their 7-bit ASCII equivalents, and the extension should be ".php". For example, yang_qi.php is the minirules file for Yáng Qí. The minirules file should just state the rules briefly, describe the pieces, and give no more history of the game than maybe who invented it. It is not intended as a detailed description of the game. That is what the webpage on the game is for. All the minirules file is for is to briefly remind players how to play without the need to go to another page.
You can have it display the pieces of the game in whatever piece set has been chosen by using the showpiece function. This is important, because it really helps the user when the rules illustrate the pieces with the same images used on the board. This function takes two argument. The first is the letter identifying the piece, and the second is text to display underneath the piece, identifying what it is called. This function should be used in a table, because it creates a table cell that shows the piece and its name. Here is an example of its usage:
<TABLE BORDER=3><TR>
<?
showpiece("K", "King");
showpiece("Q", "Queen");
showpiece("R", "Rook");
showpiece("B", "Bishop");
showpiece("N", "Knight");
showpiece("P", "Pawn");
?>
</TR></TABLE>
If you are an editor of this site, I recommend looking at some of the minirules files to understand what they should look like.
A piece set is made from a set of images in a common folder and a PHP file that sets up an array for associating letters with piece images. Each image should be a GIF or PNG file with a transparent background, and the color of the background should be #00FF00. The transparent background is required for the Table and CSS rendering methods, which both display the actual piece images in the web browser. The green background is required for the PNG and JPG rendering methods, which both make this color transparent when copying piece images into the single image being generated. The use of this particular color for backgrounds is a convention, chosen to ease conversion of graphics created for Zillions of Games, which happens to use the same convention.
When creating a new set, you do not have to create new pieces. You might just create a PHP file that picks out a new selection of the pieces already available. But if you do make your own pieces, have an editor upload them to the site. As a general policy, I won't have set files pointing to off-site graphics. You should also have an editor upload your set file to the sets subdirectory. And whoever does the uploading, you should contact me, Fergus Duniho, to have the set listed in the sets.php file so that Game Courier has access to it.
Here is an example of what a set file looks like:
<?
$dir = "http://www.chessvariants.com/graphics.dir/japshogi/";
$pieces = array(
"B" => "flip/Bishop.gif", "b" => "Bishop.gif",
"D" => "flip/RookP.gif", "d" => "RookP.gif",
"G" => "flip/Gold.gif", "g" => "Gold.gif",
"H" => "flip/BishopP.gif", "h" => "BishopP.gif",
"K" => "flip/WKing.gif", "k" => "BKing.gif",
"L" => "flip/Lance.gif", "l" => "Lance.gif",
"M" => "flip/LanceP.gif", "m" => "LanceP.gif",
"N" => "flip/Knight.gif", "n" => "Knight.gif",
"P" => "flip/Pawn.gif", "p" => "Pawn.gif",
"R" => "flip/Rook.gif", "r" => "Rook.gif",
"S" => "flip/Silver.gif", "s" => "Silver.gif",
"T" => "flip/PawnP.gif", "t" => "PawnP.gif",
"V" => "flip/SilverP.gif", "v" => "SilverP.gif",
"Y" => "flip/KnightP.gif", "y" => "KnightP.gif",
"Z" => "flip/Kamikaze.gif", "z" => "Kamikaze.gif"
);
$flipped = array (
"K" => "WKing.gif", "k" => "flip/BKing.gif"
);
if (($shape == "square") || ($shape == "grid")) {
$width=42;
$height=45;
}
else {
$width = 52;
$height = 52;
}
$flip = true;
?>
In this example, $dir indicates the directory where all the images can be found. Lowercase letters are assigned to Black pieces, and uppercase letters are assigned to White pieces. Individual letters or longer names may be used for this. The $width and $height variables indicate the dimensions for the spaces. These normally default to 50 and usually don't need to be set here, but the Shogi pieces were smaller than the default pieces. To accomodate hexagonal boards, different values have been given for nonsquare shapes. When the $flip variable is set to true, as it is here, it indicates that piece sets should be switched when the board is flipped. It is set to true here, because these pieces are distinguished by orientation rather than by color. The default is for it to be false, and for most piece sets, it does not need to be set in the piece set definition files. When it is true, a $flipped array will be used, and it will be automatically generated from the $pieces array by switching the values for lowercase and uppercase keys. When exceptions to this procedure are required, they should be made in the set file by partially defining the $flipped array ahead of time. This was done here, because in this set, the two Kings are distinguished by appearance as well as by orientation. If you happened to use longer names and did not use all lowercase for one side and all uppercase for the other, you may have to define a full $flipped array in the set file.
Although Game Courier can automatically generate boards for many games, it can't generate boards for all games. Also, you may prefer something more beautiful than the simple small-palette boards generated by Game Courier. The CSS, GIF, PNG, and JPG methods can make use of background images for boards. If there isn't already a suitable background image available, you will need to make your own. That's what this section is about.
The first thing you will need is an image editor. I use and recommend Ultimate Paint. It will let you create GIF, PNG, and JPG files, which are the three acceptible file types for images on web pages. It provides many standard tools for manipulating images, many filters, and even the ability to custom design your own filters. I made use of this ability to write a filter for drawing circular boards, which you can download here: circular-chess.afs.
This guide won't tell you how to use a graphics program. But I will describe some general techniques for designing boards. One is to first display your pieces against some tiling background image. This is to find out exactly where Game Courier will be placing the pieces on the board. If you take a screenshot of the display, you can load it into your graphics program and use it as a guide for positioning the spaces. While this is not so important for square boards, it can really come in handy for circular or hexagonal boards, not to mention boards of even more unusual designs.
You can use textures in place of solid colors by pasting solid color patterns over images of textures, then cutting them out and pasting them on your board in the right place. This is how I have created various boards with wood and marble backgrounds.
If you want to control how coordinates are displayed on your board, make two boards. Put the coordinates from White's perspective on one board, and put them from Black's perspective on the other board. Then have your preset use both boards, a different one for each player. Doing so will cause Game Courier to not display any coordinate labels, leaving only your predrawn coordinate labels.
When Game Courier calculates how moves change the board position, it does so by constructing and running a computer program written in a language designed specifically for Game Courier. This language began as a set of commands for automating tedious or special tasks, such as rotating the board for Motorotor, handling captured and dropped pieces for Shogi, or rolling dice for Vegas Fun Chess. Over time, it evolved into a full-fledged programming language that can be used for enforcing the rules of games. It is named in the Procrustean manner of starting with an acronym and finding words to fit. So GAME Code is an acronymic name for Game Courier's Automation, Management, and Enforcement Code. The main influences on GAME Code are PHP, BASIC, and the Zillions of Games language. It is written in PHP, it is an interpreted language with a structure very similar to BASIC, and it is designed for some of the same purposes as the Zillions of Games language.
To calculate how the moves of the game change the original position, Game Courier constructs and interprets a program written in the GAME Code language. This program begins with the code entered into the Pre-Game field. It then follows this with the code from the Pre-Move and Post-move fields. Unlike the code from the Pre-Game field, which it just adds as is, it encapsulates the code from these fields into separate subroutines. When Game Courier first encounters the code from these fields, it will simply mark the locations of the subroutines and skip over them. Finally, it adds the moves and the Post-game code. Before each move, it adds a line that sets the variable mln to the index of the mline array with the original move in it. This can be used for looking at the move as a string. Between this and the move, it includes a gosub to the appropriate Pre-Move subroutine. After the move, it includes a gosub to the appropriate Post-Move subroutine. The program ends with the Post-game code that follows the last move. Which body of Post-game code it executes depends on which player moved last. Here is a basic outline of what this looks like:
pre-game code sub preauto1: code endsub; sub preauto2: code endsub sub postauto1: code endsub sub postauto2: code endsub gosub preauto1; White's first move moveindex 3; gosub postauto1; gosub preauto2; moveindex 5; Black's first move gosub postauto2; This portion from gosub preauto1 to gosub postauto2 may repeat with some variation for more moves. post-game code end included code
For a more detailed example, look here.
The GAME Code language has evolved in two directions. One is as a functional programming language similar to Lisp, and the other is as an imperative language similar to C and BASIC. The functional part of the language is used for evaluating expressions, and the imperative part is used for enforcing rules and updating the board and pieces. The main features of the language are divided into the following sections:
Comments can be added with //, which is commonly used in C++ and PHP for comments. Anything following // in a line will be considered a comment. Use comments to describe what your code is doing, so that you and others may read and understand it better. It also helps to write comments as you program, because comments can remind you what you are trying to do.
These commands manipulate the gaming environment, particularly piece positions and board shape. They are presented first, because they are useful even when nothing else of GAME Code is used.
unlink * would remove all links. Inside one pair of parentheses, every coordinate listed within the same set of parentheses gets unlinked from each other. If a wildcard is used, it unlinks every coordinate matching the wildcard from every other coordinate listed and from any coordinate matching another wildcard, but it does not unlink every coordinate matching the wildcard from every other coordinate matching the same wildcard. So, unlink (e* f*) would unlink every coordinate in the e file from every coordinate in the f file and vice versa, but it would leave the coordinates in the e file linked to each other, and it would leave the coordinates in the f file linked to each other. Inside nested parentheses, it unlinks everything in one set of parentheses from everything in any other set of parentheses. So, on a normally linked chessboard, unlink (e1 e2 e3 e4 e5 e6 e7 e8) (f1 f2 f3 f4 f5 f6 f7 f8)) is equivalent to unlink (e* f*). When wildcards are used inside nested parentheses, they effectively expand into the coordinates they match. So, unlink ((e*) (f*)) is equivalent to unlink (e1 e2 e3 e4 e5 e6 e7 e8) (f1 f2 f3 f4 f5 f6 f7 f8)), as well as to unlink (e* f*). However, unlink ((e* f*) g*) is not equivalent to unlink (e* f* g*). The former would unlink anything in the e or f file from anything in the g file and vice versa, whereas the latter would unlink anything in the e, f, or g files from anything in the other two files. Note that all unlinking is symmetrical. When any two coordinates are unlinked, each gets unlinked from the other. For asymmetrical unlinking, use the link command to reestablish one of the links after unlinking with this command. Because the parser interprets anything beginning with "?" as a flag, you should avoid using wildcard pattern beginning with "?". Since coordinates are usually of fixed length anyway, a "*" should usually work just as well as a "?" at the beginning.I have also implemented a method for handling dice rolls. Enclose a list of piece letters in brackets, and move it to a space like you were moving a single piece there. For example, [KQRBNP]-Dice1. This will randomly select one of the listed pieces and place it at the coordinate die1. This is suitable mainly for Chess dice. To use it for numbered dice, you would need to include appropriate graphic images in your piece set.
For examples of automation and randomization in actual presets, check these presets out in Edit mode:
shuffle in the Pre-Game automatic move.drop, copy, and flip in the Pre-Game automatic move to randomize the setup according to Fischer's rules for randomizing the setup.In the case of ambiguous or incomplete moves, these commands will rewrite the move made by the player. Some of these commands ask for input from the player to resolve ambiguous moves. These commands are primarily useful for writing code that lets a player make all moves with the mouse.
GAME Code allows you to set and use variables. A variable may be accessed in one of two ways. In an expression, the var operator will return the value of a named variable. For this to work, the variable name should not match any operator name. The other way is to prefix the variable name with a # sign. For example:
set v 5; echo #v;
Used this way, variables work sort of like preprocessing code. In fact, this was the original reason for using the # prefix. Like preprocessing code, the variable in the line of code get replaced by its value. For example, "echo #v" becomes "echo 5". But it doesn't work entirely like preprocessing code. A typical line of code consists of a command followed by arguments, each separated by a space. When the value of a variable includes spaces, it does not change the number of arguments passed to the command. For example, this code will produce an error:
set v "a1 b1 c1 d1 e1 f1 g1 h1"; shift #v;Instead of finding eight arguments, the shift command in this example will find one unusable argument. The eval command, described later, will help you work around this.
The main rule for variable names is that they should not begin with a digit. Beyond that, any string is allowed that can be used as an array key in PHP (GAME Code's parent language). Generally, variable names should be alphanumeric strings that start with a letter. It is best to avoid the names of commands and operators as variable names.
Variable types include strings, arrays, numbers, and booleans. There is no strict typing of variables. You don't have to define a variable's type before using it. The content of a variable will determine its type. Here are some examples of setting variables to different types:
set stringvar "This variable is a string, which is a continuous series of characters."; set arrayvar (This variable is an array. Each word, including adjacent punctuation, is an element of this array.) set numvar 6; set boolvar true;
As shown above, strings are surrounded by quotation marks, and arrays are surrounded by parentheses. Arrays can include arrays by nesting parentheses. For example:
set twodarray ((One array) (another array));
For the sake of being able to reuse variable names in different subroutines, the same variable name can be used for different variables within different scopes. Even if you were careful to give all variables unique names, recursive subroutines would still require the use of scoping. Scope can differ in width, and it can differ over whether it is dynamic or lexical. GAME Code provides all combinations of these differences. When you call a subroutine from the main program, it has a narrower scope than the main program. Likewise, if you call one subroutine from another, it has narrower scope than the one you called it from. You could repeat this indefinitely, calling one subroutine from another from another, etc. Each subroutine would have a narrower scope than the one you called it from. Some variables are limited to the scope they were created in, and some are not. When a variable is limited to the scope it was created in, it does not exist in wider scopes. The other difference is between dynamic and lexical scope. A variable with dynamic scope is visible in all narrower scopes, while a variable with lexical scope is visible only in the subroutine it was created in.
| Scope | Description | Technical Details |
|---|---|---|
| Global | Being dynamic variables with the widest scope, global variables can be created and accessed from anywhere in a program. By default, any new variable is global. | Indexed to the widest scope and to the main program as $uservar[0]["main"]. |
| Local | Being dynamic variables with narrow scope, local variables are visible to any subroutines they call, but they are invisible to the main program and to any subroutines with wider scope. Where local variables exist, they supercede all dynamic variables with the same name but wider scope. So local variables supercede global variables, and local variables of narrower scope supercede local variables of wider scope. By default, any variable passed as the parameter of a subroutine is local. Other local variables must be created with the local command. | Indexed to the current scope and the main program as $uservar[$scope]["main"]. |
| Static | Being lexical variables with the widest scope, static variables are visible only to the subroutine they are created in, but they retain their value through multiple calls to the same subroutine and between subroutine calls. Where static variables exist, they take precedence over any local or global variables with the same name. Static variables must be created with the static command. | Indexed to the widest scope and to the current subroutine name as $uservar[0][$sub[$scope]]. |
| My | Being lexical variables with narrow scope, my variables are always unique to a specific subroutine call. Where they exist, they always take precedence over other variables by the same name. My variables must be created with the my command. | Indexed to the current scope and the subroutine name as $uservar[$scope][$sub[$scope]]. |
When different variables have the same name, GAME Code recognizes my variables first, static variables second, local variables third, with precedence given to more local variables, and global variables last. By default, any variable passed as a parameter of a subrountine is local to the subroutine, and any new variable is global. To use static, my, or other local variables within a subroutine, they must first be defined by the appropriate command:
Here are the commands for reading, writing, and creating variables.
Flags are global boolean variables that exist in a different namespace than the other variables. This allows the same names to be used for flags and variables. Unlike variables, flags can be named with numbers or alphanumerics beginning with numbers. Flags are normally associated with board spaces. Some commands assume that every board space has an associated flag. The significance of a board space's flag will depend upon how it gets used in a program. Sometimes it will have no significance at all. A flag's value can be accessed by prefixing its name with a ? or by using the flag operator before its name in an expression. By default, any flag is false unless it has been set. These commands deal specifically with flags:
Constants have even greater scope than variables. Let's call it trans-global scope or universal scope. While a variable's life begins and ends during a single run of the program, a constant will endure between separate runs. As you should be aware, Game Courier contructs and runs a fresh GAME Code program each time a player makes a move in a game. Once a constant gets set in a program, it will be available at the very start of the program on subsequent runs. For correspondence games, the values of constants get stored in the log, and for solitaire games, they get passed as form data.
Constants are useful when some values in a game are determined randomly, and it is important to reuse the same values at the same points in the program each time it is run. For example, when Fischer Random Chess randomly arranges the pieces at the beginning of the game, the new order of pieces needs to be remembered each time a program is run for the game in question. The usual way of remembering this information was to store the seed used for random number generation in the log. But this messed up some games when the algorithm for selecting random numbers was rewritten. This brought on the need to store this information directly in the log. Constants work better for this purpose than variables do, because the same information has to be available for the same moves time and again.
Constants can be accessed in one of two ways. The const operator will return the value of the constant whose name follows it. This works in Polish notation expressions but not elsewhere. A constant can also be accessed by preceding its name with a #, the same as is done for variable names. Using this convention, constants have higher precedence than variables. If the name belongs to a constant, the value of the constant will be used. The value of a variable will be used only if the name does not belong to a constant. The following commands are used with constants:
GAME Code gives you access to some of the PHP variables used by Game Courier. These all exist outside the namespace for user variables. I named the following command before I implemented scoping. It does not affect the global variables mentioned above.
old keyword.moved keyword.origin keyword.dest keyword.GAME Code evaluates expressions with Polish notation. This is a form of prefix notation that lists operators in an unambiguous order. Every operator gets listed before its operands, but the whole expression is evaluated in a back-to-front order, so that operands get evaluated before operators. Although Polish notation does not require parentheses to establish order of operations, GAME Code's line parsing allows the use of parentheses. Parentheses never get as far as the Polish notation calculator, but they do affect the input that does reach it. When text appears between parentheses, it gets parsed as an array. Since the process is recursive, nested parentheses will parse into multi-dimensional arrays. Arrays are useful for partitioning off operations that may take a variable number of operands. So parentheses can be used to control how many operands get passed to an operator. Some operators take the remainder of the stack for their operands unless the first operand is an array, in which case the contents of the array gets used for the operands. Another option is to include an operator at the beginning of an array and to pass the whole array as an operands to the eval operator.
Polish Notation is simply a backwards form of Reverse Polish Notation (RPN), which was commonly used in HP Calculators. RPN was easier for early calculators to handle, because it could perform operations as they were entered and did not require parentheses to indicate the order in which to perform operations. The reverse order of RPN is essential for ease of use on a hand-held calculator, but it makes no difference when the whole expression is already entered into memory. In that case, it is just as easy to start from the beginning of an expression as it is to start from the end. So Game Courier starts evaluating the expression from the end, which allows you to put operators before operands. Here are the basic principles behind RPN. Whenever an operand is entered, it is pushed onto a stack. When an operator is entered, it pops off a certain number of operands from the stack, performs an operation on them, and pushes the result onto the top of the stack. When all is done, the stack should have only one value in it, and that is the result of the whole complex operation.
Here are some examples to help you better understand. Take the expression "minus 8 3". First, 3 is pushed onto the stack, then 8 is pushed onto the stack, then both are popped off, 8 minus 3 is calculated, and the result, 5, is pushed back onto the stack. For a more complex example, consider "not and equal 7 3 not less 3 4". First, 4 and 3 are pushed onto the stack. Then they're popped off, and the value of 3 < 4, which is true, is pushed onto the stack. This value is popped back off, reversed by the not, and a false value is pushed onto the stack. Then 3 and 7 are pushed onto the stack. 7 and 3 are then popped off, and the value of 7==3, which is false, is pushed back onto the stack. The stack now has two values in it, both false. These two values are popped off for the and. The value of false and false, which is false, is pushed onto the stack. This is then popped back off, reversed by the not, and the value of true gets pushed onto the stack. This ends the calculation, and the expression, which is equivalent to !((7 == 3) && (3 >= 4)), has been calculated to be true.
The operators are organized by how many operands each takes.
empty operator to test whether your destination is empty. If you need your code to evaluate both past and potential moves, you need to use something like this: cond empty #0 capture (not empty #1), where #0 is the origin and #1 is the destination. If the piece has just moved, its origin will be empty, and capture should be used. If the piece hasn't moved, no capture has been made, and your code will need to check for potential capture by checking the contents of the destination.chr 45 is useful for putting a hyphen in a string that may be printed in messages.The four asymetrical logical operators can handle either one or two operands. It sometimes takes only one operand to know whether the whole operation is true or false. For example, x and y is false if y is false, no matter what x is. Likewise, x or y is true if y is true, no matter what x is. The same principle works for the negations of these, nand and nor. So, when there is only one operand available, and its value is enough to settle the value of the operation, each of these logical operators breaks from the expression and returns the known value right away. When the value of the sole operand is not enough to settle the value of the operation, it lets the expression continue without adding any new values to the stack. When two operands are available, each logical operator just evaluates the logical expression with both values and adds the value to the stack. The expression breaking behavior of these operators is useful for avoiding unnecessary calculation and for breaking out of recursive functions. The unless and onlyif functions also work with one or two operands.
The following table lists the truth value returned for each pair of op1 and op2:
| op1 | op2 | and | or | nand | nor |
|---|---|---|---|---|---|
| T | T | T | T | F | F |
| T | F | F | T | T | F |
| F | T | F | T | T | F |
| F | F | F | F | T | T |
These operators pop off a multiple number of arguments. When the first element on the stack is an array, some of them will pop off the array, base its calculations on the elements of the array, then push the result onto the stack. Otherwise, most of them use the entire stack for their calculations. Some use only the stack, and one, fn, pops off only as many as it needs.
If you plan to use GAME Code for more than simple tasks, such as for enforcing the rules of a game, you will need to know about control structures. These are ways of controlling which code gets executed when. Control structures may be used to conditionally execute code, to repeat code, and to better organize code by separating it into separate modules.
Conditionals evaluate expressions for truth or falsehood, and they execute certain code depending upon the value of the expression. Zero and empty values are taken to be false, while non-empty and non-zero values are taken to be true. All expressions should be written in recursive Polish notation.
if condition:
code;
elseif condition:
code;
elseif condition:
code;
else:
if condition:
code;
else:
code;
endif;
endif;
|
The if, elseif, else, and endif statements work together to create a series of tests that stops once one test turns up true. The if command is used for the first test of the series. All additional tests after the first are done with elseif. When the condition belonging to an if or elseif turns out to be true, the code following it gets executed. On reaching the next elseif or else statement, the code stops executing, and the program jumps to the endif statement. The else statement is used to introduce the code that executes when all the if and elseif tests have failed. The only statements that are required for this control structure are if and endif. The if statement must begin the structure, and the endif statement must end it. The use of elseif and else are optional. When you follow an if by another if before reaching its matching endif, then you have created a nested set of if statements. The inner if will have a higher scope, and you will need to close it with its own endif before closing the outer if. Note that an else followed by an if will raise the scope, but elseif will not. |
verify condition; | Used to conditionally exit a subroutine or the main program. When used at the base level of the program, it exits the program if the condition turns out false. When used within a subroutine, it returns from the subroutine. It will exit a subroutine or the program when its condition turns out to be false. It will allow the program to continue without interruption when it can verify that its condition is true. When used to exit a subroutine, it will return the value of false. |
switch expression:
case label:
code;
break;
case label:
case label:
code;
break;
case label label:
code;
break;
default:
code;
endswitch;
|
A switch-case block works as a computed goto for the body of code that fall between the switch command and the endswitch command. The switch command calculates the value of an expression, and when it is equal to one of the case labels, it jumps to the first line following the appropriate case label. It otherwise jumps to the line following the default label, assuming there is one, or to the end of the switch block. Since execution of lines continues until a break or endswitch command, you should normally place a break at the end of each separate body of code that belongs to a different case. As shown to the left, multiple case statements can lead to the same body of code, and a single case statement may be used for multiple labels. Case labels may be numeric values, Boolean values, or strings, but they should not be arrays. Although a series of case labels superficially resembles a series of if and elseif statements, and although they can sometimes be used for the same purpose, they are not at all the same thing. The case labels must be scalar constants, and their order does not matter, because this is a computed goto, not a series of tests. Although the PHP version of switch and case do work as a series of tests and allow expressions for cases, the version of switch and case used here is based on C, which allows only constant labels for cases. The reason for doing it this way is efficiency. A computed goto gets where it's going faster than a series of tests. If you ever do need to do a series of tests, if, elseif, and else do the job just fine. |
The for and foreach commands are used with the next command to create finite loops that iterate over the elements of an array. The do and loop commands are used to create potentially infinite loops that repeat while or until a condition is true. The break, continue, and redo commands interrupt the usual execution of for and do loops.
for (key, val) array:
if condition:
continue;
endif;
foreach val array:
if condition:
redo;
endif;
if condition:
break;
endif;
code;
next;
next;
for x range 1 10:
echo x;
next;
|
The for and foreach commands are presently the same command with different names. They are based on the foreach command in Perl and PHP, which iterates through the elements of an array. The array may be expressed by parentheses, which will be handled by the line parser, by inserting an array variable with a # in front, which will also be handled by the parser, or by including an expression that will evaluate to an array. One or two iteration variables may be given to the for or foreach command. When only one is given, it will hold the value of each array element. When two are given, they must be given as an array of two names, and the first will hold the key of each element, while the second will hold the value. The loop will end when the end of the array is reached. But it may be stopped prematurely by the break command. A continue command will stop execution of an iteration and cause the loop to go to the next array element. If the end has been reached, it will stop. A redo command goes to the beginning of an iteration without going to the next array element. It could be used to create an infinite loop with a for loop. These three commands are normally used with conditions, though they could be used unconditionally. Loops may be nested inside each other, as shown to the left. Each next corresponds to the closest previous for or foreach that doesn't correspond to another. In the example shown, the inner next goes with the foreach, while the outer next goes with the for. The bottom example shows how a for loop can mimic the behavior of a for next loop in BASIC. The range operator used here creates an array of integers from 1 to 10, which the loop effectively counts through. |
do while condition:
code;
loop;
do:
code;
loop until condition;
do:
code;
if condition:
break;
endif;
loop;
do until condition:
if condition:
redo;
elseif condition:
continue;
endif;
loop while condition;
do:
code;
loop never;
|
The do command begins a loop, and the loop command ends it. Each command can take a condition with either the while or until keyword. A do while loop, shown at the top left, acts as a while loop does in C or PHP, and do loop until, shown just below it, works as a do until loop does in C or PHP. The first is useful when your loop might not need to loop at all, and the second is useful when your loop should loop at least once. Unlike its counterparts in C and PHP, the do loop, which is based on the same construct in BASIC, has a richer syntax. It allows until at the beginning and while at the end, one apiece at each end, and even neither at each end. With no conditions at each end, it creates an infinite loop unless a break command is used to exit the loop. A break command will exit from the innermost do loop, for next loop, or switch statement it is found in. As with for loops, the redo and continue commands may be used to redirect the flow of a loop. The redo command, borrowed from Perl, jumps back to the beginning of an iteration without checking any of the conditions of the loop. A continue command jumps to the bottom condition of a loop. So, a continue command would cause a loop to stop if the right conditions were met, but a redo command could create an infinite loop that disregards the conditions of the loop. One more condition available at the end of a loop is never. This is useful when you want to raise the scope to use some local variables. Also, giving a while condition to do and a never condition to loop works like a simple if statement. |
set i 0; do while < i 10: code; inc i; loop; |
The style of for loop offerred by C and PHP is not available in GAME Code, but its behavior can be duplicated with a do while loop as shown in this example. The initialization part of the for loop occurs just before the do command. The condition part of the for loop is the condition for the do while statement, and the increment part of the for loop is done just before the loop command. This example loops ten times, counting from zero to nine. |
By default, a player can enter any type of move, any number of move primitives, and even lines of GAME Code as a single move. This freedom is great for allowing Game Courier to support almost any Chess variant, but when you're coding the rules for a particular game, restricting user input will make it much easier to write code that fully enforces the rules.
GAME Code allows you to make and use your own functions and subroutines. The general difference between them is that functions are meant to return values and should have no side effects, whereas subroutines are meant mainly for producing side effects and don't have to return values. But this distinction gets blurred, because subroutines may return values and don't have to produce side effects, and functions may produce side effects by calling subroutines that do produce them. So let me make the distinction more concrete. In GAME Code, a function is a one-line Polish Notation expression that includes placeholders for arguments, whereas a subroutine is made up of lines of code that fall between an introductory sub command and a concluding endsub command.
The sub and endsub commands are used for defining a subroutine. The gosub command is used for executing a subroutine, and the return command may be used for exiting a subroutine and returning a value. The verify command will also exit a subroutine. A subroutine is like a function, but, with the exception of arguments, it uses global variables by default, and it can be treated as nothing more than a body of code that can be reused.
Nevertheless, a subroutine can behave as a function, and subroutines can be made recursive. The local and my commands can create local variables, and the static command can create static variables, like what are commonly used in the functions of other programming languages. The arguments passed to the subroutine are available in two ways. First, the arguments are copied to the variables listed as parameters of the subroutine. These variables all have local scope. But this might not yield all the arguments, since it is possible to pass more arguments than a subroutine's parameter list is ready for. To handle a variable number of arguments, all arguments passed to a subroutine are also available in an array called subargs. Note that subargs has my scope, not ordinary local scope. Values passed through the parameter list are visible in subroutines called from the subroutine, so long as the names don't get reused, but the subargs array holds arguments only for the subroutine call it was created for.
The Polish notation calculator has a sub operator that behaves like gosub except that it directly returns the value of the subroutine and may be used in expressions. It may be used in conditionals, loops, and the setting of variable values.
def fac * #1 fn fac dec #1 unless equal #1 1 1;
Since I began programming computers, there has been a rapid evolution in their capabilities. In the early days, it was important to optimize every bit of code, because the computers then had very limited memory and very slow processors. With today's fast computers, optimization might not seem as important. But it can still matter. Consider that interpreted languages are slower than compiled languages. Any program not written into machine code has to be translated into machine code. A compiled language, such as C, saves time by doing the translation only once before running the program. An interpreted language, such as BASIC, does the translation each time it runs a program. Now consider that GAME Code is not only an interpreted language but one that it written in another interpreted language. GAME Code is written in PHP, which is written in C, which is already compiled into machine code. So, a GAME Code program will be slower than a PHP program, which will itself be slower than a C program. Furthermore, PHP imposes a time limit on how long it waits for a page to load. When a page does not load fast enough, PHP will stop execution of it and complain that it is taking too long. This is yet another reason to optimize code for speed.
Let's start with one of the biggest processing-time hogs. Checking for check, checkmate and stalemate takes considerably longer than checking whether a single move is legal. It involves checking whether several different pieces can attack the King, it explores the possible moves available to the King, and it checks the legality of various potential moves. If all of this gets done after every single move, it will eventually slow Game Courier down enough to exceed PHP's time limit on loading a page. While it should be done after every move, it only has to be done once after each move. By placing code for check, checkmate, and stalemate in the Post-Game sections, it gets executed after each player moves. By preventing any move that leaves a player in check, checkmate, or stalemate, it allows us to assume that no prior move in the game left either player in check, checkmate, or stalemate. Furthermore, by using won, lost, and drawn commands to mark the end of the game by checkmate or stalemate, it prevents further moves from being made after the game ends. So the Post-Game sections are the only place where code is needed to check for check, checkmate, stalemate, or any other win/loss/draw conditions, and it saves a great deal of processing time to not place this code anywhere but there.
When you want to check the legality of a move, the code used to evaluate the legality of the move will be determined by whatever piece was moved. There are three main ways of doing this. One is with a series of if and elseif statements, testing whether it is one piece after another. This is inefficient, because it evaluates multiple expressions until it finds the code for the piece that moved. By using switch and case, you can avoid evaluating multiple expressions, because it works as a computed goto. The switch statement evaluates the expression once and goes directly to the appropriate case statement. But even more efficient than this is the use of functions. GAME Code lets you use variables to specify function names. By defining functions named after the piece labels, you can use the label for a piece to call a function that evaluates whether the piece has made a legal move. In some cases, subroutines should be used instead of functions, because moving a piece may have side effects, such as moving the Rook while castling or taking a Pawn by en passant. In many of my include files, perhaps all of them, I have provided functions and subroutines for evaluating the legality of moves by different pieces. Besides the improvement in efficiency, this has the benefit of allowing you to reuse the same code in multiple places, such as a subroutine for telling whether the King is in check. This is done in some of my include files, such as xiangqi and chess2.
In general, functions are faster than subroutines, because a function is a single line of code, whereas a subroutine is multiple lines of code. But it also depends on what your code is doing. For repetitive tasks, the looping structures available to subroutines are considerably faster and more efficient than having a function do the same thing by recursion. In tests I ran, a recursive function couldn't iterate any more than 107 times before hanging indefinitely, and it took between 4 to 5 times as long as a for loop took to count to 107. Bear in mind that this reduction in speed is due to the recursion, not to functions being slower. Another test revealed that subroutines handle recursion even more poorly than functions do. A recursive subroutine could iterate no more than 52 times, and it took longer than the recursive function iterating 107 times. In another comparison between functions and subroutines, I have programmed Chess in two different ways. One way tells whether the King is in check with a function that checks the spaces the King could be attacked from for the pieces that could attack it from those spaces. The other way tells whether a King is in check with a subroutine that loops through the enemy pieces, testing whether any can legally move to the King's space. In tests I ran of the time each takes, the function proved much faster than the subroutine. Functions and subroutines each have their place. Functions are good for tests that just need to return a value. Subroutines are good when you need to change the board, write to the screen, loop through values, or set values of flags, variables, or constants. Subroutines are also easier to debug and understand. The subroutine I just mentioned has its uses for Chess variants with pieces different from those in Chess, such as Cannons, and it doesn't have to be rewritten for games with different pieces. In contrast, the function used to tell whether the King is in check is designed specifically for the pieces in Chess and must be rewritten for games with additional pieces. Customizing your code to a specific game can speed it up but slow down your development. You have to balance which is more important.
When checking the legality of a move with a function, you can optimize your code by making use of the built-in operators designed for this purpose. Note that operators for checking the legality of a certain type of move normally comes in two forms. For example, checkaleap will check the legality of a single leap, whereas checkleap will check the legality of any leap of the specified distance away. The first gives you precision when you need it, while the second is more efficient when it is all you need to do the job. To illustrate the difference, here are two functions for checking the legality of a Knight move:
def N checkaleap #0 #1 2 3 or checkaleap #0 #1 -2 3 or checkaleap #0 #1 2 -3 or checkaleap #0 #1 -2 -3 or checkaleap #0 #1 3 2 or checkaleap #0 #1 -3 2 or checkaleap #0 #1 3 -2 or checkaleap #0 #1 -3 -2; def N checkleap #0 #1 2 3;
The second does the same job as the first but is much more efficient. To learn more about optimization, let's consider how the first could be made even less efficient. Notice that each checkaleap statement is separated by an or. GAME Code's functions use prefix notation, which always puts the operator to the left of the operands. So you might expect the function to look like this instead:
def N or or or or or or or checkaleap #0 #1 2 3 checkaleap #0 #1 -2 3 checkaleap #0 #1 2 -3 checkaleap #0 #1 -2 -3 checkaleap #0 #1 3 2 checkaleap #0 #1 -3 2 checkaleap #0 #1 3 -2 checkaleap #0 #1 -3 -2;
This function would calculate the value of every checkaleap statement before doing any logical operations. The one that separates each pair of checkaleap statements with an or is more efficient, because it takes advantage of the or statement's ability to break out of an expression and immediately return a true value when it is given a single operand with a true value. So it will evaluate checkaleap statements only until it finds one that is true, then the function will immediately return true without evaluating the rest.
The general principle here is to make a function do no more than is necessary to calculate its value. Taking advantage of the short-circuiting ability of logical operators helps with this. It also helps to write a function to perform easier operations before more complex operations. Consider this function for a Clodhopper move from Storm the Ivory Tower:
def C cond cond empty #0 (not capture) (empty #1) (checkride #0 #1 1 1 or checkride #0 #1 0 1) (checkhop #0 #1 1 1 or checkhop #0 #1 0 1) and fn legaldir #0 #1;
Each piece in this game is restricted to moving in the directions indicated by arrows on the squares. The first thing this function does is calculate whether the move is in a legal direction with "fn legaldir #0 #1". Remember that GAME Code begins evaluation of a function from the end, not from the beginning. If its not in a legal direction, that's all it needs to know that the move is illegal, and it avoids calculating whether the move is otherwise legal. When and gets a single false value, it short-circuits the expression and returns a false value.
As a game progresses, it will be filled with a series of legal moves, and it will run the code for checking the legality of these moves each time it runs through the code. Although you could place this code in the Post-Game sections, it is not recommended, because there may be side effects to some moves, such as en passant capture, promotion, moving another piece to a different location, etc. So this code should be placed in the Post-Move sections. What you can bear in mind is breaking out of a function early is not so useful in contexts where the function will rarely have cause to break out early. For example, the chess include file has code for testing whether the King is in check from a specific piece. This code has been designed to break out early if the King is in check, but most of the time, the King will not be in check, because the code will come at the end of a legal move that did not leave the King in check. In this example, the first function is from the chess include file, and the second might be a faster way to accomplish the same thing when the King is not in check, because the match command handles all comparisons at once:
def KNIGHT check what #0 1 2 check what #0 -1 2 check what #0 1 -2 check what #0 -1 -2 check what #0 2 1 check what #0 -2 1 check what #0 2 -1 check what #0 -2 -1 target #1; def KNIGHT match #1 what #0 1 2 check #0 -1 2 what #0 1 -2 what #0 -1 -2 what #0 2 1 what #0 -2 1 what #0 2 -1 what #0 -2 -1;
Variables of higher precedence are accessed faster than variables of lower precedence. To maximize the speed at which variables are accessed, which might shave microseconds off the speed of an operation, use my scoped variables in your subroutines. Just bear in mind that my scoped variables cannot be accessed by other subroutines. Also, constants are accessed faster than variables. If you use a value that will never change, you can speed up your code a little bit by using a constant for it. Flags are accessed just as fast as constants. When all you need is a boolean value with global scope, a flag is your best choice.
Let me also point out some of the optimizations built into GAME Code. Functions make use of prefix notation, which is more optimized than the more usual infix notation. Prefix notation puts operators in an unambigous order, so that matters of precedence never arise. In contrast, infix notation puts operators in an ambiguous order that gets resolved either by precedence of operators or by parentheses. Consider the infix expression 4 + 5 * 6. Should it be (4 + 5) * 6, which is 54, or 4 + (5 * 6), which is 34? Parentheses disambiguate these two expressions, and so does order of precedence. Since multiplication precedes addition, it should be evaluated as 4 + (5 * 6). The thing is that this disambiguation is an extra step that prefix notation is able to avoid. The usual way for a computer to handle infix notation is to translate it into postfix or prefix notation and go from there. Working directly with prefix notation avoids this step. Prefix notation represents the two expressions above as * + 4 5 6 and as + 4 * 5 6, and it does not include any ambiguous expression that might be either one. Besides being more optimized than infix notation, prefix notation is not so unfamiliar to computer programmers. It is commonly used in Lisp and related languages, such as the language used for Zillions of Games, and it is the usual way to call functions in most programming languages.
While optimization is important, it isn't always best to favor optimization over other considerations, such as development time, ease of understanding your code, and the ability to debug your code. Subroutines can sometimes be easier to write, understand, and debug. These are reasons for favoring subroutines even when functions would be more efficient. Generally, a function should be limited to a single easily understood task, such as checking the legality of a single move by a specific type of piece. You might make your code more efficient with long, complex functions, but it could also make your code harder to read and understand. In the end, use your judgement.
Whenever your code produces a syntax error, you will get an error report and a full, formatted listing of the GAME Code program Game Courier has generated. This will provide useful data on what you need to fix.
If you know that something is wrong but Game Courier is not giving you any error messages, you can force it to show you the full listing of a program by adding "&showcode=true" to the URL.
While developing GAME Code, I have come up with different ways of doing the same things. I have usually developed new ways of doing things when the old ways wouldn't work for some games. The original way that I programmed Chess is described in the article Anatomy of a Preset. While the features it describes are still part of the language, and what it describes should still work, it has long been outdated. Its approach is to use a bunch of if-then statements.
def N checkleap #0 #1 1 2; def B checkride #0 #1 1 1; def R checkride #0 #1 1 0; def Q fn B #0 #1 or fn R #0 #1; def K checkleap #0 #1 1 1 or checkleap #0 #1 1 0; def P checkaleap #0 #1 1 1 or checkaleap #0 #1 -1 1;
A second set is used to tell whether the King is in check. The basic idea is to look out from the King's location for any pieces that could be checking it. Some examples look like this:
def WPAWN match P what #0 1 -1 what #0 -1 -1; def BPAWN match p what #0 1 1 what #0 -1 1; def KNIGHT check what #0 1 2 check what #0 -1 2 check what #0 1 -2 check what #0 -1 -2 check what #0 2 1 check what #0 -2 1 check what #0 2 -1 check what #0 -2 -1 target #1; def WAZIR check what #0 0 -1 check what #0 -1 0 check what #0 0 1 check what #0 1 0 target #1; def FERZ check what #0 -1 -1 check what #0 -1 1 check what #0 1 -1 check what #0 1 1 target #1; def KING fn WAZIR #0 #1 or fn FERZ #0 #1; def ROOK check insight #0 0 -1 check insight #0 -1 0 check insight #0 0 1 check insight #0 1 0 target #1; def BISHOP check insight #0 -1 -1 check insight #0 -1 1 check insight #0 1 -1 check insight #0 1 1 target #1; // These two functions are the culmination of the previous functions.; // They are used to test whether a given space is attacked by pieces from the other side.; def ATTACKEDBYB fn KING #0 k or fn BPAWN #0 or fn KNIGHT #0 n or fn ROOK #0 (r q) or fn BISHOP #0 (b q); def ATTACKEDBYW fn KING #0 K or fn WPAWN #0 or fn KNIGHT #0 N or fn ROOK #0 (R Q) or fn BISHOP #0 (B Q);
A third set is used to tell what spaces a piece might be able to move to, which is used by the stalemated subroutine to tell whether any pieces can legally move. Since a separate checkmated subroutine is used when the King is in check, the stalemated subroutines assumes the King is not in check, and it saves time by checking only the first move a piece can make in any direction. If it could make this move legally, it could make any subsequent move in the same direction. Here are some examples of these functions:
def PL array where #0 0 1 where #0 -1 1 where #0 1 1; def pl array where #0 0 -1 where #0 -1 -1 where #0 1 -1; def NL leaps #0 1 2; def BL leaps #0 1 1; def RL leaps #0 1 0; def QL merge leaps #0 1 0 leaps #0 1 1; def KL merge leaps #0 1 0 leaps #0 1 1;
This method also makes use of various subroutines, such as checks, checkmated, stalemated, and castle, as well as subroutines for the King and Pawn moves, since these sometimes include behaviors that functions cannot handle, such as en passant and promotion for Pawns, and castling for Kings. The main advantage of this method over other methods is that it is better optimized for efficiency. The main disadvantage is that it works for fewer games. The chess include file is used for games besides Chess, and other examples of its use can be found in the presets for Big Board Chess, Grotesque Chess, whose grotesque include file includes it, Extra Move Chess, whose extramove include file includes it, and Fischer Random Chess, whose fischer include file includes it.
The same methods described above also worked well for Shogi. See the Shogi preset and its shogi include file for details. Since Shogi uses mostly different pieces and allows pieces to be dropped, it got its own include file. Study that file, and you will see the same ideas at work as described above. The Shogi include file was also used with the Kamikaze Mortal Shogi preset. The Hex Shogi 91 preset uses a very similar include file called hexshogi. It differs from the Shogi include file mainly in how it defines piece movement.
Chinese Chess could not be programmed in the same way, because it used Cannons, which capture by hopping over an intervening piece. This made it difficult to check for Cannon checks by looking out from the King's perspective, or rather General's perspective in this game. Also, with Cannons in the game, the stalemated subroutine would have to check up to every possible move a piece could make, because any move might make it the screen for a Cannon, which would put the General in check. So the Chinese Chess preset and xiangqi include file handled things differently. Instead of testing for check by looking out from the General's perspective, it scanned the board for enemy pieces and checked whether any could legally move to the General's space, using a subroutine which looked like this:
sub checked general:
local piece from;
if isupper space #general:
def enemies onlylower;
else:
def enemies onlyupper;
endif;
for (from piece) fn enemies:
if fn #piece #from #general:
return true;
endif;
next;
return false;
endsub;
Since its stalemated subroutine could not make the same assumptions that the stalemated subroutine for Chess was making, the checkmated and stalemated subroutines got consolidated into a single stalemated subroutine. Instead of using a checkmated subroutine when the General was in check and the stalemated subroutine only when the General was not in check, it used stalemated subroutine whether or not the King was in check and identified checkmate simply as check plus stalemate. The xiangqi include file gets used by the Korean Chess preset and its janggi include file. The Storm the Ivory Tower preset and its ivorytower include file use basically the same approaches.
Since the code for Chinese Chess could not be used for Chess, and the code for Chinese Chess could not be used for Chess, I developed a new include file, called chess2, which could enforce the rules of Chess and make use of Cannons. While the original chess include file used three sets of functions, this new include file used only two sets of functions. The first set is used to check the legality of piece moves. It is mostly the same as described earlier, but now these functions were also being used for potential Pawn moves, which necessitated some modifications to the Pawn functions. Compare these functions to the ones above:
def P and == var ep join filename #1 rankname #0 checkleap #0 #1 1 1 or and == rankname #0 var wpr checkatwostep #0 #1 0 1 0 1 or checkleap #0 #1 0 1 and empty #1 or and islower space #1 checkleap #0 #1 1 1 and > rank #1 rank #0; def p and == var ep join filename #1 rankname #0 checkleap #0 #1 1 1 or and == rankname #0 var bpr checkatwostep #0 #1 0 -1 0 -1 or checkleap #0 #1 0 1 and empty #1 or and isupper space #1 checkleap #0 #1 1 1 and < rank #1 rank #0;
While functions could be used for potential Pawn moves, subroutines were still required for actual Pawn moves. The other set of functions was for telling what spaces a piece might move to. Unlike the same functions used in the chess include file, these had to include all possible moves. Compare these with the same functions above:
def PL array where #0 0 2 where #0 0 1 where #0 -1 1 where #0 1 1; def pL array where #0 0 -2 where #0 0 -1 where #0 -1 -1 where #0 1 -1; def NL leaps #0 1 2; def BL rays #0 1 1; def RL rays #0 1 0; def QL merge rays #0 1 0 rays #0 1 1; def KL merge leaps #0 1 0 leaps #0 1 1;
As with Chinese Chess, no checkmated subroutine was used, and checkmate was defined as check plus stalemate.
Although this method is less efficient than the earlier method, it is useful for more variants, particularly variants with Cannons, and it is easier to adapt it to new games, because it doesn't require as many new functions to be written. Other presets using the chess2 include file include Gross Chess, Victorian Chess, Hostage Chess, whose hostagechess2 include file includes it, and Voidrider Chess, whose voidrider include file includes it.
When I decided to work on programming Circular Chess, the need for a new method arose. The previous methods, from early code in Anatomy of a Preset to the chess2 methods, all used the same built-in functions to evaluate piece movement. These functions all worked on the assumption that the board was a fixed plane of coordinates that extended outward from an origin point. This worked well enough for two-dimensional games whose positions can be plotted with Cartesian coordinates, but it doesn't work so well for three-dimensional games and other games with unusual topographies. In Circular Chess, the ranks are circular, looping back into themselves, so that the a and p files are adjacent. A King, for example, should be able to move directly from one of these files to the other. But in the usual mathematical way of representing boards, these files are at opposite ends, and the functions for checking piece movement would have to get more complicated, perhaps even being replaced by subroutines. Instead of doing that, I wrote some commands and built-in functions for using logical coordinates. If you have programmed games for Zillions-of-Games, then you will already be familiar with logical coordinates. Instead of being represented by their distance from an origin point, logical coordinates are abstractions whose relations to other coordinates are determined by defining directions from them and where those directions lead. In Circular Chess, for example, I might say that moving clockwise from a1 leads to b1, and moving counterclockwise from a1 leads to p1. I could actually call these directions by any names I choose, such as north and south, or forward and backward. The commands I wrote for defining the logical coordinates on a board are called map and link. The map command creates a large set of logical coordinates at once. It is useful for quickly creating logical coordinates whose relations match the usual mathematical coordinates. The link command defines directions between individual pairs of coordinates. Here is how I defined the cooordinates in Cicular Chess:
map n 0 1 s 0 -1 w -1 0 e 1 0; // Orthogonal directions map nw -1 1 ne 1 1 sw -1 -1 se 1 -1; // Diagonal directions map nne 1 2 nnw -1 2 sse 1 -2 ssw -1 -2; // Hippogonal directions map nee 2 1 nww -2 1 sww -2 -1 see 2 -1; // Hippogonal directions
Note that I didn't use the link command. Instead, I extended the file labels (listed in the Files field) to start repeating themselves. For it to map the hippogonal directions for Knight moves, I ended it with the first two files it began with. If I hadn't extended it, these commands would have worked for defining the directions normally used in Chess. If you were programming a 3D game, you could add additional directions, such as u (for up), d (for down), un, us, ue, uw, dn, ds, de, dw, une, dne, etc. To do this, you would need to establish the distance between planes and factor that into your direction definitions. Assuming that planes are 8x8 with a distance of 9 ranks between them, here are some examples:
map u 0 9 d 0 -9; // Straight up and down map un 0 10 us 0 -8 uw -1 9 ue 1 9; // Slanted up directions map une 1 10 unw -1 10 use 1 -8 usw -1 -8; // Diagonal up directions
Having a distance of 9 between 8x8 planes allows you to place barriers between them of non-space, indicated by hyphens in the FEN code. If you place no barriers between them, then you can unlink planes with the unlink command.
Besides learning the GAME Code language from this reference manual, you can gain deeper knowledge of it by studying actual examples. I have programmed several games in this language, and the code is available for you to study and learn from. The code for a game is normally split between the preset itself, which focuses on the rules of the specific game, and an include file, which includes various functions and subroutines that will be used by the GAME Code program. These frequently include functions for evaluating the legality of a piece's move, functions or subroutines for spotting check, and subroutines for castling, checkmate and/or stalemate. Many games can use the ones written for Chess, while other games require specialized versions.
For the basics, study these presets and their include files:
For examples of presets that make use of include files designed for other games, study these presets and their include files:
For examples of more advanced programming techniques, study these presets and their include files:
For additional examples, look at the other include files in the includes directory and also study the presets that use them. You can normally tell what game an include file is for by its name.