Comments/Ratings for a Single Item
You can use any string, not just individual letters. I recommend sticking to short strings of the 26 letters identified in ASCII, so that players have an easier time typing notation.
Jean-Louis, You can use any string enclosed in brackets. Hopefully this will help you.
I have added the ability to use entirely custom piece sets in a game. These are defined in GAME Code and do not use an external PHP file. Here is some sample code I wrote:
// A demo of using multiple internal sets that do not match any set file.
// Name the sets you will use by assigning them to the $groupsets array.
// Use capitalized names for the sets. These do not match any file names.
setsystem groupsets array Abstract Alfaerie AlfaeriePNG Magnetic Motif;
// Define pieces in an array variable called mypieces.
// Start by creating an associative array of all pieces shared in common.
// The key should be a label, and the value should be a filename.
// A single line of code is broken into multiple lines of text for legibility.
set mypieces assoc
K "WKing.gif" k "BKing.gif"
Q "WQueen.gif" q "BQueen.gif"
R "WRook.gif" r "BRook.gif"
B "WBishop.gif" b "BBishop.gif"
N "WKnight.gif" n "BKnight.gif"
P "WPawn.gif" p "BPawn.gif";
// Set the $dir system variable to match the set, and modify filenames as needed.
if == pieceset Alfaerie:
setsystem dir "/graphics.dir/alfaerie/";
foreach (k v) #mypieces:
setelem mypieces #k tolower #v;
next;
elseif == pieceset AlfaeriePNG:
setsystem dir "/graphics.dir/alfaeriePNG/";
foreach (k v) #mypieces:
setelem mypieces #k tolower str_replace .gif .png #v;
next;
elseif == pieceset Magnetic:
setsystem dir "/graphics.dir/magnetic/";
elseif == pieceset Motif:
setsystem dir "/graphics.dir/motif/";
else:
// Have a default set for when the set does not match any allowed set.
// The default is Abstract.
setsystem dir "/graphics.dir/abstract/";
endif;
// Now that the pieces are defined, copy the #mypieces array to $pieces
setsystem pieces #mypieces;
Contrary to what was documented, the rlink command was not available. Maybe I accidentally used Ctrl-z too many times. So, I added it back in. I also fixed a bug in link that stopped it from working after the first pair.
I expanded the power of the map command. It was previously useful only for creating logical directions from leaps defined in terms of ranks and files. This limited it to the directions available on a simple grid. To make it easier to create directions for boards with unusual geometries, it can now be used with previously created logical directions. In the Cylindrical Chess preset I'm working on, I used link and rlink to add connections between the a and h files for east and west, and then I used map to define the diagonal and hippogonal directions without handwriting them link by link. The code looks like this:
map n 0 1 s 0 -1 w -1 0 e 1 0; // Orthogonal directions
link w (a1 h1) (a2 h2) (a3 h3) (a4 h4) (a5 h5) (a6 h6) (a7 h7) (a8 h8);
rlink e (a1 h1) (a2 h2) (a3 h3) (a4 h4) (a5 h5) (a6 h6) (a7 h7) (a8 h8);
map nw (n w) ne (n e) sw (s w) se (s e);
map nne (n ne) nnw (n nw) sse (s se) ssw (s sw);
map nee (ne e) nww (nw w) sww (sw w) see (se e);
Is there a way to detect suicide moves in GAME code?
There are two ways of doing a suicide, and these have different effects on the $old variable. Adding an @ to a space works like a promotion, which leaves the value of $old unaffected. Moving a piece nowhere gives $old an empty value. This may give a better indication that the move included a suicide, but it would come at the expense of the information about where the piece moved to.
Since it sounds like your Fire Demon piece is making a multi-part move, you may want to break down the whole move and analyze it step-by-step. You will find examples of this in multi-move variants like Marseillais Chess and Extra Move Chess.
With that in mind, another option would be to move the Fire Demon to the piece's space, then immediately move it back.
Another possibility for detecting a suicide move is to store the board position before the move into a variable, then compare the new position after the move to the stored position and see what differences there are.
Since it sounds like your Fire Demon piece is making a multi-part move, you may want to break down the whole move and analyze it step-by-step. You will find examples of this in multi-move variants like Marseillais Chess and Extra Move Chess.
Hmm. Perhaps the step-by-step approach would be better. After all, the Suzumu Shogi preset is essentially on a more restrictive form of the other multi-move presets with more pieces.
So the tentative process would then be the folowing
Do a regular move
- Normal move
- Normal capture
- King step (capture)
- King step (non-capture), pass (double move)
- Single King step (non-capture), return to origin square (double move)
Then I would need to assess whether or not there are pieces available to burn (which should be easy thanks to the burn subroutine in my Tenjiku Shogi preset). If so, after the move, do an igui capture or pass.
Or it may be simpler to simply always give the player to optionally shoot any enemy piece a King step away after moving.
Another possibility for detecting a suicide move is to store the board position before the move into a variable, then compare the new position after the move to the stored position and see what differences there are.
This could potentially solve the problems of detecting a suicide move if I knew how to do it.
Since I am already writing this, would the system used for the multi-move variants be able to handle moves with three or more parts (i.e. that of the Lion Dog in Maka Dai Dai Shogi)? This will be important for my larger games with Lion Dogs, Furious Fiends, Kirin Masters, and Phoenix Masters, as well as if I use the move breakdown method for the Fire Demon's burns.
Another possibility for detecting a suicide move is to store the board position before the move into a variable, then compare the new position after the move to the stored position and see what differences there are.
This could potentially solve the problems of detecting a suicide move if I knew how to do it.
Here's a preset I made to demonstrate how to do this:
https://www.chessvariants.com/play/pbm/play.php?game%3DChess%26settings%3Dspotcaptures
This records the position of every piece on the board in an array whose keys are coordinates and whose values are piece labels. It does this both before and after a move. It then goes through the current positions of pieces. For each empty space, represented with the @ piece, it compares it with the previous value to see if it was empty before. If it wasn't empty before, it records it as a capture.
Since I am already writing this, would the system used for the multi-move variants be able to handle moves with three or more parts (i.e. that of the Lion Dog in Maka Dai Dai Shogi)?
You could adapt it. For the multi-move variants, there are usually the same number of move parts each turn. So, it makes sense for these games to do the multi-part analysis globally for all pieces. But for games that include pieces with multi-part moves, you may want to reserve the multi-part analysis for specific pieces and tailor it to each piece that needs it.
I realized the role that $width and $height play in centering the borders for highlighting spaces, and with that in mind, I simplified the code in draw_grid_png.php and made adjustments to the $width and $height of Shogi pieces and to the values of $offx and $offy in image_dimensions.php. By using proper values for $width and $height, the code will always apply CSS for margins and padding to the space class rather than to each element of the space class.
I didn't immediately recognize the importance of $width and $height, because I created these variables years before I ever added the ability to display legal moves by highlighting spaces. These variables describe the width and height of spaces, and they are used when automatically generating a board to know what dimensions to give to spaces. But sometimes an image of a board is used instead, and when it is used, $width and $height should match the size of the spaces on the image.
This is what I was neglecting to do. The highlighting in Shogi was a bit off, because the value of $height did not match the height of the spaces on the images of Shogi boards.
Since Shogi divides each space by lines, there are two choices on how to specify the dimensions of a space. One is to use the inner space between the lines, and the other is to extend the size to include the lines. I tried both and chose the latter for Shogi.
For automatically-generated boards, the centering of borders for highlighting should now be done automatically. But for boards provided as pre-drawn images, it's important to match them up with the correct values for $width and $height. One way would be to size the board with spaces the same size as the dimensions given for the piece sets to be used with it. Abstract and Alfaerie sets both normally use 50x50 spaces. Another way would be to provide correct values for your board. I could add these to the image_dimensions.php script for specific boards.
This preset of yours for detecting captures is huge. The fact that it reliably detects where the suicide occured means that I can use it to test whether the suicide is a King's move away from the moving Fire Demon and ensure that no suicides are done when a non-Fire Demon moves, which should solve the majority of problems with coding the burning move. If something similar could be used to detect the type of piece affected by the suicide, that should be everything I need to enforce the burning rules using suicides, at least in theory. Best of all, I can just ask players that haven't made a burn after moving a Fire Demon rather than worry about displaying legal moves on the board.
You could adapt it. For the multi-move variants, there are usually the same number of move parts each turn. So, it makes sense for these games to do the multi-part analysis globally for all pieces. But for games that include pieces with multi-part moves, you may want to reserve the multi-part analysis for specific pieces and tailor it to each piece that needs it.
This is what the original Suzumu Shogi preset (and all presets using the same general codebase) does, which is largely thanks to its isdoublemove subroutine. So, I guess I could use integers instead of booleans and sub ismultimove instead of sub isdoublemove to keep track of which part of the move suffices. The only major problem is that I'm not exactly sure how to enforce rules for the third part of a three-part move. Perhaps I am overthinking it though. Perhaps just copy-pasting and tweaking the code for the second part of multi-moves will be enough. This requires testing, but thankfully, the Taishin Shogi preset (which is fully coded, at least for enforcing the old rules) affords just such an opportunity.
Edit: I figured out how to detect the type of piece that committed the suicide. Now we should be all good.
If something similar could be used to detect the type of piece affected by the suicide, that should be everything I need to enforce the burning rules using suicides, at least in theory.
That information is in #pfore. Just use the coordinates of the spaces with missing pieces as keys to this array.
As it turned out, I didn't need the suicides to perform the burns in Suzumu Shogi. I just needed to tweak the isdoublemove subroutine a little bit, add a FD subroutine for the Fire Demon, tweak the Fire Demon functions to include the new subroutine, and return the Fire Demon to its destination square with empty and add after it made a burning move. I love GAME code... :)
I use "Alfaerie for Metamachy" as a set for the GC presets of my CVs. Who could add 6 new pieces from Alfaerie , coded with 2 letters, to this set?
I would like to have added: the Snake (SN), the Ship (SH), the Bird (BI), the Tiger (TI), the Ram (RA), the Dragon (DR).
Thanks for the help.
Hi Jean-Louis,
I will take care of this this weekend.
These pieces have been added. Please let me know if you would like any adjustments.
Thanks, Greg
It is me who says you many thanks!
I beg some help, this is above my skills. I do cut and try, but I'm unable to get it. I want to code a Ship (t(FvR), vertical Gryphon) and a Snake (t(vWB), vertical Manticore). I was thinking to go from Gryphon and Manticore. How should I modify those:
def G fn (checkride #0 #1 1 0 and empty #0) where #0 sign - file #1 file #0 sign - rank #1 rank #0 #1
and != file #1 file #0 and != rank #1 rank #0 or checkleap #0 #1 1 1;
def GL mergeall leaps #0 1 1 rays where #0 1 1 1 0 rays where #0 1 -1 1 0 rays where #0 -1 -1 1 0 rays where #0 -1 1 1 0;
and :
def U fn (checkride #0 #1 1 1 and empty #0) where #0 0 sign - rank #1 rank #0 #1
or fn (checkride #0 #1 1 1 and empty #0) where #0 sign - file #1 file #0 0 #1
or checkleap #0 #1 1 0;
def UL mergeall leaps #0 1 0 rays where #0 0 1 1 1 rays where #0 0 -1 1 1 rays where #0 1 0 1 1 rays where #0 -1 0 1 1;
Thanks!!!
I think this works for the ship, but I'm not quite sure how to do the Snake
def G fn (checkride #0 #1 0 1 and empty #0)
where #0 1 1
#1
or fn (checkride #0 #1 0 1 and empty #0)
where #0 -1 1
#1
or fn (checkride #0 #1 0 -1 and empty #0)
where #0 1 -1
#1
or fn (checkride #0 #1 0 -1 and empty #0)
where #0 -1 -1
#1
or checkleap #0 #1 1 1;
def GL mergeall
leaps #0 1 1
ray where #0 1 1 0 1
ray where #0 1 -1 0 -1
ray where #0 -1 1 0 1
ray where #0 -1 -1 0 -1;
I believe you want checkaride
instead of checkride
— the latter checks all directions symmetrically (making a full gryphon plus conditional wazir moves), while the former is asymmetric.
Presumably if your suggestion for the Ship is otherwise correct, the snaketongue would similarly be:
def G fn (checkaride #0 #1 1 1 and empty #0)
where #0 0 1
#1
or fn (checkaride #0 #1 -1 1 and empty #0)
where #0 0 1
#1
or fn (checkaride #0 #1 1 -1 and empty #0)
where #0 0 -1
#1
or fn (checkaride #0 #1 -1 -1 and empty #0)
where #0 0 -1
#1
or checkleap #0 #1 1 0;
def GL mergeall
leaps #0 1 0
ray where #0 0 1 1 1
ray where #0 0 -1 1 -1
ray where #0 0 1 -1 1
ray where #0 0 -1 -1 -1;
That doesn't quite work, because leaps checks all four directions instead of just forwards and backwards. Maybe checkride shouldn't work either, but I tried it and it seems to.
This works for the Snake
def U fn (checkaride #0 #1 1 1 and empty #0)
where #0 0 1
#1
or fn (checkaride #0 #1 -1 1 and empty #0)
where #0 0 1
#1
or fn (checkaride #0 #1 1 1 and empty #0)
where #0 0 1
#1
or fn (checkaride #0 #1 -1 -1 and empty #0)
where #0 0 -1
#1
or fn (checkaride #0 #1 1 -1 and empty #0)
where #0 0 -1
#1
or checkaleap #0 #1 0 1
or checkaleap #0 #1 0 -1;
def UL mergeall
ray where #0 0 1 1 1
ray where #0 0 1 -1 1
ray where #0 0 -1 -1 -1
ray where #0 0 -1 1 -1
where #0 0 1
where #0 0 -1;
Warm thanks to both of you. It seems to work fine for both pieces. That's great.
Hello Jean-Louis, I am curious if you are interested by the horizontal counterparts of the two pieces for use in your variants.
Hello Aurelian. I have no plan to use them. There are probably very special to put in a CV, but not without interest.
I have added the following built-in functions: chars, string, hasalnum, hasalpha, hasdigit, haslower, and hasupper. The chars and string functions can be used together to process a string with a lambda function filter, using chars to turn a string into an array of characters, and using string to converted the processed array back into a string. For example, promoted pieces in Shogi put a plus sign before the usual piece label, and the boldfaced part of this line demotes a piece in Shogi by removing any non-alphabetic characters:
set demoted flipcase realname string filter lambda (isalpha #1) chars alias space $dest;
The has functions are similar to isalnum, isalpha, isdigit, islower, and isupper, but instead of checking whether every character in a string is of a particular type, these check whether any character, even just one, is of a particular type. These come in handy when piece labels include non-alphabetic characters, as promoted pieces do in Shogi.
This morning I was getting confused about what filename and rankname returned. I wanted to get the nth file or the nth rank from passing them numbers, but they only parsed coordinates and returned the file or rank name of that coordinate. So, I added the extra ability I needed to each function. If filename or rankname is passed an integer, and that integer is an array index for $file or $rank, it will now return the array value with that index. It will otherwise return an empty string. So, filename 0 will return a in Chess, and rankname 0 will return 1 in Chess.
What should I use if I want to test for past and potential captures and have captures of certain pieces always return false in a function definition?
I know how to do it for general captures.
cond empty #0 capture (not empty #1)
Perhaps this?
cond empty #0 (not match old FD +WB +CS) (not match space #1 FD +WB +CS)
What should I use if I want to test for past and potential captures have captures of certain pieces always return false in a function definition?
I'm not clear on what you're asking.
Recently I revived Suzumu Shogi and Mitsugumi Shogi and added a rule to them forbidding burning pieces (Fire Demons and Heveanly Tetrarches) from burning each other. To enforce this in the GC presets, in theory all I need to do is, on the burning move, make sure that the capture (past and potential) doesn't equal those pieces in the relevant function definitions.
If I need to test for a general capture in this situation, that is pretty easy: cond empty #0 capture (not empty #1). By extension, for ensuring a burning piece isn't captured I would, in theory, need to use cond empty #0 (not match old FD +WB +CS) (not match space #1 FD +WB +CS) in the part of the piece function used for the burning moves. The "FD +WB +CS" part is the ids of the burning pieces.
The second output for the cond statement works fine, but the first doesn't. I think what I need is something that will return the value of old at the time the function is called.
Here is the preset link for Suzumu Shogi in case you need to look at the code: Suzumu Shogi on the Chess Variants Game Courier
Here are your definitions for fd and FD:
def fd cond var firstpart (checkride #0 #1 1 1 or checkaride #0 #1 -1 0 or checkaride #0 #1 1 0 or checkmaxsteps #0 #1 3) (sub FD #0 #1 and cond empty #0 (not match old FD +WB +CS) (not match space #1 FD +WB +CS)) and #0 and #1;
def FD cond var firstpart (checkride #0 #1 1 1 or checkaride #0 #1 -1 0 or checkaride #0 #1 1 0 or checkmaxsteps #0 #1 3) (sub FD #0 #1 and cond empty #0 (not match old fd +wb +cs) (not match space #1 fd +wb +cs)) and #0 and #1;
Let me format these to show the logic better:
def fd
cond var firstpart
(checkride #0 #1 1 1 or checkaride #0 #1 -1 0 or checkaride #0 #1 1 0 or checkmaxsteps #0 #1 3)
(sub FD #0 #1
and cond empty #0
(not match old FD +WB +CS)
(not match space #1 FD +WB +CS)
)
and #0
and #1;
def FD
cond var firstpart
(checkride #0 #1 1 1 or checkaride #0 #1 -1 0 or checkaride #0 #1 1 0 or checkmaxsteps #0 #1 3)
(sub FD #0 #1
and cond empty #0
(not match old fd +wb +cs)
(not match space #1 fd +wb +cs)
)
and #0
and #1;
These first make sure that #0 and #1 are included in the function so that you can use them in parenthesized expressions. It then skips over the parenthesized expressions and goes to the variable firstpart. If its value is non-empty, it executes (checkride #0 #1 1 1 or checkaride #0 #1 -1 0 or checkaride #0 #1 1 0 or checkmaxsteps #0 #1 3), and it is empty, it executes
(sub FD #0 #1
and cond empty #0
(not match old fd +wb +cs)
(not match space #1 fd +wb +cs)
)
First, it checks whether #0 is empty. If it is, it executes (not match old fd +wb +cs), and it it isn't, it executes (not match space #1 fd +wb +cs). Each returns a truth value. If the truth value is true, then it will allow the execution of the subroutine FD. That subroutine looks like this:
sub FD from to;
return cond cond empty #from capture (not empty #to) (checkleap #from #to 0 1 or checkleap #from #to 1 1) (checkleap #from #to 0 1 or checkleap #from #to 1 1 and == var ori #to) and #to;
endsub;
For some reason, this subroutine just returns a function call without making use of the imperative programming available to subroutines.
Reformatting it to show its logic, it looks like this:
sub FD from to;
return cond
cond empty #from
capture
(not empty #to)
(checkleap #from #to 0 1
or checkleap #from #to 1 1)
(checkleap #from #to 0 1
or checkleap #from #to 1 1
and == var ori #to)
and #to;
endsub;
If a player hasn't moved yet, #from will be occupied, and if he has moved, #from will be empty. If he has moved, the inner cond returns the value of capture, and if he hasn't, it returns true if the move would result in a capture. This value is passed to the outer cond. If it's a capturing move, it can move one space in any direction. If it's not a capturing move, its move is allowed only if the value of ori is the same as to. It looks like #ori has been set to the origin. So, this might stop it from making non-capturing moves.
So perhaps I just need to put sub FD #0 #1 after the test (I tend to forget GC executes from right to left)?
Edit: That didn't work. I guess I'll just stick to the tried and true method of using the bpromote and wpromote subroutines for the actual rule enforcement.
I am curious though: if you use old in a function definition, does it return the value at the time of the function definition or the function call?
I am curious though: if you use old in a function definition, does it return the value at the time of the function definition or the function call?
I ran some tests, and it behaved as I predicted. When you just use old, it immediately replaces it with the current value of old at the time of the function definition. But when you use $old, it uses the current value each time you call the function. So, use $old, not old.
I have a piece defined this way, which used to work before the server change and now doesn't. It's failing to highlight all the legal riding moves.
def Tiger fn (checkaride #0 #1 1 1 and empty #0)
where #0 2 3
#1
or fn (checkaride #0 #1 1 -1 and empty #0)
where #0 2 -3
#1
or fn (checkaride #0 #1 -1 1 and empty #0)
where #0 -2 3
#1
or fn (checkaride #0 #1 -1 -1 and empty #0)
where #0 -2 -3
#1
or fn (checkaride #0 #1 1 1 and empty #0)
where #0 3 2
#1
or fn (checkaride #0 #1 1 -1 and empty #0)
where #0 3 -2
#1
or fn (checkaride #0 #1 -1 1 and empty #0)
where #0 -3 2
#1
or fn (checkaride #0 #1 -1 -1 and empty #0)
where #0 -3 -2
#1
or checkleap #0 #1 3 2;
def Tiger-Range mergeall
leaps #0 3 2
ray where #0 2 3 1 1
ray where #0 2 -3 1 -1
ray where #0 -2 3 -1 1
ray where #0 -2 -3 -1 -1
ray where #0 3 2 1 1
ray where #0 3 -2 1 -1
ray where #0 -3 2 -1 1
ray where #0 -3 -2 -1 -1;
Please provide a link to where this code is being used.
Okay, it's working now. I had to repair the ray function.
Dear Publishers, How can we record our own include files that are stored on and recalled from "/play/pbm/includes/" directory ? I would need this to modify move definitions of some pieces. Side question: how can we suppress old inactive setting files ? Regards, SB
Although you cannot upload anything to "/play/pbm/includes/", here is what you can do instead:
Go to your personal information page or to a page you have created, and use the File Manager to upload your include file. Make sure it is a text file with the .txt extension. In the include statement, include the path in the file name. Here's an example I tried for testing purposes:
include "/membergraphics/who/FergusDuniho/capablanca.txt";
list;
You can tell from the URL that I uploaded this to my personal information page. The list command displayed a listing of the program, which included the contents of the include file. This confirmed that it worked.
One thing I accomplished today was to fix the lograys function. It did not work appropriately for separate paths of movement that partially overlap, as we get in Chess66. As it found spaces a piece could legally move to, it would add them to an array, and it would stop if it ever reached a space already in the array. This may be fine for Spherical Chess, in which paths do not usually overlap, but it was a problem for Chess66. So, I replaced the single array with two arrays. It uses one array to keep track of the spaces found in the current direction of movement being checked. It breaks out when it reaches a space already in the array, as this is an indication of a move looping back on itself. When it is done checking a particular direction, it merges this array into an array of all spaces the piece can reach so far, and it empties the array for the spaces that can be reached by moving in a particular direction.
I've also noticed that I was expecting more of unlink than it can actually do. I was expecting it to be able to unlink indivdual directions, but all it does is completely unlink coordinates, which is overkill. I may modify it to work as I was expecting it to.
I modified unlink as I said I would. Details are under its entry on this page.
This is probably a stupid question, but can a subroutine have zero parameters?
Example:
sub <subname>;
<...code...>
endsub;
Yes, a subroutine can have no parameters.
Ok. Thanks.
When using the allow command, am I correct that the word in the word/number argument pair does not have to be a command or keyword?
Example:
allow ... one 2 two 2 onetwo 2 ...; // I was planning on using this to get around a very annoying bug I found in a future version of Suzumu Shogi's GC preset (with multi-capturing versions of the jumping Generals).
Edit: I answered my own question. You can use custom commands in allow, provided that for each custom command you include a subroutine in your Pre-Game code like the one shown below.
sub <cmdname>: return true; endsub;
Defining Functions with Local Scope
I have sometimes conditionally defined functions within a subroutine for local use, but the def
command normally defines functions at a global scope. So, if a subroutine defined a function, it could still be used when that subroutine was finished, and if it ran another subroutine (or itself recursively) that defined the same functions differently, this could mess things up. With this in mind, I realized it would be helpful to be able to define functions with a local scope.
Fortunately, I realized it was already possible, which saved me the trouble of adding this feature. To create a function with a local scope, save a lambda function to a variable with a local scope. You can then call it with the value of that variable. For example:
sub example kingpos:
local friend;
if hasupper space #kingpos:
set friend lambda (not haslower #0 and hasupper #0);
else:
set friend lambda (not hasupper #0 and haslower #0);
endif;
if fn #friend #piece:
...
endif;
endsub;
I repurposed a feature that didn't seem to have any useful purpose. Braces have normally been used to insert the value of variables into strings. When the string between the braces included spaces, these would be converted to periods. I think my intention was to turn space separated strings into multi-dimensional array variables, but in tests, this did not work. So, I replaced the implode
function used to turn the array of words into a single string with a period between each word with the function used to evaluated a GAME Code expression. The result is that a GAME Code expression can now be placed between braces in any command, and the result of that expression can be used as an argument to a command or inserted into a string. Note that expressions between braces will be evaluated immediately when the line is executed. So, they should not be used in function definitions unless the intent is to programmatically create a function. In my preliminary tests, I haven't noticed this causing any problems. I will do more tests later.
This code is designed to show that a string or expression between braces will be evaluated immediately:
set a Adam;
def a1 {#a};
def a2 #a;
print fn a1;
print fn a2;
set a Abel;
print fn a1;
print fn a2;
Here is the output:
Adam
Adam
Adam
Abel
Here's an example of including expressions between braces, first as a lone argument, then inside of a string.
set c e2;
echo {space e2} "{var c}-{join filename var c + rankname var c 2}";
Here's the output:
P e2-e4
Would it work to combine direction with where like this?
where c3 logic direction a1 c3
You may use braces where you don't need them, such as in set
, if
or elseif
. You may do this for stylistic reasons or because you can't remember which commands normally work with expressions.
set c {join e 2};
if {== #c e1}:
echo "Partridge";
elseif {== #c e2}:
echo "Turtledoves";
elseif {== #c e3}:
echo "French Hens";
endif;
This outputs Turtledoves
.
Would it work to combine direction with where like this?
where c3 logic direction a1 c3
As long as your code is using logical directions that match the output of direction
. If you have not setup logical directions, and your code only uses mathematical relations between spaces, then it will not work.
I was just trying direction
to see what kind of outputs it gives on a horizontal hexagonal board. This is the code
set north direction g7 g8;
set northeast direction g7 h7;
set northwest direction g7 f8;
set south direction g7 g6;
set southeast direction g7 h6;
set southwest direction g7 f7;
print #north #northeast #northwest #south #southeast #southwest;
and the output was
Array
(
[0] => w
[1] => se
[2] => s
[3] => nw
[4] => e
[5] => #north
)
What is happening with #north?
I didn't get the same output as you. I got this:
Array
(
[0] => w
[1] => se
[2] => s
[3] => nw
[4] => e
[5] => n
)
Ok, I left out a semicolon somewhere.
With expressions enclosed in parentheses, it's even possible to construct variable names and create some very obfuscated code. For example:
set a.1 Adam;
set a.2 Abel;
set Abel.brother.1 Cain;
set Abel.brother.2 Seth;
echo #a.{- 5 4};
echo #{#{chr - ord b 1}.2}.b{rot}her.{!= brother keeper};
The output is:
Adam
Cain
Note that rot
is just a bare string, and {rot}
just gets replaced with rot
during preprocessing.
I tried using logical directions. I would expect it to return d5. Instead, it simply prints d4;
map n 0 1 w -1 0 e 1 0 s 0 -1;
print where d4 logic n;
The documentation was incorrect. You do not need the logic
keyword.
I have made some updates to the sections Preprocessing Substitutions and Expressions. For the latter, I compared the documentation with the code and added entries for operators and built-in functions I had not yet documented.
Back when I first added fonts to Game Courier, a very different selection of fonts was available for free than what is available today. So, I thought I would add some new fonts and remove some unused fonts. Using grep, I determined that the Bodoni, Bonum, Century Schoolbook, Chicago, Crimson Text, Didot, OCRA, Pagella, Review, and TechSchool fonts were not being used in any settings file or log. So, I commented these out in play.php. I also noted that Beguiat, Nadine, Topaz New, and Uncollage were getting little use. Does anyone have any thoughts on what fonts they would like for Game Courier to use? One good place to look would be Google Fonts, since these are also available as webfonts, and I could make use of webfonts in Table and CSS diagrams. The web fonts we currently use with the website are Literata for serif, Noto Sans for sans serif, and Courier Prime for monospace. At the very least, I imagine I should add these and maybe replace Courier with Courier Prime.
I'm thinking of adding Atkinson Hyperlegible, Fira Mono, and Lexend. All three clearly distinguish characters from each other, such as 1 from I from l or 0 from O. I might replace Futura with Lexend, since Lexend is a look-a-like for Futura with some legibility improvements, such as horizontal bars on the capital I.
I have this code I'm trying, but it's not producing the result I expect.
map nw -1 1 n 0 1 w -1 0 e 1 0 s 0 -1 se 1 -1;
def nextstep
cond and samecase space #1 space #0 onboard #1 (fn nextstep #1 where #1 direction #0 #1) #1;
def TEST mergeall
fn nextstep #0 where #0 nw
fn nextstep #0 where #0 n
fn nextstep #0 where #0 w
fn nextstep #0 where #0 e
fn nextstep #0 where #0 s
fn nextstep #0 where #0 se;
print fn TEST d4;
print fn nextstep d4 where d4 s;
I thought fn TEST d4
would return an array of coordinates, but instead it seems to produce an empty array. The bottom print command works as expected, so the problem has to be with TEST, but I can't figure it out.
Your function is recursive, but you're not using unless or onlyif, which were designed specifically for use in recursive functions. Although you seem to have gotten it to work anyway, I would recommend looking into how you could rewrite your function using one of them.
I replaced your TEST function with this:
def TEST aggregate lambda (fn nextstep #frm where #frm #0) (nw n w e s se) =frm;
Except for not including empty results, it gave the same output as this:
set c1 fn nextstep d4 where d4 nw;
set c2 fn nextstep d4 where d4 n;
set c3 fn nextstep d4 where d4 w;
set c4 fn nextstep d4 where d4 e;
set c5 fn nextstep d4 where d4 s;
set c6 fn nextstep d4 where d4 se;
set ra array #c1 #c2 #c3 #c4 #c5 #c6;
print #ra;
The problem with your code was that fn is a greedy function, meaning that it interprets everything following it as an argument to the function. Although you specified only two arguments for your function, it does not take this into account. So, it does not just pop off two values and leave the remaining values untouched.
To get around this, I used aggregate with a lambda function, and I used a named variable for the main input to your TEST function so that it could be used within the lambda function without being confused for input to the lambda function. Check the documentation on these details to learn more.
Thank you, that helps. I think there might be another problem with what I was trying also.
map nw -1 1 n 0 1 w -1 0 e 1 0 s 0 -1 se 1 -1;
print mergeall where #0 n where #0 e;
print mergeall where #0 0 1 where #0 1 0;
When I run this, the output is
Array
(
)
Array
(
[0] => b1
[1] => a2
)
so it seems like the logical directions don't work in this context. I've also noticed that there are several "logical directions" links on this page that don't go anywhere.
I also have an unrelated question. Is there any way to control which space is highlighted after a move is made?
Since where can take two or three arguments, it will take three if it finds three. So, where #0 n where #0 e; is treating the value of where #0 e as an argument for the leftmost where. Try this instead:
print aggregate lambda (where #frm #0) (n e) =frm;
What would be a good way to define a piece for a hexagonal board that can slide through any path exactly 3 spaces long to any space that is no fewer than 3 spaces away? When I try checkpath
or checknsteps
the result is wrong because they assume a square grid.
It is probably best to use logical directions with a hexagonal board, and the details will depend upon what kind of hexagonal board you use. Game Courier currently supports two kinds of hexagonal boards, and it will eventually support four kinds, as the Diagram Designer currently does.
Using logical directions, for which I would recommend either degrees or the numbers on a clock face, you can use logleap to specify a series of lame leaps that must go by a certain path. Each lame leap would be a possible move for the piece, and each series of steps constituting a single lame leap should go in parentheses.
I modified the section on Data Storage to make the distinction between the name of a variable, constant or flag and a call to one. While this distinction has long been a part of the language, I hadn't expressed it in these terms.
you can use logleap to specify a series of lame leaps that must go by a certain path
This works, except that logleap doesn't seem to allow captures. Is that intended?
This works, except that logleap doesn't seem to allow captures. Is that intended?
Are you saying that if a space is occupied, logleap will not return that space as one of the spaces a piece may move to?
Yes. I've tested in in an empty preset to make sure it wasn't other code interfering.
I've gotten to the point where I see the problem. I'm testing the a modified Knight in a modified version of Cylindrical Chess. At first, it wouldn't let me move with the Knight at all. I eventually realized this was because the Knight is on the destination space during an actual move. So, I hacked the code to empty the Knight's space when it moves, and then it allowed the Knight to move. But when I gave it a target to capture, it would not display it as a legal move, though because of the hack I did, I was able to make the move anyway. I will look into this more after I eat something.
I've had that first problem you mention too and I solved it by adding a subroutine for the piece that checks the move in reverse just for post move testing. I don't know what exactly is going on there, but I've seen it with other complex pieces too.
The same thing was happening. Since the code checks the legality of an actual move after it has moved the pieces, the destination space is always occupied at that time. So, the problem is just the same as it is for a potential move that is a capture.
I think I fixed it by changing this code:
function logleap ($from, $to, $ra) {
global $map, $space;
$ns = $from;
for ($i = 0; $i < count($ra); $i++) {
$dir = $ra[$i];
$temp = false;
if (is_array($dir)) {
if (($temp = logleap($from, $to, $dir)) == $to)
return $temp;
}
elseif (isset($map[$ns][$dir]))
$ns = $map[$ns][$dir];
else
$ns = false;
if ($space[$ns] != '@')
break;
}
if (($temp == false) && ($i < count($ra)))
return false;
return $temp ? $temp : $ns;
}
to this:
function logleap ($from, $to, $ra) {
global $map, $space;
$ns = $from;
for ($i = 0; $i < count($ra); $i++) {
if (($i > 0) && ($space[$ns] != '@'))
break;
$dir = $ra[$i];
$temp = false;
if (is_array($dir)) {
if (($temp = logleap($from, $to, $dir)) == $to)
return $temp;
}
elseif (isset($map[$ns][$dir]))
$ns = $map[$ns][$dir];
else
$ns = false;
}
if (($temp == false) && ($i < count($ra)))
return false;
return $temp ? $temp : $ns;
}
That seems to work
I have added the ability to use entirely custom piece sets in a game. These are defined in GAME Code and do not use an external PHP file. Here is some sample code I wrote:
// A demo of using multiple internal sets that do not match any set file. // Name the sets you will use by assigning them to the $groupsets array. // Use capitalized names for the sets. These do not match any file names. setsystem groupsets array Abstract Alfaerie AlfaeriePNG Magnetic Motif; // Define pieces in an array variable called mypieces. // Start by creating an associative array of all pieces shared in common. // The key should be a label, and the value should be a filename. // A single line of code is broken into multiple lines of text for legibility. set mypieces assoc K "WKing.gif" k "BKing.gif" Q "WQueen.gif" q "BQueen.gif" R "WRook.gif" r "BRook.gif" B "WBishop.gif" b "BBishop.gif" N "WKnight.gif" n "BKnight.gif" P "WPawn.gif" p "BPawn.gif"; // Set the $dir system variable to match the set, and modify filenames as needed. if == pieceset Alfaerie: setsystem dir "/graphics.dir/alfaerie/"; foreach (k v) #mypieces: setelem mypieces #k tolower #v; next; elseif == pieceset AlfaeriePNG: setsystem dir "/graphics.dir/alfaeriePNG/"; foreach (k v) #mypieces: setelem mypieces #k tolower str_replace .gif .png #v; next; elseif == pieceset Magnetic: setsystem dir "/graphics.dir/magnetic/"; elseif == pieceset Motif: setsystem dir "/graphics.dir/motif/"; else: // Have a default set for when the set does not match any allowed set. // The default is Abstract. setsystem dir "/graphics.dir/abstract/"; endif; // Now that the pieces are defined, copy the #mypieces array to $pieces setsystem pieces #mypieces;
It seems that this feature is not working. Whenever I try to make a move in the sample preset, GC responds by saying that the selected set does not exist.
Easier to just use /membergraphics/MSdealerschess/. It's what I made/am making it for.
It seems that this feature is not working. Whenever I try to make a move in the sample preset, GC responds by saying that the selected set does not exist.
In general, I think this was a bad idea, and if it's not working for you, I would recommend not doing it. The problem with this idea is that it defines a piece set entirely with code, but the preset needs to know how to identify pieces even when not running any code. The alternative, which I would recommend instead, is to use a piece set that includes all the piece images you want to use, then use aliases to specify the notation you want players to use for the pieces during the course of the game. The fairychess include file is set up to use aliases, and the tutorial on it describes how you should make use of them with it.
While we're at it, the Chushin Shogi and Taishin Shogi sets (as well as their corresponding set files) can be deleted from the sets.php file, as the corresponding games no longer exist.
the Chushin Shogi and Taishin Shogi sets (as well as their corresponding set files) can be deleted from the sets.php file, as the corresponding games no longer exist.
Okay, for now I have commented out these sets in sets.php. I used grep to confirm that no settings files are using any of these sets. I also used grep to check the logs, and I found three logs using chushin_shogi_mnemonic_pieces. Here is what I got:
mitsugumi_shogi/sesquipedalian-cvgameroom-2022-45-765.php:chushin_shogi_mnemonic_pieces
suzumu_shogi/arx-cvgameroom-2021-348-992.php:chushin_shogi_mnemonic_pieces
suzumu_shogi/arx-cvgameroom-2021-348-992.php:chushin_shogi_mnemonic_pieces
Easier to just use /membergraphics/MSdealerschess/. It's what I made/am making it for.
I moved your comment here, because I assumed you were responding to A. Dewitt, whose comment I also moved here. The link you provided goes to an Interactive Diagram, but this is Game Courier. Do you plan on making a Game Courier set out of the pieces in your Interactive Diagram?
The directory I cited is full of PNG images, which can be used on Game Courier. There are over 500 pairs of them right now. (The Interactive Diagram is just a demo of how the pieces move.)
As an aside, I do plan to convert most if not all of those that I created (by one means or another) to GIF for putting into the download area.
As an aside, I do plan to convert most if not all of those that I created (by one means or another) to GIF for putting into the download area.
Why would you do that? The GIF images are legacy, and should no longer be used for new projects.
The GIF images are legacy, and should no longer be used for new projects.
I did not know that. I won't worry about that, then. Never mind. :)
Why would you do that? The GIF images are legacy, and should no longer be used for new projects.
Not all GIF images have been converted to SVG. So, if you want to use certain images, GIF is still your only choice. So, he may as well make his images available as GIFs. But if he can, he should also make them available as SVG.
Not all GIF images have been converted to SVG. So, if you want to use certain images, GIF is still your only choice. So, he may as well make his images available as GIFs. But if he can, he should also make them available as SVG.
I'm not sure how well those (the SVGs) would work out. But I can give it a shot.
Update: I tried a converter, and got some very interesting-looking white-on-black impressionistic figures that would work wonderfully if that had been my intent.
Anyone else is welcome to give some PNG-to-SVG conversion a shot to do better.
Not all GIF images have been converted to SVG. So, if you want to use certain images, GIF is still your only choice. So, he may as well make his images available as GIFs.
I think this is a bad plan, because the most-used pieces exist as quality PNG in the alfaeriePNG and alfaeriePNG35 directories, and using GIF files would also use the low-quality images for those. If additional images would have to be added to any directory, it would be better to convert the GIF images for which no SVG version exists to PNG, and add those to the PNG directories. (It should be possible to do that automated in bulk.) These pieces are only very rarely used, and one or two low-quality images don't degrade the experienced quality of a large diagram in a very conspicuous way.
In time the low-quality converted GIFs could be replaced by SVG-derived high-quality PNG, when they are actually used. Diagram-Designer links or Interactive Diagrams using the PNG pieces would then automatically be upgraded to the better quality.
The problem is that there is little incentive to put any effort in creating SVG that is unlikely to be ever used. So use should come first. But when people will be automatically referred to using GIF for their diagrams when they need a rarely used image, no use case for these images as SVG/PNG will ever be created. And if they were, the existing diagrams would not benefit from it.
For the most part, the discussion is academic, at least as far as my original and modified icons are concerned; I've tried several different tools now, and the best SVGs come out blurry, blocky, or both. The only exceptions would be those made up of only horizontal and vertical lines, and out of 151 such images there are only 7 of those, 6 of which are Tifinagh letters.
To be sure, I'd love to have SVG icons for most of these pieces, especially those used in Vanguard Chess, Short Sliders, and Zwangkrieg. My ability with static arts is just too meager.
If additional images would have to be added to any directory, it would be better to convert the GIF images for which no SVG version exists to PNG, and add those to the PNG directories. (It should be possible to do that automated in bulk.)
I can testify that this is not an especially difficult thing to do, as it's what I've done for most all of the other 360+ icons I've handled -- certainly, all of those not already shown in the Play-Test Applet. If you want to copy some of the results, they're right there for the taking (and I can handle anything that's missing).
I have made a large fraction of the existing alfaerie SVGs, (Greg did the others), but I never tried to do it in an automated way. I use Inkscape on Linux, import the GIF image into it (e.g. by 'open with'), magnify it to the desired nominal size of the SVGs (2048 x 2048?), and then trace out all outlines by placing points on those from Bezier curves. You don't need many points; for a circle a hexagon would do. (But of course there is a special function for drawing circles.) After the curve is a closed loop, I select it with the 'edit points' tool. With that I grab all the sides of the polygon that should not be straight lines, to bend those. This makes tow 'handles' appear at the ends of the side, the direction of which I adjust to the tangent of the curve in the GIF, while their length is adjusted to give a smooth curve. Then I adjust the width, color, fill color and corner rounding therough the menu that appears clicking on the lower left of the bottom tools bar. (I often start with red instead of black, to see whether I cover the outlines of the GIF well.) Finally I remove the GIF, and save the lot (after grouping everything with the menu).
This is not difficult, but it is tedious for complex images. This is why I haven't done it for every GIF that exists. That it exists doesn't necessarily mean it is useful. So I convert the remaining images only 'on demand', when I am equiping articles on variants that the AI of the Interactive Diagram can handle with such Diagrams, and the original diagram in that article used an image that doesn't exist as SVG yet.
It seems that Game Courier is having trouble displaying legal moves for pieces in certain situations. Take for example this game from Seireigi:
1. p 3g-3f
1... P 7c-7d
2. n 2i-3g
2... N 8a-7c
3. n 3g-2e
3... N 7c-8e
4. n 2e-1c; +n-dest
4... N 8e-9g; +N-dest // If you click on Black's Heavenly Horse on 1c, it shows only legal mvoes to 1a, 3a, 2b, and 2d. GC should also show legal moves on 2e and 1b, but it doesn't. However, rule-enforcement-wise, there are no problems when the Heavenly Horse moves to the latter two squares.
Here is the function that is probably at fault:
def Black_Heavenly_Horse-Range merge leaps #0 1 1 array where #0 -2 -2 where #0 0 -2 where #0 2 -2 where #0 -1 2 where #0 1 2 where #0 0 -1;
Since your Black Heavenly Horse is on the side of the board, some of the where clauses will return false, but the array of coordinates to check for legal moves on should not contain any false values. To weed out the false values, you should build your array with a lambda function, probably using aggregate. So this code may work:
def Black_Heavenly_Horse-Range merge leaps #frm 1 1 aggregate lambda (where #frm #0 #1) array ((-2 -2) (0 -2) (2 -2) (-1 2) (1 2) (0 -1)) =frm;
I'll leave it to you to test it.
This might also work. It feeds your original array into aggregate to weed out any empty values. Within parentheses, #0 should just be the value of each element passed to the lambda function, not the first argument passed to the function. If it doesn't work that way, you can replace the outer #0 with a named variable, as I did above.
def Black_Heavenly_Horse-Range aggregate lambda (#0) merge leaps #0 1 1 array where #0 -2 -2 where #0 0 -2 where #0 2 -2 where #0 -1 2 where #0 1 2 where #0 0 -1;
I decided to look into why this is a problem. It's that foreach, which is used to go through the values of a -Range function in the stalemated subroutine, exits when it gets a false value. This is because the PHP next function, which the next command uses to get the next value in an array, returns false if the array has no more elements. So, I have a conditional checking whether this value is false, and if it is false, it decrements the scope, which breaks out of the loop. To allow array elements with false values, I need another way to tell whether there are any more elements in the array.
Since each() is deprecated, I used key() to tell whether it had reached the end of the array. Although the documentation said it returned false upon reaching the end of the array, tests showed that it returned null. So, I just checked whether it was empty, which should be safe, as the key will have incremented beyond 0 after calling next(). So, now the foreach command will not terminate early when reaching a false value, and your original code should now work properly.
Thanks for fixing that.
Would it be possible to create a directory at /graphics.dir for Atlantic Graphics?
Well, I don't like them very much (too many of them look like Knights, with only minute differences), and the number of unorthodox pieces is very small. But I don't think that should stop them from being available. I could not find any SVG files in the upload directory for the Atlantic Graphics page, though; it seems you put all the pieces in a single (non-SVG) image there. So there was nothing for me to copy to /graphics.dir/svg/atlantic/ .
Well, that's how most chess fonts handle such designs, I didn't like it either. The quantity is not an issue as I've designed another block of pieces, which covers pretty much all common chess variants as well as Shogi, yet I wanted to try out the original ones before moving forward.
It was impossible for me to directly upload SVG files in the page's directory and I got a message saying 'Upload of ... has been aborted, because it has an extension of: .svg ... To upload any other kind of file, it must be preapproved by an editor who will upload it for you the first time.'
That must be an oversight, as I see no reason to exclude any particular graphics format. I tried to change the file upload script to also allow svg extension, and hope I did this right. So please try again. You could also put them in a zip file and upload that; this was already amongst the allowed extensions.
I uploaded the SVG files in the page's file section, I hope now it works. The Zip file is also available there (Atlantic Graphics.zip), it was originally uploaded at the time the page was created.
Ah, I overlooked that zip file. Perhaps because the name was displayed in quotes, because of the space in it. Anyway, I copied all the svg files to /graphics.dir/svg/atlantic/ .
Thanks. I knew that the space could cause some problems, but didn't bother to change that.
100 comments displayed
Permalink to the exact comments currently displayed.
I apologise if my question is trivial, all this looks very complex for me. I want to use a set "Alfaerie for Metamachy" but for the moment it is limited to 26 pieces, using the 26 letters of the alphabet. Would it be possible to add few more using other letters such as the diacritic letters may be like é/É? Thanks