Python vs Modula-2

(7) Computer Game of 'Corner the Queen'

To pyvsmod6     To Workbench Python

<Game of 'Corner the Queen'>

This game is played by two opponents, who move the figure of Queen toward the corner, or goal, in one of three possible directions and in any straight steps(see right). The opponent wins if he or she has made the Queen arrive at the goal.

More detailed explanations are given in queen1 and queen3 (both in Japanese).

<Task>

Suppose that you participate in the game, but the opponent is replaced by a computer. The task is to make the program that displays the game board on the screen.
For Modula-2 a text window is invoked.

<Strategy>

We will follow the strategy of a teaching assistant at UC Davis Mr. Kreylos (1999). He exploited a function for quickly locating zero positions of Grundy numbers, implemented in Python and Modula-2 by calcWinningPos() and calculateWinningPosition(), respectively.

Flow Chart
Python

Modules pygame and pygwidgets make it possible to use the mouse as input means. (See, for details, I. Kalb, Object-Oriented Python (No Starch Press, 2022))

Modula-2

Text window is opened allowing for text menus. A game starts with the key input of 'G'.

Screen Shot
Python

is Queen and is the goal.
(Left below) Stand-by state before starting. (Right below) PC has won. Clicking 'Restart' leads to stand-by state.

Modula-2

o is Queen. (Below) PC has placed Queen at [6,4], waiting for User to input next position.

Structure of Program
<Python>

[Main] Main_CornerTheQueen.py

# Corner The Queen, 9-06-2023
    ... LOOP ...
    ... PC's move ...
    if (state == STATE_QUEEN_1):
        if isValid:
            iprev = i
            jprev = j
            i, j = calcPCmove(i, j)
            x = coordForCell(i, x0, space)  
            y = coordForCell(N-1-j, y0, space)         

            isPCgoal = (i==0) and (j==0)
            if isPCgoal:
                newText = 'PC has won!'
                oTextField.setValue(newText)
                chordSound.play()

        else:
            i = iprev
            j = jprev
            x = coordForCell(i, x0, space)  
            y = coordForCell(N-1-j, y0, space)         
            badSound.play()
        state = STATE_QUEEN_2
    .......
[Module] CornerTheQueenMove.py
# CornerTheQueenMove
# Based on Oliver Kreylos's program (1999)
from numpy import sqrt

# /************************
# Check if a move is legal:
# ************************/*)
def isMoveLegal(oldi, oldj, newi, newj):
	if (oldj == newj):    # Horizontal move 
		legal = (newi>=0) and (newi<oldi)
	else:
		if (oldi == newi): # Vertical move 
			legal = (newj>=0) and (newj<oldj)
		elif (newi-oldi) == (newj-oldj): # Diagonal move
			legal = (newi>=0) and (newj>=0) 
            and (newi<oldi)
		else: #  Some other move 
			legal= False
	return legal

#/*************************************************
# Mirror a _Position about the board's main diagonal:
#*************************************************/*)
def mirrorPos(i, j):
    ii = j
    jj = i
    return ii,jj

#*/*************************************************
# Calculate the winning _Position with a given index:
#*************************************************
def calcWinningPos(index):
	rindex = index // 2
	goldenRatio = 0.5*(sqrt(5.) + 1.)
	winj = int(goldenRatio*float(rindex))
	wini = winj + rindex
	if (index % 2 == 0):
		wini,winj = mirrorPos(wini, winj)
	return wini,winj

#/*******************************
# Test if two _Positions are equal:
#*******************************/*)
def arePosEqual(i1,j1,i2,j2):
    return (i1 == i2) and (j1 == j2)

#/*********************************
# Calculate a very intelligent move:
#*********************************/*)

def calcPCmove(i, j):
	isFound = False
	index = 0
	while (not isFound):
		newi,newj = calcWinningPos(index)
		if arePosEqual(i, j, newi,newj):
			if newi>=newj:
				newi -= 1
			else:
				newj -= 1
			isFound = True
		else:
			if isMoveLegal(i,j,newi,newj):
				isFound = True
		index += 1
  
	return newi,newj

<Modula-2>

[MAIN] CornerTheQueen.MOD

・・・
BEGIN
  openTextWin(W1,'Graphic Application');
  ・・・LOOP・・・
     playGame(boardSize);
  ・・・・・・
[SUB] SubQueenGameK0.MOD
(*/************************
Check if a move is legal:
************************/*)
PROCEDURE isMoveLegal(old, new: _Position): BOOLEAN;
  VAR
    legal: BOOLEAN;
  BEGIN	
    IF (old.y = new.y) THEN   
      legal:= (new.x>=0) AND (new.x<old.x);
    ELSE
      IF (old.x = new.x) THEN 
	     legal:= (new.y>=0) AND (new.y<old.y);
      ELSIF (new.x-old.x) = (new.y-old.y) THEN 
	     legal:= (new.x>=0) 
		      AND (new.y>=0) AND (new.x<old.x);
      ELSE 
	     legal:= FALSE;
      END;
    END;
    RETURN legal;
  END isMoveLegal;

(*/*************************************************
Mirror a _Position about the board's main diagonal:
*************************************************/*)
PROCEDURE mirrorPosition(current: _Position): _Position;
  VAR
    new: _Position;
  BEGIN	
    new.x:= current.y;
    new.y:= current.x;
    RETURN new;
  END mirrorPosition;

(*/*************************************************
Calculate the winning _Position with a given index:
*************************************************/*)

PROCEDURE calculateWinningPosition
               (index: INTEGER): _Position;
  VAR
    realIndex  : INTEGER;
    goldenRatio: LONGREAL;
    winner     : _Position;
  BEGIN
	realIndex := index DIV 2;
	goldenRatio := 0.5*(sqrt(5.0)+1.0);
	winner.y := INT(goldenRatio*LFLOAT(realIndex));
	winner.x := winner.y + realIndex;
	IF (index MOD 2 = 0) THEN
	  winner:= mirrorPosition(winner);
	END;
	RETURN winner;
  END calculateWinningPosition;

(*/*******************************
Test if two _Positions are equal:
*******************************/*)
PROCEDURE arePositionsEqual
                (pos1, pos2: _Position): BOOLEAN;
  BEGIN
    RETURN (pos1.x = pos2.x)AND(pos1.y = pos2.y);
  END arePositionsEqual;

(*/*********************************
Calculate a very intelligent move:
*********************************/*)
PROCEDURE calculateComputerMove
                 (current: _Position ): _Position;
  VAR
    new:           _Position;
    index:         INTEGER;
    _PositionFound: BOOLEAN;
  BEGIN
    _PositionFound:= FALSE;
    index:= 0;
    WHILE (NOT _PositionFound) DO
       new:= calculateWinningPosition(index);
       IF arePositionsEqual(current,new) THEN
     	 IF (new.x>=new.y) THEN DEC(new.x);
	     ELSE DEC(new.y);
	     END;
	     _PositionFound:= TRUE;
       ELSE
         IF isMoveLegal(current,new) THEN
	       _PositionFound:=TRUE;
 	     END;
       END;
       INC(index);
     END;
     RETURN new;
  END calculateComputerMove;
 
(*/********************************************
Play the game on a board of the current size:
********************************************/*) 
  PROCEDURE playGame(boardSize: INTEGER);
  VAR
    current: _Position; 
    winningPlayer: INTEGER; 
  BEGIN	
    current:= getInitialPosition(boardSize);
    winningPlayer:= 1;
    WHILE(NOT isLowerLeftCorner(current)) DO
      current:= calculateComputerMove(current);
      Display(current); 
      WrStr(" I (computer) moved the queen to Position (");
      WrInt(current.x,1); WrStr(','); 
	  WrInt(current.y,1); WrStr(')'); WrLn;
	  WrLn;
	  Lib.Delay(1000);
      IF (isLowerLeftCorner(current)) THEN
	    winningPlayer:= 2;
      ELSE
	    WrStr(' Your turn!');
        current:= getUserMove(current);
	    Display(current); Lib.Delay(1500);
      END;
    END;
     WrLn;
    IF (winningPlayer=1) THEN
		WrStr(" #################"); WrLn;
		WrStr(" # You have won! #"); WrLn;
		WrStr(" #################"); WrLn;
    ELSE
		WrStr(" ***************"); WrLn;
		WrStr(" * I have won! *"); WrLn;
		WrStr(" ***************"); WrLn;
    END;
    WrLn;
   END playGame;

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