Hex so beautiful. And works!!!

This commit is contained in:
jona605a
2020-08-07 23:31:10 +02:00
parent b45aac1d1c
commit e6ce610366
3 changed files with 143 additions and 82 deletions

View File

@ -56,7 +56,7 @@ async def runHex(channel,command,user):
f.write(str(oldImage.id)) f.write(str(oldImage.id))
if gameDone: if gameDone:
with open("resources/games/games.json", "r") as f: with open("resources/games/hexGames.json", "r") as f:
data = json.load(f) data = json.load(f)
try: try:
@ -66,11 +66,16 @@ async def runHex(channel,command,user):
except: except:
logThis("The old image was already deleted") logThis("The old image was already deleted")
winner = data["hex games"][str(channel.id)]["winner"] winner = data[str(channel.id)]["winner"]
if winner != 0: if winner != 0:
addMoney(data["hex games"][str(channel.id)]["players"][winner-1].lower(),20) addMoney(data[str(channel.id)]["players"][winner-1].lower(),20)
deleteGame("hex games",str(channel.id)) #deleteGame("hex games",str(channel.id))
with open("resources/games/hexGames.json", "r") as f:
data = json.load(f)
del data[str(channel.id)]
with open("resources/games/hexGames.json", "w") as f:
json.dump(data,f,indent=4)

View File

@ -15,7 +15,7 @@ AIScoresHex = {
"avoid losing": 100 "avoid losing": 100
} }
boardWidth = 11 BOARDWIDTH = 11
# Parses command # Parses command
@ -44,9 +44,9 @@ def parseHex(command, channel, user):
# Placing a piece # Placing a piece
elif commands[0] == "place": elif commands[0] == "place":
try: try:
return placeHex(channel,int(commands[1]),commands[2]) return placeHex(channel,commands[1], user)
except: 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 return "I didn't get that. To place a piece use \"!hex place [position]\". A valid position is e.g. \"E2\".", False, False, False, False
else: 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 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
@ -81,13 +81,14 @@ def hexStart(channel, user, opponent):
diffText = "" diffText = ""
# board is 11x11 # board is 11x11
board = [ [ 0 for i in range(boardWidth) ] for j in range(boardWidth) ] board = [ [ 0 for i in range(BOARDWIDTH) ] for j in range(BOARDWIDTH) ]
players = [user,opponent] players = [user,opponent]
random.shuffle(players) # random starting player random.shuffle(players) # random starting player
winningPieces = [[""],[""],[""]] # etc. winningPieces = [[""],[""],[""]] # etc.
lastMove = (5,5)
data[channel] = {"board": board,"winner":0, data[channel] = {"board":board, "winner":0,
"players":players, "winningPieces":winningPieces,"turn":0,"difficulty":difficulty} "players":players, "winningPieces":winningPieces, "turn":1, "difficulty":difficulty, "lastMove":lastMove}
with open("resources/games/hexGames.json", "w") as f: with open("resources/games/hexGames.json", "w") as f:
json.dump(data,f,indent=4) json.dump(data,f,indent=4)
@ -103,11 +104,15 @@ def hexStart(channel, user, opponent):
return "There's already a hex game going on in this channel", False, False, False, False return "There's already a hex game going on in this channel", False, False, False, False
# Places a piece at the given location and checks things afterwards # Places a piece at the given location and checks things afterwards
def placeHex(channel : str,player : int,position : str): def placeHex(channel : str,position : str, user):
with open("resources/games/hexGames.json", "r") as f: with open("resources/games/hexGames.json", "r") as f:
data = json.load(f) data = json.load(f)
if channel in data: if channel in data:
if user in data[channel]["players"]:
turn = data[channel]["turn"]
player = data[channel]["players"].index(user)+1
if player == turn:
board = data[channel]["board"] board = data[channel]["board"]
logThis("Placing a piece on the board with placeHex()") logThis("Placing a piece on the board with placeHex()")
@ -117,13 +122,15 @@ def placeHex(channel : str,player : int,position : str):
if isinstance(board, list): if isinstance(board, list):
# If the move is valid: # If the move is valid:
data[channel]["board"] = board data[channel]["board"] = board
turn = (data[channel]["turn"]+1)%2 turn = 1 if turn == 2 else 2
data[channel]["turn"] = turn data[channel]["turn"] = turn
"""
with open("resources/games/hexGames.json", "w") as f: with open("resources/games/hexGames.json", "w") as f:
json.dump(data,f,indent=4) json.dump(data,f,indent=4)
"""
# Checking for a win # Checking for a win
logThis("Checking for win") logThis("Checking for win")
won, winningPieces = isHexWon(data[channel]["board"]) won, winningPieces = isHexWon(data[channel]["board"])
@ -139,15 +146,16 @@ def placeHex(channel : str,player : int,position : str):
winAmount = data[channel]["difficulty"]^2+5 winAmount = data[channel]["difficulty"]^2+5
message += " Adding "+str(winAmount)+" GwendoBucks to their account." message += " Adding "+str(winAmount)+" GwendoBucks to their account."
else:""" else:"""
gameWon = False
message = getName(data[channel]["players"][player-1])+" placed at "+position+". It's now "+getName(data[channel]["players"][turn])+"'s turn."
gameWon = False
message = getName(data[channel]["players"][player-1])+" placed at "+position.upper()+". It's now "+getName(data[channel]["players"][turn-1])+"'s turn."
data[channel]["lastMove"] = (int(position[1])-1, ord(position[0])-97)
with open("resources/games/hexGames.json", "w") as f: with open("resources/games/hexGames.json", "w") as f:
json.dump(data,f,indent=4) json.dump(data,f,indent=4)
# Is it Gwendolyn's turn? # Is it now Gwendolyn's turn?
gwendoTurn = False gwendoTurn = False
if data[channel]["players"][turn] == "Gwendolyn": if data[channel]["players"][turn-1] == "Gwendolyn":
logThis("It's Gwendolyn's turn") logThis("It's Gwendolyn's turn")
gwendoTurn = True gwendoTurn = True
@ -158,7 +166,14 @@ def placeHex(channel : str,player : int,position : str):
else: else:
# Invalid move. "board" is the error message # Invalid move. "board" is the error message
message = board message = board
return message, True, True, False, False return message, False, False, False, False
else:
# Move out of turn
message = "It isn't your turn, it is "+getName(data[channel]["players"][turn-1])+"'s turn."
return message, False, False, False, False
else:
message = "You can't place when you're not in the game. The game's players are: "+getName(data[channel]["players"][0])+" and "+getName(data[channel]["players"][1])+"."
return message, False, False, False, False
else: else:
return "There's no game in this channel", False, False, False, False return "There's no game in this channel", False, False, False, False
@ -168,9 +183,9 @@ def placeOnHexBoard(board,player,position):
# Translates the position # Translates the position
position = position.lower() position = position.lower()
try: try:
column = ord(position[0])-97 # ord() translates from letter to number column = ord(position[0]) - 97 # ord() translates from letter to number
row = int(position[1])-1 row = int(position[1:]) - 1
if column not in range(boardWidth) or row not in range(boardWidth): if column not in range(BOARDWIDTH) or row not in range(BOARDWIDTH):
logThis("Position out of bounds (error code 1533)") logThis("Position out of bounds (error code 1533)")
return "Error. That position is out of bounds." return "Error. That position is out of bounds."
except: except:
@ -202,31 +217,50 @@ def hexAI(channel):
board = data[channel]["board"] board = data[channel]["board"]
player = data[channel]["players"].index("Gwendolyn")+1 player = data[channel]["players"].index("Gwendolyn")+1
difficulty = data[channel]["difficulty"] #difficulty = data[channel]["difficulty"]
lastMove = data[channel]["lastMove"]
# These moves are the last move +- 2.
moves = [[(lastMove[0]+j-2,lastMove[1]+i-2) for i in range(5) if lastMove[1]+i-2 in range(11)] for j in range(5) if lastMove[0]+j-2 in range(11)]
moves = sum(moves,[])
chosenMove = None
safety = 0
while chosenMove == None:
safety += 1
if safety > 1000:
break
candidate = random.choice(moves)
if board[candidate[0]][candidate[1]] == 0:
chosenMove = candidate
logThis("Last move was "+str(lastMove))
logThis("Chosen move is "+str(chosenMove))
"""
scores = [-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf] scores = [-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf]
for column in range(0,boardWidth): for column in range(0,BOARDWIDTH):
testBoard = copy.deepcopy(board) testBoard = copy.deepcopy(board)
# Testing a move
testBoard = placeOnHexBoard(testBoard,player,column) testBoard = placeOnHexBoard(testBoard,player,column)
# Evaluating that move
if testBoard != None: if testBoard != None:
scores[column] = minimaxHex(testBoard,difficulty,player%2+1,player,-math.inf,math.inf,False) scores[column] = minimaxHex(testBoard,difficulty,player%2+1,player,-math.inf,math.inf,False)
logThis("Best score for column "+str(column)+" is "+str(scores[column])) logThis("Best score for column "+str(column)+" is "+str(scores[column]))
possibleScores = scores.copy() possibleScores = scores.copy()
while (min(possibleScores) <= (max(possibleScores) - max(possibleScores)/10)) and len(possibleScores) != 1: while (min(possibleScores) < (max(possibleScores)*0.9)):
possibleScores.remove(min(possibleScores)) possibleScores.remove(min(possibleScores))
highest_score = random.choice(possibleScores) highest_score = random.choice(possibleScores)
indices = [i for i, x in enumerate(scores) if x == highest_score] indices = [i for i, x in enumerate(scores) if x == highest_score]
placement = random.choice(indices) """
return placeHex(channel,player,placement) placement = "abcdefghijk"[chosenMove[1]]+str(chosenMove[0]+1)
return placeHex(channel,placement, "Gwendolyn")
# Calculates points for a board # Calculates points for a board
def AICalcHexPoints(board,player): def AICalcHexPoints(board,player):
score = 0 score = 0
otherPlayer = player%2+1 #otherPlayer = player%2+1
## Checks if anyone has won ## Checks if anyone has won
@ -259,7 +293,7 @@ def minimaxHex(board, depth, player , originalPlayer, alpha, beta, maximizingPla
return points return points
if maximizingPlayer: if maximizingPlayer:
value = -math.inf value = -math.inf
for column in range(0,boardWidth): for column in range(0,BOARDWIDTH):
testBoard = copy.deepcopy(board) testBoard = copy.deepcopy(board)
testBoard = placeOnHexBoard(testBoard,player,column) testBoard = placeOnHexBoard(testBoard,player,column)
if testBoard != None: if testBoard != None:
@ -272,7 +306,7 @@ def minimaxHex(board, depth, player , originalPlayer, alpha, beta, maximizingPla
return value return value
else: else:
value = math.inf value = math.inf
for column in range(0,boardWidth): for column in range(0,BOARDWIDTH):
testBoard = copy.deepcopy(board) testBoard = copy.deepcopy(board)
testBoard = placeOnHexBoard(testBoard,player,column) testBoard = placeOnHexBoard(testBoard,player,column)
if testBoard != None: if testBoard != None:

View File

@ -23,12 +23,14 @@ X_THICKNESS = HEXTHICKNESS * math.cos(math.pi/6)
Y_THICKNESS = HEXTHICKNESS * math.sin(math.pi/6) Y_THICKNESS = HEXTHICKNESS * math.sin(math.pi/6)
BACKGROUND_COLOR = (230,230,230) BACKGROUND_COLOR = (230,230,230)
BETWEEN_COLOR = BACKGROUND_COLOR BETWEEN_COLOR = BACKGROUND_COLOR
BLANK_COLOR = "lightgrey" BLANK_COLOR = "lightgrey" # maybe lighter?
BOARDCOORDINATES = [ [(X_OFFSET + HEXAGONWIDTH*(column + row/2),Y_OFFSET + HEXAGONHEIGHT*row) for column in range(11)] for row in range(11)] # These are the coordinates for the upperleft corner of every hex BOARDCOORDINATES = [ [(X_OFFSET + HEXAGONWIDTH*(column + row/2),Y_OFFSET + HEXAGONHEIGHT*row) for column in range(11)] for row in range(11)] # These are the coordinates for the upperleft corner of every hex
COLHEXTHICKNESS = 4 COLHEXTHICKNESS = 4
COLX_THICKNESS = COLHEXTHICKNESS * math.cos(math.pi/6) COLX_THICKNESS = COLHEXTHICKNESS * math.cos(math.pi/6)
COLY_THICKNESS = COLHEXTHICKNESS * math.sin(math.pi/6) COLY_THICKNESS = COLHEXTHICKNESS * math.sin(math.pi/6)
X_NAME = {1:175, 2:CANVAS_WIDTH-100}
Y_NAME = {1:CANVAS_HEIGHT-150, 2:150}
NAMEHEXPADDING = 75
def drawBoard(channel): def drawBoard(channel):
logThis("Drawing empty Hex board") logThis("Drawing empty Hex board")
@ -106,6 +108,26 @@ def drawBoard(channel):
else: else:
player2 = getName(players[1]) player2 = getName(players[1])
""" """
with open("resources/games/hexGames.json", "r") as f:
data = json.load(f)
for p in [1,2]:
playername = getName(data[channel]["players"][p-1])
# Draw name
x = X_NAME[p]
x -= fnt.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned
y = Y_NAME[p]
d.text((x,y),playername, font=fnt, fill = TEXTCOLOR)
# Draw a half-size Hexagon to indicate the player's color
x -= NAMEHEXPADDING # To the left of both names
d.polygon([
(x, y),
(x+HEXAGONWIDTH/4, y-SIDELENGTH/4),
(x+HEXAGONWIDTH/2, y),
(x+HEXAGONWIDTH/2, y+SIDELENGTH/2),
(x+HEXAGONWIDTH/4, y+SIDELENGTH*3/4),
(x, y+SIDELENGTH/2),
],fill = PIECECOLOR[p])
im.save("resources/games/hexBoards/board"+channel+".png") im.save("resources/games/hexBoards/board"+channel+".png")
@ -119,7 +141,7 @@ def drawHexPlacement(channel,player,position):
# We don't need to error-check, because the position is already checked in placeOnHexBoard() # We don't need to error-check, because the position is already checked in placeOnHexBoard()
position = position.lower() position = position.lower()
column = ord(position[0])-97 # ord() translates from letter to number column = ord(position[0])-97 # ord() translates from letter to number
row = int(position[1])-1 row = int(position[1:])-1
# Find the coordinates for the filled hex drawing # Find the coordinates for the filled hex drawing
hexCoords = [ hexCoords = [
@ -136,7 +158,7 @@ def drawHexPlacement(channel,player,position):
with Image.open(FILEPATH) as im: with Image.open(FILEPATH) as im:
d = ImageDraw.Draw(im,"RGBA") d = ImageDraw.Draw(im,"RGBA")
# Draws the hex piece # Draws the hex piece
d.polygon(hexCoords,fill = PIECECOLOR[player]) d.polygon(hexCoords,fill = PIECECOLOR[player], outline = BETWEEN_COLOR)
# Save # Save
im.save(FILEPATH) im.save(FILEPATH)
@ -144,12 +166,12 @@ def drawHexPlacement(channel,player,position):
logThis("Error drawing new hex on board (error code 1541") logThis("Error drawing new hex on board (error code 1541")
if __name__ == '__main__': if __name__ == '__main__':
drawBoard("HexTest2") drawBoard("resources/games/hexBoards/boardTest.png")
drawHexPlacement("HexTest2",1,"f7") drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"f7")
drawHexPlacement("HexTest2",2,"f8") drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"f8")
drawHexPlacement("HexTest2",1,"h6") drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"h6")
drawHexPlacement("HexTest2",2,"e8") drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"e8")
drawHexPlacement("HexTest2",1,"e7") drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"e7")
drawHexPlacement("HexTest2",2,"c9") drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"c9")
drawHexPlacement("HexTest2",1,"g8") drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"g8")
drawHexPlacement("HexTest2",2,"h4") drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"h4")