diff --git a/funcs/games/gameLoops.py b/funcs/games/gameLoops.py index 9f9ae55..1a0e003 100644 --- a/funcs/games/gameLoops.py +++ b/funcs/games/gameLoops.py @@ -56,7 +56,7 @@ async def runHex(channel,command,user): f.write(str(oldImage.id)) if gameDone: - with open("resources/games/games.json", "r") as f: + with open("resources/games/hexGames.json", "r") as f: data = json.load(f) try: @@ -66,11 +66,16 @@ async def runHex(channel,command,user): except: logThis("The old image was already deleted") - winner = data["hex games"][str(channel.id)]["winner"] + winner = data[str(channel.id)]["winner"] 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) diff --git a/funcs/games/hex.py b/funcs/games/hex.py index 1dc7d5f..01ba8e5 100644 --- a/funcs/games/hex.py +++ b/funcs/games/hex.py @@ -15,7 +15,7 @@ AIScoresHex = { "avoid losing": 100 } -boardWidth = 11 +BOARDWIDTH = 11 # Parses command @@ -44,9 +44,9 @@ def parseHex(command, channel, user): # Placing a piece elif commands[0] == "place": try: - return placeHex(channel,int(commands[1]),commands[2]) + return placeHex(channel,commands[1], user) 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: 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 = "" # 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] random.shuffle(players) # random starting player winningPieces = [[""],[""],[""]] # etc. + lastMove = (5,5) - data[channel] = {"board": board,"winner":0, - "players":players, "winningPieces":winningPieces,"turn":0,"difficulty":difficulty} + data[channel] = {"board":board, "winner":0, + "players":players, "winningPieces":winningPieces, "turn":1, "difficulty":difficulty, "lastMove":lastMove} with open("resources/games/hexGames.json", "w") as f: json.dump(data,f,indent=4) @@ -103,62 +104,76 @@ def hexStart(channel, user, opponent): 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 -def placeHex(channel : str,player : int,position : str): +def placeHex(channel : str,position : str, user): with open("resources/games/hexGames.json", "r") as f: data = json.load(f) if channel in data: - board = data[channel]["board"] + if user in data[channel]["players"]: + turn = data[channel]["turn"] + player = data[channel]["players"].index(user)+1 + if player == turn: + board = data[channel]["board"] - logThis("Placing a piece on the board with placeHex()") - # Places on board - board = placeOnHexBoard(board,player,position) - - if isinstance(board, list): - # If the move is valid: - data[channel]["board"] = board - turn = (data[channel]["turn"]+1)%2 - data[channel]["turn"] = turn - - with open("resources/games/hexGames.json", "w") as f: - json.dump(data,f,indent=4) - - """ - # Checking for a win - logThis("Checking for win") - won, winningPieces = isHexWon(data[channel]["board"]) - - if won != 0: - gameWon = True - data[channel]["winner"] = won - data[channel]["winningPieces"] = winningPieces + logThis("Placing a piece on the board with placeHex()") + # Places on board + board = placeOnHexBoard(board,player,position) + if isinstance(board, list): + # If the move is valid: + data[channel]["board"] = board + turn = 1 if turn == 2 else 2 + data[channel]["turn"] = turn + - message = data[channel]["players"][won-1]+" won!" - if data[channel]["players"][won-1] != "Gwendolyn": - winAmount = data[channel]["difficulty"]^2+5 - message += " Adding "+str(winAmount)+" GwendoBucks to their account." - else:""" - gameWon = False - message = getName(data[channel]["players"][player-1])+" placed at "+position+". It's now "+getName(data[channel]["players"][turn])+"'s turn." - - with open("resources/games/hexGames.json", "w") as f: - json.dump(data,f,indent=4) - - # Is it Gwendolyn's turn? - gwendoTurn = False - if data[channel]["players"][turn] == "Gwendolyn": - logThis("It's Gwendolyn's turn") - gwendoTurn = True - - # Update the board - hexDraw.drawHexPlacement(channel,player,position) + """ + with open("resources/games/hexGames.json", "w") as f: + json.dump(data,f,indent=4) - return message, True, True, gameWon, gwendoTurn + + # Checking for a win + logThis("Checking for win") + won, winningPieces = isHexWon(data[channel]["board"]) + + if won != 0: + gameWon = True + data[channel]["winner"] = won + data[channel]["winningPieces"] = winningPieces + + + message = data[channel]["players"][won-1]+" won!" + if data[channel]["players"][won-1] != "Gwendolyn": + winAmount = data[channel]["difficulty"]^2+5 + message += " Adding "+str(winAmount)+" GwendoBucks to their account." + else:""" + + 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: + json.dump(data,f,indent=4) + + # Is it now Gwendolyn's turn? + gwendoTurn = False + if data[channel]["players"][turn-1] == "Gwendolyn": + logThis("It's Gwendolyn's turn") + gwendoTurn = True + + # Update the board + hexDraw.drawHexPlacement(channel,player,position) + + return message, True, True, gameWon, gwendoTurn + else: + # Invalid move. "board" is the error message + message = board + 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: - # Invalid move. "board" is the error message - message = board - return message, True, True, False, False + 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: return "There's no game in this channel", False, False, False, False @@ -168,9 +183,9 @@ def placeOnHexBoard(board,player,position): # Translates the position position = position.lower() try: - column = ord(position[0])-97 # ord() translates from letter to number - row = int(position[1])-1 - if column not in range(boardWidth) or row not in range(boardWidth): + column = ord(position[0]) - 97 # ord() translates from letter to number + row = int(position[1:]) - 1 + if column not in range(BOARDWIDTH) or row not in range(BOARDWIDTH): logThis("Position out of bounds (error code 1533)") return "Error. That position is out of bounds." except: @@ -202,31 +217,50 @@ def hexAI(channel): board = data[channel]["board"] 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] - for column in range(0,boardWidth): + for column in range(0,BOARDWIDTH): testBoard = copy.deepcopy(board) + # Testing a move testBoard = placeOnHexBoard(testBoard,player,column) + # Evaluating that move if testBoard != None: 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])) 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)) highest_score = random.choice(possibleScores) 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 def AICalcHexPoints(board,player): score = 0 - otherPlayer = player%2+1 + #otherPlayer = player%2+1 ## Checks if anyone has won @@ -259,7 +293,7 @@ def minimaxHex(board, depth, player , originalPlayer, alpha, beta, maximizingPla return points if maximizingPlayer: value = -math.inf - for column in range(0,boardWidth): + for column in range(0,BOARDWIDTH): testBoard = copy.deepcopy(board) testBoard = placeOnHexBoard(testBoard,player,column) if testBoard != None: @@ -272,7 +306,7 @@ def minimaxHex(board, depth, player , originalPlayer, alpha, beta, maximizingPla return value else: value = math.inf - for column in range(0,boardWidth): + for column in range(0,BOARDWIDTH): testBoard = copy.deepcopy(board) testBoard = placeOnHexBoard(testBoard,player,column) if testBoard != None: diff --git a/funcs/games/hexDraw.py b/funcs/games/hexDraw.py index 9746d33..432ad14 100644 --- a/funcs/games/hexDraw.py +++ b/funcs/games/hexDraw.py @@ -23,12 +23,14 @@ X_THICKNESS = HEXTHICKNESS * math.cos(math.pi/6) Y_THICKNESS = HEXTHICKNESS * math.sin(math.pi/6) BACKGROUND_COLOR = (230,230,230) 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 COLHEXTHICKNESS = 4 COLX_THICKNESS = COLHEXTHICKNESS * math.cos(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): logThis("Drawing empty Hex board") @@ -106,6 +108,26 @@ def drawBoard(channel): else: 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") @@ -119,7 +141,7 @@ def drawHexPlacement(channel,player,position): # We don't need to error-check, because the position is already checked in placeOnHexBoard() position = position.lower() 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 hexCoords = [ @@ -136,7 +158,7 @@ def drawHexPlacement(channel,player,position): with Image.open(FILEPATH) as im: d = ImageDraw.Draw(im,"RGBA") # Draws the hex piece - d.polygon(hexCoords,fill = PIECECOLOR[player]) + d.polygon(hexCoords,fill = PIECECOLOR[player], outline = BETWEEN_COLOR) # Save im.save(FILEPATH) @@ -144,12 +166,12 @@ def drawHexPlacement(channel,player,position): logThis("Error drawing new hex on board (error code 1541") if __name__ == '__main__': - drawBoard("HexTest2") - drawHexPlacement("HexTest2",1,"f7") - drawHexPlacement("HexTest2",2,"f8") - drawHexPlacement("HexTest2",1,"h6") - drawHexPlacement("HexTest2",2,"e8") - drawHexPlacement("HexTest2",1,"e7") - drawHexPlacement("HexTest2",2,"c9") - drawHexPlacement("HexTest2",1,"g8") - drawHexPlacement("HexTest2",2,"h4") + drawBoard("resources/games/hexBoards/boardTest.png") + drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"f7") + drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"f8") + drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"h6") + drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"e8") + drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"e7") + drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"c9") + drawHexPlacement("resources/games/hexBoards/boardTest.png",1,"g8") + drawHexPlacement("resources/games/hexBoards/boardTest.png",2,"h4")