<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.
|
<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. |
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 [Module] CornerTheQueenMove.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 ....... # 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 [SUB] SubQueenGameK0.MOD・・・ BEGIN openTextWin(W1,'Graphic Application'); ・・・LOOP・・・ playGame(boardSize); ・・・・・・ (*/************************ 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; |