-2

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
Charles
  • 50,943
  • 13
  • 104
  • 142
Romulus
  • 138
  • 2
  • 12
  • 2
    We're not here to do your homework. Sounds like you don't know how things usually work around here. [What have you tried?](http://mattgemmell.com/2008/12/08/what-have-you-tried/). – martineau Dec 03 '12 at 02:31
  • I know that. Is that what I'm asking ? no. But I'll post what I have so far. – Romulus Dec 03 '12 at 02:34
  • 1
    Sorry, but is sounds like you want to know exactly how to code them and looks like you haven't tried anything. – martineau Dec 03 '12 at 02:39
  • the actual code I can figure out. I just need help whether I am heading in the right direction or not, not the straight up code ! – Romulus Dec 03 '12 at 02:45
  • 1
    OK, here's a little help: `check_move()` has to determine if a neighboring tile _in the specified direction_ is occupied by an opponents piece and, if it is, then check whether the next spot after that _in the same direction_ is either empty or similarly occupied by the opponent. This continues until either an empty spot is found or the edge of the board is reached. A tricky part is watching out for the board's boundaries. A good way to work on this problem would be to write out a description of what each function has to do sort of like I just did. – martineau Dec 03 '12 at 03:37
  • oh so it has to keep checking for enemy pieces until it reaches the grid(board) boundaries, in each of the 8 possible directions. – Romulus Dec 03 '12 at 03:47
  • It has to be able to do that in any of the possible directions, but it's only needs to actually check the one direction specified to it via its 'd' argument. Also, since the very definition of a possible move is recursive, you may be able to write code that figures it out the same way {hint}. – martineau Dec 03 '12 at 04:00
  • 1
    ohhh ok yea that makes sense. So basically just make the check_move recursive to check the directions. – Romulus Dec 03 '12 at 04:11
  • 9
    You are breaching the code of conduct signed by you prior to this semester. If caught, expect me to testify against you at the fullest extent at your hearing. Professor Zongpeng Li –  Dec 06 '12 at 19:32

1 Answers1

2

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).

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • yea thats what I started to do! oh ok I'll look into that. Also for your 4. point what do you mean by that? Do you mean if once checked and is a valid move then check to make sure no friendly piece? – Romulus Dec 03 '12 at 03:49
  • No, I mean that in Reversi you need your move to be "surrounding" a line of enemy pieces. That is, your new piece is on one end of the line and an existing one is at the other end. If the other end is empty (or is at the edge of the board) your move doesn't flip any pieces in that direction and so may not be valid. – Blckknght Dec 03 '12 at 04:13
  • oh I get it. Thanks makes sense now. Yea I'll keep writing up just the basic english version of my intended code then maybe I can get the real thing faster. – Romulus Dec 03 '12 at 04:24
  • @Romulus: Get it to work first, see if it needs to be faster, and then profile it to find out what's slowing it down. Sometimes you can tell that without profiling since you know what it's doing when internally. – martineau Dec 03 '12 at 04:28
  • yea good idea thanks! I'll do that. – Romulus Dec 03 '12 at 04:38