Difficulty added when starting a game of fourinarow, on a scale of 1-5. The Hex functions are added, but right now they are mostly copies of the fourinarow functions, and do not work
331 lines
12 KiB
Python
331 lines
12 KiB
Python
import json
|
|
import random
|
|
import copy
|
|
import math
|
|
|
|
from . import hexDraw
|
|
from funcs import logThis
|
|
|
|
# This is totally copied from the four in a row game. Just modified
|
|
|
|
# Parses command
|
|
def parseHex(command, channel, user):
|
|
commands = command.split()
|
|
if command == "" or command == " ":
|
|
return "I didn't get that. Use \"!hex start [opponent]\" to start a game.", False, False, False, False
|
|
|
|
elif commands[0] == "start":
|
|
# Starting a game
|
|
if len(commands) == 1: # if the commands is "!hex start", the opponent is Gwendolyn
|
|
commands.append("Gwendolyn")
|
|
return hexStart(channel,user,commands[1]) # commands[1] is the opponent
|
|
|
|
# Stopping the game
|
|
elif commands[0] == "stop":
|
|
with open("resources/games/games.json", "r") as f:
|
|
data = json.load(f)
|
|
|
|
if user in data["hex games"][channel]["players"]:
|
|
return "Ending game.", False, False, True, False
|
|
else:
|
|
return "You can't end a game where you're not a player.", False, False, False, False
|
|
|
|
# Placing a piece
|
|
elif commands[0] == "place":
|
|
try:
|
|
return placePiece(channel,int(commands[1]),commands[2])
|
|
except:
|
|
return "I didn't get that. To place a piece use \"!hex place [player number] [position]\". A valid position is e.g. \"e2\".", False, False, False, False
|
|
else:
|
|
return "I didn't get that. Use \"!hex start [opponent]\" to start a game or \"!hex stop\" to stop a current game.", False, False, False, False
|
|
|
|
|
|
# Starts the game
|
|
def hexStart(channel, user, opponent):
|
|
with open("resources/games/games.json", "r") as f:
|
|
data = json.load(f)
|
|
|
|
if user.lower() != opponent.lower():
|
|
if channel not in data["4 in a row games"]:
|
|
|
|
if opponent in ["1","2","3","4","5"]:
|
|
difficulty = int(opponent)
|
|
opponent = "Gwendolyn"
|
|
elif opponent in ["0","6","7","8","9","10","69","100","420"]:
|
|
return "That difficulty doesn't exist", False, False, False, False
|
|
else:
|
|
# Opponent is another player
|
|
difficulty = "NA"
|
|
|
|
board = [ [ 0 for i in range(columnCount) ] for j in range(rowCount) ]
|
|
players = [user,opponent]
|
|
random.shuffle(players)
|
|
|
|
data["4 in a row games"][channel] = {"board": board,"winner":0,"win direction":"",
|
|
"win coordinates":[0,0],"players":players,"turn":0,"difficulty":difficulty}
|
|
|
|
with open("resources/games/games.json", "w") as f:
|
|
json.dump(data,f,indent=4)
|
|
|
|
fourInARowDraw.drawImage(channel)
|
|
|
|
gwendoTurn = False
|
|
|
|
if players[0] == "Gwendolyn":
|
|
gwendoTurn = True
|
|
|
|
return "Started hex game against "+opponent+". It's "+players[0]+"'s turn", True, False, False, gwendoTurn
|
|
else:
|
|
return "There's already a hex game going on in this channel", False, False, False, False
|
|
else:
|
|
return "You can't play against yourself", False, False, False, False
|
|
|
|
# Places a piece at the lowest available point in a specific column
|
|
def placePiece(channel : str,player : int,column : int):
|
|
with open("resources/games/games.json", "r") as f:
|
|
data = json.load(f)
|
|
|
|
if channel in data["4 in a row games"]:
|
|
board = data["4 in a row games"][channel]["board"]
|
|
|
|
board = placeOnBoard(board,player,column)
|
|
|
|
|
|
if board != None:
|
|
data["4 in a row games"][channel]["board"] = board
|
|
turn = (data["4 in a row games"][channel]["turn"]+1)%2
|
|
data["4 in a row games"][channel]["turn"] = turn
|
|
|
|
with open("resources/games/games.json", "w") as f:
|
|
json.dump(data,f,indent=4)
|
|
|
|
logThis("Checking for win")
|
|
won, winDirection, winCoordinates = isWon(data["4 in a row games"][channel]["board"])
|
|
|
|
if won != 0:
|
|
gameWon = True
|
|
data["4 in a row games"][channel]["winner"] = won
|
|
data["4 in a row games"][channel]["win direction"] = winDirection
|
|
data["4 in a row games"][channel]["win coordinates"] = winCoordinates
|
|
|
|
message = data["4 in a row games"][channel]["players"][won-1]+" won."
|
|
if data["4 in a row games"][channel]["players"][won-1] != "Gwendolyn":
|
|
message += " Adding 20 GwendoBucks to their account."
|
|
elif 0 not in board[0]:
|
|
gameWon = True
|
|
message = "It's a draw!"
|
|
else:
|
|
gameWon = False
|
|
message = data["4 in a row games"][channel]["players"][player-1]+" placed a piece in column "+str(column+1)+". It's now "+data["4 in a row games"][channel]["players"][turn]+"'s turn."
|
|
|
|
with open("resources/games/games.json", "w") as f:
|
|
json.dump(data,f,indent=4)
|
|
|
|
gwendoTurn = False
|
|
|
|
if data["4 in a row games"][channel]["players"][turn] == "Gwendolyn":
|
|
logThis("It's Gwendolyn's turn")
|
|
gwendoTurn = True
|
|
|
|
fourInARowDraw.drawImage(channel)
|
|
return message, True, True, gameWon, gwendoTurn
|
|
else:
|
|
return "There isn't any room in that column", True, True, False, False
|
|
else:
|
|
return "There's no game in this channel", False, False, False, False
|
|
|
|
# Returns a board where a piece has been placed in the column
|
|
def placeOnBoard(board,player,column):
|
|
placementx, placementy = -1, column
|
|
|
|
for x in range(len(board)):
|
|
if board[x][column] == 0:
|
|
placementx = x
|
|
|
|
board[placementx][placementy] = player
|
|
|
|
if placementx == -1:
|
|
return None
|
|
else:
|
|
return board
|
|
|
|
# Checks if someone has won the game and returns the winner
|
|
def isWon(board):
|
|
won = 0
|
|
winDirection = ""
|
|
winCoordinates = [0,0]
|
|
|
|
for row in range(len(board)):
|
|
for place in range(len(board[row])):
|
|
if won == 0:
|
|
piecePlayer = board[row][place]
|
|
if piecePlayer != 0:
|
|
# Checks horizontal
|
|
if place <= columnCount-4:
|
|
pieces = [board[row][place+1],board[row][place+2],board[row][place+3]]
|
|
else:
|
|
pieces = [0]
|
|
|
|
if all(x == piecePlayer for x in pieces):
|
|
won = piecePlayer
|
|
winDirection = "h"
|
|
winCoordinates = [row,place]
|
|
|
|
# Checks vertical
|
|
if row <= rowCount-4:
|
|
pieces = [board[row+1][place],board[row+2][place],board[row+3][place]]
|
|
else:
|
|
pieces = [0]
|
|
|
|
if all(x == piecePlayer for x in pieces):
|
|
won = piecePlayer
|
|
winDirection = "v"
|
|
winCoordinates = [row,place]
|
|
|
|
# Checks right diagonal
|
|
if row <= rowCount-4 and place <= columnCount-4:
|
|
pieces = [board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]]
|
|
else:
|
|
pieces = [0]
|
|
|
|
if all(x == piecePlayer for x in pieces):
|
|
won = piecePlayer
|
|
winDirection = "r"
|
|
winCoordinates = [row,place]
|
|
|
|
# Checks left diagonal
|
|
if row <= rowCount-4 and place >= 3:
|
|
pieces = [board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]]
|
|
else:
|
|
pieces = [0]
|
|
|
|
if all(x == piecePlayer for x in pieces):
|
|
won = piecePlayer
|
|
winDirection = "l"
|
|
winCoordinates = [row,place]
|
|
|
|
|
|
return won, winDirection, winCoordinates
|
|
|
|
# Plays as the AI
|
|
def fourInARowAI(channel):
|
|
logThis("Figuring out best move")
|
|
with open("resources/games/games.json", "r") as f:
|
|
data = json.load(f)
|
|
|
|
board = data["4 in a row games"][channel]["board"]
|
|
player = data["4 in a row games"][channel]["players"].index("Gwendolyn")+1
|
|
difficulty = data["4 in a row games"][channel]["difficulty"]
|
|
|
|
scores = [-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf]
|
|
for column in range(0,columnCount):
|
|
testBoard = copy.deepcopy(board)
|
|
testBoard = placeOnBoard(testBoard,player,column)
|
|
if testBoard != None:
|
|
scores[column] = minimax(testBoard,difficulty,player%2+1,player,-math.inf,math.inf,False)
|
|
logThis("Best score for column "+str(column)+" is "+str(scores[column]))
|
|
|
|
possibleScores = scores.copy()
|
|
|
|
while (min(possibleScores) <= (max(possibleScores) - max(possibleScores)/10)) and len(possibleScores) != 1:
|
|
possibleScores.remove(min(possibleScores))
|
|
|
|
highest_score = random.choice(possibleScores)
|
|
|
|
indices = [i for i, x in enumerate(scores) if x == highest_score]
|
|
placement = random.choice(indices)
|
|
return placePiece(channel,player,placement)
|
|
|
|
# Calculates points for a board
|
|
def AICalcPoints(board,player):
|
|
score = 0
|
|
otherPlayer = player%2+1
|
|
|
|
# Adds points for middle placement
|
|
for row in range(len(board)):
|
|
if board[row][3] == player:
|
|
score += AIScores["middle"]
|
|
|
|
# Checks horizontal
|
|
for row in range(rowCount):
|
|
rowArray = [int(i) for i in list(board[row])]
|
|
for place in range(columnCount-3):
|
|
window = rowArray[place:place+4]
|
|
score += evaluateWindow(window,player,otherPlayer)
|
|
|
|
# Checks Vertical
|
|
for column in range(columnCount):
|
|
columnArray = [int(i[column]) for i in list(board)]
|
|
for place in range(rowCount-3):
|
|
window = columnArray[place:place+4]
|
|
score += evaluateWindow(window,player,otherPlayer)
|
|
|
|
# Checks right diagonal
|
|
for row in range(rowCount-3):
|
|
for place in range(columnCount-3):
|
|
window = [board[row][place],board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]]
|
|
score += evaluateWindow(window,player,otherPlayer)
|
|
|
|
# Checks left diagonal
|
|
for row in range(rowCount-3):
|
|
for place in range(3,columnCount):
|
|
window = [board[row][place],board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]]
|
|
score += evaluateWindow(window,player,otherPlayer)
|
|
|
|
|
|
## Checks if anyone has won
|
|
#won = isWon(board)[0]
|
|
|
|
## Add points if AI wins
|
|
#if won == player:
|
|
# score += AIScores["win"]
|
|
|
|
return score
|
|
|
|
|
|
def evaluateWindow(window,player,otherPlayer):
|
|
if window.count(player) == 4:
|
|
return AIScores["win"]
|
|
elif window.count(player) == 3 and window.count(0) == 1:
|
|
return AIScores["three in a row"]
|
|
elif window.count(player) == 2 and window.count(0) == 2:
|
|
return AIScores["two in a row"]
|
|
elif window.count(otherPlayer) == 4:
|
|
return AIScores["enemy win"]
|
|
else:
|
|
return 0
|
|
|
|
def minimax(board, depth, player , originalPlayer, alpha, beta, maximizingPlayer):
|
|
terminal = ((isWon(board)[0] != 0) or (0 not in board[0]))
|
|
# The depth is how many moves ahead the computer checks. This value is the difficulty.
|
|
if depth == 0 or terminal:
|
|
points = AICalcPoints(board,originalPlayer)
|
|
return points
|
|
if maximizingPlayer:
|
|
value = -math.inf
|
|
for column in range(0,columnCount):
|
|
testBoard = copy.deepcopy(board)
|
|
testBoard = placeOnBoard(testBoard,player,column)
|
|
if testBoard != None:
|
|
evaluation = minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,False)
|
|
if evaluation < -9000: evaluation += AIScores["avoid losing"]
|
|
value = max(value,evaluation)
|
|
alpha = max(alpha,evaluation)
|
|
if beta <= alpha:
|
|
break
|
|
return value
|
|
else:
|
|
value = math.inf
|
|
for column in range(0,columnCount):
|
|
testBoard = copy.deepcopy(board)
|
|
testBoard = placeOnBoard(testBoard,player,column)
|
|
if testBoard != None:
|
|
evaluation = minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,True)
|
|
if evaluation < -9000: evaluation += AIScores["avoid losing"]
|
|
value = min(value,evaluation)
|
|
beta = min(beta,evaluation)
|
|
if beta <= alpha:
|
|
break
|
|
return value
|
|
|