Python vs Modula-2

(6) Grundy 数

To pyvsmod7     To Workbench Python

<Grundy number>

Grundy 数, Gi,j, は次の 'Corner the Queen' ゲームなどで重要な役割を果たす数で, ゲーム盤のマス目に付属する量です。次のように定義すると

Grundy 数は次式で与えられます。
ここで mex(S) は集合Sから除外された数のうちの最小値 (minimum excluded) です。'move p' とは p を縦横斜めに可能な限りまっすぐに動かして得られる点の集合のことです。
<Task>

ここの課題は、与えられたゲーム盤のすべてのマス目について Gi,j を計算することです。 やり方は queen2 に出ています。まず左の式が Gi,j についての一次式であることを認識します。順番をうまく取ればスーッと答えが出ます。

Table of Grundy numbers (zeros are stressed)
CornerTheQueen.Py

CornerTheQueen.MOD

Source codes
<Python (Object Oriented Programming)>

[Main] Main_CornerTheQueen.py

# Corner The Queen, 8-25-2023
from scipy import *
import numpy as np
import matplotlib.pyplot as plt
import CornerTheQueen as CQ

print('\x1b[38;2;5;86;243m'+'Queen'+'\x1b[0m') #blue
print('\x1b[38;2;255;0;0m'+'Queen'+'\x1b[0m') #red
print()

isDn = False
pr = CQ.calcGrundy(9)
print(pr)
print()
pr.outGr(isDn)
[Module] CornerTheQueen.py
# Corner The Queen, 8-25-2023
# gives Grundy numbers for the game of "Corner The Queen"

import numpy as np
boardSize = 31
maxMember = 2*boardSize
toChk = False

def intToStr(i,n):
    s = str(i)
    le = len(s)
    while le<n:
        s = " " + s
        le += 1
    return s
         
def sumLists(a, b, c):
    p=[]
    for q in a:
        p.append(q)
    for q in b:
        p.append(q)
    for q in c:
        p.append(q)
    p.sort()
    return p
 
def mex(gList):
    wList = []
    for i in range(maxMember):
        wList.append(i)
       
    if toChk:
        print('wholeList=',wList)
        
    for p in gList:
        if wList.count(p)>0:
            wList.remove(p)
        else:
            if toChk:
                print(p," is not in list")
            
    if toChk:
        print('work list=',wList)
        
    k = 0
    while(True):
        if wList.count(k)>0:  # print('min=',k)
            break
        k += 1
        if k>maxMember:
            k=-1   # strange result!
            break
    return k

class calcGrundy:
    def __init__(self, N):
        
        self.Gr = np.zeros((boardSize,boardSize),dtype=int)
        self.N = N
        
        def moveH(x,y):
            mySet = []
            for xx in range(x):
                mySet.append(self.Gr[xx,y])
            return  mySet

        def moveV(x,y):
            mySet = []
            for yy in range(y):
                mySet.append(self.Gr[x,yy])
            return mySet
 
        def moveT(x,y):
            mySet = []
            tn = min(x,y)
            for tt in range(tn):
                mySet.append(self.Gr[x-tn+tt,y-tn+tt])
            return mySet
    
        for z in range(N):
            self.Gr[z,0] = z
            self.Gr[0,z] = z

######## Execution starts here ########        
        for n in range(N-1):
            nn = n + 1      # nn=1..N-1
            for x in range(nn):
                xSet 
                = sumLists(moveH(x,nn), moveV(x,nn), moveT(x,nn))
                self.Gr[x,nn] = mex(xSet)
            for y in range(nn):
                ySet 
                = sumLists(moveH(nn,y), moveV(nn,y), moveT(nn,y))
                self.Gr[nn,y] = mex(ySet)
            tSet 
            = sumLists(moveH(nn,nn), moveV(nn,nn), moveT(nn,nn))
            self.Gr[nn,nn] = mex(tSet)
####### Execution finished here ######## 
       
####### Output of Grundy values ########        
        print('Grundy values for board size = ', N)   
        for j in range(N):
            for i in range(N):
                print("  ",self.Gr[i,j],end="")
            print()
        print()    
######## End of Grundy value out ########        

######## Methods ##########
    def outGr(self,isDn):
        sR0 = '\x1b[38;2;255;0;0m'
        sR1 = '\x1b[0m'
        print('Grundy values for board size = ', self.N)   
        print('----------------------------------------')

        def printArray(j):
            print(intToStr(j,3),"] ", end="")
            for i in range(self.N):
                w = self.Gr[i,j]
                s = intToStr(w,3) 
                if w==0:
                    s = sR0 + s + sR1
                print(s,end="")
            print()
           
        if isDn:
            print('      ',end="")
            for i in range(self.N):
                s = intToStr(i,3)
                print(s,end="")
            print()
            print('----------------------------------------')
            for j in range(self.N):
                printArray(j)
        else:
            for j in range(self.N):
                jj=self.N - 1 - j
                printArray(jj)
            print('----------------------------------------')
            print('      ',end="")
            for i in range(self.N):
                s = intToStr(i,3)
                print(s,end="")
            print()
<Modula-2 (Structured Programming)>

[MAIN] CornerTheQueen.MOD

・・・
BEGIN
  openTextWin(W1,'Graphic Application');
  initXDSgraph(_big); 
  prepareWholeSet();
  WrStr(' Calculating Grundy values for maximum board size, ');
    WrInt(_boardSize,1); WrLn;
  initGrundy(Gr);
  calcGrundy(Gr);
  listOfZeros();
  ・・・
[SUB] SubQueenGame.MOD
PROCEDURE mex(Set: gSetTp): INTEGER;
  VAR
    i: INTEGER;
  BEGIN
    prepareWholeSet();
    minComplementSet(Set,i);
    RETURN i;
  END mex;

PROCEDURE moveH(x,y: INTEGER): gSetTp;
  VAR
    xx: INTEGER;
    mySet : gSetTp;
  BEGIN
    mySet := gSetTp{};
    FOR xx:=0 TO x-1 DO
      INCL(mySet, Gr[xx,y]);
    END;
    RETURN mySet;
  END moveH;

PROCEDURE moveV(x,y: INTEGER): gSetTp;
  VAR
    yy: INTEGER;
    mySet : gSetTp;
  BEGIN
    mySet := gSetTp{};
    FOR yy:=0 TO y-1 DO
      INCL(mySet, Gr[x,yy]);
    END;
    RETURN mySet;
  END moveV;

PROCEDURE moveT(x,y: INTEGER): gSetTp;
  VAR
    tt,tn : INTEGER;
    mySet : gSetTp;
  BEGIN
    mySet := gSetTp{};
    tn := min(x,y);
    FOR tt:= 0 TO tn-1 DO
      INCL(mySet, Gr[x-tn+tt,y-tn+tt]);
    END;
    RETURN mySet;
  END moveT;

PROCEDURE calcGrundy(VAR Gr: grundyTp);
  VAR
    x,y,z,n,xx,yy,N: INTEGER;
    mySet : gSetTp;
  BEGIN
    N := _boardSize;
    Gr[0,0]:= 0;
    numA:= 0; numB:= 0;

    FOR n:=1 TO N-1 DO  (* circumference*)
      FOR x:=0 TO n-1 DO
	    mySet := moveH(x,n) + moveV(x,n) + moveT(x,n);
	    Gr[x,n]:= mex(mySet);
	    IF Gr[x,n]=0 THEN
	      IF x<n THEN
	        zeroA[numA].x:= x; zeroA[numA].y:= n; INC(numA);
	      ELSE
	        zeroB[numB].x:= x; zeroB[numB].y:= n; INC(numB);
	    END;
	  END;
    END;

    FOR y:=0 TO n-1 DO
 	  mySet := moveH(n,y) + moveV(n,y) + moveT(n,y);
	  Gr[n,y]:= mex(mySet);
	  IF Gr[n,y]=0 THEN
	    IF n<y THEN
	      zeroA[numA].x:= n; zeroA[numA].y:= y; INC(numA);
	    ELSE
	      zeroB[numB].x:= n; zeroB[numB].y:= y; INC(numB);
	    END;
	  END;
      mySet := moveH(n,n) + moveV(n,n) + moveT(n,n);	
      Gr[n,n]:=mex(mySet);	
      IF Gr[n,n]=0 THEN
	    WinAuxIO.hitKey('Zero in diagonal posision, OK?');
      END;
    END;
  END calcGrundy;

9-21, 4-6-2023, S. Hayashi