import
wx
import
os
from
functools
import
partial
import
random
from
math
import
inf
from
concurrent
import
futures
thread_pool_executor
=
futures.ThreadPoolExecutor(max_workers
=
1
)
gameRules
=
applicationHelp
=
class
OthelloGameFrame(wx.Frame):
ID_NEW_SINGLE_PLAYER
=
wx.NewIdRef(
1
)
ID_SAVE_AND_QUIT
=
wx.NewIdRef(
1
)
ID_GAMERULES
=
wx.NewIdRef(
1
)
ID_QUIT_WITHOUT_SAVING
=
wx.NewIdRef(
1
)
PLAYER1BUTT
=
wx.NewIdRef(
1
)
PLAYER2BUTT
=
wx.NewIdRef(
1
)
bgAndBoardColor
=
wx.Colour(
0x41
,
0xA1
,
0x23
)
player1Color
=
wx.Colour(
0x00
,
0x00
,
0x00
)
player2Color
=
wx.Colour(
0xFF
,
0xFF
,
0xFF
)
def
__init__ (
self
,
*
args,
*
*
kwArgs):
wx.Frame.__init__(
self
,
*
args,
*
*
kwArgs)
self
.saveFile
=
""
self
.firstPlayersMove
=
True
self
.gameAltered
=
False
self
.robot
=
False
self
.SetBackgroundColour(wx.Colour(
0x30
,
0x30
,
0x30
))
self
.CreateStatusBar()
self
.optionsMenu
=
wx.Menu()
menuNewGame
=
self
.optionsMenu.Append(wx.ID_NEW,
"&New Game"
,
"Start a new game."
)
menuNewSinglePlayer
=
self
.optionsMenu.Append(
self
.ID_NEW_SINGLE_PLAYER,
"New Single &Player Game"
,
"Start a game against the AI. This feature is currently unavailable."
)
self
.optionsMenu.AppendSeparator()
menuSaveGame
=
self
.optionsMenu.Append(wx.ID_SAVE,
"&Save Game"
,
"Save the current game and remember the filename for future saves."
)
menuSaveAs
=
self
.optionsMenu.Append(wx.ID_SAVEAS,
"Save Game &As..."
,
"Save a previously save game under a new name."
)
menuOpenGame
=
self
.optionsMenu.Append(wx.ID_OPEN,
"&Open Game"
,
"Open a previously saved game."
)
menuSaveAndQuit
=
self
.optionsMenu.Append(
self
.ID_SAVE_AND_QUIT,
"Sa&ve and Quit"
,
"Save the game and quit."
)
menuQuitWithoutSaving
=
self
.optionsMenu.Append(
self
.ID_QUIT_WITHOUT_SAVING,
"Quit &Without Saving"
,
"Close the application without saving the game."
)
self
.helpMenu
=
wx.Menu()
menuShowGamerules
=
self
.helpMenu.Append(
self
.ID_GAMERULES,
"Game&rules"
,
"Explains Othello game rules."
)
menuShowAppHelp
=
self
.helpMenu.Append(wx.ID_HELP,
"Application &Help"
,
"How to use this software"
)
self
.toolbar
=
wx.MenuBar()
self
.toolbar.Append(
self
.optionsMenu,
"&Options"
)
self
.toolbar.Append(
self
.helpMenu,
"&Help"
)
self
.SetMenuBar(
self
.toolbar)
player1ButtonFont
=
wx.Font(
30
, wx.ROMAN, wx.NORMAL, wx.NORMAL)
player2ButtonFont
=
wx.Font(
30
, wx.ROMAN, wx.NORMAL, wx.NORMAL)
gameboardButtonFont
=
wx.Font(
12
, wx.ROMAN, wx.NORMAL, wx.NORMAL)
buttonGrid
=
wx.GridSizer(
8
,
8
,
0
,
0
)
buttonGrid.SetHGap(
2
)
buttonGrid.SetVGap(
2
)
self
.gameboard
=
[]
for
row
in
range
(
8
):
board_columns
=
[]
for
col
in
range
(
8
):
btn
=
wx.Button(
self
, style
=
wx.NO_BORDER)
btn.SetFont(gameboardButtonFont)
btn.SetBackgroundColour(
self
.bgAndBoardColor)
btn.SetForegroundColour(wx.Colour(
0xFF
,
0x00
,
0x00
))
btn.Bind(wx.EVT_BUTTON,
partial(
self
.gameboardButtonClicked, row
=
row, col
=
col))
buttonGrid.Add(btn,
0
, wx.EXPAND)
board_columns.append(btn)
self
.gameboard.append(board_columns)
self
.player1Butt
=
wx.Button(
self
,
self
.PLAYER1BUTT,
"Player 1"
, style
=
wx.NO_BORDER)
self
.player1Butt.SetFont(player1ButtonFont)
self
.player1Butt.SetBackgroundColour(
self
.player1Color)
self
.player1Butt.SetForegroundColour(wx.Colour(
0xFF
,
0x00
,
0x00
))
self
.player2Butt
=
wx.Button(
self
,
self
.PLAYER2BUTT,
"Player 2"
, style
=
wx.NO_BORDER)
self
.player2Butt.SetFont(player2ButtonFont)
self
.player2Butt.SetBackgroundColour(
self
.player2Color)
self
.layout
=
wx.BoxSizer(wx.VERTICAL)
self
.layout.Add(
self
.player1Butt,
1
, wx.EXPAND)
self
.layout.Add(buttonGrid,
8
, wx.CENTRE)
self
.layout.Add(
self
.player2Butt,
1
, wx.EXPAND)
self
.SetSizer(
self
.layout)
self
.Bind(wx.EVT_MENU,
self
.newGame, menuNewGame)
self
.Bind(wx.EVT_MENU,
self
.newSinglePlayer, menuNewSinglePlayer)
self
.Bind(wx.EVT_MENU,
self
.saveGame, menuSaveGame)
self
.Bind(wx.EVT_MENU,
self
.saveAs, menuSaveAs)
self
.Bind(wx.EVT_MENU,
self
.openGame, menuOpenGame)
self
.Bind(wx.EVT_MENU,
self
.saveAndQuit, menuSaveAndQuit)
self
.Bind(wx.EVT_MENU,
self
.quitWithoutSaving, menuQuitWithoutSaving)
self
.Bind(wx.EVT_MENU,
self
.showGameRules, menuShowGamerules)
self
.Bind(wx.EVT_MENU,
self
.showAppHelp, menuShowAppHelp)
self
.Bind(wx.EVT_BUTTON,
self
.player1ButtonClicked,
id
=
self
.PLAYER1BUTT)
self
.Bind(wx.EVT_BUTTON,
self
.player2ButtonClicked,
id
=
self
.PLAYER2BUTT)
self
.Bind(wx.EVT_CLOSE,
self
.quitWithoutSaving)
self
.Show(
True
)
self
.newGame()
def
newGame (
self
, event
=
None
):
self
.saveFile
=
""
self
.gameAltered
=
False
for
row
in
range
(
8
):
for
col
in
range
(
8
):
self
.gameboard[row][col].SetBackgroundColour(
self
.bgAndBoardColor)
self
.gameboard[
3
][
3
].SetBackgroundColour(
self
.player2Color)
self
.gameboard[
3
][
4
].SetBackgroundColour(
self
.player1Color)
self
.gameboard[
4
][
3
].SetBackgroundColour(
self
.player1Color)
self
.gameboard[
4
][
4
].SetBackgroundColour(
self
.player2Color)
self
.firstPlayersMove
=
True
self
.player1Butt.SetForegroundColour(
"RED"
)
self
.player2Butt.SetForegroundColour(
"BLACK"
)
def
newSinglePlayer (
self
, event
=
None
):
with SelectDifficultyDialog(
self
) as dlg:
if
dlg.ShowModal()
=
=
wx.ID_OK:
self
.newGame()
self
.robot
=
PlayerBot(
self
, dlg.getCurrentDifficulty())
self
.SetStatusText("")
else
:
self
.SetStatusText(
"New single player aborted."
)
return
def
saveGame (
self
, event
=
None
):
saveList
=
[[]]
for
row
in
range
(
8
):
for
col
in
range
(
8
):
saveList[
-
1
].append(
{
str
(
self
.bgAndBoardColor):
0
,
str
(
self
.player1Color):
1
,
str
(
self
.player2Color):
2
}[
str
(
self
.gameboard[row][col].GetBackgroundColour())])
if
row !
=
7
: saveList.append([])
isSinglePlayer
=
False
;
if
self
.robot: isSinglePlayer
=
True
saveDict
=
{
"saveList"
: saveList,
"firstPlayersMove"
:
self
.firstPlayersMove,
"isSinglePlayer"
: isSinglePlayer}
if
self
.robot: saveDict[
"difficulty"
]
=
self
.robot.difficulty
if
self
.saveFile
=
=
"":
fd
=
wx.FileDialog(
self
,
"Select a file"
, os.getcwd(), "
", "
*
.txt", wx.FD_OPEN)
if
fd.ShowModal()
=
=
wx.ID_OK:
self
.saveFile
=
fd.GetPath()
else
:
fd.Destroy()
return
fd.Destroy()
with
open
(
self
.saveFile,
"w"
) as f:
try
:
f.write(
repr
(saveDict))
except
FileNotFoundError:
mdlg
=
wx.MessageDialog(
self
,
"The currently selected file could not be accessed at this time. Please try again."
,
"wxOthello"
, wx.OK)
mdlg.ShowModal()
mdlg.Destroy()
self
.saveFile
=
""
self
.gameAltered
=
False
def
saveAs (
self
, event
=
None
):
self
.saveFile
=
""
self
.saveGame()
def
openGame (
self
, event
=
None
):
fd
=
wx.FileDialog(
self
,
"Select a file"
, os.getcwd(), "
", "
*
.txt", wx.FD_OPEN)
if
fd.ShowModal()
=
=
wx.ID_OK:
self
.saveFile
=
fd.GetPath()
else
:
fd.Destroy()
return
fd.Destroy()
with
open
(
self
.saveFile,
"r"
) as f:
try
:
saveDict
=
eval
(f.read())
except
FileNotFoundError:
mdlg
=
wx.MessageDialog(
self
,
"The currently selected file could not be accessed at this time. Please try again."
,
"wxOthello"
, wx.OK)
mdlg.ShowModal()
mdlg.Destroy()
self
.saveFile
=
""
return
except
SyntaxError:
mdlg
=
wx.MessageDialog(
self
,
"The currently selected file is either corrupted or its contents incompatible with opening in this game."
,
"wxOthello"
, wx.OK)
mdlg.ShowModal()
mdlg.Destroy()
self
.saveFile
=
""
return
self
.firstPlayersMove
=
saveDict[
"firstPlayersMove"
]
if
"isSinglePlayer"
in
saveDict:
if
saveDict[
"isSinglePlayer"
]:
self
.robot
=
PlayerBot(
self
, saveDict[
"difficulty"
])
for
row
in
range
(
8
):
for
col
in
range
(
8
):
self
.gameboard[row][col].SetBackgroundColour([
self
.bgAndBoardColor,
self
.player1Color,
self
.player2Color][saveDict[
"saveList"
][row][col]])
self
.Refresh()
self
.gameAltered
=
False
def
saveAndQuit (
self
, event
=
None
):
self
.saveGame()
self
.Destroy()
exit()
def
quitWithoutSaving (
self
, event
=
None
):
if
self
.gameAltered:
usersFinalDecision
=
wx.MessageBox(
"All progress you've made in this game will be lost. Are you sure you want to quit without saving? Answering 'no' will open a save dialog if no file was selected previously then exit the game."
,
"wxOthello"
, wx.YES_NO | wx.CANCEL,
self
)
if
usersFinalDecision
=
=
wx.YES:
self
.Destroy()
exit()
elif
usersFinalDecision
=
=
wx.NO:
self
.saveGame()
elif
usersFinalDecision
=
=
wx.CANCEL:
return
else
:
self
.Destroy()
exit()
def
showGameRules (
self
, event
=
None
):
global
gameRules
self
.showHelp(gameRules)
def
showAppHelp (
self
, event
=
None
):
global
applicationHelp
self
.showHelp(applicationHelp)
def
showHelp(
self
, helpText):
with HelpWindow(
self
, helpText) as hdlg:
hdlg.ShowModal()
def
player1ButtonClicked (
self
, event
=
None
):
self
.firstPlayersMove
=
True
self
.player1Butt.SetForegroundColour(
"RED"
)
self
.player2Butt.SetForegroundColour(
"BLACK"
)
def
player2ButtonClicked (
self
, event
=
None
):
self
.firstPlayersMove
=
False
self
.player1Butt.SetForegroundColour(
"WHITE"
)
self
.player2Butt.SetForegroundColour(
"RED"
)
if
self
.robot:
self
.robot.makeMove()
def
displayErrorOnButton(row, col, message):
self
.gameboard[row][col].SetLabel(message)
wx.MilliSleep(
1000
)
self
.gameboard[row][col].SetLabel("")
def
gameboardButtonClicked (
self
, event
=
None
, row
=
0
, col
=
0
):
if
self
.firstPlayersMove:
me
=
self
.player1Color
opponent
=
self
.player2Color
else
:
me
=
self
.player2Color
opponent
=
self
.player1Color
moveIsValid, message
=
self
.isValidMove(row, col, me, opponent)
if
not
moveIsValid:
self
.gameboard[row][col].SetLabel(message)
wx.MilliSleep(
1000
)
self
.gameboard[row][col].SetLabel("")
return
self
.makeMove(row, col, me, opponent)
self
.gameAltered
=
True
winDetected, message
=
self
.detectWin()
if
winDetected:
m
=
wx.MessageDialog(
self
, message,
"wxOthello"
)
m.ShowModal()
m.Destroy()
self
.gameAltered
=
False
self
.firstPlayersMove
=
not
self
.firstPlayersMove
if
self
.firstPlayersMove:
self
.player1Butt.SetForegroundColour(
"RED"
)
self
.player2Butt.SetForegroundColour(
"BLACK"
)
else
:
self
.player1Butt.SetForegroundColour(
"WHITE"
)
self
.player2Butt.SetForegroundColour(
"RED"
)
if
not
self
.moveAvailableToPlayer(opponent, me):
if
opponent
=
=
self
.player1Color:
self
.SetStatusText(
"No move available to player 1."
)
else
:
self
.SetStatusText(
"No move available to player 2."
)
else
:
self
.SetStatusText("")
if
self
.robot
and
not
self
.firstPlayersMove:
self
.robot.makeMove()
return
def
moveAvailableToPlayer(
self
, me, opponent):
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
self
.isValidMove(row, col, me, opponent)[
0
]:
return
True
return
False
def
isValidMove (
self
, row, col, me, opponent):
if
self
.gameboard[row][col].GetBackgroundColour() !
=
self
.bgAndBoardColor:
return
False
,
"This Space\nIs Occupied!"
scanningDirections
=
((
-
1
,
0
), (
0
,
1
), (
1
,
0
), (
0
,
-
1
),
(
-
1
,
-
1
), (
-
1
,
1
), (
1
,
1
), (
1
,
-
1
))
message
=
"No Adjacent\nGamepieces!"
for
SDRow, SDCol
in
scanningDirections:
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
sawOpponent
=
False
while
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
self
.bgAndBoardColor:
break
else
:
message
=
"No Pieces\nTo Flip!"
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
opponent: sawOpponent
=
True
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
me
and
sawOpponent:
return
True
,
"You won't see this message!"
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
me
and
not
sawOpponent:
break
currentRow
+
=
SDRow
currentCol
+
=
SDCol
return
False
, message
def
makeMove (
self
, row, col, me, opponent):
self
.gameboard[row][col].SetBackgroundColour(me)
scanningDirections
=
((
-
1
,
0
), (
0
,
1
), (
1
,
0
), (
0
,
-
1
),
(
-
1
,
-
1
), (
-
1
,
1
), (
1
,
1
), (
1
,
-
1
))
for
SDRow, SDCol
in
scanningDirections:
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
sawOpponent
=
False
canFlipPieces
=
False
while
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
self
.bgAndBoardColor:
break
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
opponent: sawOpponent
=
True
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
me
and
sawOpponent:
canFlipPieces
=
True
break
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
me
and
not
sawOpponent:
break
currentRow
+
=
SDRow
currentCol
+
=
SDCol
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
while
canFlipPieces
and
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
opponent:
self
.gameboard[currentRow][currentCol].SetBackgroundColour(me)
elif
self
.gameboard[currentRow][currentCol].GetBackgroundColour()
=
=
me:
break
else
:
print
(
"Kyle, you have some debugging to do! This else clause is never supposed to execute. Something has gone horribly wrong!"
)
currentRow
+
=
SDRow
currentCol
+
=
SDCol
def
detectWin (
self
):
noValidMoves
=
True
player1Count
=
0
player2Count
=
0
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
self
.isValidMove(row, col,
self
.player1Color,
self
.player2Color)[
0
]
or
self
.isValidMove(row, col,
self
.player2Color,
self
.player1Color)[
0
]: noValidMoves
=
False
if
self
.gameboard[row][col].GetBackgroundColour()
=
=
self
.player1Color: player1Count
+
=
1
if
self
.gameboard[row][col].GetBackgroundColour()
=
=
self
.player2Color: player2Count
+
=
1
if
noValidMoves:
if
player1Count > player2Count:
return
True
,
"The winner is player 1!"
elif
player1Count
=
=
player2Count:
return
True
,
"This game is a draw!"
elif
player1Count < player2Count:
return
True
,
"The winner is player 2!"
else
:
return
False
,
"You're not supposed to see this message."
class
PlayerBot:
positionalScoringMap
=
((
500
,
-
200
,
99
,
80
,
80
,
99
,
-
200
,
500
),
(
-
200
,
-
300
,
-
10
,
-
5
,
-
5
,
-
10
,
-
300
,
-
200
),
(
99
,
-
10
,
200
,
150
,
150
,
200
,
-
10
,
99
),
(
80
,
-
5
,
150
,
100
,
100
,
150
,
-
5
,
80
),
(
80
,
-
5
,
150
,
100
,
100
,
150
,
-
5
,
80
),
(
99
,
-
10
,
200
,
150
,
150
,
200
,
-
10
,
99
),
(
-
200
,
-
300
,
-
10
,
-
5
,
-
5
,
-
10
,
-
300
,
-
200
),
(
500
,
-
200
,
99
,
80
,
80
,
99
,
-
200
,
500
))
bdCode
=
0
p1Code
=
1
p2Code
=
2
ST_EARLY
=
3
ST_MID
=
4
ST_LATE
=
5
TOP
=
(
0
,
2
)
LEFT
=
(
2
,
0
)
RIGHT
=
(
2
,
6
)
BOTTOM
=
(
6
,
2
)
def
__init__ (
self
, parent, difficulty):
self
.parent
=
parent
self
.difficulty
=
difficulty
self
.gameState
=
self
.getInitialGameState()
self
.movesDetrimentalToMidgame
=
[]
def
getInitialGameState (
self
):
gameState
=
[]
for
row
in
range
(
8
):
currentRow
=
[]
for
col
in
range
(
8
):
currentRow.append(
self
.bdCode)
gameState.append(currentRow)
currentRow
=
[]
gameState[
2
][
2
]
=
self
.p2Code
gameState[
2
][
3
]
=
self
.p1Code
gameState[
3
][
2
]
=
self
.p1Code
gameState[
3
][
3
]
=
self
.p2Code
return
gameState
def
makeMove(
self
):
self
.updateGameState()
validMoves
=
self
.getAllValidMoves(
self
.gameState,
self
.p2Code,
self
.p1Code)
if
validMoves
=
=
[]:
self
.parent.player1ButtonClicked()
return
if
random.randint(
0
,
100
) <
=
self
.difficulty:
_, bestMove
=
self
.miniMax(
self
.gameState,
5
,
-
inf, inf,
True
)
self
.parent.gameboardButtonClicked(row
=
bestMove[
0
], col
=
bestMove[
1
])
return
else
:
randomMove
=
validMoves[random.randint(
0
,
len
(validMoves)
-
1
)]
self
.parent.gameboardButtonClicked(row
=
randomMove[
0
], col
=
randomMove[
1
])
self
.updateGameState()
def
miniMax (
self
, gameState, depth, alpha, beta, maximizingPlayer):
if
depth
=
=
0
or
(
self
.getAllValidMoves(gameState,
self
.p2Code,
self
.p1Code)
=
=
[]
and
self
.getAllValidMoves(gameState,
self
.p1Code,
self
.p2Code)):
return
self
.scoreGameState(gameState), (
0
,
0
)
if
maximizingPlayer:
allValidMoves
=
self
.getAllValidMoves(gameState,
self
.p2Code,
self
.p1Code)
maxScore
=
-
inf
bestMove
=
(
0
,
0
)
for
move
in
allValidMoves:
_, gameStateAfterMove
=
self
.scoreMove(move, gameState,
self
.p2Code,
self
.p1Code)
moveScore, _
=
self
.miniMax(gameStateAfterMove, depth
-
1
, alpha, beta,
False
)
if
moveScore > alpha: alpha
=
moveScore
if
alpha >
=
beta:
break
if
moveScore > maxScore:
maxScore
=
moveScore
bestMove
=
move
return
maxScore, bestMove
else
:
allValidMoves
=
self
.getAllValidMoves(gameState,
self
.p1Code,
self
.p2Code)
minScore
=
inf
bestMove
=
(
0
,
0
)
for
move
in
allValidMoves:
_, gameStateAfterMove
=
self
.scoreMove(move, gameState,
self
.p1Code,
self
.p2Code)
moveScore, _
=
self
.miniMax(gameStateAfterMove, depth
-
1
, alpha, beta,
True
)
if
moveScore < beta: beta
=
moveScore
if
alpha >
=
beta:
break
if
moveScore < minScore:
minScore
=
moveScore
bestMove
=
move
return
minScore, bestMove
def
getAllValidMoves (
self
, gameState, me, opponent):
validMoves
=
[]
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
self
.isValidMove(row, col, me, opponent, gameState): validMoves.append( (row, col) )
return
validMoves
def
scoreGameState (
self
, gameState):
score
=
0
me
=
self
.p2Code
opponent
=
self
.p1Code
validMoves
=
self
.getAllValidMoves(gameState, me, opponent)
if
len
(validMoves)
=
=
0
:
if
len
(
self
.getAllValidMoves(gameState, opponent, me))
=
=
0
:
myScore
=
0
oppScore
=
0
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me: myScore
+
=
1
if
gameState[row][col]
=
=
opponent: oppScore
+
=
1
score
+
=
myScore
-
oppScore
if
score >
0
:
return
999_999_999_999
+
score
elif
score
=
=
0
:
return
0
elif
score <
0
:
return
-
999_999_999_999
+
score
else
:
score
-
=
250
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me: score
+
=
self
.positionalScoringMap[row][col]
if
gameState[row][col]
=
=
opponent: score
-
=
self
.positionalScoringMap[row][col]
score
+
=
self
.findShieldedPieces(gameState, me, opponent)
*
15
score
-
=
self
.findShieldedPieces(gameState, opponent, me)
*
15
score
+
=
self
.findPermanentPieces(gameState, me, opponent)
*
100
score
-
=
self
.findPermanentPieces(gameState, opponent, me)
*
100
myScore
=
0
oppScore
=
0
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me: myScore
+
=
1
if
gameState[row][col]
=
=
opponent: oppScore
+
=
1
score
+
=
(myScore
-
oppScore)
*
10
score
+
=
(
len
(validMoves)
-
len
(
self
.getAllValidMoves(gameState, opponent, me)))
*
30
return
score
def
findPermanentPieces (
self
, gameState, me, opponent):
permanentPieceCount
=
0
topLeftHorizontalEdge
=
0
topLeftVerticalEdge
=
0
topRightHorizontalEdge
=
0
topRightVerticalEdge
=
0
bottomLeftVerticalEdge
=
0
bottomLeftHorizontalEdge
=
0
bottomRightVerticalEdge
=
0
bottomRightHorizontalEdge
=
0
if
gameState[
0
][
0
]
=
=
me:
row
=
0
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me:
topLeftHorizontalEdge
+
=
1
else
:
break
col
=
0
for
row
in
range
(
8
):
if
gameState[row][col]
=
=
me:
topLeftVerticalEdge
+
=
1
else
:
break
if
gameState[
0
][
7
]
=
=
me:
row
=
0
for
col
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
topRightHorizontalEdge
+
=
1
else
:
break
col
=
7
for
row
in
range
(
8
):
if
gameState[row][col]
=
=
me:
topRightVerticalEdge
+
=
1
else
:
break
if
gameState[
7
][
0
]
=
=
me:
row
=
7
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me:
bottomLeftHorizontalEdge
+
=
1
else
:
break
col
=
0
for
row
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
bottomLeftVerticalEdge
+
=
1
else
:
break
if
gameState[
7
][
7
]
=
=
me:
row
=
7
for
col
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
bottomRightHorizontalEdge
+
=
1
else
:
break
col
=
7
for
row
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
bottomRightVerticalEdge
+
=
1
else
:
break
TLHedgeCounted
=
False
; TLVedgeCounted
=
False
TRHedgeCounted
=
False
; TRVedgeCounted
=
False
BLHedgeCounted
=
False
; BLVedgeCounted
=
False
BRHedgeCounted
=
False
; BRVedgeCounted
=
False
if
topLeftHorizontalEdge
=
=
8
:
TRHedgeCounted
=
True
canBreak
=
False
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
canBreak
=
True
break
if
canBreak:
break
if
topLeftVerticalEdge
=
=
8
and
not
TRHedgeCounted:
BRVedgeCounted
=
True
canBreak
=
False
for
col
in
range
(
8
):
for
row
in
range
(
8
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
canBreak
=
True
break
if
canBreak:
break
if
bottomRightHorizontalEdge
=
=
8
:
BRHedgeCounted
=
True
canBreak
=
False
for
row
in
range
(
7
,
-
1
,
-
1
):
for
col
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
canBreak
=
True
break
if
canBreak:
break
if
bottomRightVerticalEdge
=
=
8
and
not
BRHedgeCounted:
TRVedgeCounted
=
True
canBreak
=
False
for
col
in
range
(
7
,
-
1
,
-
1
):
for
row
in
range
(
7
,
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
canBreak
=
True
break
if
canBreak:
break
if
topLeftHorizontalEdge <
=
topLeftVerticalEdge
and
not
TLHedgeCounted
and
not
TLVedgeCounted:
permanentPieceCount
+
=
topLeftVerticalEdge
-
topLeftHorizontalEdge
HScanDist
=
topLeftVerticalEdge
for
row
in
range
(
8
):
for
col
in
range
(HScanDist):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
HScanDist
=
0
break
if
HScanDist >
0
:
HScanDist
-
=
1
else
:
break
TLHedgeCounted
=
True
TLVedgeCounted
=
True
elif
topLeftVerticalEdge <
=
topLeftHorizontalEdge
and
not
TLHedgeCounted
and
not
TLVedgeCounted:
permanentPieceCount
+
=
topLeftHorizontalEdge
-
topLeftVerticalEdge
VScanDist
=
topLeftVerticalEdge
for
row
in
range
(
8
):
for
col
in
range
(VScanDist):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
VScanDist
=
0
break
if
VScanDist >
0
:
VScanDist
-
=
1
else
:
break
TLHedgeCounted
=
True
TLVedgeCounted
=
True
if
topRightHorizontalEdge <
=
topRightVerticalEdge
and
not
TRHedgeCounted
and
not
TRVedgeCounted:
permanentPieceCount
+
=
topRightVerticalEdge
-
topRightHorizontalEdge
HScanDist
=
topRightHorizontalEdge
for
row
in
range
(
8
):
for
col
in
range
(
7
,
-
HScanDist
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
HScanDist
=
0
break
if
HScanDist >
0
:
HScanDist
-
=
1
else
:
break
TRHedgeCounted
=
True
TRVedgeCounted
=
True
elif
topRightVerticalEdge <
=
topRightHorizontalEdge
and
not
TRHedgeCounted
and
not
TRVedgeCounted:
permanentPieceCount
+
=
topRightHorizontalEdge
-
topRightVerticalEdge
VScanDist
=
topRightVerticalEdge
for
col
in
range
(
7
,
-
1
,
-
1
):
for
row
in
range
(VScanDist):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
VScanDist
=
0
break
if
VScanDist >
0
:
VScanDist
-
=
1
else
:
break
TRHedgeCounted
=
True
TRVedgeCounted
=
True
if
bottomLeftHorizontalEdge <
=
bottomLeftVerticalEdge
and
not
BLHedgeCounted
and
not
BLVedgeCounted:
permanentPieceCount
+
=
bottomLeftVerticalEdge
-
bottomLeftHorizontalEdge
HScanDist
=
bottomLeftHorizontalEdge
for
row
in
range
(
7
,
-
1
,
-
1
):
for
col
in
range
(HScanDist):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
HScanDist
=
0
break
if
HScanDist >
0
:
HScanDist
-
=
1
else
:
break
BLHedgeCounted
=
True
BLVedgeCounted
=
True
elif
bottomLeftVerticalEdge <
=
bottomLeftHorizontalEdge
and
not
BLVedgeCounted
and
not
BLHedgeCounted:
permanentPieceCount
+
=
bottomLeftHorizontalEdge
-
bottomLeftVerticalEdge
VScanDist
=
bottomLeftVerticalEdge
for
col
in
range
(
8
):
for
row
in
range
(
7
,
-
VScanDist
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
VScanDist
=
0
break
if
VScanDist >
0
:
VScanDist
-
=
1
else
:
break
BLVedgeCounted
=
True
BLHedgeCounted
=
True
if
bottomRightHorizontalEdge <
=
bottomRightVerticalEdge
and
not
BRHedgeCounted
and
not
BRVedgeCounted:
permanentPieceCount
+
=
bottomRightVerticalEdge
-
bottomRightHorizontalEdge
HScanDist
=
bottomRightHorizontalEdge
for
row
in
range
(
7
,
-
1
,
-
1
):
for
col
in
range
(
7
,
-
HScanDist
-
1
,
-
1
):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
HScanDist
=
0
break
if
HScanDist >
0
:
HScanDist
-
=
1
else
:
break
BRedgeCounted
=
True
BLedgeCounted
=
True
elif
bottomRightVerticalEdge <
=
bottomRightHorizontalEdge
and
not
BRHedgeCounted
and
not
BRVedgeCounted:
permanentPieceCount
+
=
bottomRightVerticalEdge
-
bottomRightHorizontalEdge
VScanDist
=
bottomRightVerticalEdge
for
col
in
range
(
7
,
-
1
,
-
1
):
for
row
in
range
(VScanDist):
if
gameState[row][col]
=
=
me:
permanentPieceCount
+
=
1
else
:
VScanDist
=
0
break
if
VScanDist >
0
:
VScanDist
-
=
1
else
:
break
BRHedgeCounted
=
True
BRVedgeCounted
=
True
return
permanentPieceCount
def
findShieldedPieces (
self
, gameState, me, opponent):
shieldedPiecesCount
=
0
movesAvailableToOpponent
=
self
.getAllValidMoves(gameState, me, opponent)
for
sRow
in
range
(
8
):
for
sCol
in
range
(
8
):
if
gameState[sRow][sCol]
=
=
me:
foundPossibleAttack
=
False
for
move
in
movesAvailableToOpponent:
_, gameStateAfterMove
=
self
.scoreMove(move, gameState, me, opponent)
if
gameStateAfterMove[sRow][sCol] !
=
me: foundPossibleAttack
=
True
if
not
foundPossibleAttack: shieldedPiecesCount
+
=
1
return
shieldedPiecesCount
def
isValidMove (
self
, row, col, me, opponent, gameState):
if
gameState[row][col] !
=
self
.bdCode:
return
False
scanningDirections
=
((
-
1
,
0
), (
0
,
1
), (
1
,
0
), (
0
,
-
1
),
(
-
1
,
-
1
), (
-
1
,
1
), (
1
,
1
), (
1
,
-
1
))
for
SDRow, SDCol
in
scanningDirections:
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
sawOpponent
=
False
while
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
gameState[currentRow][currentCol]
=
=
self
.bdCode:
break
if
gameState[currentRow][currentCol]
=
=
opponent: sawOpponent
=
True
if
gameState[currentRow][currentCol]
=
=
me
and
sawOpponent:
return
True
if
gameState[currentRow][currentCol]
=
=
me
and
not
sawOpponent:
break
currentRow
+
=
SDRow
currentCol
+
=
SDCol
return
False
def
updateGameState (
self
):
for
row
in
range
(
8
):
for
col
in
range
(
8
):
if
self
.parent.gameboard[row][col].GetBackgroundColour()
=
=
self
.parent.bgAndBoardColor:
self
.gameState[row][col]
=
self
.bdCode
elif
self
.parent.gameboard[row][col].GetBackgroundColour()
=
=
self
.parent.player1Color:
self
.gameState[row][col]
=
self
.p1Code
elif
self
.parent.gameboard[row][col].GetBackgroundColour()
=
=
self
.parent.player2Color:
self
.gameState[row][col]
=
self
.p2Code
def
scoreMove (
self
, possibleMove, gameState, me, opponent):
gameState
=
gameState.copy()
row, col
=
possibleMove
moveScore
=
1
scanningDirections
=
((
-
1
,
0
), (
0
,
1
), (
1
,
0
), (
0
,
-
1
),
(
-
1
,
-
1
), (
-
1
,
1
), (
1
,
1
), (
1
,
-
1
))
for
SDRow, SDCol
in
scanningDirections:
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
sawOpponent
=
False
canCountPieces
=
False
while
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
gameState[currentRow][currentCol]
=
=
self
.bdCode:
break
if
gameState[currentRow][currentCol]
=
=
self
.p1Code: sawOpponent
=
True
if
gameState[currentRow][currentCol]
=
=
me
and
sawOpponent:
canCountPieces
=
True
break
if
gameState[currentRow][currentCol]
=
=
me
and
not
sawOpponent:
break
currentRow
+
=
SDRow
currentCol
+
=
SDCol
currentRow
=
row
+
SDRow
currentCol
=
col
+
SDCol
while
canCountPieces
and
currentRow
in
range
(
0
,
8
)
and
currentCol
in
range
(
0
,
8
):
if
gameState[currentRow][currentCol]
=
=
opponent:
gameState[currentRow][currentCol]
=
me
moveScore
+
=
1
elif
gameState[currentRow][currentCol]
=
=
me:
break
currentRow
+
=
SDRow
currentCol
+
=
SDCol
return
moveScore, gameState
class
SelectDifficultyDialog (wx.Dialog):
ID_DIFFICULTY_SLIDER
=
wx.NewIdRef(
1
)
def
__init__(
self
, parent):
wx.Dialog.__init__(
self
, parent
=
parent,
id
=
-
1
, title
=
"Please Select Difficulty"
)
self
.SetMinSize(wx.Size(
400
,
290
))
self
.prompt
=
wx.StaticText(
self
,
-
1
,
"Please Use the slider to select difficulty. The further to the right, the harder the game."
)
self
.difficultyDescriptor
=
wx.StaticText(
self
,
-
1
,
"Game Difficulty: 75 - A little challenging"
)
self
.difficultySlider
=
wx.Slider(
self
,
self
.ID_DIFFICULTY_SLIDER,
75
)
self
.okButton
=
wx.Button(
self
, wx.ID_OK,
"Set Difficulty"
)
self
.layout
=
wx.BoxSizer(wx.VERTICAL)
self
.layout.Add(
self
.prompt,
1
, wx.EXPAND)
self
.layout.Add(
self
.difficultyDescriptor,
1
, wx.EXPAND)
self
.layout.Add(
self
.difficultySlider,
3
, wx.EXPAND)
self
.layout.Add(
self
.okButton,
2
, wx.EXPAND)
self
.SetSizer(
self
.layout)
self
.Fit()
self
.Bind(wx.EVT_COMMAND_SCROLL,
self
.diffSelectionChanged,
id
=
self
.ID_DIFFICULTY_SLIDER)
self
.Bind(wx.EVT_BUTTON,
lambda
evt:
self
.EndModal(wx.ID_OK),
id
=
wx.ID_OK)
self
.Bind(wx.EVT_CLOSE,
lambda
evt:
self
.EndModal(wx.ID_CANCEL))
def
diffSelectionChanged (
self
, event
=
None
):
currentDifficulty
=
self
.difficultySlider.GetValue()
if
currentDifficulty
in
range
(
0
,
26
):
self
.difficultyDescriptor.SetLabel(
"Game Difficulty: {} - Childs play"
.
format
(currentDifficulty))
elif
currentDifficulty
in
range
(
25
,
51
):
self
.difficultyDescriptor.SetLabel(
"Game Difficulty: {} - My cat could do it."
.
format
(currentDifficulty))
elif
currentDifficulty
in
range
(
50
,
76
):
self
.difficultyDescriptor.SetLabel(
"Game Difficulty: {} - A little challenging"
.
format
(currentDifficulty))
elif
currentDifficulty
in
range
(
75
,
101
):
self
.difficultyDescriptor.SetLabel(
"Game Difficulty: {} - Only you can win!"
.
format
(currentDifficulty))
else
:
self
.difficultyDescriptor.SetLabel(
"Kyle, you have some debugging to do! Unexpected output: {}"
.
format
(currentDifficulty))
def
getCurrentDifficulty (
self
):
return
self
.difficultySlider.GetValue()
class
HelpWindow(wx.Dialog):
def
__init__ (
self
, parent, helpText):
wx.Dialog.__init__(
self
, parent,
-
1
, helpText.split(
"\n"
)[
0
])
self
.SetMinSize(wx.Size(
400
,
400
))
self
.topExitButton
=
wx.Button(
self
, wx.ID_CLOSE,
"Close Help"
)
self
.helpDisplay
=
wx.TextCtrl(parent
=
self
,
id
=
wx.ID_ANY, value
=
helpText,
style
=
wx.TE_MULTILINE | wx.TE_READONLY)
self
.bottomExitButton
=
wx.Button(
self
, wx.ID_CLOSE,
"Close Help"
)
self
.layout
=
wx.BoxSizer(wx.VERTICAL)
self
.layout.Add(
self
.topExitButton,
1
, wx.EXPAND)
self
.layout.Add(
self
.helpDisplay,
10
, wx.EXPAND)
self
.layout.Add(
self
.bottomExitButton,
1
, wx.EXPAND)
self
.SetSizer(
self
.layout)
self
.Fit()
self
.Bind(wx.EVT_BUTTON,
self
.closeHelp,
id
=
wx.ID_CLOSE)
def
closeHelp(
self
, event
=
None
):
self
.EndModal(wx.ID_OK)
if
__name__
=
=
"__main__"
:
app
=
wx.App()
theApp
=
OthelloGameFrame(parent
=
None
,
id
=
wx.ID_ANY, size
=
(
700
,
800
), title
=
"wxOthello"
)
app.MainLoop()