💾 Archived View for g.mikf.pl › gemlog › 2022-04-08-3mc.gmi captured on 2022-04-29 at 11:53:52. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-04-28)

➡️ Next capture (2023-04-26)

-=-=-=-=-=-=-

'3 Man Chess: In The Round' in APL (NARS2000). Part 2.

2022-04-08

See previous post:

2022-04-07 '3 Man Chess: In The Round' in APL (NARS2000). Part 1.

After 'addvec', another function there is about whether, given an adequate board situation and rest of the game state, a move is possible from the given coordinates or from any coordinates.

Figure: function 'can'

r ← figtype can vec
:if vec≡0 0  ⋄  r ← 0  ⋄ :return   ⋄ :endif
:select figtype

:case PAWN
:if vec[1]=1 ⋄ :andif 1≥∣vec[2] ⋄ r←'1≠1⌷'
:elseif vec ≡ 2 0               ⋄ r←'2=1⌷'
:else  ⋄  r ← 0   ⋄ :endif

:case PAWNCENTER
:if vec[1]=¯1 ⋄ :andif 1≥|vec[2] ⋄ r←'1≠1⌷'
:else  ⋄  r ← 0   ⋄ :endif

:case KING
:if           0 2 ≡ |vec  ⋄  '0 5≡8|'
:elseif 1 1 ≡ {⍵≤1}¨|vec
 :if vec[1]=¯1 ⋄ '1≠1⌷' ⋄ :else ⋄ 1 ⋄ :endif
:else  ⋄  r ← 0   ⋄ :endif

:case ROOK
:if vec[1] = 0      ⋄  r ← vec[1]≠24
:elseif vec[2] = 0
  r←{vec[1]<0: '1≤' ⋄ '12≥'},⍕vec[1],'+1⌷'
:else  ⋄  r ← 0   ⋄ :endif

:case BISHOP
:if 1 1 ≡ 11≤{⍵ (|⍵)} vec  ⋄ r ← 0
:elseif {⍵[1]=⍵[2]} |vec
  r←{vec[1]<0: '1≤' ⋄ '12≥'},⍕vec[1],'+1⌷'
:else  ⋄  r ← 0   ⋄ :endif

:case QUEEN
:if 0∊vec
  r ← ROOK ∇ vec
:elseif {⍵[1]=⍵[2]} |vec
  r ← BISHOP ∇ vec
:else  ⋄  r ← 0   ⋄ :endif

:case KNIGHT
:if ⍬ ≡ 2 1 §|vec ⋄ :select vec[1]
 :case ¯1 ⋄ r←'1≠1⌷'
 :case ¯2 ⋄ r←'2<1⌷'
 :else ⋄ r ← 1 ⋄ :end
:else  ⋄  r ← 0   ⋄ :endif

:endselect

The function 'can' accepts a left argument 'figtype' and right argument 'vec'. It starts with checking if the 'vec' argument matches a "0 0" vector and if it does, it immediately returns zero (boolean false) and then executes a RETURN statement that ends the execution of the function.

The rest of the function body is a SELECT statement with the 'figtype' argument as denominator. The variable names in the cases are globally assigned: PAWN←6 ⋄ PAWNCENTER←7 ⋄ KING←5 ⋄ QUEEN←4 ⋄ BISHOP←3 ⋄ KNIGHT←2 ⋄ ROOK←1

PAWNCENTER is treated as a separate piece in this implementation because pawns that cross the center move in opposite direction and it is not sure if with enough captures there couldn't be an ambiguity, nor a method for distinguishing has been derived.

The function's return value specification is as follows: integer zero if the move is unconditionally impossible regardless of square of origin, integer one if the move is unconditionally possible regardless of square of origin, and a string describing a predicate for squares of origin for the move to be possible.

In case of a pawn that hasn't yet crossed the center:

if the rank coordinate of the vector is one and the absolute value from the file coordinate of the vector doesn't exceed one, a predicate is returned that takes the rank coordinate of its argument and returns whether it's unequal to one (outermost rank);

if the vector matches "2 0" it returns a predicate whether the rank coordinate of its argument equals 2 (the pawns' starting rank).

Otherwise it returns zero.

In case of a pawn that has crossed the center:

if the rank coordinate of the vector is minus one and the absolute value from the file coordinate of the vector doesn't exceed one, again a predicate is returned that takes the rank coordinate of its argument and returns whether it's unequal to one (outermost rank);

otherwise it returns zero.

In case of a king: if the vector is "0 2" or "0 ¯2" (castling move vector) (both cases are checked for via the Magnitude (absolute value) operator) a predicate is returned for whether the starting position is a king's starting position: the argument is first taken modulo 8 (disregarding the eights-not-zeroes cases, see figure below) and then matched against "0 5";

if the vector, put through absolute value operator, put through a check on each coordinate if it's less or equal one (that's an anonymous function here given to the Each a.k.a. Map operator) matches "1 1" (i.e. both resolved true) then: if the rank coordinate of the vector is minus one then a predicate on whether the rank coordinate of its argument is not one (outermost rank), otherwise one (boolean true);

otherwise it returns zero (boolean false).

Figure: comparison of file modulo ways in Immediate Execution Mode

      8|⍳24
1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0
      1+8|¯1+⍳24
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 

In case of a rook:

if the rank coordinate of the vector is zero (i.e. vector is filewise) an unconditional boolean value is returned whether the file coordinate of the vector isn't 24 (that would mean returning to the square of origin, and rules forbid this for allowing to evade zugzwang);

if the file coordinate of the vector is zero (i.e. vector is rankwise) a predicate is returned composed of (contatenated from):

Otherwise it returns zero.

In case of a bishop:

if the rank coordinate of the vector and the absolute value of the file coordinate of the vector both exceed 11, it returns zero;

if the absolute values of the coordinates of the vectors are equal, a predicate is returned the same as the predicate in case of a rankwise vector for a rook, above;

otherwise, zero is returned.

In case of a queen:

if any of the vector coordinates is zero, the function is called recursively with the same 'vec' argument but as if for a rook;

if the absolute values of the coordinates of the vectors are equal, the function is called recursively with the same 'vec' argument but as if for a bishop;

otherwise, zero is returned.

In case of a knight:

if the vector coordinates contain two and one (symmetric set difference between the absolute-valued vector and "2 1" is nil (represented by the Zilde character)) (i.e. the absolute-valued vector is "2 1" or "1 2"):

otherwise, zero is returned.

There is no default case with an assignment to the return variable, so in case of a bad 'figtype' argument i would expect a 'syntax error' - but i'm not sure, i would have to check and i'm too lazy.

The application of a string-expressed predicate

"Lambdas", or anonymous functions, can't be returned directly from functions nor they can be returned in Immediate Execution Mode. Not in NARS2000 nor GNU APL, but it's said to be possible in Dyalog APL. The common workaround is to return strings:

https://stackoverflow.com/questions/65462605/is-it-possible-to-return-lambdas-in-a-function

Strings can represent anonymous functions with the omegas and curly braces (those can be omitted and added in the code that uses the string) or they be written so as to allow the append of the argument on the right side. In the latter case, the omega can be appended to the right and the whole thing wrapped in curly braces to make it an anonymous function anyway.

Those string-expressed functions can be applied by formatting their argument into a string and concatenating into the expression to be executed. But another thing that is possible for the Execute function is to perform an assignment:

      ⍎'test←{⍵<0}'
      ⎕vr 'test'
    ∇ test←{
[1]   ⍵<0
[2]   }
    ∇ 2022 4 8 11 38 16 747 (UTC)

Hence if we prepare a string with the curly braces and the omega and prepend it with an assignment to some name (that can be a one localized to the function), we can have our lambda passed.

Of course the matter becomes more problematic once we would want to pass larger values that would need more computation to convert to string and back, that's where proper lambdas would be better.

It is said that the APL way is to use operators with derived functions, but they are almost like some predefined currying, I can't vary valence with them like my 'can' function does.

Questions

I am a bit unmotivated to get deeper into stuff in these posts. Any questions are more than welcome and will constitute an appendix to the post or a chapter to the next one. Please.

The writing of this post

The head hurt a bit, especially at the end. I am in a train that goes for over five hours, and I have so far spent in it three hours intermittently writing this on my laptop.

Writing the explanations of the code resulted in fixing quite several bugs in it, so also that.

What I have more

I already had (at the very beginning) started out with some things related more to the board (its representation, starting position...) but they are better to be withheld for these posts until more gets done in the area. Maybe it will be the next part that they will come to?