Answer To: Hi I need some help with code for a python checkers game. The changes that need to be made to the...
Pritam answered on Mar 30 2021
from graphics import *
import sys
#import tkMessageBox
import tkinter.messagebox as tkMessageBox
#+-added to be able to select a random number from a range of numbers
from random import randrange
'''
The revisions in the code are labelled by the prefix '#+-'
'''
class Checkers:
def __init__(s):
s.state = 'CustomSetup'
s.is1P = False
#+-added string
s.compIsColour = 'not playing' #computer not playing by default
s.placeColour = 'White'
s.placeRank = 'Pawn'
s.placeType = 'Place' #Place or Delete (piece)
s.pTurn = 'White' #White or Black (turn)
s.selectedTile = ''
s.selectedTileAt = []
s.hasMoved = False
s.pieceCaptured = False
s.BoardDimension = 8
s.numPiecesAllowed = 12
s.win = GraphWin('Checkers',600,600) #draws screen
s.win.setBackground('White')
s.win.setCoords(-1,-3,11,9) #creates a coordinate system for the window
s.ClearBoard()
s.tiles = [[Tile(s.win,i,j,False) for i in range(s.BoardDimension)] for j in range(s.BoardDimension)] #creates the 2D list and initializes all 8x8 entries to an empty tile
#+-added two lists
s.moves = []
s.badMoves = []
gridLetters = ['A','B','C','D','E','F','G','H',]
for i in range(s.BoardDimension):
Text(Point(-0.5,i+0.5),i+1).draw(s.win) #left and right numbers for grid
Text(Point(8.5,i+0.5),i+1).draw(s.win)
Text(Point(i+0.5,-0.5),gridLetters[i]).draw(s.win) #bottom and top letters
Text(Point(i+0.5,8.5),gridLetters[i]).draw(s.win)
s.SetButtons()
s.SetupBoard()
####
#handles the setup of the board (i.e. piece placement)
####
def SetupBoard(s):
while s.state == 'CustomSetup':
s.Click()
if s.state == 'Play':
s.Play()
####
#handles the general play of the game
####
def Play(s):
while s.state == 'Play':
#+-added if statement
if s.is1P and s.compIsColour == s.pTurn:
s.CompTurn()
else:
s.Click()
if s.state == 'CustomSetup':
s.SetupBoard()
####
#+-added to be able to control the computer's turn
####
def CompTurn(s):
s.moves = s.movesAvailable()
s.badMoves = []
#######consider making s.goodMoves which replaces s.badMoves for many uses (changes conditions a bit as well)
#To prevent leaving the back row (currently top priority)
for move in s.moves:
if s.movesFromBack(move):
s.badMoves.append(move)
s.removeBadMoves()
#Promotes move which make kings
for move in s.moves:
if not (((s.moveEndsAt(move)[1]==7 and s.tiles[move[0]][move[1]].isWhite) or \
(s.moveEndsAt(move)[1]==0 and s.tiles[move[0]][move[1]].isBlack)) and \
s.tiles[move[0]][move[1]].isPawn):
s.badMoves.append(move)
s.removeBadMoves()
#Promotes moves which take kings for free
for move in s.moves:
if not ((s.tiles[move[2]][move[3]].isKing) and s.isMoveSafe(move)):
s.badMoves.append(move)
s.removeBadMoves()
#This may not always work, it is not expected/designed to (hopefully it usually works)
#Promotes moves which trade your pawn for opponent king
for move in s.moves:
if not ((s.tiles[move[0]][move[1]].isPawn) and (s.tiles[move[2]][move[3]].isKing) and not s.isMoveSafe(move)):
s.badMoves.append(move)
s.removeBadMoves()
#Promotes moves which trade kings when ahead in total value of pieces
for move in s.moves:
if not ((s.tiles[move[0]][move[1]].isKing) and (s.tiles[move[2]][move[3]].isKing) and not s.isMoveSafe(move) and s.diffValue() >= 2):
s.badMoves.append(move)
s.removeBadMoves()
#Promotes moves which trade pawns when ahead in total value of pieces
for move in s.moves:
if not ((s.tiles[move[0]][move[1]].isPawn) and (s.tiles[move[2]][move[3]].isPawn) and not s.isMoveSafe(move) and s.diffValue() >= 2):
s.badMoves.append(move)
s.removeBadMoves()
#This may not always work, it is not expected/designed to (hopefully it usually works)
#It is meant to promote the use of moves that allow capture of another piece afterwards
for move in s.moves:
if not s.PieceCanCapture(s.moveEndsAt(move)[0],s.moveEndsAt(move)[1]):
s.badMoves.append(move)
s.removeBadMoves()
#Promotes moves which does not endanger itself needlessly
for move in s.moves:
if not s.isMoveSafe(move):
s.badMoves.append(move)
s.removeBadMoves()
#To prevent leaving the side columns (eliminates these moves from the random choice that follows)
for move in s.moves:
if s.movesFromSide(move):
s.badMoves.append(move)
s.removeBadMoves()
#should implement the next part to pick at random from the remaining moves (does not do this)
#performs the select and move action for the computer
m = randrange(0,len(s.moves))
if s.selectedTileAt == []:
s.Action(s.moves[m][0],s.moves[m][1])
s.Action(s.moves[m][2],s.moves[m][3])
####
#+-added to determine whether the player whose turn it is has more pieces than opponent
####
def hasMorePieces(s):
return s.numColour(s.pTurn) > s.numColour(s.opposite(s.pTurn))
def diffValuePieces(s):
diff = s.pieceValue(s.pTurn) - s.pieceValue(s.opposite(s.pTurn))
return diff
#+-added new method
#Returns true if a GIVEN piece cannot be taken immediately after ITS proposed move
#########Might want another method to know if a move exposes another piece
#Likely to not work (depends on how PieceCanCapturePiece method works)
def isMoveSafe(s,move):
X1,Y1 = [s.moveEndsAt(move)[0]-1,s.moveEndsAt(move)[0]+1],[s.moveEndsAt(move)[1]-1,s.moveEndsAt(move)[1]+1]
for i in range(2):
for j in range(2):
if s.SpecialPCCP(s.tiles[move[0]][move[1]].pieceColour,X1[i],Y1[j],s.moveEndsAt(move)[0],s.moveEndsAt(move)[1],move[0],move[1]):
return False
return True
#+-added new method
#modification to PieceCanCapturePiece such that it works with the isMoveSafe method
def SpecialPCCP(s,piece2Colour,x,y,X,Y,initX,initY):
X1,X2,Y1,Y2 = [x-1,x+1],[x-2,x+2],[y-1,y+1],[y-2,y+2]
if ((0<=X<8) and (0<=Y<8)) and ((0<=x<8) and (0<=y<8)):
if (piece2Colour == s.opposite(s.tiles[x][y].pieceColour)):
if s.CanDoWalk(x,y,X,Y,exception=False):
for i in range(2):
for j in range(2):
if X1[i]==X and Y1[j]==Y:
if (0<=X2[i]<8) and (0<=Y2[j]<8):
if not (s.tiles[X2[i]][Y2[j]].isPiece) or (X2[i]==initX and Y2[j]==initY):
return True
return False
#+-added new method
#Removes badMoves from the list of moves that can be made
#if all of the moves that can be made are badMoves, we still need to do something so it leaves all of them
#Note: at any time this is run, the moves that are considered bad should be considered bad for the same reason (i.e. they are equivalently bad)
def removeBadMoves(s): #assumes moves were added to badMoves in the same order as they appear in moves
if s.moves != s.badMoves:
for move in s.badMoves:
s.moves.remove(move)
s.badMoves = []
#+-added new method
def movesFromSide(s,move):
if (move[0]==0 or move[0]==7):
return True
else:
return False
#+-added new method
def movesFromBack(s,move):
if (move[1]==0 and s.compIsColour=='White') or \
(move[1]==7 and s.compIsColour=='Black'):
return True
else:
return False
#+-added new method
def moveEndsAt(s,move): #takes 4 element array representing a move
if s.tiles[move[2]][move[3]].isPiece:
return [move[0]+(move[2]-move[0])*2,move[1]+(move[3]-move[1])*2]
else:
return [move[2],move[3]]
#+-added to calculate all the available valid moves
def movesAvailable(s):
moves=[]
for j in range(8):
for i in range(8):
X1,Y1 = [i-1,i+1],[j-1,j+1]
for a in range(2):
for b in range(2):
if 0<=X1[a]<8 and 0<=Y1[b]<8:
if s.moveIsValid(i,j,X1[a],Y1[b]):
moves.append([i,j,X1[a],Y1[b]])
return moves
#Resets the board to be empty
def ClearBoard(s):
s.tiles=[[Tile(s.win,i,j,False) for i in range(s.BoardDimension)] for j in range(s.BoardDimension)] #creates the 2D list and initializes all 8x8 entries to an empty tile
for i in range(s.BoardDimension):
for j in range(s.BoardDimension):
s.ColourButton(s.TileColour(i,j),i,j)
s.state = 'CustomSetup'
s.pTurn = 'White'
s.SetButtons()
def ColourButton(s,colour,X,Y,width=1,height=1): #function to create a rectangle with a given colour, size, and location
rect = Rectangle(Point(X,Y),Point(X+width,Y+height))
rect.setFill(colour)
rect.draw(s.win)
def TileColour(s,x,y):
if (x%2 == 0 and y%2 == 0) or (x%2 == 1 and y%2 == 1):
return 'Red' #sets every other square to red
else:
return 'White' #every non red square to white
##########
#Draws Buttons
##########
def SetButtons(s):
s.ColourButton('White',-1,-3,12,2)
s.ColourButton('White',9,-1,2,10)
if s.state == 'CustomSetup':
s.DrawStandard()
s.DrawStart()
s.DrawClear()
s.Draw1P()
s.Draw2P()
s.DrawLoad()
s.DrawSave()
s.DrawTurn()
s.DrawX()
s.DrawW()
s.DrawB()
s.DrawK()
s.DrawDel()
s.DrawScore() #not actually a button
elif s.state == 'Play':
#+-updated method name
s.DrawResign()
s.DrawSave()
s.DrawTurn()
s.DrawX()
s.DrawScore() #not actually a button
def DrawStandard(s):
s.ColourButton('White',-1,-2,2,1) #Standard Setup button
...