Python vs Modula-2

(7) 'Corner the Queen' というゲーム

To pyvsmod6     To Workbench Python

< 'Corner the Queen' というゲーム

二人でゲーム盤を前にして競うゲームです。Corner は動詞なので Queen をコーナーに向けて交互にお運びするという意味で、実際にコーナーに置けたら勝ちというゲームです。

既に queen1queen2queen3 と内容はカブリますが、こちらはプログラミングに力点を置いています。

<Task>

ここでの課題は相手をパソコンに交替させることで、ゲーム盤はスクリーンに現れます。Modula-2の場合、テキストウィンドウが開きます。

<Strategy>

作戦としては UC Davis校のTAさん Mr. Kreylos (1999). が作ってくれたプログラムを書き換えます。Grundy数がゼロの場所をみつける関数calcWinningPos() とcalculateWinningPosition()をそれぞれPythonとModula-2で書いています。

Flow Chart
Python

pygame と pygwidgets によってマウスが使えるようになりました。(I. Kalb, Object-Oriented Python (No Starch Press, 2022)を参考にしました)

Modula-2

テキストウィンドウの中でテキストメニューが使えます。キーボードから'G'を入力すればゲームが始まります。

Screen Shot
Python

が Queen で がゴールです。
(左下) スタート前のスタンバイ。 (下右) PC の勝ち。 'Restart' をクリックすればスタンバイになります。

Modula-2

o が Queen です。 (下) パソコン側がQueenを [6,4]に起きました。こちらが入力するのを待っています。

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