pythonpython-3.xquickdrawreversi

python3 reversi game code


I have a 2 files for the reversi game for python 3. My first file is called reversi.py. It implements (a) the high level control of the game play (e.g., getting moves from the computer player and the human player, determining whether a player runs out of moves, determining the end of the game and declare the result of the game), (b) the rendering of the game through QuickDraw, as well as (c) taking the input from the human player.

Heres the file:

from game import *
from copy import deepcopy
from time import sleep
import sys
from subprocess import PIPE,Popen
from threading import Thread
from queue import Queue,Empty



def ParseEvent() :
    r=receive()
    if r==None:return None,None
    s=[x.rstrip().strip() for x in r.split(":")]
    return s[0],s[1]


def highlight(move):
    send( 'color 255 0 0\n circle %d %d 11\n' % \
        (240+(move[0]+0.5)*40, 100+(move[1]+0.5)*40))


def stone(x,y,turn):
    send('color %d %d %d\n' % \
        (255*(1-turn),255*(1-turn),255*(1-turn)))
    send('fillcircle %d %d %d\n' % \
        (240+(x+0.5)*40, 100+(y+0.5)*40, 17))


def draw(board):
    send('color 20 20 20\n clear\n color 15 15 100\n')
    send( 'button button1 600 20 100 20 "Calculate W:L"')
    send('fillrect %d %d %d %d\n' % (240,100,320,320))
    send('color 0 70 0\n') 
    for i in range(1,8):
        send('line %d %d %d %d\n' %\
            (240+i*40,100, 240+i*40,420))
        send('line %d %d %d %d\n' %\
            (240, 100+i*40, 560, 100+i*40))
    for i in range(8):
        for j in range(8):
            if board[i][j] >= 0:
                stone(i, j, board[i][j])

def makeBoard():
    board = []
    for i in range(8):
        board.append([])
        for j in range(8):
            board[i].append(-1)  
    board[3][3], board[4][4], board[3][4], board[4][3] = 0,0,1,1
    return board



def _queueqd(ip,q):
    for line in iter(ip.readline,b''):
        q.put(line)
    ip.close()

def send(*args):
    s=""
    for a in args:
        s+=str(a)
        if s[-1]!=" ":s+=" "
    if s!="":
        op.write((s[:-1]+"\n").encode('utf-8'))
    else:
        op.write("\n".encode('utf-8'))

def receive():
    try:
        return qdq.get_nowait().decode('utf-8')[:-1]
    except Empty:
        return None
def running():
    return qdp.poll()==None


def simulate(numTimes=1000):
    w=0
    b=0
    for i in range(numTimes):
        goesFirst=i&1
        gb=makeBoard()
        sg = Game(gb, goesFirst)
        count = 4
        while count < 64: # max num of stones is 64
            sg.get_moves()
            if sg.moves == []: 
                sg.turn = 1 - sg.turn
                sg.get_moves()
                if sg.moves!= []:
                    continue 
                sg.turn = 1 - sg.turn
                break 
            if sg.turn==0:
                mv = sg.select_move()
            else:
                mv= sg.moves[int(len(sg.moves)*random.random())]
            sg.play_move(mv)
            sg.turn=1-sg.turn
            count+=1
        num_white, num_black = sg.final_result()
        if num_white>num_black:w+=1
        if num_black>num_white:b+=1
    return w/b

try:
    qdp = Popen("java -jar quickdraw.jar",shell=True,stdout=PIPE,stdin=PIPE)
    op=qdp.stdin
    qdq = Queue()
    qdt = Thread(target=_queueqd,args=(qdp.stdout,qdq))
    qdt.daemon=True
    qdt.start()
except:
    print("quickdraw.jar must be in the same directory as this python script.")
    quit()

send( "mouseclick True\n" )
# initialize the board, get ready for the game 

board =makeBoard()
draw(board) 


# game time, let's play...
# human player is white and moves first

g = Game(board, 0)
count = 4
while count < 64: # max num of stones is 64
    g.get_moves()
    if g.moves == []: 
        g.turn = 1 - g.turn
        g.get_moves()
        if  g.moves!= []:
            continue 
        g.turn = 1 - g.turn
        break 
    if g.turn == 0:
        while running():
            event, val = ParseEvent()
            if event == "MouseClicked":
                val=[ int(x) for x in val.split(",")]
                move = [(val[0]-240)//40, (val[1]-100)//40]
                if move in g.moves: break
            elif event=="ButtonClicked":
                send("colour 20 20 20\nfillrect 600 50 200 50\ncolour 255 255 255")
                send('text "Calculating..." 600 80')
                r=simulate()
                send("colour 20 20 20\nfillrect 600 50 200 50\ncolour 255 255 255")
                send("text ",'"W:L=',r,'"'," 600 80")
    else:
        move = g.select_move()
    stone(move[0], move[1], g.turn)
    highlight(move)
    sleep(1)
    board = deepcopy(g.board)
    g.play_move(move)
    g.turn=1-g.turn
    for i in range(8):
        for j in range(8):
            if board[i][j]!= g.board[i][j]:
                stone(i,j,1-g.turn)
    sleep(1)
    stone(move[0], move[1], 1-g.turn) # redraw to de-highlight
    count += 1


# game over, let's announce the result:

num_white, num_black = g.final_result()
send('color 255 255 255\n')
if num_black > num_white:
    send('text "black wins!" 370 470\n')
elif num_black < num_white:
    send('text "white wins!" 370 470\n')
else:
    send('text "tie!" 390 470\n')
send('text "black:" 325 500\n')
send('text "%d" 375 500\n' % num_black)
send('text "white:" 425 500\n')
send('text "%d" 475 500\n' % num_white)

while running():
    pass

And this file is done. My second file is called Game.py. The first file imports this one for the moves the player can make.

import random

class Game:
    delta = [[0,-1],[1,-1],[1,0],[1,1],[0,1],\
                [-1,1],[-1,0],[-1,-1]]

    def __init__(self, board, turn):
        self.board = board
        self.turn = turn
        self.moves = self.get_moves()

    def check_move(self, move, d):
        pass #which I have to figure out!

    def get_moves(self):
        pass ##################

    def select_move(self):
        pass ##################

    def play_move(self, move): 
        m,n=move[0],move[1]
        for d in range(8):
            i, j = m, n
            if self.check_move(move,d):
                i, j = i+self.delta[d][0], j+self.delta[d][1]
                while self.board[i][j] == 1-self.turn:
                    self.board[i][j] = self.turn
                    i, j = i+self.delta[d][0], j+self.delta[d][1]
        self.board[m][n]=self.turn

    def final_result(self): 
        black, white = 0, 0   # color 0 is white
        for i in range(8):
            for j in range(8):
                if self.board[i][j] == 0: white += 1
                elif self.board[i][j] == 1: black += 1
        return white,black

I am supposed to fill in the three missing definitions. The check move(self, move, d) method checks whether a given move is validated along a certain direction d, assuming the given turn of play. It returns True or False accordingly. In other words, if starting at the grid specified by move, a number of opponent stones along the direction d can be captured, then a True should be returned. This method may be convenient to use in other methods such as get moves() and play move().The get moves(self) method finds all the valid moves for the current turn, and returns them in a list. For example, if there are two moves at [2,4] and [3,6], then the return value should be [[2,4], [3,6]]. The select move(self) method picks a move from self.move and returns it. Apparently the easiest way is to return a random move. SO basically I know what each of the three functions should do and why they are needed for the game, but I don't know exactly how to code them... any help or guidance would be appreciated!!

so basically I have this is a base for "check_move" function:

def check_move(self,move,d):
    check neighboring tile:
        if empty:
            False #can't move
        if enemy present:
            while there are enemy pieces:
                True #can move

is that the right direction?

ALso for the get_moves I have this reasoning:

def get_moves(self): # grid[ij]
    for i in range 
    for j in range
        check for possible move directions:
            check move again:
                if yes then append[ij] to a list of valid moves
        return list of valid moves

Solution

  • I think you should first, before you start writing code, think about what defines a valid move in Reversi. Try writing in English what the definition is. For instance, here's the conditions I can think of in a few seconds:

    1. The tested tile must be empty
    2. The tested tile must have an enemy piece next to it in the selected direction.
    3. Past that enemy piece in the given direction, there may be more enemy pieces.
    4. Past all the enemy pieces, there must be a friendly piece (not an empty space or the edge of the board).

    I'm not sure if that's comprehensive or not, but it's a reasonable start. Then, think about how to test each of those. Some may easily flow from each other, while others may be separate tests.

    I'd also not be shy about reading and adapting the existing code you've been given. For instance, the play_move method just below the method's you're going to implement does a very similar loop to what you're going to want in your check_move method (though it's flipping the tiles, rather than checking that they can be flipped).