< 'Corner the Queen' というゲーム
二人でゲーム盤を前にして競うゲームです。Corner は動詞なので Queen をコーナーに向けて交互にお運びするという意味で、実際にコーナーに置けたら勝ちというゲームです。 |
|
<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 で ■ がゴールです。 |
Modula-2
o が Queen です。 (下) パソコン側がQueenを [6,4]に起きました。こちらが入力するのを待っています。 |
|
|
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; |