Check out Smess, our featured variant for February, 2025.


[ Help | Earliest Comments | Latest Comments ]
[ List All Subjects of Discussion | Create New Subject of Discussion ]
[ List Earliest Comments Only For Pages | Games | Rated Pages | Rated Games | Subjects of Discussion ]

Comments/Ratings for a Single Item

EarliestEarlier Reverse Order Later
The Fairychess Include File Tutorial. How to use the fairychess include file to program games for Game Courier.[All Comments] [Add Comment or Rating]
Daniel Zacharias wrote on Wed, Apr 10, 2024 03:56 AM UTC:

I'm having trouble with pawn promotion in this preset. I've set wprom and bprom and promotion does work, but it's not being optional like I expect. Instead it just auto promotes pawns as soon as they reach the promotion zone. As far as I can tell, there should be a promotion choice before the last rank even if there's only one item in wprom.


🕸📝Fergus Duniho wrote on Wed, Apr 10, 2024 12:56 PM UTC in reply to Daniel Zacharias from 03:56 AM:

Start by accurately writing the promotion rules in English. It looks like you copied the rules from Gross Chess even though Pawns in Obento Chess promote only to Flying Ox. I’ll check it out later when I’m on my desktop.


Daniel Zacharias wrote on Wed, Apr 10, 2024 01:28 PM UTC in reply to Fergus Duniho from 12:56 PM:

I did copy from Gross chess. The descriptions should be fixed now


🕸📝Fergus Duniho wrote on Wed, Apr 10, 2024 07:57 PM UTC in reply to Daniel Zacharias from 01:28 PM:

In looking into this, I tested Gross Chess to see if it had the same problem, but before I could tell, I encountered another problem with it. I realized that for a game like Gross Chess it wouldn't do to use bprom and wprom as though they had static values. So I rewrote the fairychess include file and the Gross Chess code to support dynamic values for what a piece is allowed to promote to on a given space.

This makes use of some new functions that end with "-Promote". Here are the default functions for the Pawns:

def White_Pawn-Promote var wprom;
def Black_Pawn-Promote var bprom;

For backwards compatibility with the original way of handling promotions, these just return the value of wprom or bprom. And for additional backwards compatibility, the stalemated subroutine will use these functions only if the piece is not in the promotable array. So, to enable the use of these functions for providing dynamic values for what a piece can promote to, you should unset promotable or set it to an empty array. I added this line to Gross Chess after including the fairychess include file.

unset promotable;

Since the default functions return static values, they need to be rewritten for the particular game they are for. Here are the functions I wrote for Gross Chess:

def White_Pawn-Promote merge intersection var cap elem - rank #0 9 ((B N V W) (B N V W C R S) (B N V W C R S A M Q)) elem - rank #0 9 ((P) (P));
def Black_Pawn-Promote merge intersection var cap elem rank #0 ((b n v w c r s a m q) (b n v w c r s) (b n v w)) elem rank #0 (() (p) (p));

Since what a Pawn may promote to in Gross Chess depends upon the rank it is on, I used the rank value for black (or a value calculated from the rank value for white) as the index for a couple of arrays from which it extracted a particular value. For example, black can promote on ranks 0-2, as they are designated internally. So, this code will return the element of the array with the same index as the rank value:

elem rank #0 ((b n v w c r s a m q) (b n v w c r s) (b n v w))

Since white promotes on ranks 9-11, I subtracted 9 to get a value from 0 to 2 for any rank promotions are allowed on or a number that is out of range for any other rank. So, this works similarly:

elem - rank #0 9 ((B N V W) (B N V W C R S) (B N V W C R S A M Q))

Since the last rank for black is 0, and the last for white is 11, and 11-9 is 2, these list sets of promotion options in the reverse order from each other.

Since promotion options are limited to captured pieces, each function calculates the intersection of the value above with the captured pieces. This looks like this for black:

intersection var cap elem rank #0 ((b n v w c r s a m q) (b n v w c r s) (b n v w))

Finally, I get to the part that is relevant to Obento Chess. Whether it can promote to a Pawn as a way of declining promotion depends on the rank but not on what has been captured. So Pawns were not included in the main lists of promotion options. Instead, it merges the intersection calculated above with the value of another array element. Again, the specific array element is a function of the rank. Here is what it looks like for black:

elem rank #0 (() (p) (p))

Since declining promotion is not an option for black on rank 0, an empty array is provided for the element with an index of 0. This is not necessary for white, as the rank it cannot decline promotion on has a higher index.

elem - rank #0 9 ((P) (P))

For Obento Chess, you might use functions like these:

def White_Pawn-Promote elem - rank #0 9 ((FP) (FP) (F));
def Black_Pawn-Promote elem rank #0 ((f) (fp) (fp));

You could handle promotion for the other promotable pieces with similar functions for each specific piece.


Daniel Zacharias wrote on Wed, Apr 10, 2024 11:51 PM UTC in reply to Fergus Duniho from 07:57 PM:

Thank you, that does work better; although I still don't get why the previous way wasn't working since I wasn't changing the pawn subroutine which seems to modify promotion options before the last rank.

Anyway, this reveals another, but more minor, problem, which is that when a pawn gets to the promotion zone the text in the option selection box is large enough that the lines overlap slightly.


🕸📝Fergus Duniho wrote on Thu, Apr 11, 2024 12:33 AM UTC in reply to Fergus Duniho from Wed Apr 10 07:57 PM:

For Obento Chess, you might use functions like these:

def White_Pawn-Promote elem - rank #0 9 ((FP) (FP) (F));
def Black_Pawn-Promote elem rank #0 ((f) (fp) (fp));

That should be this:

def White_Pawn-Promote elem - rank #0 9 ((F P) (F P) (F));
def Black_Pawn-Promote elem rank #0 ((f) (f p) (f p));

🕸📝Fergus Duniho wrote on Thu, Apr 11, 2024 01:02 AM UTC in reply to Daniel Zacharias from Wed Apr 10 11:51 PM:

I still don't get why the previous way wasn't working since I wasn't changing the pawn subroutine which seems to modify promotion options before the last rank.

The Pawn subroutines are only for actual moves, but nothing was being done to handle variable values of wprom or bprom for potential moves. The stalemated subroutine was supposed to provide a list of all legal moves, but it was omitting moves where someone declines promoting a Pawn. By having functions that dynamically calculate what a piece may promote to, it is now able to provide an accurate list of legal moves for games with variable promotion rules, such as Gross Chess has.

Anyway, this reveals another, but more minor, problem, which is that when a pawn gets to the promotion zone the text in the option selection box is large enough that the lines overlap slightly.

I increased the line-height and made some changes to the borders.


🕸📝Fergus Duniho wrote on Sun, May 12, 2024 10:29 PM UTC:

The changes to Game Courier I described here were for the purpose of writing a checked function that would do the same job as the checked subroutine but even faster. This is for the sake of illustrating the gains in speed of using functions rather than subroutines when a function will do the job. Here's the code for the subroutine and the function:

sub checked king:
    my from piece;
    local movetype;

    set movetype CHECK;
    if isupper cond empty var king $moved space var king:
        def enemies onlylower;
    else:
        def enemies onlyupper;
    endif;
    for (from piece) fn enemies:
        if fn const alias #piece #from var king:
            return #from;
        endif;
    next;
    return false;
endsub;

def checked anytrue lambda (fn const alias #0 var key var king) 
cond isupper cond empty var king $moved space var king (onlylower) (onlyupper) 
=movetype CHECK 
=king;

I have tested the function out in Ultima, because its stalemated subroutine is not widely used in other presets, and its piece functions make use of the movetype value to determine whether it is a regular move or a checking move. Things appear to be working in Ultima. In speed tests comparing 1000 repetitions of each, the function is around twice as fast. In these results, the first, third, and fifth are the subroutine, and the second, fourth, and sixth are the function.

Elapsed time: 0.92847084999084 seconds

Elapsed time: 0.45569705963135 seconds

Elapsed time: 0.87418103218079 seconds

Elapsed time: 0.46827101707458 seconds

Elapsed time: 1.1367251873016 seconds

Elapsed time: 0.65254402160645 seconds

🕸📝Fergus Duniho wrote on Mon, May 13, 2024 02:20 AM UTC in reply to Fergus Duniho from Sun May 12 10:29 PM:

I was thinking of splitting the checked function into separate checked-real and checked-potential functions, but as I was looking into which would be which, it looked like there was never any time when it was passed an empty space after the King just moved. In the Pre-Move sections, kpos or Kpos would be updated before calling the function, and in stalemated, it would check whether the King was the moving piece and pass the King's new position if it was. This meant I could reduce the function to this:

def checked anytrue lambda (fn const alias #0 var key var king) 
cond isupper space var king (onlylower) (onlyupper) 
=movetype CHECK 
=king;

Just in case, I ran this code in Ultima as a test:

sub checked king:
  my from piece;
  local movetype;

  set movetype CHECK;
  if empty var king:
    die "The King space at {#king} is empty.";
  endif;
  if isupper cond empty var king $moved space var king:
    def enemies onlylower;
  else:
    def enemies onlyupper;
  endif;
  for (from piece) fn enemies:
    if fn const alias #piece #from var king:
      return #from;
    endif;
  next;
  return false;
endsub;
def checked sub checked #0;

This code would exit right away with an error message if the subroutine, which was called by the function here, got passed an empty space. I then looked at completed games using the same include file, and they did not exit with the error message. So, I got rid of this code and modified the function, tested Ultima again, and it still worked for both actual checks and for moves that would move the King into check.

With that change made, I ran the speed tests again and got these results:

Elapsed time: 1.0438919067383 seconds

Elapsed time: 0.46452307701111 seconds

Elapsed time: 1.013090133667 seconds

Elapsed time: 0.48957395553589 seconds

Elapsed time: 1.1313791275024 seconds

Elapsed time: 0.49660110473633 seconds

In each pair the subroutine is first, and the function is second, and in each case the function takes less than half the time. In both this case and the previous one, these tests were done on the opening position in Chess, in which the King is not in check, meaning that it checks for check from every enemy piece without exiting early.


9 comments displayed

EarliestEarlier Reverse Order Later

Permalink to the exact comments currently displayed.